ZIm/crates/language/src/task_context.rs
Piotr Osiewicz 4dc61f7ccd
Extensions registering tasks (#9572)
This PR also introduces built-in tasks for Rust and Elixir. Note that
this is not a precedent for future PRs to include tasks for more
languages; we simply want to find the rough edges with tasks & language
integrations before proceeding to task contexts provided by extensions.

As is, we'll load tasks for all loaded languages, so in order to get
Elixir tasks, you have to open an Elixir buffer first. I think it sort
of makes sense (though it's not ideal), as in the future where
extensions do provide their own tasks.json, we'd like to limit the # of
tasks surfaced to the user to make them as relevant to the project at
hand as possible.

Release Notes:

- Added built-in tasks for Rust and Elixir files.
2024-03-22 16:18:33 +01:00

109 lines
3.2 KiB
Rust

use crate::{LanguageRegistry, Location};
use anyhow::Result;
use gpui::{AppContext, Context, Model};
use std::sync::Arc;
use task::{static_source::tasks_for, static_source::TaskDefinitions, TaskSource, TaskVariables};
/// Language Contexts are used by Zed tasks to extract information about source file.
pub trait ContextProvider: Send + Sync {
fn build_context(&self, _: Location, _: &mut AppContext) -> Result<TaskVariables> {
Ok(TaskVariables::default())
}
fn associated_tasks(&self) -> Option<TaskDefinitions> {
None
}
}
/// A context provider that finds out what symbol is currently focused in the buffer.
pub struct SymbolContextProvider;
impl ContextProvider for SymbolContextProvider {
fn build_context(
&self,
location: Location,
cx: &mut AppContext,
) -> gpui::Result<TaskVariables> {
let symbols = location
.buffer
.read(cx)
.snapshot()
.symbols_containing(location.range.start, None);
let symbol = symbols.and_then(|symbols| {
symbols.last().map(|symbol| {
let range = symbol
.name_ranges
.last()
.cloned()
.unwrap_or(0..symbol.text.len());
symbol.text[range].to_string()
})
});
Ok(TaskVariables::from_iter(
symbol.map(|symbol| ("ZED_SYMBOL".to_string(), symbol)),
))
}
}
/// A ContextProvider that doesn't provide any task variables on it's own, though it has some associated tasks.
pub struct ContextProviderWithTasks {
definitions: TaskDefinitions,
}
impl ContextProviderWithTasks {
pub fn new(definitions: TaskDefinitions) -> Self {
Self { definitions }
}
}
impl ContextProvider for ContextProviderWithTasks {
fn associated_tasks(&self) -> Option<TaskDefinitions> {
Some(self.definitions.clone())
}
fn build_context(&self, location: Location, cx: &mut AppContext) -> Result<TaskVariables> {
SymbolContextProvider.build_context(location, cx)
}
}
/// A source that pulls in the tasks from language registry.
pub struct LanguageSource {
languages: Arc<LanguageRegistry>,
}
impl LanguageSource {
pub fn new(
languages: Arc<LanguageRegistry>,
cx: &mut AppContext,
) -> Model<Box<dyn TaskSource>> {
cx.new_model(|_| Box::new(Self { languages }) as Box<_>)
}
}
impl TaskSource for LanguageSource {
fn as_any(&mut self) -> &mut dyn std::any::Any {
self
}
fn tasks_for_path(
&mut self,
_: Option<&std::path::Path>,
_: &mut gpui::ModelContext<Box<dyn TaskSource>>,
) -> Vec<Arc<dyn task::Task>> {
self.languages
.to_vec()
.into_iter()
.filter_map(|language| {
language
.context_provider()?
.associated_tasks()
.map(|tasks| (tasks, language))
})
.flat_map(|(tasks, language)| {
let language_name = language.name();
let id_base = format!("buffer_source_{language_name}");
tasks_for(tasks, &id_base)
})
.collect()
}
}