Merge Zed task context providing logic (#10544)

Before, `tasks_ui` set most of the context with `SymbolContextProvider`
providing the symbol data part of the context. Now, there's a
`BasicContextProvider` that forms all standard Zed context and it
automatically serves as a base, with no need for other providers like
`RustContextProvider` to call it as before.

Also, stop adding `SelectedText` task variable into the context for
blank text selection.

Release Notes:

- N/A
This commit is contained in:
Kirill Bulatov 2024-04-15 10:52:15 +02:00 committed by GitHub
parent 97c5cffbe3
commit 573ba83034
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 173 additions and 124 deletions

View file

@ -1,12 +1,16 @@
use std::{path::PathBuf, sync::Arc};
use std::{
path::{Path, PathBuf},
sync::Arc,
};
use ::settings::Settings;
use anyhow::Context;
use editor::Editor;
use gpui::{AppContext, ViewContext, WindowContext};
use language::{Language, Point};
use language::{BasicContextProvider, ContextProvider, Language};
use modal::{Spawn, TasksModal};
use project::{Location, TaskSourceKind, WorktreeId};
use task::{ResolvedTask, TaskContext, TaskTemplate, TaskVariables, VariableName};
use task::{ResolvedTask, TaskContext, TaskTemplate, TaskVariables};
use util::ResultExt;
use workspace::Workspace;
@ -146,100 +150,88 @@ fn active_item_selection_properties(
}
fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> TaskContext {
let cwd = task_cwd(workspace, cx).log_err().flatten();
let current_editor = workspace
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx));
if let Some(current_editor) = current_editor {
(|| {
let editor = current_editor.read(cx);
fn task_context_impl(workspace: &Workspace, cx: &mut WindowContext<'_>) -> Option<TaskContext> {
let cwd = task_cwd(workspace, cx).log_err().flatten();
let editor = workspace
.active_item(cx)
.and_then(|item| item.act_as::<Editor>(cx))?;
let (selection, buffer, editor_snapshot) = editor.update(cx, |editor, cx| {
let selection = editor.selections.newest::<usize>(cx);
let (buffer, _, _) = editor
.buffer()
.read(cx)
.point_to_buffer_offset(selection.start, cx)?;
let snapshot = editor.snapshot(cx);
Some((selection, buffer, snapshot))
})?;
let language_context_provider = buffer
.read(cx)
.language()
.and_then(|language| language.context_provider())?;
current_editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
let selection_range = selection.range();
let start = snapshot
.display_snapshot
.buffer_snapshot
.anchor_after(selection_range.start)
.text_anchor;
let end = snapshot
.display_snapshot
.buffer_snapshot
.anchor_after(selection_range.end)
.text_anchor;
let Point { row, column } = snapshot
.display_snapshot
.buffer_snapshot
.offset_to_point(selection_range.start);
let row = row + 1;
let column = column + 1;
let location = Location {
buffer: buffer.clone(),
range: start..end,
};
let current_file = location
.buffer
let selection_range = selection.range();
let start = editor_snapshot
.display_snapshot
.buffer_snapshot
.anchor_after(selection_range.start)
.text_anchor;
let end = editor_snapshot
.display_snapshot
.buffer_snapshot
.anchor_after(selection_range.end)
.text_anchor;
let worktree_abs_path = buffer
.read(cx)
.file()
.map(|file| WorktreeId::from_usize(file.worktree_id()))
.and_then(|worktree_id| {
workspace
.project()
.read(cx)
.file()
.and_then(|file| file.as_local())
.map(|file| file.abs_path(cx).to_string_lossy().to_string());
let worktree_id = location
.buffer
.read(cx)
.file()
.map(|file| WorktreeId::from_usize(file.worktree_id()));
let context = buffer
.read(cx)
.language()
.and_then(|language| language.context_provider())
.and_then(|provider| provider.build_context(location, cx).ok());
let worktree_path = worktree_id.and_then(|worktree_id| {
workspace
.project()
.read(cx)
.worktree_for_id(worktree_id, cx)
.map(|worktree| worktree.read(cx).abs_path().to_string_lossy().to_string())
});
let selected_text = buffer.read(cx).chars_for_range(selection_range).collect();
let mut task_variables = TaskVariables::from_iter([
(VariableName::Row, row.to_string()),
(VariableName::Column, column.to_string()),
(VariableName::SelectedText, selected_text),
]);
if let Some(path) = current_file {
task_variables.insert(VariableName::File, path);
}
if let Some(worktree_path) = worktree_path {
task_variables.insert(VariableName::WorktreeRoot, worktree_path);
}
if let Some(language_context) = context {
task_variables.extend(language_context);
}
Some(TaskContext {
cwd: cwd.clone(),
task_variables,
})
})
})()
.unwrap_or_else(|| TaskContext {
.worktree_for_id(worktree_id, cx)
.map(|worktree| worktree.read(cx).abs_path())
});
let location = Location {
buffer,
range: start..end,
};
let task_variables = combine_task_variables(
worktree_abs_path.as_deref(),
location,
language_context_provider.as_ref(),
cx,
)
.log_err()?;
Some(TaskContext {
cwd,
task_variables: Default::default(),
task_variables,
})
}
task_context_impl(workspace, cx).unwrap_or_default()
}
fn combine_task_variables(
worktree_abs_path: Option<&Path>,
location: Location,
context_provider: &dyn ContextProvider,
cx: &mut WindowContext<'_>,
) -> anyhow::Result<TaskVariables> {
if context_provider.is_basic() {
context_provider
.build_context(worktree_abs_path, &location, cx)
.context("building basic provider context")
} else {
TaskContext {
cwd,
task_variables: Default::default(),
}
let mut basic_context = BasicContextProvider
.build_context(worktree_abs_path, &location, cx)
.context("building basic default context")?;
basic_context.extend(
context_provider
.build_context(worktree_abs_path, &location, cx)
.context("building provider context ")?,
);
Ok(basic_context)
}
}
@ -325,7 +317,7 @@ mod tests {
use editor::Editor;
use gpui::{Entity, TestAppContext};
use language::{Language, LanguageConfig, SymbolContextProvider};
use language::{BasicContextProvider, Language, LanguageConfig};
use project::{FakeFs, Project};
use serde_json::json;
use task::{TaskContext, TaskVariables, VariableName};
@ -375,7 +367,7 @@ mod tests {
name: (_) @name) @item"#,
)
.unwrap()
.with_context_provider(Some(Arc::new(SymbolContextProvider))),
.with_context_provider(Some(Arc::new(BasicContextProvider))),
);
let typescript_language = Arc::new(
@ -393,7 +385,7 @@ mod tests {
")" @context)) @item"#,
)
.unwrap()
.with_context_provider(Some(Arc::new(SymbolContextProvider))),
.with_context_provider(Some(Arc::new(BasicContextProvider))),
);
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
let worktree_id = project.update(cx, |project, cx| {
@ -435,7 +427,6 @@ mod tests {
(VariableName::WorktreeRoot, "/dir".into()),
(VariableName::Row, "1".into()),
(VariableName::Column, "1".into()),
(VariableName::SelectedText, "".into())
])
}
);
@ -469,7 +460,6 @@ mod tests {
(VariableName::WorktreeRoot, "/dir".into()),
(VariableName::Row, "1".into()),
(VariableName::Column, "1".into()),
(VariableName::SelectedText, "".into()),
(VariableName::Symbol, "this_is_a_test".into()),
])
}