mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-25 08:55:10 +03:00
tasks: Add experimental support for user-defined task variables (#13699)
Context: @bennetbo spotted a regression in handling of `cargo run` task in zed repo following a merge of #13658. We've started invoking `cargo run` from the folder of an active file whereas previously we did it from the workspace root. We brainstormed few solutions that involved adding a separate task that gets invoked at a workspace level, but I realized that a cleaner solution may be to finally add user-configured task variables. This way, we can choose which crate to run by default at a workspace level. This has been originally brought up in the context of javascript tasks in https://github.com/zed-industries/zed/pull/12118#issuecomment-2129232114 Note that this is intended for internal use only for the time being. /cc @RemcoSmitsDev we should be unblocked on having runner-dependant tasks now. Release notes: - N/A
This commit is contained in:
parent
065ab93ca7
commit
bac6e2fee7
@ -19,6 +19,13 @@
|
|||||||
"JavaScript": {
|
"JavaScript": {
|
||||||
"tab_size": 2,
|
"tab_size": 2,
|
||||||
"formatter": "prettier"
|
"formatter": "prettier"
|
||||||
|
},
|
||||||
|
"Rust": {
|
||||||
|
"tasks": {
|
||||||
|
"variables": {
|
||||||
|
"RUST_DEFAULT_PACKAGE_RUN": "zed"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"formatter": "auto",
|
"formatter": "auto",
|
||||||
|
@ -128,7 +128,14 @@
|
|||||||
// The default number of lines to expand excerpts in the multibuffer by.
|
// The default number of lines to expand excerpts in the multibuffer by.
|
||||||
"expand_excerpt_lines": 3,
|
"expand_excerpt_lines": 3,
|
||||||
// Globs to match against file paths to determine if a file is private.
|
// Globs to match against file paths to determine if a file is private.
|
||||||
"private_files": ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"],
|
"private_files": [
|
||||||
|
"**/.env*",
|
||||||
|
"**/*.pem",
|
||||||
|
"**/*.key",
|
||||||
|
"**/*.cert",
|
||||||
|
"**/*.crt",
|
||||||
|
"**/secrets.yml"
|
||||||
|
],
|
||||||
// Whether to use additional LSP queries to format (and amend) the code after
|
// Whether to use additional LSP queries to format (and amend) the code after
|
||||||
// every "trigger" symbol input, defined by LSP server capabilities.
|
// every "trigger" symbol input, defined by LSP server capabilities.
|
||||||
"use_on_type_format": true,
|
"use_on_type_format": true,
|
||||||
@ -666,6 +673,10 @@
|
|||||||
// "max_scroll_history_lines": 10000,
|
// "max_scroll_history_lines": 10000,
|
||||||
},
|
},
|
||||||
"code_actions_on_format": {},
|
"code_actions_on_format": {},
|
||||||
|
/// Settings related to running tasks.
|
||||||
|
"tasks": {
|
||||||
|
"variables": {}
|
||||||
|
},
|
||||||
// An object whose keys are language names, and whose values
|
// An object whose keys are language names, and whose values
|
||||||
// are arrays of filenames or extensions of files that should
|
// are arrays of filenames or extensions of files that should
|
||||||
// use those languages.
|
// use those languages.
|
||||||
|
@ -8469,13 +8469,14 @@ impl Editor {
|
|||||||
runnable: &mut Runnable,
|
runnable: &mut Runnable,
|
||||||
cx: &WindowContext<'_>,
|
cx: &WindowContext<'_>,
|
||||||
) -> Vec<(TaskSourceKind, TaskTemplate)> {
|
) -> Vec<(TaskSourceKind, TaskTemplate)> {
|
||||||
let (inventory, worktree_id) = project.read_with(cx, |project, cx| {
|
let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
|
||||||
let worktree_id = project
|
let (worktree_id, file) = project
|
||||||
.buffer_for_id(runnable.buffer)
|
.buffer_for_id(runnable.buffer)
|
||||||
.and_then(|buffer| buffer.read(cx).file())
|
.and_then(|buffer| buffer.read(cx).file())
|
||||||
.map(|file| WorktreeId::from_usize(file.worktree_id()));
|
.map(|file| (WorktreeId::from_usize(file.worktree_id()), file.clone()))
|
||||||
|
.unzip();
|
||||||
|
|
||||||
(project.task_inventory().clone(), worktree_id)
|
(project.task_inventory().clone(), worktree_id, file)
|
||||||
});
|
});
|
||||||
|
|
||||||
let inventory = inventory.read(cx);
|
let inventory = inventory.read(cx);
|
||||||
@ -8485,7 +8486,12 @@ impl Editor {
|
|||||||
.flat_map(|tag| {
|
.flat_map(|tag| {
|
||||||
let tag = tag.0.clone();
|
let tag = tag.0.clone();
|
||||||
inventory
|
inventory
|
||||||
.list_tasks(Some(runnable.language.clone()), worktree_id)
|
.list_tasks(
|
||||||
|
file.clone(),
|
||||||
|
Some(runnable.language.clone()),
|
||||||
|
worktree_id,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(move |(_, template)| {
|
.filter(move |(_, template)| {
|
||||||
template.tags.iter().any(|source_tag| source_tag == &tag)
|
template.tags.iter().any(|source_tag| source_tag == &tag)
|
||||||
|
@ -120,6 +120,8 @@ pub struct LanguageSettings {
|
|||||||
pub code_actions_on_format: HashMap<String, bool>,
|
pub code_actions_on_format: HashMap<String, bool>,
|
||||||
/// Whether to perform linked edits
|
/// Whether to perform linked edits
|
||||||
pub linked_edits: bool,
|
pub linked_edits: bool,
|
||||||
|
/// Task configuration for this language.
|
||||||
|
pub tasks: LanguageTaskConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageSettings {
|
impl LanguageSettings {
|
||||||
@ -340,6 +342,10 @@ pub struct LanguageSettingsContent {
|
|||||||
///
|
///
|
||||||
/// Default: true
|
/// Default: true
|
||||||
pub linked_edits: Option<bool>,
|
pub linked_edits: Option<bool>,
|
||||||
|
/// Task configuration for this language.
|
||||||
|
///
|
||||||
|
/// Default: {}
|
||||||
|
pub tasks: Option<LanguageTaskConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The contents of the inline completion settings.
|
/// The contents of the inline completion settings.
|
||||||
@ -546,6 +552,13 @@ fn scroll_debounce_ms() -> u64 {
|
|||||||
50
|
50
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The task settings for a particular language.
|
||||||
|
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||||
|
pub struct LanguageTaskConfig {
|
||||||
|
/// Extra task variables to set for a particular language.
|
||||||
|
pub variables: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl InlayHintSettings {
|
impl InlayHintSettings {
|
||||||
/// Returns the kinds of inlay hints that are enabled based on the settings.
|
/// Returns the kinds of inlay hints that are enabled based on the settings.
|
||||||
pub fn enabled_inlay_hint_kinds(&self) -> HashSet<Option<InlayHintKind>> {
|
pub fn enabled_inlay_hint_kinds(&self) -> HashSet<Option<InlayHintKind>> {
|
||||||
@ -823,6 +836,7 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent
|
|||||||
src.code_actions_on_format.clone(),
|
src.code_actions_on_format.clone(),
|
||||||
);
|
);
|
||||||
merge(&mut settings.linked_edits, src.linked_edits);
|
merge(&mut settings.linked_edits, src.linked_edits);
|
||||||
|
merge(&mut settings.tasks, src.tasks.clone());
|
||||||
|
|
||||||
merge(
|
merge(
|
||||||
&mut settings.preferred_line_length,
|
&mut settings.preferred_line_length,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::ops::Range;
|
use std::{ops::Range, sync::Arc};
|
||||||
|
|
||||||
use crate::{Location, Runnable};
|
use crate::{Location, Runnable};
|
||||||
|
|
||||||
@ -31,7 +31,11 @@ pub trait ContextProvider: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Provides all tasks, associated with the current language.
|
/// Provides all tasks, associated with the current language.
|
||||||
fn associated_tasks(&self) -> Option<TaskTemplates> {
|
fn associated_tasks(
|
||||||
|
&self,
|
||||||
|
_: Option<Arc<dyn crate::File>>,
|
||||||
|
_cx: &AppContext,
|
||||||
|
) -> Option<TaskTemplates> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{AsyncAppContext, Task};
|
use gpui::{AppContext, AsyncAppContext, Task};
|
||||||
use http::github::latest_github_release;
|
use http::github::latest_github_release;
|
||||||
pub use language::*;
|
pub use language::*;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
@ -501,7 +501,11 @@ impl ContextProvider for GoContextProvider {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn associated_tasks(&self) -> Option<TaskTemplates> {
|
fn associated_tasks(
|
||||||
|
&self,
|
||||||
|
_: Option<Arc<dyn language::File>>,
|
||||||
|
_: &AppContext,
|
||||||
|
) -> Option<TaskTemplates> {
|
||||||
Some(TaskTemplates(vec![
|
Some(TaskTemplates(vec![
|
||||||
TaskTemplate {
|
TaskTemplate {
|
||||||
label: format!(
|
label: format!(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use gpui::AppContext;
|
||||||
use language::{ContextProvider, LanguageServerName, LspAdapter, LspAdapterDelegate};
|
use language::{ContextProvider, LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||||
use lsp::LanguageServerBinary;
|
use lsp::LanguageServerBinary;
|
||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
@ -220,7 +221,11 @@ impl ContextProvider for PythonContextProvider {
|
|||||||
Ok(task::TaskVariables::from_iter([unittest_target]))
|
Ok(task::TaskVariables::from_iter([unittest_target]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn associated_tasks(&self) -> Option<TaskTemplates> {
|
fn associated_tasks(
|
||||||
|
&self,
|
||||||
|
_: Option<Arc<dyn language::File>>,
|
||||||
|
_: &AppContext,
|
||||||
|
) -> Option<TaskTemplates> {
|
||||||
Some(TaskTemplates(vec![
|
Some(TaskTemplates(vec![
|
||||||
TaskTemplate {
|
TaskTemplate {
|
||||||
label: "execute selection".to_owned(),
|
label: "execute selection".to_owned(),
|
||||||
|
@ -2,9 +2,10 @@ use anyhow::{anyhow, bail, Context, Result};
|
|||||||
use async_compression::futures::bufread::GzipDecoder;
|
use async_compression::futures::bufread::GzipDecoder;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures::{io::BufReader, StreamExt};
|
use futures::{io::BufReader, StreamExt};
|
||||||
use gpui::AsyncAppContext;
|
use gpui::{AppContext, AsyncAppContext};
|
||||||
use http::github::{latest_github_release, GitHubLspBinaryVersion};
|
use http::github::{latest_github_release, GitHubLspBinaryVersion};
|
||||||
pub use language::*;
|
pub use language::*;
|
||||||
|
use language_settings::all_language_settings;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use lsp::LanguageServerBinary;
|
use lsp::LanguageServerBinary;
|
||||||
use project::project_settings::{BinarySettings, ProjectSettings};
|
use project::project_settings::{BinarySettings, ProjectSettings};
|
||||||
@ -407,7 +408,22 @@ impl ContextProvider for RustContextProvider {
|
|||||||
Ok(TaskVariables::default())
|
Ok(TaskVariables::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn associated_tasks(&self) -> Option<TaskTemplates> {
|
fn associated_tasks(
|
||||||
|
&self,
|
||||||
|
file: Option<Arc<dyn language::File>>,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> Option<TaskTemplates> {
|
||||||
|
const DEFAULT_RUN_NAME_STR: &'static str = "RUST_DEFAULT_PACKAGE_RUN";
|
||||||
|
let package_to_run = all_language_settings(file.as_ref(), cx)
|
||||||
|
.language(Some("Rust"))
|
||||||
|
.tasks
|
||||||
|
.variables
|
||||||
|
.get(DEFAULT_RUN_NAME_STR);
|
||||||
|
let run_task_args = if let Some(package_to_run) = package_to_run {
|
||||||
|
vec!["run".into(), "-p".into(), package_to_run.clone()]
|
||||||
|
} else {
|
||||||
|
vec!["run".into()]
|
||||||
|
};
|
||||||
Some(TaskTemplates(vec![
|
Some(TaskTemplates(vec![
|
||||||
TaskTemplate {
|
TaskTemplate {
|
||||||
label: format!(
|
label: format!(
|
||||||
@ -501,7 +517,7 @@ impl ContextProvider for RustContextProvider {
|
|||||||
TaskTemplate {
|
TaskTemplate {
|
||||||
label: "cargo run".into(),
|
label: "cargo run".into(),
|
||||||
command: "cargo".into(),
|
command: "cargo".into(),
|
||||||
args: vec!["run".into()],
|
args: run_task_args,
|
||||||
cwd: Some("$ZED_DIRNAME".to_owned()),
|
cwd: Some("$ZED_DIRNAME".to_owned()),
|
||||||
..TaskTemplate::default()
|
..TaskTemplate::default()
|
||||||
},
|
},
|
||||||
|
@ -10861,12 +10861,19 @@ impl Project {
|
|||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<Result<Vec<(TaskSourceKind, TaskTemplate)>>> {
|
) -> Task<Result<Vec<(TaskSourceKind, TaskTemplate)>>> {
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
let language = location
|
let (file, language) = location
|
||||||
.and_then(|location| location.buffer.read(cx).language_at(location.range.start));
|
.map(|location| {
|
||||||
|
let buffer = location.buffer.read(cx);
|
||||||
|
(
|
||||||
|
buffer.file().cloned(),
|
||||||
|
buffer.language_at(location.range.start),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
Task::ready(Ok(self
|
Task::ready(Ok(self
|
||||||
.task_inventory()
|
.task_inventory()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.list_tasks(language, worktree)))
|
.list_tasks(file, language, worktree, cx)))
|
||||||
} else if let Some(project_id) = self
|
} else if let Some(project_id) = self
|
||||||
.remote_id()
|
.remote_id()
|
||||||
.filter(|_| self.ssh_connection_string(cx).is_some())
|
.filter(|_| self.ssh_connection_string(cx).is_some())
|
||||||
|
@ -15,7 +15,7 @@ use futures::{
|
|||||||
};
|
};
|
||||||
use gpui::{AppContext, Context, Model, ModelContext, Task};
|
use gpui::{AppContext, Context, Model, ModelContext, Task};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::{ContextProvider, Language, Location};
|
use language::{ContextProvider, File, Language, Location};
|
||||||
use task::{
|
use task::{
|
||||||
static_source::StaticSource, ResolvedTask, TaskContext, TaskId, TaskTemplate, TaskTemplates,
|
static_source::StaticSource, ResolvedTask, TaskContext, TaskId, TaskTemplate, TaskTemplates,
|
||||||
TaskVariables, VariableName,
|
TaskVariables, VariableName,
|
||||||
@ -155,14 +155,16 @@ impl Inventory {
|
|||||||
/// returns all task templates with their source kinds, in no specific order.
|
/// returns all task templates with their source kinds, in no specific order.
|
||||||
pub fn list_tasks(
|
pub fn list_tasks(
|
||||||
&self,
|
&self,
|
||||||
|
file: Option<Arc<dyn File>>,
|
||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
worktree: Option<WorktreeId>,
|
worktree: Option<WorktreeId>,
|
||||||
|
cx: &AppContext,
|
||||||
) -> Vec<(TaskSourceKind, TaskTemplate)> {
|
) -> Vec<(TaskSourceKind, TaskTemplate)> {
|
||||||
let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
|
let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
|
||||||
name: language.name(),
|
name: language.name(),
|
||||||
});
|
});
|
||||||
let language_tasks = language
|
let language_tasks = language
|
||||||
.and_then(|language| language.context_provider()?.associated_tasks())
|
.and_then(|language| language.context_provider()?.associated_tasks(file, cx))
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|tasks| tasks.0.into_iter())
|
.flat_map(|tasks| tasks.0.into_iter())
|
||||||
.flat_map(|task| Some((task_source_kind.as_ref()?, task)));
|
.flat_map(|task| Some((task_source_kind.as_ref()?, task)));
|
||||||
@ -207,8 +209,11 @@ impl Inventory {
|
|||||||
let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
|
let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
|
||||||
name: language.name(),
|
name: language.name(),
|
||||||
});
|
});
|
||||||
|
let file = location
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|location| location.buffer.read(cx).file().cloned());
|
||||||
let language_tasks = language
|
let language_tasks = language
|
||||||
.and_then(|language| language.context_provider()?.associated_tasks())
|
.and_then(|language| language.context_provider()?.associated_tasks(file, cx))
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|tasks| tasks.0.into_iter())
|
.flat_map(|tasks| tasks.0.into_iter())
|
||||||
.flat_map(|task| Some((task_source_kind.as_ref()?, task)));
|
.flat_map(|task| Some((task_source_kind.as_ref()?, task)));
|
||||||
@ -471,9 +476,9 @@ mod test_inventory {
|
|||||||
worktree: Option<WorktreeId>,
|
worktree: Option<WorktreeId>,
|
||||||
cx: &mut TestAppContext,
|
cx: &mut TestAppContext,
|
||||||
) -> Vec<String> {
|
) -> Vec<String> {
|
||||||
inventory.update(cx, |inventory, _| {
|
inventory.update(cx, |inventory, cx| {
|
||||||
inventory
|
inventory
|
||||||
.list_tasks(None, worktree)
|
.list_tasks(None, None, worktree, cx)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(_, task)| task.label)
|
.map(|(_, task)| task.label)
|
||||||
.sorted()
|
.sorted()
|
||||||
@ -486,9 +491,9 @@ mod test_inventory {
|
|||||||
task_name: &str,
|
task_name: &str,
|
||||||
cx: &mut TestAppContext,
|
cx: &mut TestAppContext,
|
||||||
) {
|
) {
|
||||||
inventory.update(cx, |inventory, _| {
|
inventory.update(cx, |inventory, cx| {
|
||||||
let (task_source_kind, task) = inventory
|
let (task_source_kind, task) = inventory
|
||||||
.list_tasks(None, None)
|
.list_tasks(None, None, None, cx)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|(_, task)| task.label == task_name)
|
.find(|(_, task)| task.label == task_name)
|
||||||
.unwrap_or_else(|| panic!("Failed to find task with name {task_name}"));
|
.unwrap_or_else(|| panic!("Failed to find task with name {task_name}"));
|
||||||
@ -639,7 +644,11 @@ impl ContextProviderWithTasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ContextProvider for ContextProviderWithTasks {
|
impl ContextProvider for ContextProviderWithTasks {
|
||||||
fn associated_tasks(&self) -> Option<TaskTemplates> {
|
fn associated_tasks(
|
||||||
|
&self,
|
||||||
|
_: Option<Arc<dyn language::File>>,
|
||||||
|
_: &AppContext,
|
||||||
|
) -> Option<TaskTemplates> {
|
||||||
Some(self.templates.clone())
|
Some(self.templates.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user