From fd05f17fa7e0253bc98698e84a1f8939bcb6616f Mon Sep 17 00:00:00 2001
From: Lukas Spiss <35728419+Spissable@users.noreply.github.com>
Date: Fri, 18 Jul 2025 12:38:18 +0100
Subject: [PATCH] go: Support raw string subtest names (#34636)
Currently, we're not able to run Go sub-tests that have a raw string
(e.g. we're using multi-line names a lot) via the UI. I added the
changes that are needed, plus a handful of tests to cover the basics.
Quick comparison:
Before:
After:
Release Notes:
- Added support for Go subtest runner with raw string names
---
crates/languages/src/go.rs | 121 +++++++++++++++++++++++++-
crates/languages/src/go/runnables.scm | 5 +-
2 files changed, 121 insertions(+), 5 deletions(-)
diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs
index 25aa5a67b9..16c1b67203 100644
--- a/crates/languages/src/go.rs
+++ b/crates/languages/src/go.rs
@@ -41,7 +41,7 @@ static VERSION_REGEX: LazyLock =
LazyLock::new(|| Regex::new(r"\d+\.\d+\.\d+").expect("Failed to create VERSION_REGEX"));
static GO_ESCAPE_SUBTEST_NAME_REGEX: LazyLock = LazyLock::new(|| {
- Regex::new(r#"[.*+?^${}()|\[\]\\]"#).expect("Failed to create GO_ESCAPE_SUBTEST_NAME_REGEX")
+ Regex::new(r#"[.*+?^${}()|\[\]\\"']"#).expect("Failed to create GO_ESCAPE_SUBTEST_NAME_REGEX")
});
const BINARY: &str = if cfg!(target_os = "windows") {
@@ -685,11 +685,20 @@ impl ContextProvider for GoContextProvider {
}
fn extract_subtest_name(input: &str) -> Option {
- let replaced_spaces = input.trim_matches('"').replace(' ', "_");
+ let content = if input.starts_with('`') && input.ends_with('`') {
+ input.trim_matches('`')
+ } else {
+ input.trim_matches('"')
+ };
+
+ let processed = content
+ .chars()
+ .map(|c| if c.is_whitespace() { '_' } else { c })
+ .collect::();
Some(
GO_ESCAPE_SUBTEST_NAME_REGEX
- .replace_all(&replaced_spaces, |caps: ®ex::Captures| {
+ .replace_all(&processed, |caps: ®ex::Captures| {
format!("\\{}", &caps[0])
})
.to_string(),
@@ -700,7 +709,7 @@ fn extract_subtest_name(input: &str) -> Option {
mod tests {
use super::*;
use crate::language;
- use gpui::Hsla;
+ use gpui::{AppContext, Hsla, TestAppContext};
use theme::SyntaxTheme;
#[gpui::test]
@@ -790,4 +799,108 @@ mod tests {
})
);
}
+
+ #[gpui::test]
+ fn test_go_runnable_detection(cx: &mut TestAppContext) {
+ let language = language("go", tree_sitter_go::LANGUAGE.into());
+
+ let interpreted_string_subtest = r#"
+ package main
+
+ import "testing"
+
+ func TestExample(t *testing.T) {
+ t.Run("subtest with double quotes", func(t *testing.T) {
+ // test code
+ })
+ }
+ "#;
+
+ let raw_string_subtest = r#"
+ package main
+
+ import "testing"
+
+ func TestExample(t *testing.T) {
+ t.Run(`subtest with
+ multiline
+ backticks`, func(t *testing.T) {
+ // test code
+ })
+ }
+ "#;
+
+ let buffer = cx.new(|cx| {
+ crate::Buffer::local(interpreted_string_subtest, cx).with_language(language.clone(), cx)
+ });
+ cx.executor().run_until_parked();
+
+ let runnables: Vec<_> = buffer.update(cx, |buffer, _| {
+ let snapshot = buffer.snapshot();
+ snapshot
+ .runnable_ranges(0..interpreted_string_subtest.len())
+ .collect()
+ });
+
+ assert!(
+ runnables.len() == 2,
+ "Should find test function and subtest with double quotes, found: {}",
+ runnables.len()
+ );
+
+ let buffer = cx.new(|cx| {
+ crate::Buffer::local(raw_string_subtest, cx).with_language(language.clone(), cx)
+ });
+ cx.executor().run_until_parked();
+
+ let runnables: Vec<_> = buffer.update(cx, |buffer, _| {
+ let snapshot = buffer.snapshot();
+ snapshot
+ .runnable_ranges(0..raw_string_subtest.len())
+ .collect()
+ });
+
+ assert!(
+ runnables.len() == 2,
+ "Should find test function and subtest with backticks, found: {}",
+ runnables.len()
+ );
+ }
+
+ #[test]
+ fn test_extract_subtest_name() {
+ // Interpreted string literal
+ let input_double_quoted = r#""subtest with double quotes""#;
+ let result = extract_subtest_name(input_double_quoted);
+ assert_eq!(result, Some(r#"subtest_with_double_quotes"#.to_string()));
+
+ let input_double_quoted_with_backticks = r#""test with `backticks` inside""#;
+ let result = extract_subtest_name(input_double_quoted_with_backticks);
+ assert_eq!(result, Some(r#"test_with_`backticks`_inside"#.to_string()));
+
+ // Raw string literal
+ let input_with_backticks = r#"`subtest with backticks`"#;
+ let result = extract_subtest_name(input_with_backticks);
+ assert_eq!(result, Some(r#"subtest_with_backticks"#.to_string()));
+
+ let input_raw_with_quotes = r#"`test with "quotes" and other chars`"#;
+ let result = extract_subtest_name(input_raw_with_quotes);
+ assert_eq!(
+ result,
+ Some(r#"test_with_\"quotes\"_and_other_chars"#.to_string())
+ );
+
+ let input_multiline = r#"`subtest with
+ multiline
+ backticks`"#;
+ let result = extract_subtest_name(input_multiline);
+ assert_eq!(
+ result,
+ Some(r#"subtest_with_________multiline_________backticks"#.to_string())
+ );
+
+ let input_with_double_quotes = r#"`test with "double quotes"`"#;
+ let result = extract_subtest_name(input_with_double_quotes);
+ assert_eq!(result, Some(r#"test_with_\"double_quotes\""#.to_string()));
+ }
}
diff --git a/crates/languages/src/go/runnables.scm b/crates/languages/src/go/runnables.scm
index bdeb77b46c..49e112b860 100644
--- a/crates/languages/src/go/runnables.scm
+++ b/crates/languages/src/go/runnables.scm
@@ -38,7 +38,10 @@
arguments: (
argument_list
.
- (interpreted_string_literal) @_subtest_name
+ [
+ (interpreted_string_literal)
+ (raw_string_literal)
+ ] @_subtest_name
.
(func_literal
parameters: (