diff --git a/syntax/compile_xstr.go b/syntax/compile_xstr.go index 53ed8faa..f1d341c2 100644 --- a/syntax/compile_xstr.go +++ b/syntax/compile_xstr.go @@ -17,6 +17,7 @@ var ( indentRE = regexp.MustCompile(`(\n[\t ]*)\z`) firstIndentRE = regexp.MustCompile(`\A((\n[\t ]+)(?:\n)|(\n))`) lastSpacesRE = regexp.MustCompile(`\n([ \t]*)\z`) + spaces = regexp.MustCompile(`\A[\t ]+`) ) func (pc ParseContext) compileExpandableString(ctx context.Context, b ast.Branch, c ast.Children) (rel.Expr, error) { @@ -25,6 +26,13 @@ func (pc ParseContext) compileExpandableString(ctx context.Context, b ast.Branch parts := []interface{}{} ws := quote[2:] + + // retain initial spaces + if spaces.MatchString(ws) { + parts = append(parts, ws) + ws = "" + } + trim := "" trimIndent := func(s string) { s = ws + s diff --git a/syntax/parse_string_test.go b/syntax/parse_string_test.go index a85725bd..f3ce7fa9 100644 --- a/syntax/parse_string_test.go +++ b/syntax/parse_string_test.go @@ -16,6 +16,18 @@ func TestXStringSimple(t *testing.T) { AssertCodesEvalToSameValue(t, `"a42k3.142z" `, `$"a${6*7}k${//math.pi:.3f}z"`) } +func TestXStringRetainInitialSpaces(t *testing.T) { + t.Parallel() + + AssertCodesEvalToSameValue(t, `" " `, `$" " `) + AssertCodesEvalToSameValue(t, `" x" `, `$" x" `) + AssertCodesEvalToSameValue(t, `" abc" `, `$" ${'abc'}"`) + AssertCodesEvalToSameValue(t, `" abc"`, `$" ${$' abc'}"`) + + // suppress empty line + AssertCodesEvalToSameValue(t, `""`, `$" ${''}"`) +} + func TestXStringBackquote(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `"" `, "$``")