Stricten Zed Task variable API (#10163)
Introduce `VariableName` enum to simplify Zed task templating management: now all the variables can be looked up statically and can be checked/modified in a centralized way: e.g. `ZED_` prefix is now added for all such custom vars. Release Notes: - N/A
This commit is contained in:
parent
ee1b1779f1
commit
1085642c88
10 changed files with 188 additions and 91 deletions
|
@ -3,13 +3,17 @@ use crate::{LanguageRegistry, Location};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gpui::{AppContext, Context, Model};
|
use gpui::{AppContext, Context, Model};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use task::{static_source::tasks_for, static_source::TaskDefinitions, TaskSource, TaskVariables};
|
use task::{
|
||||||
|
static_source::{tasks_for, TaskDefinitions},
|
||||||
|
TaskSource, TaskVariables, VariableName,
|
||||||
|
};
|
||||||
|
|
||||||
/// Language Contexts are used by Zed tasks to extract information about source file.
|
/// Language Contexts are used by Zed tasks to extract information about source file.
|
||||||
pub trait ContextProvider: Send + Sync {
|
pub trait ContextProvider: Send + Sync {
|
||||||
fn build_context(&self, _: Location, _: &mut AppContext) -> Result<TaskVariables> {
|
fn build_context(&self, _: Location, _: &mut AppContext) -> Result<TaskVariables> {
|
||||||
Ok(TaskVariables::default())
|
Ok(TaskVariables::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn associated_tasks(&self) -> Option<TaskDefinitions> {
|
fn associated_tasks(&self) -> Option<TaskDefinitions> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -29,18 +33,16 @@ impl ContextProvider for SymbolContextProvider {
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.snapshot()
|
.snapshot()
|
||||||
.symbols_containing(location.range.start, None);
|
.symbols_containing(location.range.start, None);
|
||||||
let symbol = symbols.and_then(|symbols| {
|
let symbol = symbols.unwrap_or_default().last().map(|symbol| {
|
||||||
symbols.last().map(|symbol| {
|
|
||||||
let range = symbol
|
let range = symbol
|
||||||
.name_ranges
|
.name_ranges
|
||||||
.last()
|
.last()
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(0..symbol.text.len());
|
.unwrap_or(0..symbol.text.len());
|
||||||
symbol.text[range].to_string()
|
symbol.text[range].to_string()
|
||||||
})
|
|
||||||
});
|
});
|
||||||
Ok(TaskVariables::from_iter(
|
Ok(TaskVariables::from_iter(
|
||||||
symbol.map(|symbol| ("ZED_SYMBOL".to_string(), symbol)),
|
Some(VariableName::Symbol).zip(symbol),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,10 @@ use std::{
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use task::static_source::{Definition, TaskDefinitions};
|
use task::{
|
||||||
|
static_source::{Definition, TaskDefinitions},
|
||||||
|
VariableName,
|
||||||
|
};
|
||||||
use util::{
|
use util::{
|
||||||
fs::remove_matching,
|
fs::remove_matching,
|
||||||
github::{latest_github_release, GitHubLspBinaryVersion},
|
github::{latest_github_release, GitHubLspBinaryVersion},
|
||||||
|
@ -557,25 +560,32 @@ pub(super) fn elixir_task_context() -> ContextProviderWithTasks {
|
||||||
label: "Elixir: test suite".to_owned(),
|
label: "Elixir: test suite".to_owned(),
|
||||||
command: "mix".to_owned(),
|
command: "mix".to_owned(),
|
||||||
args: vec!["test".to_owned()],
|
args: vec!["test".to_owned()],
|
||||||
..Default::default()
|
..Definition::default()
|
||||||
},
|
},
|
||||||
Definition {
|
Definition {
|
||||||
label: "Elixir: failed tests suite".to_owned(),
|
label: "Elixir: failed tests suite".to_owned(),
|
||||||
command: "mix".to_owned(),
|
command: "mix".to_owned(),
|
||||||
args: vec!["test".to_owned(), "--failed".to_owned()],
|
args: vec!["test".to_owned(), "--failed".to_owned()],
|
||||||
..Default::default()
|
..Definition::default()
|
||||||
},
|
},
|
||||||
Definition {
|
Definition {
|
||||||
label: "Elixir: test file".to_owned(),
|
label: "Elixir: test file".to_owned(),
|
||||||
command: "mix".to_owned(),
|
command: "mix".to_owned(),
|
||||||
args: vec!["test".to_owned(), "$ZED_FILE".to_owned()],
|
args: vec!["test".to_owned(), VariableName::Symbol.template_value()],
|
||||||
..Default::default()
|
..Definition::default()
|
||||||
},
|
},
|
||||||
Definition {
|
Definition {
|
||||||
label: "Elixir: test at current line".to_owned(),
|
label: "Elixir: test at current line".to_owned(),
|
||||||
command: "mix".to_owned(),
|
command: "mix".to_owned(),
|
||||||
args: vec!["test".to_owned(), "$ZED_FILE:$ZED_ROW".to_owned()],
|
args: vec![
|
||||||
..Default::default()
|
"test".to_owned(),
|
||||||
|
format!(
|
||||||
|
"{}:{}",
|
||||||
|
VariableName::File.template_value(),
|
||||||
|
VariableName::Row.template_value()
|
||||||
|
),
|
||||||
|
],
|
||||||
|
..Definition::default()
|
||||||
},
|
},
|
||||||
Definition {
|
Definition {
|
||||||
label: "Elixir: break line".to_owned(),
|
label: "Elixir: break line".to_owned(),
|
||||||
|
@ -585,9 +595,13 @@ pub(super) fn elixir_task_context() -> ContextProviderWithTasks {
|
||||||
"mix".to_owned(),
|
"mix".to_owned(),
|
||||||
"test".to_owned(),
|
"test".to_owned(),
|
||||||
"-b".to_owned(),
|
"-b".to_owned(),
|
||||||
"$ZED_FILE:$ZED_ROW".to_owned(),
|
format!(
|
||||||
|
"{}:{}",
|
||||||
|
VariableName::File.template_value(),
|
||||||
|
VariableName::Row.template_value()
|
||||||
|
),
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Definition::default()
|
||||||
},
|
},
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use smol::fs::{self, File};
|
||||||
use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, sync::Arc};
|
use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, sync::Arc};
|
||||||
use task::{
|
use task::{
|
||||||
static_source::{Definition, TaskDefinitions},
|
static_source::{Definition, TaskDefinitions},
|
||||||
TaskVariables,
|
TaskVariables, VariableName,
|
||||||
};
|
};
|
||||||
use util::{
|
use util::{
|
||||||
fs::remove_matching,
|
fs::remove_matching,
|
||||||
|
@ -322,6 +322,9 @@ impl LspAdapter for RustLspAdapter {
|
||||||
|
|
||||||
pub(crate) struct RustContextProvider;
|
pub(crate) struct RustContextProvider;
|
||||||
|
|
||||||
|
const RUST_PACKAGE_TASK_VARIABLE: VariableName =
|
||||||
|
VariableName::Custom(Cow::Borrowed("RUST_PACKAGE"));
|
||||||
|
|
||||||
impl ContextProvider for RustContextProvider {
|
impl ContextProvider for RustContextProvider {
|
||||||
fn build_context(
|
fn build_context(
|
||||||
&self,
|
&self,
|
||||||
|
@ -347,19 +350,24 @@ impl ContextProvider for RustContextProvider {
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
if let Some(package_name) = package_name {
|
if let Some(package_name) = package_name {
|
||||||
context.0.insert("ZED_PACKAGE".to_owned(), package_name);
|
context.insert(RUST_PACKAGE_TASK_VARIABLE.clone(), package_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(context)
|
Ok(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn associated_tasks(&self) -> Option<TaskDefinitions> {
|
fn associated_tasks(&self) -> Option<TaskDefinitions> {
|
||||||
Some(TaskDefinitions(vec![
|
Some(TaskDefinitions(vec![
|
||||||
Definition {
|
Definition {
|
||||||
label: "Rust: Test current crate".to_owned(),
|
label: "Rust: Test current crate".to_owned(),
|
||||||
command: "cargo".into(),
|
command: "cargo".into(),
|
||||||
args: vec!["test".into(), "-p".into(), "$ZED_PACKAGE".into()],
|
args: vec![
|
||||||
..Default::default()
|
"test".into(),
|
||||||
|
"-p".into(),
|
||||||
|
RUST_PACKAGE_TASK_VARIABLE.template_value(),
|
||||||
|
],
|
||||||
|
..Definition::default()
|
||||||
},
|
},
|
||||||
Definition {
|
Definition {
|
||||||
label: "Rust: Test current function".to_owned(),
|
label: "Rust: Test current function".to_owned(),
|
||||||
|
@ -367,29 +375,33 @@ impl ContextProvider for RustContextProvider {
|
||||||
args: vec![
|
args: vec![
|
||||||
"test".into(),
|
"test".into(),
|
||||||
"-p".into(),
|
"-p".into(),
|
||||||
"$ZED_PACKAGE".into(),
|
RUST_PACKAGE_TASK_VARIABLE.template_value(),
|
||||||
"--".into(),
|
"--".into(),
|
||||||
"$ZED_SYMBOL".into(),
|
VariableName::Symbol.template_value(),
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Definition::default()
|
||||||
},
|
},
|
||||||
Definition {
|
Definition {
|
||||||
label: "Rust: cargo run".into(),
|
label: "Rust: cargo run".into(),
|
||||||
command: "cargo".into(),
|
command: "cargo".into(),
|
||||||
args: vec!["run".into()],
|
args: vec!["run".into()],
|
||||||
..Default::default()
|
..Definition::default()
|
||||||
},
|
},
|
||||||
Definition {
|
Definition {
|
||||||
label: "Rust: cargo check current crate".into(),
|
label: "Rust: cargo check current crate".into(),
|
||||||
command: "cargo".into(),
|
command: "cargo".into(),
|
||||||
args: vec!["check".into(), "-p".into(), "$ZED_PACKAGE".into()],
|
args: vec![
|
||||||
..Default::default()
|
"check".into(),
|
||||||
|
"-p".into(),
|
||||||
|
RUST_PACKAGE_TASK_VARIABLE.template_value(),
|
||||||
|
],
|
||||||
|
..Definition::default()
|
||||||
},
|
},
|
||||||
Definition {
|
Definition {
|
||||||
label: "Rust: cargo check workspace".into(),
|
label: "Rust: cargo check workspace".into(),
|
||||||
command: "cargo".into(),
|
command: "cargo".into(),
|
||||||
args: vec!["check".into(), "--workspace".into()],
|
args: vec!["check".into(), "--workspace".into()],
|
||||||
..Default::default()
|
..Definition::default()
|
||||||
},
|
},
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,7 +258,7 @@ pub mod test_inventory {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(&self, _cwd: TaskContext) -> Option<task::SpawnInTerminal> {
|
fn prepare_exec(&self, _cwd: TaskContext) -> Option<task::SpawnInTerminal> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use collections::HashMap;
|
||||||
use gpui::ModelContext;
|
use gpui::ModelContext;
|
||||||
use static_source::RevealStrategy;
|
use static_source::RevealStrategy;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
pub use vscode_format::VsCodeTaskFile;
|
pub use vscode_format::VsCodeTaskFile;
|
||||||
|
@ -41,15 +42,78 @@ pub struct SpawnInTerminal {
|
||||||
pub reveal: RevealStrategy,
|
pub reveal: RevealStrategy,
|
||||||
}
|
}
|
||||||
|
|
||||||
type VariableName = String;
|
/// Variables, available for use in [`TaskContext`] when a Zed's task gets turned into real command.
|
||||||
type VariableValue = String;
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum VariableName {
|
||||||
|
/// An absolute path of the currently opened file.
|
||||||
|
File,
|
||||||
|
/// An absolute path of the currently opened worktree, that contains the file.
|
||||||
|
WorktreeRoot,
|
||||||
|
/// A symbol text, that contains latest cursor/selection position.
|
||||||
|
Symbol,
|
||||||
|
/// A row with the latest cursor/selection position.
|
||||||
|
Row,
|
||||||
|
/// A column with the latest cursor/selection position.
|
||||||
|
Column,
|
||||||
|
/// Text from the latest selection.
|
||||||
|
SelectedText,
|
||||||
|
/// Custom variable, provided by the plugin or other external source.
|
||||||
|
/// Will be printed with `ZED_` prefix to avoid potential conflicts with other variables.
|
||||||
|
Custom(Cow<'static, str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VariableName {
|
||||||
|
/// Generates a `$VARIABLE`-like string value to be used in templates.
|
||||||
|
/// Custom variables are wrapped in `${}` to avoid substitution issues with whitespaces.
|
||||||
|
pub fn template_value(&self) -> String {
|
||||||
|
if matches!(self, Self::Custom(_)) {
|
||||||
|
format!("${{{self}}}")
|
||||||
|
} else {
|
||||||
|
format!("${self}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for VariableName {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::File => write!(f, "ZED_FILE"),
|
||||||
|
Self::WorktreeRoot => write!(f, "ZED_WORKTREE_ROOT"),
|
||||||
|
Self::Symbol => write!(f, "ZED_SYMBOL"),
|
||||||
|
Self::Row => write!(f, "ZED_ROW"),
|
||||||
|
Self::Column => write!(f, "ZED_COLUMN"),
|
||||||
|
Self::SelectedText => write!(f, "ZED_SELECTED_TEXT"),
|
||||||
|
Self::Custom(s) => write!(f, "ZED_{s}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Container for predefined environment variables that describe state of Zed at the time the task was spawned.
|
/// Container for predefined environment variables that describe state of Zed at the time the task was spawned.
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct TaskVariables(pub HashMap<VariableName, VariableValue>);
|
pub struct TaskVariables(HashMap<VariableName, String>);
|
||||||
|
|
||||||
impl FromIterator<(String, String)> for TaskVariables {
|
impl TaskVariables {
|
||||||
fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
|
/// Converts the container into a map of environment variables and their values.
|
||||||
|
fn into_env_variables(self) -> HashMap<String, String> {
|
||||||
|
self.0
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, value)| (name.to_string(), value))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts another variable into the container, overwriting the existing one if it already exists — in this case, the old value is returned.
|
||||||
|
pub fn insert(&mut self, variable: VariableName, value: String) -> Option<String> {
|
||||||
|
self.0.insert(variable, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extends the container with another one, overwriting the existing variables on collision.
|
||||||
|
pub fn extend(&mut self, other: Self) {
|
||||||
|
self.0.extend(other.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromIterator<(VariableName, String)> for TaskVariables {
|
||||||
|
fn from_iter<T: IntoIterator<Item = (VariableName, String)>>(iter: T) -> Self {
|
||||||
Self(HashMap::from_iter(iter))
|
Self(HashMap::from_iter(iter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +138,7 @@ pub trait Task {
|
||||||
fn cwd(&self) -> Option<&str>;
|
fn cwd(&self) -> Option<&str>;
|
||||||
/// Sets up everything needed to spawn the task in the given directory (`cwd`).
|
/// Sets up everything needed to spawn the task in the given directory (`cwd`).
|
||||||
/// If a task is intended to be spawned in the terminal, it should return the corresponding struct filled with the data necessary.
|
/// If a task is intended to be spawned in the terminal, it should return the corresponding struct filled with the data necessary.
|
||||||
fn exec(&self, cx: TaskContext) -> Option<SpawnInTerminal>;
|
fn prepare_exec(&self, cx: TaskContext) -> Option<SpawnInTerminal>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`Source`] produces tasks that can be scheduled.
|
/// [`Source`] produces tasks that can be scheduled.
|
||||||
|
|
|
@ -36,7 +36,7 @@ impl Task for OneshotTask {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(&self, cx: TaskContext) -> Option<SpawnInTerminal> {
|
fn prepare_exec(&self, cx: TaskContext) -> Option<SpawnInTerminal> {
|
||||||
if self.id().0.is_empty() {
|
if self.id().0.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ impl Task for OneshotTask {
|
||||||
command: self.id().0.clone(),
|
command: self.id().0.clone(),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
cwd,
|
cwd,
|
||||||
env: task_variables.0,
|
env: task_variables.into_env_variables(),
|
||||||
use_new_terminal: Default::default(),
|
use_new_terminal: Default::default(),
|
||||||
allow_concurrent_runs: Default::default(),
|
allow_concurrent_runs: Default::default(),
|
||||||
reveal: RevealStrategy::default(),
|
reveal: RevealStrategy::default(),
|
||||||
|
|
|
@ -42,23 +42,24 @@ pub fn tasks_for(tasks: TaskDefinitions, id_base: &str) -> Vec<Arc<dyn Task>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Task for StaticTask {
|
impl Task for StaticTask {
|
||||||
fn exec(&self, cx: TaskContext) -> Option<SpawnInTerminal> {
|
fn prepare_exec(&self, cx: TaskContext) -> Option<SpawnInTerminal> {
|
||||||
let TaskContext {
|
let TaskContext {
|
||||||
cwd,
|
cwd,
|
||||||
task_variables,
|
task_variables,
|
||||||
} = cx;
|
} = cx;
|
||||||
|
let task_variables = task_variables.into_env_variables();
|
||||||
let cwd = self
|
let cwd = self
|
||||||
.definition
|
.definition
|
||||||
.cwd
|
.cwd
|
||||||
.clone()
|
.clone()
|
||||||
.and_then(|path| {
|
.and_then(|path| {
|
||||||
subst::substitute(&path, &task_variables.0)
|
subst::substitute(&path, &task_variables)
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
.or(cwd);
|
.or(cwd);
|
||||||
let mut definition_env = self.definition.env.clone();
|
let mut definition_env = self.definition.env.clone();
|
||||||
definition_env.extend(task_variables.0);
|
definition_env.extend(task_variables);
|
||||||
Some(SpawnInTerminal {
|
Some(SpawnInTerminal {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
cwd,
|
cwd,
|
||||||
|
|
|
@ -3,7 +3,10 @@ use collections::HashMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use crate::static_source::{Definition, TaskDefinitions};
|
use crate::{
|
||||||
|
static_source::{Definition, TaskDefinitions},
|
||||||
|
VariableName,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
@ -129,10 +132,16 @@ impl TryFrom<VsCodeTaskFile> for TaskDefinitions {
|
||||||
|
|
||||||
fn try_from(value: VsCodeTaskFile) -> Result<Self, Self::Error> {
|
fn try_from(value: VsCodeTaskFile) -> Result<Self, Self::Error> {
|
||||||
let replacer = EnvVariableReplacer::new(HashMap::from_iter([
|
let replacer = EnvVariableReplacer::new(HashMap::from_iter([
|
||||||
("workspaceFolder".to_owned(), "ZED_WORKTREE_ROOT".to_owned()),
|
(
|
||||||
("file".to_owned(), "ZED_FILE".to_owned()),
|
"workspaceFolder".to_owned(),
|
||||||
("lineNumber".to_owned(), "ZED_ROW".to_owned()),
|
VariableName::WorktreeRoot.to_string(),
|
||||||
("selectedText".to_owned(), "ZED_SELECTED_TEXT".to_owned()),
|
),
|
||||||
|
("file".to_owned(), VariableName::File.to_string()),
|
||||||
|
("lineNumber".to_owned(), VariableName::Row.to_string()),
|
||||||
|
(
|
||||||
|
"selectedText".to_owned(),
|
||||||
|
VariableName::SelectedText.to_string(),
|
||||||
|
),
|
||||||
]));
|
]));
|
||||||
let definitions = value
|
let definitions = value
|
||||||
.tasks
|
.tasks
|
||||||
|
|
|
@ -5,7 +5,7 @@ use gpui::{AppContext, ViewContext, WindowContext};
|
||||||
use language::Point;
|
use language::Point;
|
||||||
use modal::{Spawn, TasksModal};
|
use modal::{Spawn, TasksModal};
|
||||||
use project::{Location, WorktreeId};
|
use project::{Location, WorktreeId};
|
||||||
use task::{Task, TaskContext, TaskVariables};
|
use task::{Task, TaskContext, TaskVariables, VariableName};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ pub fn init(cx: &mut AppContext) {
|
||||||
} else {
|
} else {
|
||||||
old_context
|
old_context
|
||||||
};
|
};
|
||||||
|
|
||||||
schedule_task(workspace, task.as_ref(), task_context, false, cx)
|
schedule_task(workspace, task.as_ref(), task_context, false, cx)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -40,19 +39,19 @@ pub fn init(cx: &mut AppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_task_or_modal(workspace: &mut Workspace, action: &Spawn, cx: &mut ViewContext<Workspace>) {
|
fn spawn_task_or_modal(workspace: &mut Workspace, action: &Spawn, cx: &mut ViewContext<Workspace>) {
|
||||||
|
match &action.task_name {
|
||||||
|
Some(name) => spawn_task_with_name(name.clone(), cx),
|
||||||
|
None => {
|
||||||
let inventory = workspace.project().read(cx).task_inventory().clone();
|
let inventory = workspace.project().read(cx).task_inventory().clone();
|
||||||
let workspace_handle = workspace.weak_handle();
|
let workspace_handle = workspace.weak_handle();
|
||||||
let cwd = task_cwd(workspace, cx).log_err().flatten();
|
let cwd = task_cwd(workspace, cx).log_err().flatten();
|
||||||
let task_context = task_context(workspace, cwd, cx);
|
let task_context = task_context(workspace, cwd, cx);
|
||||||
if let Some(name) = action.task_name.clone() {
|
|
||||||
// Do not actually show the modal.
|
|
||||||
spawn_task_with_name(name.clone(), cx);
|
|
||||||
} else {
|
|
||||||
workspace.toggle_modal(cx, |cx| {
|
workspace.toggle_modal(cx, |cx| {
|
||||||
TasksModal::new(inventory, task_context, workspace_handle, cx)
|
TasksModal::new(inventory, task_context, workspace_handle, cx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn spawn_task_with_name(name: String, cx: &mut ViewContext<Workspace>) {
|
fn spawn_task_with_name(name: String, cx: &mut ViewContext<Workspace>) {
|
||||||
cx.spawn(|workspace, mut cx| async move {
|
cx.spawn(|workspace, mut cx| async move {
|
||||||
|
@ -157,20 +156,18 @@ fn task_context(
|
||||||
let selected_text = buffer.read(cx).chars_for_range(selection_range).collect();
|
let selected_text = buffer.read(cx).chars_for_range(selection_range).collect();
|
||||||
|
|
||||||
let mut task_variables = TaskVariables::from_iter([
|
let mut task_variables = TaskVariables::from_iter([
|
||||||
("ZED_ROW".into(), row.to_string()),
|
(VariableName::Row, row.to_string()),
|
||||||
("ZED_COLUMN".into(), column.to_string()),
|
(VariableName::Column, column.to_string()),
|
||||||
("ZED_SELECTED_TEXT".into(), selected_text),
|
(VariableName::SelectedText, selected_text),
|
||||||
]);
|
]);
|
||||||
if let Some(path) = current_file {
|
if let Some(path) = current_file {
|
||||||
task_variables.0.insert("ZED_FILE".into(), path);
|
task_variables.insert(VariableName::File, path);
|
||||||
}
|
}
|
||||||
if let Some(worktree_path) = worktree_path {
|
if let Some(worktree_path) = worktree_path {
|
||||||
task_variables
|
task_variables.insert(VariableName::WorktreeRoot, worktree_path);
|
||||||
.0
|
|
||||||
.insert("ZED_WORKTREE_ROOT".into(), worktree_path);
|
|
||||||
}
|
}
|
||||||
if let Some(language_context) = context {
|
if let Some(language_context) = context {
|
||||||
task_variables.0.extend(language_context.0);
|
task_variables.extend(language_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(TaskContext {
|
Some(TaskContext {
|
||||||
|
@ -198,7 +195,7 @@ fn schedule_task(
|
||||||
omit_history: bool,
|
omit_history: bool,
|
||||||
cx: &mut ViewContext<'_, Workspace>,
|
cx: &mut ViewContext<'_, Workspace>,
|
||||||
) {
|
) {
|
||||||
let spawn_in_terminal = task.exec(task_cx.clone());
|
let spawn_in_terminal = task.prepare_exec(task_cx.clone());
|
||||||
if let Some(spawn_in_terminal) = spawn_in_terminal {
|
if let Some(spawn_in_terminal) = spawn_in_terminal {
|
||||||
if !omit_history {
|
if !omit_history {
|
||||||
workspace.project().update(cx, |project, cx| {
|
workspace.project().update(cx, |project, cx| {
|
||||||
|
@ -255,7 +252,7 @@ mod tests {
|
||||||
use language::{Language, LanguageConfig, SymbolContextProvider};
|
use language::{Language, LanguageConfig, SymbolContextProvider};
|
||||||
use project::{FakeFs, Project, TaskSourceKind};
|
use project::{FakeFs, Project, TaskSourceKind};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use task::{oneshot_source::OneshotSource, TaskContext, TaskVariables};
|
use task::{oneshot_source::OneshotSource, TaskContext, TaskVariables, VariableName};
|
||||||
use ui::VisualContext;
|
use ui::VisualContext;
|
||||||
use workspace::{AppState, Workspace};
|
use workspace::{AppState, Workspace};
|
||||||
|
|
||||||
|
@ -363,11 +360,11 @@ mod tests {
|
||||||
TaskContext {
|
TaskContext {
|
||||||
cwd: Some("/dir".into()),
|
cwd: Some("/dir".into()),
|
||||||
task_variables: TaskVariables::from_iter([
|
task_variables: TaskVariables::from_iter([
|
||||||
("ZED_FILE".into(), "/dir/rust/b.rs".into()),
|
(VariableName::File, "/dir/rust/b.rs".into()),
|
||||||
("ZED_WORKTREE_ROOT".into(), "/dir".into()),
|
(VariableName::WorktreeRoot, "/dir".into()),
|
||||||
("ZED_ROW".into(), "1".into()),
|
(VariableName::Row, "1".into()),
|
||||||
("ZED_COLUMN".into(), "1".into()),
|
(VariableName::Column, "1".into()),
|
||||||
("ZED_SELECTED_TEXT".into(), "".into())
|
(VariableName::SelectedText, "".into())
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -380,12 +377,12 @@ mod tests {
|
||||||
TaskContext {
|
TaskContext {
|
||||||
cwd: Some("/dir".into()),
|
cwd: Some("/dir".into()),
|
||||||
task_variables: TaskVariables::from_iter([
|
task_variables: TaskVariables::from_iter([
|
||||||
("ZED_FILE".into(), "/dir/rust/b.rs".into()),
|
(VariableName::File, "/dir/rust/b.rs".into()),
|
||||||
("ZED_WORKTREE_ROOT".into(), "/dir".into()),
|
(VariableName::WorktreeRoot, "/dir".into()),
|
||||||
("ZED_SYMBOL".into(), "this_is_a_rust_file".into()),
|
(VariableName::Row, "1".into()),
|
||||||
("ZED_ROW".into(), "1".into()),
|
(VariableName::Column, "15".into()),
|
||||||
("ZED_COLUMN".into(), "15".into()),
|
(VariableName::SelectedText, "is_i".into()),
|
||||||
("ZED_SELECTED_TEXT".into(), "is_i".into()),
|
(VariableName::Symbol, "this_is_a_rust_file".into()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -397,12 +394,12 @@ mod tests {
|
||||||
TaskContext {
|
TaskContext {
|
||||||
cwd: Some("/dir".into()),
|
cwd: Some("/dir".into()),
|
||||||
task_variables: TaskVariables::from_iter([
|
task_variables: TaskVariables::from_iter([
|
||||||
("ZED_FILE".into(), "/dir/a.ts".into()),
|
(VariableName::File, "/dir/a.ts".into()),
|
||||||
("ZED_WORKTREE_ROOT".into(), "/dir".into()),
|
(VariableName::WorktreeRoot, "/dir".into()),
|
||||||
("ZED_SYMBOL".into(), "this_is_a_test".into()),
|
(VariableName::Row, "1".into()),
|
||||||
("ZED_ROW".into(), "1".into()),
|
(VariableName::Column, "1".into()),
|
||||||
("ZED_COLUMN".into(), "1".into()),
|
(VariableName::SelectedText, "".into()),
|
||||||
("ZED_SELECTED_TEXT".into(), "".into()),
|
(VariableName::Symbol, "this_is_a_test".into()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -366,9 +366,7 @@ impl PickerDelegate for TasksModalDelegate {
|
||||||
let task_index = self.matches.get(self.selected_index())?.candidate_id;
|
let task_index = self.matches.get(self.selected_index())?.candidate_id;
|
||||||
let tasks = self.candidates.as_ref()?;
|
let tasks = self.candidates.as_ref()?;
|
||||||
let (_, task) = tasks.get(task_index)?;
|
let (_, task) = tasks.get(task_index)?;
|
||||||
// .exec doesn't actually spawn anything; it merely prepares a spawning command,
|
let mut spawn_prompt = task.prepare_exec(self.task_context.clone())?;
|
||||||
// which we can use for substitution.
|
|
||||||
let mut spawn_prompt = task.exec(self.task_context.clone())?;
|
|
||||||
if !spawn_prompt.args.is_empty() {
|
if !spawn_prompt.args.is_empty() {
|
||||||
spawn_prompt.command.push(' ');
|
spawn_prompt.command.push(' ');
|
||||||
spawn_prompt
|
spawn_prompt
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue