1//! Baseline interface of Tasks in Zed: all tasks in Zed are intended to use those for implementing their own logic.
2#![deny(missing_docs)]
3
4pub mod oneshot_source;
5pub mod static_source;
6mod vscode_format;
7
8use collections::HashMap;
9use gpui::ModelContext;
10use static_source::RevealStrategy;
11use std::any::Any;
12use std::borrow::Cow;
13use std::path::{Path, PathBuf};
14use std::sync::Arc;
15pub use vscode_format::VsCodeTaskFile;
16
17/// Task identifier, unique within the application.
18/// Based on it, task reruns and terminal tabs are managed.
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub struct TaskId(pub String);
21
22/// Contains all information needed by Zed to spawn a new terminal tab for the given task.
23#[derive(Debug, Clone)]
24pub struct SpawnInTerminal {
25 /// Id of the task to use when determining task tab affinity.
26 pub id: TaskId,
27 /// Human readable name of the terminal tab.
28 pub label: String,
29 /// Executable command to spawn.
30 pub command: String,
31 /// Arguments to the command.
32 pub args: Vec<String>,
33 /// Current working directory to spawn the command into.
34 pub cwd: Option<PathBuf>,
35 /// Env overrides for the command, will be appended to the terminal's environment from the settings.
36 pub env: HashMap<String, String>,
37 /// Whether to use a new terminal tab or reuse the existing one to spawn the process.
38 pub use_new_terminal: bool,
39 /// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish.
40 pub allow_concurrent_runs: bool,
41 /// What to do with the terminal pane and tab, after the command was started.
42 pub reveal: RevealStrategy,
43}
44
45/// Variables, available for use in [`TaskContext`] when a Zed's task gets turned into real command.
46#[derive(Debug, Clone, PartialEq, Eq, Hash)]
47pub enum VariableName {
48 /// An absolute path of the currently opened file.
49 File,
50 /// An absolute path of the currently opened worktree, that contains the file.
51 WorktreeRoot,
52 /// A symbol text, that contains latest cursor/selection position.
53 Symbol,
54 /// A row with the latest cursor/selection position.
55 Row,
56 /// A column with the latest cursor/selection position.
57 Column,
58 /// Text from the latest selection.
59 SelectedText,
60 /// Custom variable, provided by the plugin or other external source.
61 /// Will be printed with `ZED_` prefix to avoid potential conflicts with other variables.
62 Custom(Cow<'static, str>),
63}
64
65impl VariableName {
66 /// Generates a `$VARIABLE`-like string value to be used in templates.
67 /// Custom variables are wrapped in `${}` to avoid substitution issues with whitespaces.
68 pub fn template_value(&self) -> String {
69 if matches!(self, Self::Custom(_)) {
70 format!("${{{self}}}")
71 } else {
72 format!("${self}")
73 }
74 }
75}
76
77impl std::fmt::Display for VariableName {
78 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
79 match self {
80 Self::File => write!(f, "ZED_FILE"),
81 Self::WorktreeRoot => write!(f, "ZED_WORKTREE_ROOT"),
82 Self::Symbol => write!(f, "ZED_SYMBOL"),
83 Self::Row => write!(f, "ZED_ROW"),
84 Self::Column => write!(f, "ZED_COLUMN"),
85 Self::SelectedText => write!(f, "ZED_SELECTED_TEXT"),
86 Self::Custom(s) => write!(f, "ZED_{s}"),
87 }
88 }
89}
90
91/// Container for predefined environment variables that describe state of Zed at the time the task was spawned.
92#[derive(Clone, Debug, Default, PartialEq, Eq)]
93pub struct TaskVariables(HashMap<VariableName, String>);
94
95impl TaskVariables {
96 /// Converts the container into a map of environment variables and their values.
97 fn into_env_variables(self) -> HashMap<String, String> {
98 self.0
99 .into_iter()
100 .map(|(name, value)| (name.to_string(), value))
101 .collect()
102 }
103
104 /// Inserts another variable into the container, overwriting the existing one if it already exists — in this case, the old value is returned.
105 pub fn insert(&mut self, variable: VariableName, value: String) -> Option<String> {
106 self.0.insert(variable, value)
107 }
108
109 /// Extends the container with another one, overwriting the existing variables on collision.
110 pub fn extend(&mut self, other: Self) {
111 self.0.extend(other.0);
112 }
113}
114
115impl FromIterator<(VariableName, String)> for TaskVariables {
116 fn from_iter<T: IntoIterator<Item = (VariableName, String)>>(iter: T) -> Self {
117 Self(HashMap::from_iter(iter))
118 }
119}
120
121/// Keeps track of the file associated with a task and context of tasks execution (i.e. current file or current function)
122#[derive(Clone, Debug, Default, PartialEq, Eq)]
123pub struct TaskContext {
124 /// A path to a directory in which the task should be executed.
125 pub cwd: Option<PathBuf>,
126 /// Additional environment variables associated with a given task.
127 pub task_variables: TaskVariables,
128}
129
130/// Represents a short lived recipe of a task, whose main purpose
131/// is to get spawned.
132pub trait Task {
133 /// Unique identifier of the task to spawn.
134 fn id(&self) -> &TaskId;
135 /// Human readable name of the task to display in the UI.
136 fn name(&self) -> &str;
137 /// Task's current working directory. If `None`, current project's root will be used.
138 fn cwd(&self) -> Option<&str>;
139 /// Sets up everything needed to spawn the task in the given directory (`cwd`).
140 /// If a task is intended to be spawned in the terminal, it should return the corresponding struct filled with the data necessary.
141 fn prepare_exec(&self, cx: TaskContext) -> Option<SpawnInTerminal>;
142}
143
144/// [`Source`] produces tasks that can be scheduled.
145///
146/// Implementations of this trait could be e.g. [`StaticSource`] that parses tasks from a .json files and provides process templates to be spawned;
147/// another one could be a language server providing lenses with tests or build server listing all targets for a given project.
148pub trait TaskSource: Any {
149 /// A way to erase the type of the source, processing and storing them generically.
150 fn as_any(&mut self) -> &mut dyn Any;
151 /// Collects all tasks available for scheduling, for the path given.
152 fn tasks_for_path(
153 &mut self,
154 path: Option<&Path>,
155 cx: &mut ModelContext<Box<dyn TaskSource>>,
156 ) -> Vec<Arc<dyn Task>>;
157}