tasks: Expose captured variables to ContextProvider (#12134)

This PR changes the interface of ContextProvider, allowing it to inspect
*all* variables set so far during the process of building
`TaskVariables`. This makes it possible to capture e.g. an identifier in
tree-sitter query, process it and then export it as a task variable.

Notably, the list of variables includes captures prefixed with leading
underscore; they are removed after all calls to `build_context`, but it
makes it possible to capture something and then conditionally preserve
it (and perhaps modify it).

Release Notes:

- N/A
This commit is contained in:
Piotr Osiewicz 2024-05-22 19:45:43 +02:00 committed by GitHub
parent ba9449692e
commit 58796a8480
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 212 additions and 201 deletions

View file

@ -406,6 +406,7 @@ struct RunnableTasks {
templates: Vec<(TaskSourceKind, TaskTemplate)>, templates: Vec<(TaskSourceKind, TaskTemplate)>,
// We need the column at which the task context evaluation should take place. // We need the column at which the task context evaluation should take place.
column: u32, column: u32,
// Values of all named captures, including those starting with '_'
extra_variables: HashMap<String, String>, extra_variables: HashMap<String, String>,
} }
@ -3973,7 +3974,7 @@ impl Editor {
this.completion_tasks.clear(); this.completion_tasks.clear();
this.discard_inline_completion(false, cx); this.discard_inline_completion(false, cx);
let task_context = tasks.as_ref().zip(this.workspace.clone()).and_then( let tasks = tasks.as_ref().zip(this.workspace.clone()).and_then(
|(tasks, (workspace, _))| { |(tasks, (workspace, _))| {
let position = Point::new(buffer_row, tasks.1.column); let position = Point::new(buffer_row, tasks.1.column);
let range_start = buffer.read(cx).anchor_at(position, Bias::Right); let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
@ -3981,27 +3982,27 @@ impl Editor {
buffer: buffer.clone(), buffer: buffer.clone(),
range: range_start..range_start, range: range_start..range_start,
}; };
workspace
.update(cx, |workspace, cx| {
tasks::task_context_for_location(workspace, location, cx)
})
.ok()
.flatten()
},
);
let tasks = tasks.zip(task_context).map(|(tasks, mut task_context)| {
// Fill in the environmental variables from the tree-sitter captures // Fill in the environmental variables from the tree-sitter captures
let mut additional_task_variables = TaskVariables::default(); let mut captured_task_variables = TaskVariables::default();
for (capture_name, value) in tasks.1.extra_variables.clone() { for (capture_name, value) in tasks.1.extra_variables.clone() {
additional_task_variables.insert( captured_task_variables.insert(
task::VariableName::Custom(capture_name.into()), task::VariableName::Custom(capture_name.into()),
value.clone(), value.clone(),
); );
} }
task_context
.task_variables
.extend(additional_task_variables);
workspace
.update(cx, |workspace, cx| {
tasks::task_context_for_location(
captured_task_variables,
workspace,
location,
cx,
)
})
.ok()
.flatten()
.map(|task_context| {
Arc::new(ResolvedTasks { Arc::new(ResolvedTasks {
templates: tasks templates: tasks
.1 .1
@ -4013,11 +4014,13 @@ impl Editor {
.map(|task| (kind.clone(), task)) .map(|task| (kind.clone(), task))
}) })
.collect(), .collect(),
position: snapshot position: snapshot.buffer_snapshot.anchor_before(
.buffer_snapshot Point::new(multibuffer_point.row, tasks.1.column),
.anchor_before(Point::new(multibuffer_point.row, tasks.1.column)), ),
}) })
}); })
},
);
let spawn_straight_away = tasks let spawn_straight_away = tasks
.as_ref() .as_ref()
.map_or(false, |tasks| tasks.templates.len() == 1) .map_or(false, |tasks| tasks.templates.len() == 1)

View file

