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.  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! {
|
lazy_static! {
|
||||||
static ref GOPLS_VERSION_REGEX: Regex = Regex::new(r"\d+\.\d+\.\d+").unwrap();
|
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)]
|
#[async_trait(?Send)]
|
||||||
|
@ -443,6 +446,8 @@ fn adjust_runs(
|
||||||
pub(crate) struct GoContextProvider;
|
pub(crate) struct GoContextProvider;
|
||||||
|
|
||||||
const GO_PACKAGE_TASK_VARIABLE: VariableName = VariableName::Custom(Cow::Borrowed("GO_PACKAGE"));
|
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 {
|
impl ContextProvider for GoContextProvider {
|
||||||
fn build_context(
|
fn build_context(
|
||||||
|
@ -457,11 +462,10 @@ impl ContextProvider for GoContextProvider {
|
||||||
.file()
|
.file()
|
||||||
.and_then(|file| Some(file.as_local()?.abs_path(cx)));
|
.and_then(|file| Some(file.as_local()?.abs_path(cx)));
|
||||||
|
|
||||||
Ok(
|
let go_package_variable = local_abs_path
|
||||||
if let Some(buffer_dir) = local_abs_path
|
.as_deref()
|
||||||
.as_deref()
|
.and_then(|local_abs_path| local_abs_path.parent())
|
||||||
.and_then(|local_abs_path| local_abs_path.parent())
|
.map(|buffer_dir| {
|
||||||
{
|
|
||||||
// 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.
|
||||||
|
@ -477,14 +481,19 @@ impl ContextProvider for GoContextProvider {
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| format!("{}", buffer_dir.to_string_lossy()));
|
.unwrap_or_else(|| format!("{}", buffer_dir.to_string_lossy()));
|
||||||
|
|
||||||
TaskVariables::from_iter(Some((
|
(GO_PACKAGE_TASK_VARIABLE.clone(), package_name.to_string())
|
||||||
GO_PACKAGE_TASK_VARIABLE.clone(),
|
});
|
||||||
package_name.to_string(),
|
|
||||||
)))
|
let _subtest_name = variables.get(&VariableName::Custom(Cow::Borrowed("_subtest_name")));
|
||||||
} else {
|
|
||||||
TaskVariables::default()
|
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> {
|
fn associated_tasks(&self) -> Option<TaskTemplates> {
|
||||||
|
@ -517,6 +526,46 @@ impl ContextProvider for GoContextProvider {
|
||||||
args: vec!["test".into(), "./...".into()],
|
args: vec!["test".into(), "./...".into()],
|
||||||
..TaskTemplate::default()
|
..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 {
|
TaskTemplate {
|
||||||
label: format!("go run {}", GO_PACKAGE_TASK_VARIABLE.template_value(),),
|
label: format!("go run {}", GO_PACKAGE_TASK_VARIABLE.template_value(),),
|
||||||
command: "go".into(),
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,15 +1,62 @@
|
||||||
|
; Functions names start with `Test`
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
(function_declaration name: (_) @run
|
(function_declaration name: (_) @run
|
||||||
(#match? @run "^Test.*"))
|
(#match? @run "^Test.*"))
|
||||||
) @_
|
) @_
|
||||||
(#set! tag go-test)
|
(#set! tag go-test)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
; `t.Run`
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
(function_declaration name: (_) @run
|
(call_expression
|
||||||
(#eq? @run "main"))
|
function: (
|
||||||
) @_
|
selector_expression
|
||||||
(#set! tag go-main)
|
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)],
|
vec![Arc::new(go::GoLspAdapter)],
|
||||||
GoContextProvider
|
GoContextProvider
|
||||||
);
|
);
|
||||||
|
|
||||||
language!(
|
language!(
|
||||||
"json",
|
"json",
|
||||||
vec![Arc::new(json::JsonLspAdapter::new(
|
vec![Arc::new(json::JsonLspAdapter::new(
|
||||||
|
|
|
@ -679,4 +679,27 @@ mod tests {
|
||||||
expected.sort_by_key(|var| var.to_string());
|
expected.sort_by_key(|var| var.to_string());
|
||||||
assert_eq!(resolved_variables, expected)
|
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…
Add table
Add a link
Reference in a new issue