Limit the extension tasks in the modal to current language only (#10207)
Release Notes: - N/A
This commit is contained in:
parent
c851e6edba
commit
7b636d9774
9 changed files with 128 additions and 186 deletions
|
@ -9,14 +9,15 @@ use std::{
|
|||
use collections::{HashMap, VecDeque};
|
||||
use gpui::{AppContext, Context, Model, ModelContext, Subscription};
|
||||
use itertools::Itertools;
|
||||
use task::{Task, TaskContext, TaskId, TaskSource};
|
||||
use language::Language;
|
||||
use task::{static_source::tasks_for, Task, TaskContext, TaskSource};
|
||||
use util::{post_inc, NumericPrefixWithSuffix};
|
||||
use worktree::WorktreeId;
|
||||
|
||||
/// Inventory tracks available tasks for a given project.
|
||||
pub struct Inventory {
|
||||
sources: Vec<SourceInInventory>,
|
||||
last_scheduled_tasks: VecDeque<(TaskId, TaskContext)>,
|
||||
last_scheduled_tasks: VecDeque<(Arc<dyn Task>, TaskContext)>,
|
||||
}
|
||||
|
||||
struct SourceInInventory {
|
||||
|
@ -33,17 +34,17 @@ pub enum TaskSourceKind {
|
|||
UserInput,
|
||||
/// ~/.config/zed/task.json - like global files with task definitions, applicable to any path
|
||||
AbsPath(PathBuf),
|
||||
/// Worktree-specific task definitions, e.g. dynamic tasks from open worktree file, or tasks from the worktree's .zed/task.json
|
||||
/// Tasks from the worktree's .zed/task.json
|
||||
Worktree { id: WorktreeId, abs_path: PathBuf },
|
||||
/// Buffer-specific task definitions, originating in e.g. language extension.
|
||||
Buffer,
|
||||
/// Languages-specific tasks coming from extensions.
|
||||
Language { name: Arc<str> },
|
||||
}
|
||||
|
||||
impl TaskSourceKind {
|
||||
fn abs_path(&self) -> Option<&Path> {
|
||||
match self {
|
||||
Self::AbsPath(abs_path) | Self::Worktree { abs_path, .. } => Some(abs_path),
|
||||
Self::UserInput | Self::Buffer => None,
|
||||
Self::UserInput | Self::Language { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,21 +126,38 @@ impl Inventory {
|
|||
)
|
||||
}
|
||||
|
||||
/// Pulls its sources to list runanbles for the path given (up to the source to decide what to return for no path).
|
||||
/// Pulls its sources to list runnables for the editor given, or all runnables for no editor.
|
||||
pub fn list_tasks(
|
||||
&self,
|
||||
path: Option<&Path>,
|
||||
language: Option<Arc<Language>>,
|
||||
worktree: Option<WorktreeId>,
|
||||
lru: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Vec<(TaskSourceKind, Arc<dyn Task>)> {
|
||||
let task_source_kind = language.as_ref().map(|language| TaskSourceKind::Language {
|
||||
name: language.name(),
|
||||
});
|
||||
let language_tasks = language
|
||||
.and_then(|language| {
|
||||
let tasks = language.context_provider()?.associated_tasks()?;
|
||||
Some((tasks, language))
|
||||
})
|
||||
.map(|(tasks, language)| {
|
||||
let language_name = language.name();
|
||||
let id_base = format!("buffer_source_{language_name}");
|
||||
tasks_for(tasks, &id_base)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.flat_map(|task| Some((task_source_kind.as_ref()?, task)));
|
||||
|
||||
let mut lru_score = 0_u32;
|
||||
let tasks_by_usage = if lru {
|
||||
self.last_scheduled_tasks.iter().rev().fold(
|
||||
HashMap::default(),
|
||||
|mut tasks, (id, context)| {
|
||||
|mut tasks, (task, context)| {
|
||||
tasks
|
||||
.entry(id)
|
||||
.entry(task.id().clone())
|
||||
.or_insert_with(|| (post_inc(&mut lru_score), Some(context)));
|
||||
tasks
|
||||
},
|
||||
|
@ -158,10 +176,11 @@ impl Inventory {
|
|||
.flat_map(|source| {
|
||||
source
|
||||
.source
|
||||
.update(cx, |source, cx| source.tasks_for_path(path, cx))
|
||||
.update(cx, |source, cx| source.tasks_to_schedule(cx))
|
||||
.into_iter()
|
||||
.map(|task| (&source.kind, task))
|
||||
})
|
||||
.chain(language_tasks)
|
||||
.map(|task| {
|
||||
let usages = if lru {
|
||||
tasks_by_usage
|
||||
|
@ -206,21 +225,13 @@ impl Inventory {
|
|||
}
|
||||
|
||||
/// Returns the last scheduled task, if any of the sources contains one with the matching id.
|
||||
pub fn last_scheduled_task(&self, cx: &mut AppContext) -> Option<(Arc<dyn Task>, TaskContext)> {
|
||||
self.last_scheduled_tasks
|
||||
.back()
|
||||
.and_then(|(id, task_context)| {
|
||||
// TODO straighten the `Path` story to understand what has to be passed here: or it will break in the future.
|
||||
self.list_tasks(None, None, false, cx)
|
||||
.into_iter()
|
||||
.find(|(_, task)| task.id() == id)
|
||||
.map(|(_, task)| (task, task_context.clone()))
|
||||
})
|
||||
pub fn last_scheduled_task(&self) -> Option<(Arc<dyn Task>, TaskContext)> {
|
||||
self.last_scheduled_tasks.back().cloned()
|
||||
}
|
||||
|
||||
/// Registers task "usage" as being scheduled – to be used for LRU sorting when listing all tasks.
|
||||
pub fn task_scheduled(&mut self, id: TaskId, task_context: TaskContext) {
|
||||
self.last_scheduled_tasks.push_back((id, task_context));
|
||||
pub fn task_scheduled(&mut self, task: Arc<dyn Task>, task_context: TaskContext) {
|
||||
self.last_scheduled_tasks.push_back((task, task_context));
|
||||
if self.last_scheduled_tasks.len() > 5_000 {
|
||||
self.last_scheduled_tasks.pop_front();
|
||||
}
|
||||
|
@ -229,7 +240,7 @@ impl Inventory {
|
|||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub mod test_inventory {
|
||||
use std::{path::Path, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{AppContext, Context as _, Model, ModelContext, TestAppContext};
|
||||
use task::{Task, TaskContext, TaskId, TaskSource};
|
||||
|
@ -288,9 +299,8 @@ pub mod test_inventory {
|
|||
}
|
||||
|
||||
impl TaskSource for StaticTestSource {
|
||||
fn tasks_for_path(
|
||||
fn tasks_to_schedule(
|
||||
&mut self,
|
||||
_path: Option<&Path>,
|
||||
_cx: &mut ModelContext<Box<dyn TaskSource>>,
|
||||
) -> Vec<Arc<dyn Task>> {
|
||||
self.tasks
|
||||
|
@ -307,14 +317,13 @@ pub mod test_inventory {
|
|||
|
||||
pub fn list_task_names(
|
||||
inventory: &Model<Inventory>,
|
||||
path: Option<&Path>,
|
||||
worktree: Option<WorktreeId>,
|
||||
lru: bool,
|
||||
cx: &mut TestAppContext,
|
||||
) -> Vec<String> {
|
||||
inventory.update(cx, |inventory, cx| {
|
||||
inventory
|
||||
.list_tasks(path, worktree, lru, cx)
|
||||
.list_tasks(None, worktree, lru, cx)
|
||||
.into_iter()
|
||||
.map(|(_, task)| task.name().to_string())
|
||||
.collect()
|
||||
|
@ -332,20 +341,19 @@ pub mod test_inventory {
|
|||
.into_iter()
|
||||
.find(|(_, task)| task.name() == task_name)
|
||||
.unwrap_or_else(|| panic!("Failed to find task with name {task_name}"));
|
||||
inventory.task_scheduled(task.1.id().clone(), TaskContext::default());
|
||||
inventory.task_scheduled(task.1, TaskContext::default());
|
||||
});
|
||||
}
|
||||
|
||||
pub fn list_tasks(
|
||||
inventory: &Model<Inventory>,
|
||||
path: Option<&Path>,
|
||||
worktree: Option<WorktreeId>,
|
||||
lru: bool,
|
||||
cx: &mut TestAppContext,
|
||||
) -> Vec<(TaskSourceKind, String)> {
|
||||
inventory.update(cx, |inventory, cx| {
|
||||
inventory
|
||||
.list_tasks(path, worktree, lru, cx)
|
||||
.list_tasks(None, worktree, lru, cx)
|
||||
.into_iter()
|
||||
.map(|(source_kind, task)| (source_kind, task.name().to_string()))
|
||||
.collect()
|
||||
|
@ -363,12 +371,12 @@ mod tests {
|
|||
#[gpui::test]
|
||||
fn test_task_list_sorting(cx: &mut TestAppContext) {
|
||||
let inventory = cx.update(Inventory::new);
|
||||
let initial_tasks = list_task_names(&inventory, None, None, true, cx);
|
||||
let initial_tasks = list_task_names(&inventory, None, true, cx);
|
||||
assert!(
|
||||
initial_tasks.is_empty(),
|
||||
"No tasks expected for empty inventory, but got {initial_tasks:?}"
|
||||
);
|
||||
let initial_tasks = list_task_names(&inventory, None, None, false, cx);
|
||||
let initial_tasks = list_task_names(&inventory, None, false, cx);
|
||||
assert!(
|
||||
initial_tasks.is_empty(),
|
||||
"No tasks expected for empty inventory, but got {initial_tasks:?}"
|
||||
|
@ -405,24 +413,24 @@ mod tests {
|
|||
"3_task".to_string(),
|
||||
];
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, false, cx),
|
||||
list_task_names(&inventory, None, false, cx),
|
||||
&expected_initial_state,
|
||||
"Task list without lru sorting, should be sorted alphanumerically"
|
||||
);
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, true, cx),
|
||||
list_task_names(&inventory, None, true, cx),
|
||||
&expected_initial_state,
|
||||
"Tasks with equal amount of usages should be sorted alphanumerically"
|
||||
);
|
||||
|
||||
register_task_used(&inventory, "2_task", cx);
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, false, cx),
|
||||
list_task_names(&inventory, None, false, cx),
|
||||
&expected_initial_state,
|
||||
"Task list without lru sorting, should be sorted alphanumerically"
|
||||
);
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, true, cx),
|
||||
list_task_names(&inventory, None, true, cx),
|
||||
vec![
|
||||
"2_task".to_string(),
|
||||
"1_a_task".to_string(),
|
||||
|
@ -436,12 +444,12 @@ mod tests {
|
|||
register_task_used(&inventory, "1_task", cx);
|
||||
register_task_used(&inventory, "3_task", cx);
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, false, cx),
|
||||
list_task_names(&inventory, None, false, cx),
|
||||
&expected_initial_state,
|
||||
"Task list without lru sorting, should be sorted alphanumerically"
|
||||
);
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, true, cx),
|
||||
list_task_names(&inventory, None, true, cx),
|
||||
vec![
|
||||
"3_task".to_string(),
|
||||
"1_task".to_string(),
|
||||
|
@ -468,12 +476,12 @@ mod tests {
|
|||
"11_hello".to_string(),
|
||||
];
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, false, cx),
|
||||
list_task_names(&inventory, None, false, cx),
|
||||
&expected_updated_state,
|
||||
"Task list without lru sorting, should be sorted alphanumerically"
|
||||
);
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, true, cx),
|
||||
list_task_names(&inventory, None, true, cx),
|
||||
vec![
|
||||
"3_task".to_string(),
|
||||
"1_task".to_string(),
|
||||
|
@ -486,12 +494,12 @@ mod tests {
|
|||
|
||||
register_task_used(&inventory, "11_hello", cx);
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, false, cx),
|
||||
list_task_names(&inventory, None, false, cx),
|
||||
&expected_updated_state,
|
||||
"Task list without lru sorting, should be sorted alphanumerically"
|
||||
);
|
||||
assert_eq!(
|
||||
list_task_names(&inventory, None, None, true, cx),
|
||||
list_task_names(&inventory, None, true, cx),
|
||||
vec![
|
||||
"11_hello".to_string(),
|
||||
"3_task".to_string(),
|
||||
|
@ -633,36 +641,25 @@ mod tests {
|
|||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for path in [
|
||||
None,
|
||||
Some(path_1),
|
||||
Some(path_2),
|
||||
Some(worktree_path_1),
|
||||
Some(worktree_path_2),
|
||||
] {
|
||||
assert_eq!(
|
||||
list_tasks(&inventory_with_statics, path, None, false, cx),
|
||||
all_tasks,
|
||||
"Path {path:?} choice should not adjust static runnables"
|
||||
);
|
||||
assert_eq!(
|
||||
list_tasks(&inventory_with_statics, path, Some(worktree_1), false, cx),
|
||||
worktree_1_tasks
|
||||
.iter()
|
||||
.chain(worktree_independent_tasks.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
"Path {path:?} choice should not adjust static runnables for worktree_1"
|
||||
);
|
||||
assert_eq!(
|
||||
list_tasks(&inventory_with_statics, path, Some(worktree_2), false, cx),
|
||||
worktree_2_tasks
|
||||
.iter()
|
||||
.chain(worktree_independent_tasks.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
"Path {path:?} choice should not adjust static runnables for worktree_2"
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
list_tasks(&inventory_with_statics, None, false, cx),
|
||||
all_tasks,
|
||||
);
|
||||
assert_eq!(
|
||||
list_tasks(&inventory_with_statics, Some(worktree_1), false, cx),
|
||||
worktree_1_tasks
|
||||
.iter()
|
||||
.chain(worktree_independent_tasks.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
assert_eq!(
|
||||
list_tasks(&inventory_with_statics, Some(worktree_2), false, cx),
|
||||
worktree_2_tasks
|
||||
.iter()
|
||||
.chain(worktree_independent_tasks.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue