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