@ -1,17 +1,16 @@
use crate::Editor; use crate::Editor;
use std::{path::Path, sync::Arc};
use anyhow::Context; use anyhow::Context;
use gpui::WindowContext; use gpui::{Model, WindowContext};
use language::{BasicContextProvider, ContextProvider}; use language::ContextProvider;
use project::{Location, WorktreeId}; use project::{BasicContextProvider, Location, Project};
use task::{TaskContext, TaskVariables, VariableName}; use task::{TaskContext, TaskVariables, VariableName};
use text::Point; use text::Point;
use util::ResultExt; use util::ResultExt;
use workspace::Workspace; use workspace::Workspace;
pub(crate) fn task_context_for_location( pub(crate) fn task_context_for_location(
captured_variables: TaskVariables,
workspace: &Workspace, workspace: &Workspace,
location: Location, location: Location,
cx: &mut WindowContext<'_>, cx: &mut WindowContext<'_>,
@ -20,31 +19,16 @@ pub(crate) fn task_context_for_location(
.log_err() .log_err()
.flatten(); .flatten();
let buffer = location.buffer.clone(); let mut task_variables = combine_task_variables(
let language_context_provider = buffer captured_variables,
.read(cx)
.language()
.and_then(|language| language.context_provider())
.unwrap_or_else(|| Arc::new(BasicContextProvider));
let worktree_abs_path = buffer
.read(cx)
.file()
.map(|file| WorktreeId::from_usize(file.worktree_id()))
.and_then(|worktree_id| {
workspace
.project()
.read(cx)
.worktree_for_id(worktree_id, cx)
.map(|worktree| worktree.read(cx).abs_path())
});
let task_variables = combine_task_variables(
worktree_abs_path.as_deref(),
location, location,
language_context_provider.as_ref(), workspace.project().clone(),
cx, cx,
) )
.log_err()?; .log_err()?;
// Remove all custom entries starting with _, as they're not intended for use by the end user.
task_variables.sweep();
Some(TaskContext { Some(TaskContext {
cwd, cwd,
task_variables, task_variables,
@ -84,21 +68,21 @@ fn task_context_with_editor(
buffer, buffer,
range: start..end, range: start..end,
}; };
task_context_for_location(workspace, location.clone(), cx).map(|mut task_context| { let captured_variables = {
let mut variables = TaskVariables::default();
for range in location for range in location
.buffer .buffer
.read(cx) .read(cx)
.snapshot() .snapshot()
.runnable_ranges(location.range) .runnable_ranges(location.range.clone())
{ {
for (capture_name, value) in range.extra_captures { for (capture_name, value) in range.extra_captures {
task_context variables.insert(VariableName::Custom(capture_name.into()), value);
.task_variables
.insert(VariableName::Custom(capture_name.into()), value);
} }
} }
task_context variables
}) };
task_context_for_location(captured_variables, workspace, location.clone(), cx)
} }
pub fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> TaskContext { pub fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> TaskContext {
@ -114,24 +98,26 @@ pub fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> TaskCo
} }
fn combine_task_variables( fn combine_task_variables(
worktree_abs_path: Option<&Path>, mut captured_variables: TaskVariables,
location: Location, location: Location,
context_provider: &dyn ContextProvider, project: Model<Project>,
cx: &mut WindowContext<'_>, cx: &mut WindowContext<'_>,
) -> anyhow::Result<TaskVariables> { ) -> anyhow::Result<TaskVariables> {
if context_provider.is_basic() { let language_context_provider = location
context_provider .buffer
.build_context(worktree_abs_path, &location, cx) .read(cx)
.context("building basic provider context") .language()
} else { .and_then(|language| language.context_provider());
let mut basic_context = BasicContextProvider let baseline = BasicContextProvider::new(project)
.build_context(worktree_abs_path, &location, cx) .build_context(&captured_variables, &location, cx)
.context("building basic default context")?; .context("building basic default context")?;
basic_context.extend( captured_variables.extend(baseline);
context_provider if let Some(provider) = language_context_provider {
.build_context(worktree_abs_path, &location, cx) captured_variables.extend(
provider
.build_context(&captured_variables, &location, cx)
.context("building provider context ")?, .context("building provider context ")?,
); );
Ok(basic_context)
} }
Ok(captured_variables)
} }

View file

@ -30,10 +30,10 @@ use gpui::{
}; };
use http::{AsyncBody, HttpClient, HttpClientWithUrl}; use http::{AsyncBody, HttpClient, HttpClientWithUrl};
use language::{ use language::{
ContextProviderWithTasks, LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry, LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry, QUERY_FILENAME_PREFIXES,
QUERY_FILENAME_PREFIXES,
}; };
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use project::ContextProviderWithTasks;
use semantic_version::SemanticVersion; use semantic_version::SemanticVersion;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::Settings; use settings::Settings;

View file

@ -3024,6 +3024,7 @@ impl BufferSnapshot {
tags.sort_by_key(|(range, _)| range == &maximum_range); tags.sort_by_key(|(range, _)| range == &maximum_range);
let split_point = tags.partition_point(|(range, _)| range != &maximum_range); let split_point = tags.partition_point(|(range, _)| range != &maximum_range);
let (extra_captures, tags) = tags.split_at(split_point); let (extra_captures, tags) = tags.split_at(split_point);
let extra_captures = extra_captures let extra_captures = extra_captures
.into_iter() .into_iter()
.map(|(range, name)| { .map(|(range, name)| {

View file

@ -58,9 +58,7 @@ use std::{
}; };
use syntax_map::{QueryCursorHandle, SyntaxSnapshot}; use syntax_map::{QueryCursorHandle, SyntaxSnapshot};
use task::RunnableTag; use task::RunnableTag;
pub use task_context::{ pub use task_context::{ContextProvider, RunnableRange};
BasicContextProvider, ContextProvider, ContextProviderWithTasks, RunnableRange,
};
use theme::SyntaxTheme; use theme::SyntaxTheme;
use tree_sitter::{self, wasmtime, Query, QueryCursor, WasmStore}; use tree_sitter::{self, wasmtime, Query, QueryCursor, WasmStore};
@ -1016,7 +1014,7 @@ impl Language {
for (ix, name) in query.capture_names().iter().enumerate() { for (ix, name) in query.capture_names().iter().enumerate() {
if *name == "run" { if *name == "run" {
run_capture_index = Some(ix as u32); run_capture_index = Some(ix as u32);
} else if !name.starts_with('_') { } else {
runnable_tags.insert(ix as u32, RunnableTag(name.to_string().into())); runnable_tags.insert(ix as u32, RunnableTag(name.to_string().into()));
} }
} }

View file

@ -1,12 +1,12 @@
use std::{ops::Range, path::Path}; use std::ops::Range;
use crate::{Location, Runnable}; use crate::{Location, Runnable};
use anyhow::Result; use anyhow::Result;
use collections::HashMap; use collections::HashMap;
use gpui::AppContext; use gpui::AppContext;
use task::{TaskTemplates, TaskVariables, VariableName}; use task::{TaskTemplates, TaskVariables};
use text::{BufferId, Point, ToPoint}; use text::BufferId;
pub struct RunnableRange { pub struct RunnableRange {
pub buffer_id: BufferId, pub buffer_id: BufferId,
@ -22,7 +22,7 @@ pub trait ContextProvider: Send + Sync {
/// Builds a specific context to be placed on top of the basic one (replacing all conflicting entries) and to be used for task resolving later. /// Builds a specific context to be placed on top of the basic one (replacing all conflicting entries) and to be used for task resolving later.
fn build_context( fn build_context(
&self, &self,
_worktree_abs_path: Option<&Path>, _variables: &TaskVariables,
_location: &Location, _location: &Location,
_cx: &mut AppContext, _cx: &mut AppContext,
) -> Result<TaskVariables> { ) -> Result<TaskVariables> {
@ -33,100 +33,4 @@ pub trait ContextProvider: Send + Sync {
fn associated_tasks(&self) -> Option<TaskTemplates> { fn associated_tasks(&self) -> Option<TaskTemplates> {
None None
} }
// Determines whether the [`BasicContextProvider`] variables should be filled too (if `false`), or omitted (if `true`).
fn is_basic(&self) -> bool {
false
}
}
/// A context provided that tries to provide values for all non-custom [`VariableName`] variants for a currently opened file.
/// Applied as a base for every custom [`ContextProvider`] unless explicitly oped out.
pub struct BasicContextProvider;
impl ContextProvider for BasicContextProvider {
fn is_basic(&self) -> bool {
true
}
fn build_context(
&self,
worktree_abs_path: Option<&Path>,
location: &Location,
cx: &mut AppContext,
) -> Result<TaskVariables> {
let buffer = location.buffer.read(cx);
let buffer_snapshot = buffer.snapshot();
let symbols = buffer_snapshot.symbols_containing(location.range.start, None);
let symbol = symbols.unwrap_or_default().last().map(|symbol| {
let range = symbol
.name_ranges
.last()
.cloned()
.unwrap_or(0..symbol.text.len());
symbol.text[range].to_string()
});
let current_file = buffer
.file()
.and_then(|file| file.as_local())
.map(|file| file.abs_path(cx).to_string_lossy().to_string());
let Point { row, column } = location.range.start.to_point(&buffer_snapshot);
let row = row + 1;
let column = column + 1;
let selected_text = buffer
.chars_for_range(location.range.clone())
.collect::<String>();
let mut task_variables = TaskVariables::from_iter([
(VariableName::Row, row.to_string()),
(VariableName::Column, column.to_string()),
]);
if let Some(symbol) = symbol {
task_variables.insert(VariableName::Symbol, symbol);
}
if !selected_text.trim().is_empty() {
task_variables.insert(VariableName::SelectedText, selected_text);
}
if let Some(path) = current_file {
task_variables.insert(VariableName::File, path);
}
if let Some(worktree_path) = worktree_abs_path {
task_variables.insert(
VariableName::WorktreeRoot,
worktree_path.to_string_lossy().to_string(),
);
}
Ok(task_variables)
}
}
/// A ContextProvider that doesn't provide any task variables on it's own, though it has some associated tasks.
pub struct ContextProviderWithTasks {
templates: TaskTemplates,
}
impl ContextProviderWithTasks {
pub fn new(definitions: TaskTemplates) -> Self {
Self {
templates: definitions,
}
}
}
impl ContextProvider for ContextProviderWithTasks {
fn associated_tasks(&self) -> Option<TaskTemplates> {
Some(self.templates.clone())
}
fn build_context(
&self,
worktree_abs_path: Option<&Path>,
location: &Location,
cx: &mut AppContext,
) -> Result<TaskVariables> {
BasicContextProvider.build_context(worktree_abs_path, location, cx)
}
} }

View file

@ -1,4 +1,4 @@
use language::ContextProviderWithTasks; use project::ContextProviderWithTasks;
use task::{TaskTemplate, TaskTemplates, VariableName}; use task::{TaskTemplate, TaskTemplates, VariableName};
pub(super) fn bash_task_context() -> ContextProviderWithTasks { pub(super) fn bash_task_context() -> ContextProviderWithTasks {

View file

@ -16,7 +16,7 @@ use std::{
borrow::Cow, borrow::Cow,
ffi::{OsStr, OsString}, ffi::{OsStr, OsString},
ops::Range, ops::Range,
path::{Path, PathBuf}, path::PathBuf,
str, str,
sync::{ sync::{
atomic::{AtomicBool, Ordering::SeqCst}, atomic::{AtomicBool, Ordering::SeqCst},
@ -447,7 +447,7 @@ const GO_PACKAGE_TASK_VARIABLE: VariableName = VariableName::Custom(Cow::Borrowe
impl ContextProvider for GoContextProvider { impl ContextProvider for GoContextProvider {
fn build_context( fn build_context(
&self, &self,
worktree_abs_path: Option<&Path>, variables: &TaskVariables,
location: &Location, location: &Location,
cx: &mut gpui::AppContext, cx: &mut gpui::AppContext,
) -> Result<TaskVariables> { ) -> Result<TaskVariables> {
@ -465,7 +465,8 @@ impl ContextProvider for GoContextProvider {
// Prefer the relative form `./my-nested-package/is-here` over // Prefer the relative form `./my-nested-package/is-here` over
// absolute path, because it's more readable in the modal, but // absolute path, because it's more readable in the modal, but
// the absolute path also works. // the absolute path also works.
let package_name = worktree_abs_path let package_name = variables
.get(&VariableName::WorktreeRoot)
.and_then(|worktree_abs_path| buffer_dir.strip_prefix(worktree_abs_path).ok()) .and_then(|worktree_abs_path| buffer_dir.strip_prefix(worktree_abs_path).ok())
.map(|relative_pkg_dir| { .map(|relative_pkg_dir| {
if relative_pkg_dir.as_os_str().is_empty() { if relative_pkg_dir.as_os_str().is_empty() {

View file

@ -1,8 +1,9 @@
use anyhow::Result; use anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
use language::{ContextProviderWithTasks, LanguageServerName, LspAdapter, LspAdapterDelegate}; use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
use lsp::LanguageServerBinary; use lsp::LanguageServerBinary;
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use project::ContextProviderWithTasks;
use std::{ use std::{
any::Any, any::Any,
ffi::OsString, ffi::OsString,

View file

@ -328,7 +328,7 @@ const RUST_PACKAGE_TASK_VARIABLE: VariableName =
impl ContextProvider for RustContextProvider { impl ContextProvider for RustContextProvider {
fn build_context( fn build_context(
&self, &self,
_: Option<&Path>, _: &TaskVariables,
location: &Location, location: &Location,
cx: &mut gpui::AppContext, cx: &mut gpui::AppContext,
) -> Result<TaskVariables> { ) -> Result<TaskVariables> {

View file

@ -113,7 +113,9 @@ pub use fs::*;
pub use language::Location; pub use language::Location;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX; pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
pub use task_inventory::{Inventory, TaskSourceKind}; pub use task_inventory::{
BasicContextProvider, ContextProviderWithTasks, Inventory, TaskSourceKind,
};
pub use worktree::{ pub use worktree::{
DiagnosticSummary, Entry, EntryKind, File, LocalWorktree, PathChange, ProjectEntryId, DiagnosticSummary, Entry, EntryKind, File, LocalWorktree, PathChange, ProjectEntryId,
RepositoryEntry, UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, RepositoryEntry, UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId,

View file

@ -6,6 +6,7 @@ use std::{
sync::Arc, sync::Arc,
}; };
use anyhow::Result;
use collections::{btree_map, BTreeMap, VecDeque}; use collections::{btree_map, BTreeMap, VecDeque};
use futures::{ use futures::{
channel::mpsc::{unbounded, UnboundedSender}, channel::mpsc::{unbounded, UnboundedSender},
@ -13,13 +14,17 @@ use futures::{
}; };
use gpui::{AppContext, Context, Model, ModelContext, Task}; use gpui::{AppContext, Context, Model, ModelContext, Task};
use itertools::Itertools; use itertools::Itertools;
use language::Language; use language::{ContextProvider, Language, Location};
use task::{ use task::{
static_source::StaticSource, ResolvedTask, TaskContext, TaskId, TaskTemplate, VariableName, static_source::StaticSource, ResolvedTask, TaskContext, TaskId, TaskTemplate, TaskTemplates,
TaskVariables, VariableName,
}; };
use text::{Point, ToPoint};
use util::{post_inc, NumericPrefixWithSuffix}; use util::{post_inc, NumericPrefixWithSuffix};
use worktree::WorktreeId; use worktree::WorktreeId;
use crate::Project;
/// Inventory tracks available tasks for a given project. /// Inventory tracks available tasks for a given project.
pub struct Inventory { pub struct Inventory {
sources: Vec<SourceInInventory>, sources: Vec<SourceInInventory>,
@ -491,6 +496,102 @@ mod test_inventory {
} }
} }
/// A context provided that tries to provide values for all non-custom [`VariableName`] variants for a currently opened file.
/// Applied as a base for every custom [`ContextProvider`] unless explicitly oped out.
pub struct BasicContextProvider {
project: Model<Project>,
}
impl BasicContextProvider {
pub fn new(project: Model<Project>) -> Self {
Self { project }
}
}
impl ContextProvider for BasicContextProvider {
fn build_context(
&self,
_: &TaskVariables,
location: &Location,
cx: &mut AppContext,
) -> Result<TaskVariables> {
let buffer = location.buffer.read(cx);
let buffer_snapshot = buffer.snapshot();
let symbols = buffer_snapshot.symbols_containing(location.range.start, None);
let symbol = symbols.unwrap_or_default().last().map(|symbol| {
let range = symbol
.name_ranges
.last()
.cloned()
.unwrap_or(0..symbol.text.len());
symbol.text[range].to_string()
});
let current_file = buffer
.file()
.and_then(|file| file.as_local())
.map(|file| file.abs_path(cx).to_string_lossy().to_string());
let Point { row, column } = location.range.start.to_point(&buffer_snapshot);
let row = row + 1;
let column = column + 1;
let selected_text = buffer
.chars_for_range(location.range.clone())
.collect::<String>();
let mut task_variables = TaskVariables::from_iter([
(VariableName::Row, row.to_string()),
(VariableName::Column, column.to_string()),
]);
if let Some(symbol) = symbol {
task_variables.insert(VariableName::Symbol, symbol);
}
if !selected_text.trim().is_empty() {
task_variables.insert(VariableName::SelectedText, selected_text);
}
if let Some(path) = current_file {
task_variables.insert(VariableName::File, path);
}
let worktree_abs_path = buffer
.file()
.map(|file| WorktreeId::from_usize(file.worktree_id()))
.and_then(|worktree_id| {
self.project
.read(cx)
.worktree_for_id(worktree_id, cx)
.map(|worktree| worktree.read(cx).abs_path())
});
if let Some(worktree_path) = worktree_abs_path {
task_variables.insert(
VariableName::WorktreeRoot,
worktree_path.to_string_lossy().to_string(),
);
}
Ok(task_variables)
}
}
/// A ContextProvider that doesn't provide any task variables on it's own, though it has some associated tasks.
pub struct ContextProviderWithTasks {
templates: TaskTemplates,
}
impl ContextProviderWithTasks {
pub fn new(definitions: TaskTemplates) -> Self {
Self {
templates: definitions,
}
}
}
impl ContextProvider for ContextProviderWithTasks {
fn associated_tasks(&self) -> Option<TaskTemplates> {
Some(self.templates.clone())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use gpui::TestAppContext; use gpui::TestAppContext;

View file

@ -185,6 +185,20 @@ impl TaskVariables {
pub fn extend(&mut self, other: Self) { pub fn extend(&mut self, other: Self) {
self.0.extend(other.0); self.0.extend(other.0);
} }
/// Get the value associated with given variable name, if there is one.
pub fn get(&self, key: &VariableName) -> Option<&str> {
self.0.get(key).map(|s| s.as_str())
}
/// Clear out variables obtained from tree-sitter queries, which are prefixed with '_' character
pub fn sweep(&mut self) {
self.0.retain(|name, _| {
if let VariableName::Custom(name) = name {
!name.starts_with('_')
} else {
true
}
})
}
} }
impl FromIterator<(VariableName, String)> for TaskVariables { impl FromIterator<(VariableName, String)> for TaskVariables {

View file

@ -153,8 +153,8 @@ mod tests {
use editor::Editor; use editor::Editor;
use gpui::{Entity, TestAppContext}; use gpui::{Entity, TestAppContext};
use language::{BasicContextProvider, Language, LanguageConfig}; use language::{Language, LanguageConfig};
use project::{FakeFs, Project}; use project::{BasicContextProvider, FakeFs, Project};
use serde_json::json; use serde_json::json;
use task::{TaskContext, TaskVariables, VariableName}; use task::{TaskContext, TaskVariables, VariableName};
use ui::VisualContext; use ui::VisualContext;
@ -191,7 +191,7 @@ mod tests {
}), }),
) )
.await; .await;
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
let rust_language = Arc::new( let rust_language = Arc::new(
Language::new( Language::new(
LanguageConfig::default(), LanguageConfig::default(),
@ -203,7 +203,7 @@ mod tests {
name: (_) @name) @item"#, name: (_) @name) @item"#,
) )
.unwrap() .unwrap()
.with_context_provider(Some(Arc::new(BasicContextProvider))), .with_context_provider(Some(Arc::new(BasicContextProvider::new(project.clone())))),
); );
let typescript_language = Arc::new( let typescript_language = Arc::new(
@ -221,9 +221,9 @@ mod tests {
")" @context)) @item"#, ")" @context)) @item"#,
) )
.unwrap() .unwrap()
.with_context_provider(Some(Arc::new(BasicContextProvider))), .with_context_provider(Some(Arc::new(BasicContextProvider::new(project.clone())))),
); );
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
let worktree_id = project.update(cx, |project, cx| { let worktree_id = project.update(cx, |project, cx| {
project.worktrees().next().unwrap().read(cx).id() project.worktrees().next().unwrap().read(cx).id()
}); });

View file

@ -540,8 +540,8 @@ mod tests {
use editor::Editor; use editor::Editor;
use gpui::{TestAppContext, VisualTestContext}; use gpui::{TestAppContext, VisualTestContext};
use language::{ContextProviderWithTasks, Language, LanguageConfig, LanguageMatcher, Point}; use language::{Language, LanguageConfig, LanguageMatcher, Point};
use project::{FakeFs, Project}; use project::{ContextProviderWithTasks, FakeFs, Project};
use serde_json::json; use serde_json::json;
use task::TaskTemplates; use task::TaskTemplates;
use workspace::CloseInactiveTabsAndPanes; use workspace::CloseInactiveTabsAndPanes;