mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-27 23:59:52 +03:00
Sort tasks modal entries by last used time
This commit is contained in:
parent
9f7e625d37
commit
96d9df073e
@ -2,13 +2,16 @@
|
||||
|
||||
use std::{any::TypeId, path::Path, sync::Arc};
|
||||
|
||||
use collections::{HashMap, VecDeque};
|
||||
use gpui::{AppContext, Context, Model, ModelContext, Subscription};
|
||||
use itertools::Itertools;
|
||||
use task::{Source, Task, TaskId};
|
||||
use util::post_inc;
|
||||
|
||||
/// Inventory tracks available tasks for a given project.
|
||||
pub struct Inventory {
|
||||
sources: Vec<SourceInInventory>,
|
||||
pub last_scheduled_task: Option<TaskId>,
|
||||
last_scheduled_tasks: VecDeque<TaskId>,
|
||||
}
|
||||
|
||||
struct SourceInInventory {
|
||||
@ -21,7 +24,7 @@ impl Inventory {
|
||||
pub(crate) fn new(cx: &mut AppContext) -> Model<Self> {
|
||||
cx.new_model(|_| Self {
|
||||
sources: Vec::new(),
|
||||
last_scheduled_task: None,
|
||||
last_scheduled_tasks: VecDeque::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -39,6 +42,7 @@ impl Inventory {
|
||||
self.sources.push(source);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn source<T: Source>(&self) -> Option<Model<Box<dyn Source>>> {
|
||||
let target_type_id = std::any::TypeId::of::<T>();
|
||||
self.sources.iter().find_map(
|
||||
@ -55,25 +59,75 @@ impl Inventory {
|
||||
}
|
||||
|
||||
/// Pulls its sources to list runanbles for the path given (up to the source to decide what to return for no path).
|
||||
pub fn list_tasks(&self, path: Option<&Path>, cx: &mut AppContext) -> Vec<Arc<dyn Task>> {
|
||||
let mut tasks = Vec::new();
|
||||
for source in &self.sources {
|
||||
tasks.extend(
|
||||
pub fn list_tasks(
|
||||
&self,
|
||||
path: Option<&Path>,
|
||||
lru: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Vec<Arc<dyn 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| {
|
||||
tasks.entry(id).or_insert_with(|| post_inc(&mut lru_score));
|
||||
tasks
|
||||
})
|
||||
} else {
|
||||
HashMap::default()
|
||||
};
|
||||
self.sources
|
||||
.iter()
|
||||
.flat_map(|source| {
|
||||
source
|
||||
.source
|
||||
.update(cx, |source, cx| source.tasks_for_path(path, cx)),
|
||||
);
|
||||
}
|
||||
tasks
|
||||
.update(cx, |source, cx| source.tasks_for_path(path, cx))
|
||||
})
|
||||
.map(|task| {
|
||||
let usages = if lru {
|
||||
tasks_by_usage
|
||||
.get(&task.id())
|
||||
.copied()
|
||||
.unwrap_or_else(|| post_inc(&mut lru_score))
|
||||
} else {
|
||||
post_inc(&mut lru_score)
|
||||
};
|
||||
(task, usages)
|
||||
})
|
||||
.sorted_unstable_by(|(task_a, usages_a), (task_b, usages_b)| {
|
||||
usages_a
|
||||
.cmp(usages_b)
|
||||
.then(task_a.name().cmp(task_b.name()))
|
||||
})
|
||||
.map(|(task, _)| task)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 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>> {
|
||||
self.last_scheduled_task.as_ref().and_then(|id| {
|
||||
self.last_scheduled_tasks.back().and_then(|id| {
|
||||
// TODO straighten the `Path` story to understand what has to be passed here: or it will break in the future.
|
||||
self.list_tasks(None, cx)
|
||||
self.list_tasks(None, false, cx)
|
||||
.into_iter()
|
||||
.find(|task| task.id() == id)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn task_scheduled(&mut self, id: TaskId) {
|
||||
self.last_scheduled_tasks.push_back(id);
|
||||
if self.last_scheduled_tasks.len() > 5_000 {
|
||||
self.last_scheduled_tasks.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn todo_kb() {
|
||||
todo!("TODO kb LRU tests")
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ fn schedule_task(workspace: &Workspace, task: &dyn Task, cx: &mut ViewContext<'_
|
||||
if let Some(spawn_in_terminal) = spawn_in_terminal {
|
||||
workspace.project().update(cx, |project, cx| {
|
||||
project.task_inventory().update(cx, |inventory, _| {
|
||||
inventory.last_scheduled_task = Some(task.id().clone());
|
||||
inventory.task_scheduled(task.id().clone());
|
||||
})
|
||||
});
|
||||
cx.emit(workspace::Event::SpawnTask(spawn_in_terminal));
|
||||
|
@ -24,7 +24,7 @@ pub(crate) struct TasksModalDelegate {
|
||||
matches: Vec<StringMatch>,
|
||||
selected_index: usize,
|
||||
workspace: WeakView<Workspace>,
|
||||
last_prompt: String,
|
||||
prompt: String,
|
||||
}
|
||||
|
||||
impl TasksModalDelegate {
|
||||
@ -35,20 +35,21 @@ impl TasksModalDelegate {
|
||||
candidates: Vec::new(),
|
||||
matches: Vec::new(),
|
||||
selected_index: 0,
|
||||
last_prompt: String::default(),
|
||||
prompt: String::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_oneshot(&mut self, cx: &mut AppContext) -> Option<Arc<dyn Task>> {
|
||||
let oneshot_source = self
|
||||
.inventory
|
||||
.update(cx, |this, _| this.source::<OneshotSource>())?;
|
||||
oneshot_source.update(cx, |this, _| {
|
||||
let Some(this) = this.as_any().downcast_mut::<OneshotSource>() else {
|
||||
return None;
|
||||
};
|
||||
Some(this.spawn(self.last_prompt.clone()))
|
||||
})
|
||||
self.inventory
|
||||
.update(cx, |inventory, _| inventory.source::<OneshotSource>())?
|
||||
.update(cx, |oneshot_source, _| {
|
||||
Some(
|
||||
oneshot_source
|
||||
.as_any()
|
||||
.downcast_mut::<OneshotSource>()?
|
||||
.spawn(self.prompt.clone()),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,12 +133,7 @@ impl PickerDelegate for TasksModalDelegate {
|
||||
picker.delegate.candidates = picker
|
||||
.delegate
|
||||
.inventory
|
||||
.update(cx, |inventory, cx| inventory.list_tasks(None, cx));
|
||||
picker
|
||||
.delegate
|
||||
.candidates
|
||||
.sort_by(|a, b| a.name().cmp(&b.name()));
|
||||
|
||||
.update(cx, |inventory, cx| inventory.list_tasks(None, true, cx));
|
||||
picker
|
||||
.delegate
|
||||
.candidates
|
||||
@ -167,7 +163,7 @@ impl PickerDelegate for TasksModalDelegate {
|
||||
.update(&mut cx, |picker, _| {
|
||||
let delegate = &mut picker.delegate;
|
||||
delegate.matches = matches;
|
||||
delegate.last_prompt = query;
|
||||
delegate.prompt = query;
|
||||
|
||||
if delegate.matches.is_empty() {
|
||||
delegate.selected_index = 0;
|
||||
@ -184,7 +180,7 @@ impl PickerDelegate for TasksModalDelegate {
|
||||
let current_match_index = self.selected_index();
|
||||
|
||||
let task = if secondary {
|
||||
if !self.last_prompt.trim().is_empty() {
|
||||
if !self.prompt.trim().is_empty() {
|
||||
self.spawn_oneshot(cx)
|
||||
} else {
|
||||
None
|
||||
|
Loading…
Reference in New Issue
Block a user