mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-28 09:54:33 +00:00
go: Add runnables for Go (#12003)
Implemented runnables for specially for running tests for Go. I'm grateful for your feedback because this is my first experience with Rust and Zed codebase. ![resim](https://github.com/zed-industries/zed/assets/1047345/789b31da-554f-47cd-a08c-444eced104f4) https://github.com/zed-industries/zed/assets/1047345/ae1abd9e-3657-4322-9c28-02d0752b5ccd Release Notes: - Added Runnables/Tasks for: - Run test functions which start with "Test" - Run subtests - Run benchmark tests - Run main function --------- Co-authored-by: Thorsten Ball <mrnugget@gmail.com> Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
parent
5665cad250
commit
ddb551c794
4 changed files with 155 additions and 23 deletions
|
@ -39,6 +39,9 @@ impl GoLspAdapter {
|
|||
|
||||
lazy_static! {
|
||||
static ref GOPLS_VERSION_REGEX: Regex = Regex::new(r"\d+\.\d+\.\d+").unwrap();
|
||||
static ref GO_EXTRACT_SUBTEST_NAME_REGEX: Regex =
|
||||
Regex::new(r#".*t\.Run\("([^"]*)".*"#).unwrap();
|
||||
static ref GO_ESCAPE_SUBTEST_NAME_REGEX: Regex = Regex::new(r#"[.*+?^${}()|\[\]\\]"#).unwrap();
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
|
@ -443,6 +446,8 @@ fn adjust_runs(
|
|||
pub(crate) struct GoContextProvider;
|
||||
|
||||
const GO_PACKAGE_TASK_VARIABLE: VariableName = VariableName::Custom(Cow::Borrowed("GO_PACKAGE"));
|
||||
const GO_SUBTEST_NAME_TASK_VARIABLE: VariableName =
|
||||
VariableName::Custom(Cow::Borrowed("GO_SUBTEST_NAME"));
|
||||
|
||||
impl ContextProvider for GoContextProvider {
|
||||
fn build_context(
|
||||
|
@ -457,11 +462,10 @@ impl ContextProvider for GoContextProvider {
|
|||
.file()
|
||||
.and_then(|file| Some(file.as_local()?.abs_path(cx)));
|
||||
|
||||
Ok(
|
||||
if let Some(buffer_dir) = local_abs_path
|
||||
.as_deref()
|
||||
.and_then(|local_abs_path| local_abs_path.parent())
|
||||
{
|
||||
let go_package_variable = local_abs_path
|
||||
.as_deref()
|
||||
.and_then(|local_abs_path| local_abs_path.parent())
|
||||
.map(|buffer_dir| {
|
||||
// Prefer the relative form `./my-nested-package/is-here` over
|
||||
// absolute path, because it's more readable in the modal, but
|
||||
// the absolute path also works.
|
||||
|
@ -477,14 +481,19 @@ impl ContextProvider for GoContextProvider {
|
|||
})
|
||||
.unwrap_or_else(|| format!("{}", buffer_dir.to_string_lossy()));
|
||||
|
||||
TaskVariables::from_iter(Some((
|
||||
GO_PACKAGE_TASK_VARIABLE.clone(),
|
||||
package_name.to_string(),
|
||||
)))
|
||||
} else {
|
||||
TaskVariables::default()
|
||||
},
|
||||
)
|
||||
(GO_PACKAGE_TASK_VARIABLE.clone(), package_name.to_string())
|
||||
});
|
||||
|
||||
let _subtest_name = variables.get(&VariableName::Custom(Cow::Borrowed("_subtest_name")));
|
||||
|
||||
let go_subtest_variable = extract_subtest_name(_subtest_name.unwrap_or(""))
|
||||
.map(|subtest_name| (GO_SUBTEST_NAME_TASK_VARIABLE.clone(), subtest_name));
|
||||
|
||||
Ok(TaskVariables::from_iter(
|
||||
[go_package_variable, go_subtest_variable]
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
))
|
||||
}
|
||||
|
||||
fn associated_tasks(&self) -> Option<TaskTemplates> {
|
||||
|
@ -517,6 +526,46 @@ impl ContextProvider for GoContextProvider {
|
|||
args: vec!["test".into(), "./...".into()],
|
||||
..TaskTemplate::default()
|
||||
},
|
||||
TaskTemplate {
|
||||
label: format!(
|
||||
"go test {} -run {}/{}",
|
||||
GO_PACKAGE_TASK_VARIABLE.template_value(),
|
||||
VariableName::Symbol.template_value(),
|
||||
GO_SUBTEST_NAME_TASK_VARIABLE.template_value(),
|
||||
),
|
||||
command: "go".into(),
|
||||
args: vec![
|
||||
"test".into(),
|
||||
GO_PACKAGE_TASK_VARIABLE.template_value(),
|
||||
"-v".into(),
|
||||
"-run".into(),
|
||||
format!(
|
||||
"^{}$/^{}$",
|
||||
VariableName::Symbol.template_value(),
|
||||
GO_SUBTEST_NAME_TASK_VARIABLE.template_value(),
|
||||
),
|
||||
],
|
||||
tags: vec!["go-subtest".to_owned()],
|
||||
..TaskTemplate::default()
|
||||
},
|
||||
TaskTemplate {
|
||||
label: format!(
|
||||
"go test {} -bench {}",
|
||||
GO_PACKAGE_TASK_VARIABLE.template_value(),
|
||||
VariableName::Symbol.template_value()
|
||||
),
|
||||
command: "go".into(),
|
||||
args: vec![
|
||||
"test".into(),
|
||||
GO_PACKAGE_TASK_VARIABLE.template_value(),
|
||||
"-benchmem".into(),
|
||||
"-run=^$".into(),
|
||||
"-bench".into(),
|
||||
format!("^{}$", VariableName::Symbol.template_value()),
|
||||
],
|
||||
tags: vec!["go-benchmark".to_owned()],
|
||||
..TaskTemplate::default()
|
||||
},
|
||||
TaskTemplate {
|
||||
label: format!("go run {}", GO_PACKAGE_TASK_VARIABLE.template_value(),),
|
||||
command: "go".into(),
|
||||
|
@ -528,6 +577,18 @@ impl ContextProvider for GoContextProvider {
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_subtest_name(input: &str) -> Option<String> {
|
||||
let replaced_spaces = input.trim_matches('"').replace(' ', "_");
|
||||
|
||||
Some(
|
||||
GO_ESCAPE_SUBTEST_NAME_REGEX
|
||||
.replace_all(&replaced_spaces, |caps: ®ex::Captures| {
|
||||
format!("\\{}", &caps[0])
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -1,15 +1,62 @@
|
|||
; Functions names start with `Test`
|
||||
(
|
||||
(
|
||||
(function_declaration name: (_) @run
|
||||
(#match? @run "^Test.*"))
|
||||
) @_
|
||||
(#set! tag go-test)
|
||||
(
|
||||
(function_declaration name: (_) @run
|
||||
(#match? @run "^Test.*"))
|
||||
) @_
|
||||
(#set! tag go-test)
|
||||
)
|
||||
|
||||
; `t.Run`
|
||||
(
|
||||
(
|
||||
(function_declaration name: (_) @run
|
||||
(#eq? @run "main"))
|
||||
) @_
|
||||
(#set! tag go-main)
|
||||
(
|
||||
(call_expression
|
||||
function: (
|
||||
selector_expression
|
||||
field: _ @run @_name
|
||||
(#eq? @_name "Run")
|
||||
)
|
||||
arguments: (
|
||||
argument_list
|
||||
.
|
||||
(interpreted_string_literal) @_subtest_name
|
||||
.
|
||||
(func_literal
|
||||
parameters: (
|
||||
parameter_list
|
||||
(parameter_declaration
|
||||
name: (identifier) @_param_name
|
||||
type: (pointer_type
|
||||
(qualified_type
|
||||
package: (package_identifier) @_pkg
|
||||
name: (type_identifier) @_type
|
||||
(#eq? @_pkg "testing")
|
||||
(#eq? @_type "T")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
) @_second_argument
|
||||
)
|
||||
)
|
||||
) @_
|
||||
(#set! tag go-subtest)
|
||||
)
|
||||
|
||||
; Functions names start with `Benchmark`
|
||||
(
|
||||
(
|
||||
(function_declaration name: (_) @run @_name
|
||||
(#match? @_name "^Benchmark.+"))
|
||||
) @_
|
||||
(#set! tag go-benchmark)
|
||||
)
|
||||
|
||||
; go run
|
||||
(
|
||||
(
|
||||
(function_declaration name: (_) @run
|
||||
(#eq? @run "main"))
|
||||
) @_
|
||||
(#set! tag go-main)
|
||||
)
|
||||
|
|
|
@ -113,6 +113,7 @@ pub fn init(
|
|||
vec![Arc::new(go::GoLspAdapter)],
|
||||
GoContextProvider
|
||||
);
|
||||
|
||||
language!(
|
||||
"json",
|
||||
vec![Arc::new(json::JsonLspAdapter::new(
|
||||
|
|
|
@ -679,4 +679,27 @@ mod tests {
|
|||
expected.sort_by_key(|var| var.to_string());
|
||||
assert_eq!(resolved_variables, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn substitute_funky_labels() {
|
||||
let faulty_go_test = TaskTemplate {
|
||||
label: format!(
|
||||
"go test {}/{}",
|
||||
VariableName::Symbol.template_value(),
|
||||
VariableName::Symbol.template_value(),
|
||||
),
|
||||
command: "go".into(),
|
||||
args: vec![format!(
|
||||
"^{}$/^{}$",
|
||||
VariableName::Symbol.template_value(),
|
||||
VariableName::Symbol.template_value()
|
||||
)],
|
||||
..TaskTemplate::default()
|
||||
};
|
||||
let mut context = TaskContext::default();
|
||||
context
|
||||
.task_variables
|
||||
.insert(VariableName::Symbol, "my-symbol".to_string());
|
||||
assert!(faulty_go_test.resolve_task("base", &context).is_some());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue