Skip to content

Quote multiline scalars that start with tabs - #388

Open
Guflly wants to merge 1 commit into
yaml:mainfrom
Guflly:fix/leading-tab-scalar
Open

Guflly wants to merge 1 commit into
yaml:mainfrom
Guflly:fix/leading-tab-scalar

Conversation

@Guflly

@Guflly Guflly commented Aug 3, 2026

Copy link
Copy Markdown

Fixes #383.

Multiline values whose first content character is a tab are now emitted as double-quoted scalars, so output from Marshal can be read back by Unmarshal. The same selection is used for unstyled Nodes created with SetString.

Tabs on later lines continue to use literal style. Tests cover direct leading tabs, initial line breaks, map values, and Nodes.

@ccoVeille ccoVeille left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for PR.

I reviewed it.

Comment thread yaml_test.go
{
"\tThis starts with tab\nand is long enough\nfor literal style",
"|-\n \tThis starts with tab\n and is long enough\n for literal style\n",
"\"\\tThis starts with tab\\nand is long enough\\nfor literal style\"\n",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could use string literal (here and everywhere) to improve readability

Suggested change
"\"\\tThis starts with tab\\nand is long enough\\nfor literal style\"\n",
`"\tThis starts with tab\nand is long enough\nfor literal style"`+"\n",

Comment thread internal/libyaml/node.go
Comment on lines +253 to +256
withoutLeadingBreaks := strings.TrimLeft(s, "\r\n\u0085\u2028\u2029")
if strings.HasPrefix(withoutLeadingBreaks, "\t") {
return false
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised to see the Unicode separators in addition of \r\n

Could you cover this with tests?

Also I would like to see these Unicode character to constants with clear name and comment.

I'm a bit surprised by the fact it trims everything. It could have consequences for multi line maybe.

Something like \n\n\n\t

Maybe I'm wrong.

I feel like the issue you try to fix is about having some characters in front of \t

I feel like using strings.HasPrefix with iterative checks such as \r\n\t, \n\t, \uxxxx\t

Comment thread yaml_test.go
Comment on lines +3031 to +3076
func TestLeadingTabScalarRoundTrip(t *testing.T) {
testCases := []struct {
value string
mapYAML string
scalarYAML string
}{
{
"\tthis\nis\nmultiline",
"text: \"\\tthis\\nis\\nmultiline\"\n",
"\"\\tthis\\nis\\nmultiline\"\n",
},
{
"\n\tthis\nnext",
"text: \"\\n\\tthis\\nnext\"\n",
"\"\\n\\tthis\\nnext\"\n",
},
{
"first\n\tsecond",
"text: |-\n first\n \tsecond\n",
"|-\n first\n \tsecond\n",
},
}

for _, testCase := range testCases {
wantMap := map[string]string{"text": testCase.value}
data, err := yaml.Marshal(wantMap)
assert.NoError(t, err)
assert.Equal(t, testCase.mapYAML, string(data))

var gotMap map[string]string
err = yaml.Unmarshal(data, &gotMap)
assert.NoError(t, err)
assert.DeepEqual(t, wantMap, gotMap)

var node yaml.Node
node.SetString(testCase.value)
data, err = yaml.Marshal(&node)
assert.NoError(t, err)
assert.Equal(t, testCase.scalarYAML, string(data))

var gotScalar string
err = yaml.Unmarshal(data, &gotScalar)
assert.NoError(t, err)
assert.Equal(t, testCase.value, gotScalar)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have something in the test suite that only uses .yaml files to handle this round trip check. I think it's in emitter.yaml

Could you please check?

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Emitted yaml not accepted back when block scalar starts with a tab character

2 participants