debugger/tasks: Remove TaskType enum (#29208)
Closes #ISSUE Release Notes: - N/A --------- Co-authored-by: Cole Miller <m@cole-miller.net> Co-authored-by: Anthony Eid <hello@anthonyeid.me> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: Anthony <anthony@zed.dev> Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
parent
053fafa90e
commit
67615b968b
53 changed files with 1272 additions and 1114 deletions
|
@ -13,14 +13,15 @@ use collections::{HashMap, HashSet, VecDeque};
|
|||
use gpui::{App, AppContext as _, Entity, SharedString, Task};
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
ContextProvider, File, Language, LanguageToolchainStore, Location,
|
||||
Buffer, ContextProvider, File, Language, LanguageToolchainStore, Location,
|
||||
language_settings::language_settings,
|
||||
};
|
||||
use lsp::{LanguageServerId, LanguageServerName};
|
||||
use settings::{InvalidSettingsError, TaskKind, parse_json_with_comments};
|
||||
use paths::{debug_task_file_name, task_file_name};
|
||||
use settings::{InvalidSettingsError, parse_json_with_comments};
|
||||
use task::{
|
||||
DebugTaskTemplate, ResolvedTask, TaskContext, TaskId, TaskTemplate, TaskTemplates,
|
||||
TaskVariables, VariableName,
|
||||
DebugScenario, ResolvedTask, TaskContext, TaskId, TaskTemplate, TaskTemplates, TaskVariables,
|
||||
VariableName,
|
||||
};
|
||||
use text::{BufferId, Point, ToPoint};
|
||||
use util::{NumericPrefixWithSuffix, ResultExt as _, paths::PathExt as _, post_inc};
|
||||
|
@ -32,13 +33,84 @@ use crate::{task_store::TaskSettingsLocation, worktree_store::WorktreeStore};
|
|||
#[derive(Debug, Default)]
|
||||
pub struct Inventory {
|
||||
last_scheduled_tasks: VecDeque<(TaskSourceKind, ResolvedTask)>,
|
||||
templates_from_settings: ParsedTemplates,
|
||||
templates_from_settings: InventoryFor<TaskTemplate>,
|
||||
scenarios_from_settings: InventoryFor<DebugScenario>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ParsedTemplates {
|
||||
global: HashMap<PathBuf, Vec<TaskTemplate>>,
|
||||
worktree: HashMap<WorktreeId, HashMap<(Arc<Path>, TaskKind), Vec<TaskTemplate>>>,
|
||||
// Helper trait for better error messages in [InventoryFor]
|
||||
trait InventoryContents: Clone {
|
||||
const GLOBAL_SOURCE_FILE: &'static str;
|
||||
const LABEL: &'static str;
|
||||
}
|
||||
|
||||
impl InventoryContents for TaskTemplate {
|
||||
const GLOBAL_SOURCE_FILE: &'static str = "tasks.json";
|
||||
const LABEL: &'static str = "tasks";
|
||||
}
|
||||
|
||||
impl InventoryContents for DebugScenario {
|
||||
const GLOBAL_SOURCE_FILE: &'static str = "debug.json";
|
||||
|
||||
const LABEL: &'static str = "debug scenarios";
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InventoryFor<T> {
|
||||
global: HashMap<PathBuf, Vec<T>>,
|
||||
worktree: HashMap<WorktreeId, HashMap<Arc<Path>, Vec<T>>>,
|
||||
}
|
||||
|
||||
impl<T: InventoryContents> InventoryFor<T> {
|
||||
fn worktree_scenarios(
|
||||
&self,
|
||||
worktree: Option<WorktreeId>,
|
||||
) -> impl '_ + Iterator<Item = (TaskSourceKind, T)> {
|
||||
worktree.into_iter().flat_map(|worktree| {
|
||||
self.worktree
|
||||
.get(&worktree)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flat_map(|(directory, templates)| {
|
||||
templates.iter().map(move |template| (directory, template))
|
||||
})
|
||||
.map(move |(directory, template)| {
|
||||
(
|
||||
TaskSourceKind::Worktree {
|
||||
id: worktree,
|
||||
directory_in_worktree: directory.to_path_buf(),
|
||||
id_base: Cow::Owned(format!(
|
||||
"local worktree {} from directory {directory:?}",
|
||||
T::LABEL
|
||||
)),
|
||||
},
|
||||
template.clone(),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn global_scenarios(&self) -> impl '_ + Iterator<Item = (TaskSourceKind, T)> {
|
||||
self.global.iter().flat_map(|(file_path, templates)| {
|
||||
templates.into_iter().map(|template| {
|
||||
(
|
||||
TaskSourceKind::AbsPath {
|
||||
id_base: Cow::Owned(format!("global {}", T::GLOBAL_SOURCE_FILE)),
|
||||
abs_path: file_path.clone(),
|
||||
},
|
||||
template.clone(),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for InventoryFor<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
global: HashMap::default(),
|
||||
worktree: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kind of a source the tasks are fetched from, used to display more source information in the UI.
|
||||
|
@ -134,22 +206,40 @@ impl Inventory {
|
|||
cx.new(|_| Self::default())
|
||||
}
|
||||
|
||||
pub fn list_debug_tasks(&self) -> Vec<&TaskTemplate> {
|
||||
self.templates_from_settings
|
||||
.worktree
|
||||
.values()
|
||||
.flat_map(|tasks| {
|
||||
tasks.iter().filter_map(|(kind, tasks)| {
|
||||
if matches!(kind.1, TaskKind::Debug) {
|
||||
Some(tasks)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
pub fn list_debug_scenarios(&self, worktree: Option<WorktreeId>) -> Vec<DebugScenario> {
|
||||
let global_scenarios = self.global_debug_scenarios_from_settings();
|
||||
let worktree_scenarios = self.worktree_scenarios_from_settings(worktree);
|
||||
|
||||
worktree_scenarios
|
||||
.chain(global_scenarios)
|
||||
.map(|(_, scenario)| scenario)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn task_template_by_label(
|
||||
&self,
|
||||
buffer: Option<Entity<Buffer>>,
|
||||
label: &str,
|
||||
cx: &App,
|
||||
) -> Option<TaskTemplate> {
|
||||
let (worktree_id, file, language) = buffer
|
||||
.map(|buffer| {
|
||||
let buffer = buffer.read(cx);
|
||||
let file = buffer.file().cloned();
|
||||
(
|
||||
file.as_ref().map(|file| file.worktree_id(cx)),
|
||||
file,
|
||||
buffer.language().cloned(),
|
||||
)
|
||||
})
|
||||
.unwrap_or((None, None, None));
|
||||
|
||||
self.list_tasks(file, language, worktree_id, cx)
|
||||
.iter()
|
||||
.find(|(_, template)| template.label == label)
|
||||
.map(|val| val.1.clone())
|
||||
}
|
||||
|
||||
/// Pulls its task sources relevant to the worktree and the language given,
|
||||
/// returns all task templates with their source kinds, worktree tasks first, language tasks second
|
||||
/// and global tasks last. No specific order inside source kinds groups.
|
||||
|
@ -160,10 +250,11 @@ impl Inventory {
|
|||
worktree: Option<WorktreeId>,
|
||||
cx: &App,
|
||||
) -> Vec<(TaskSourceKind, TaskTemplate)> {
|
||||
let global_tasks = self.global_templates_from_settings();
|
||||
let worktree_tasks = self.worktree_templates_from_settings(worktree);
|
||||
let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
|
||||
name: language.name().into(),
|
||||
});
|
||||
let global_tasks = self.global_templates_from_settings();
|
||||
let language_tasks = language
|
||||
.filter(|language| {
|
||||
language_settings(Some(language.name()), file.as_ref(), cx)
|
||||
|
@ -173,11 +264,11 @@ impl Inventory {
|
|||
.and_then(|language| language.context_provider()?.associated_tasks(file, cx))
|
||||
.into_iter()
|
||||
.flat_map(|tasks| tasks.0.into_iter())
|
||||
.flat_map(|task| Some((task_source_kind.clone()?, task)))
|
||||
.chain(global_tasks);
|
||||
.flat_map(|task| Some((task_source_kind.clone()?, task)));
|
||||
|
||||
self.worktree_templates_from_settings(worktree)
|
||||
worktree_tasks
|
||||
.chain(language_tasks)
|
||||
.chain(global_tasks)
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
@ -358,51 +449,27 @@ impl Inventory {
|
|||
fn global_templates_from_settings(
|
||||
&self,
|
||||
) -> impl '_ + Iterator<Item = (TaskSourceKind, TaskTemplate)> {
|
||||
self.templates_from_settings
|
||||
.global
|
||||
.iter()
|
||||
.flat_map(|(file_path, templates)| {
|
||||
templates.into_iter().map(|template| {
|
||||
(
|
||||
TaskSourceKind::AbsPath {
|
||||
id_base: match template.task_type {
|
||||
task::TaskType::Script => Cow::Borrowed("global tasks.json"),
|
||||
task::TaskType::Debug(_) => Cow::Borrowed("global debug.json"),
|
||||
},
|
||||
abs_path: file_path.clone(),
|
||||
},
|
||||
template.clone(),
|
||||
)
|
||||
})
|
||||
})
|
||||
self.templates_from_settings.global_scenarios()
|
||||
}
|
||||
|
||||
fn global_debug_scenarios_from_settings(
|
||||
&self,
|
||||
) -> impl '_ + Iterator<Item = (TaskSourceKind, DebugScenario)> {
|
||||
self.scenarios_from_settings.global_scenarios()
|
||||
}
|
||||
|
||||
fn worktree_scenarios_from_settings(
|
||||
&self,
|
||||
worktree: Option<WorktreeId>,
|
||||
) -> impl '_ + Iterator<Item = (TaskSourceKind, DebugScenario)> {
|
||||
self.scenarios_from_settings.worktree_scenarios(worktree)
|
||||
}
|
||||
|
||||
fn worktree_templates_from_settings(
|
||||
&self,
|
||||
worktree: Option<WorktreeId>,
|
||||
) -> impl '_ + Iterator<Item = (TaskSourceKind, TaskTemplate)> {
|
||||
worktree.into_iter().flat_map(|worktree| {
|
||||
self.templates_from_settings
|
||||
.worktree
|
||||
.get(&worktree)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flat_map(|(directory, templates)| {
|
||||
templates.iter().map(move |template| (directory, template))
|
||||
})
|
||||
.map(move |((directory, _task_kind), template)| {
|
||||
(
|
||||
TaskSourceKind::Worktree {
|
||||
id: worktree,
|
||||
directory_in_worktree: directory.to_path_buf(),
|
||||
id_base: Cow::Owned(format!(
|
||||
"local worktree tasks from directory {directory:?}"
|
||||
)),
|
||||
},
|
||||
template.clone(),
|
||||
)
|
||||
})
|
||||
})
|
||||
self.templates_from_settings.worktree_scenarios(worktree)
|
||||
}
|
||||
|
||||
/// Updates in-memory task metadata from the JSON string given.
|
||||
|
@ -413,7 +480,6 @@ impl Inventory {
|
|||
&mut self,
|
||||
location: TaskSettingsLocation<'_>,
|
||||
raw_tasks_json: Option<&str>,
|
||||
task_kind: TaskKind,
|
||||
) -> Result<(), InvalidSettingsError> {
|
||||
let raw_tasks = match parse_json_with_comments::<Vec<serde_json::Value>>(
|
||||
raw_tasks_json.unwrap_or("[]"),
|
||||
|
@ -424,21 +490,16 @@ impl Inventory {
|
|||
path: match location {
|
||||
TaskSettingsLocation::Global(path) => path.to_owned(),
|
||||
TaskSettingsLocation::Worktree(settings_location) => {
|
||||
task_kind.config_in_dir(settings_location.path)
|
||||
settings_location.path.join(task_file_name())
|
||||
}
|
||||
},
|
||||
message: format!("Failed to parse tasks file content as a JSON array: {e}"),
|
||||
});
|
||||
}
|
||||
};
|
||||
let new_templates = raw_tasks
|
||||
.into_iter()
|
||||
.filter_map(|raw_template| match &task_kind {
|
||||
TaskKind::Script => serde_json::from_value::<TaskTemplate>(raw_template).log_err(),
|
||||
TaskKind::Debug => serde_json::from_value::<DebugTaskTemplate>(raw_template)
|
||||
.log_err()
|
||||
.map(|content| content.to_zed_format()),
|
||||
});
|
||||
let new_templates = raw_tasks.into_iter().filter_map(|raw_template| {
|
||||
serde_json::from_value::<TaskTemplate>(raw_template).log_err()
|
||||
});
|
||||
|
||||
let parsed_templates = &mut self.templates_from_settings;
|
||||
match location {
|
||||
|
@ -454,14 +515,72 @@ impl Inventory {
|
|||
if let Some(worktree_tasks) =
|
||||
parsed_templates.worktree.get_mut(&location.worktree_id)
|
||||
{
|
||||
worktree_tasks.remove(&(Arc::from(location.path), task_kind));
|
||||
worktree_tasks.remove(location.path);
|
||||
}
|
||||
} else {
|
||||
parsed_templates
|
||||
.worktree
|
||||
.entry(location.worktree_id)
|
||||
.or_default()
|
||||
.insert((Arc::from(location.path), task_kind), new_templates);
|
||||
.insert(Arc::from(location.path), new_templates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates in-memory task metadata from the JSON string given.
|
||||
/// Will fail if the JSON is not a valid array of objects, but will continue if any object will not parse into a [`TaskTemplate`].
|
||||
///
|
||||
/// Global tasks are updated for no worktree provided, otherwise the worktree metadata for a given path will be updated.
|
||||
pub(crate) fn update_file_based_scenarios(
|
||||
&mut self,
|
||||
location: TaskSettingsLocation<'_>,
|
||||
raw_tasks_json: Option<&str>,
|
||||
) -> Result<(), InvalidSettingsError> {
|
||||
let raw_tasks = match parse_json_with_comments::<Vec<serde_json::Value>>(
|
||||
raw_tasks_json.unwrap_or("[]"),
|
||||
) {
|
||||
Ok(tasks) => tasks,
|
||||
Err(e) => {
|
||||
return Err(InvalidSettingsError::Debug {
|
||||
path: match location {
|
||||
TaskSettingsLocation::Global(path) => path.to_owned(),
|
||||
TaskSettingsLocation::Worktree(settings_location) => {
|
||||
settings_location.path.join(debug_task_file_name())
|
||||
}
|
||||
},
|
||||
message: format!("Failed to parse tasks file content as a JSON array: {e}"),
|
||||
});
|
||||
}
|
||||
};
|
||||
let new_templates = raw_tasks.into_iter().filter_map(|raw_template| {
|
||||
serde_json::from_value::<DebugScenario>(raw_template).log_err()
|
||||
});
|
||||
|
||||
let parsed_scenarios = &mut self.scenarios_from_settings;
|
||||
match location {
|
||||
TaskSettingsLocation::Global(path) => {
|
||||
parsed_scenarios
|
||||
.global
|
||||
.entry(path.to_owned())
|
||||
.insert_entry(new_templates.collect());
|
||||
}
|
||||
TaskSettingsLocation::Worktree(location) => {
|
||||
let new_templates = new_templates.collect::<Vec<_>>();
|
||||
if new_templates.is_empty() {
|
||||
if let Some(worktree_tasks) =
|
||||
parsed_scenarios.worktree.get_mut(&location.worktree_id)
|
||||
{
|
||||
worktree_tasks.remove(location.path);
|
||||
}
|
||||
} else {
|
||||
parsed_scenarios
|
||||
.worktree
|
||||
.entry(location.worktree_id)
|
||||
.or_default()
|
||||
.insert(Arc::from(location.path), new_templates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -677,6 +796,10 @@ impl ContextProvider for BasicContextProvider {
|
|||
|
||||
Task::ready(Ok(task_variables))
|
||||
}
|
||||
|
||||
fn debug_adapter(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A ContextProvider that doesn't provide any task variables on it's own, though it has some associated tasks.
|
||||
|
@ -700,6 +823,10 @@ impl ContextProvider for ContextProviderWithTasks {
|
|||
) -> Option<TaskTemplates> {
|
||||
Some(self.templates.clone())
|
||||
}
|
||||
|
||||
fn debug_adapter(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -744,7 +871,6 @@ mod tests {
|
|||
Some(&mock_tasks_from_names(
|
||||
expected_initial_state.iter().map(|name| name.as_str()),
|
||||
)),
|
||||
settings::TaskKind::Script,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
|
@ -800,7 +926,6 @@ mod tests {
|
|||
.into_iter()
|
||||
.chain(expected_initial_state.iter().map(|name| name.as_str())),
|
||||
)),
|
||||
settings::TaskKind::Script,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
|
@ -925,7 +1050,6 @@ mod tests {
|
|||
.iter()
|
||||
.map(|(_, name)| name.as_str()),
|
||||
)),
|
||||
settings::TaskKind::Script,
|
||||
)
|
||||
.unwrap();
|
||||
inventory
|
||||
|
@ -937,7 +1061,6 @@ mod tests {
|
|||
Some(&mock_tasks_from_names(
|
||||
worktree_1_tasks.iter().map(|(_, name)| name.as_str()),
|
||||
)),
|
||||
settings::TaskKind::Script,
|
||||
)
|
||||
.unwrap();
|
||||
inventory
|
||||
|
@ -949,7 +1072,6 @@ mod tests {
|
|||
Some(&mock_tasks_from_names(
|
||||
worktree_2_tasks.iter().map(|(_, name)| name.as_str()),
|
||||
)),
|
||||
settings::TaskKind::Script,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue