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:
Anıl Şenay 2024-05-26 16:16:52 +03:00 committed by GitHub
parent 5665cad250
commit ddb551c794
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 155 additions and 23 deletions

View file

@ -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: &regex::Captures| {
format!("\\{}", &caps[0])
})
.to_string(),
)
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -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)
)

View file

@ -113,6 +113,7 @@ pub fn init(
vec![Arc::new(go::GoLspAdapter)],
GoContextProvider
);
language!(
"json",
vec![Arc::new(json::JsonLspAdapter::new(

View file

@ -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());
}
}