From 8529e6da398c061800fff8ada1b74cfa44a14038 Mon Sep 17 00:00:00 2001 From: Rupayon Haldar <80724680+rupayon123@users.noreply.github.com> Date: Wed, 23 Sep 2026 08:38:21 -0400 Subject: [PATCH] Keep comment markers inside quoted values --- parser.go | 48 +++++++++++++++++++++++++++++++++++------------- parser_test.go | 20 ++++++++++++++++++++ 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/parser.go b/parser.go index 513b19e..a4bf9d4 100644 --- a/parser.go +++ b/parser.go @@ -233,6 +233,40 @@ func hasSurroundedQuote(in string, quote byte) bool { strings.IndexByte(in[1:], quote) == len(in)-2 } +// inlineCommentIndex finds the first comment marker outside quoted text. +// With SpaceBeforeInlineComment, the space belongs to the comment, as before. +func inlineCommentIndex(line string, spaceBefore bool) int { + var quote byte + escaped := false + for i := 0; i < len(line); i++ { + b := line[i] + if quote != 0 { + if b == '\\' && !escaped { + escaped = true + continue + } + if b == quote && !escaped { + quote = 0 + } + escaped = false + continue + } + if (b == '\'' || b == '"') && (i == 0 || line[i-1] == ' ' || line[i-1] == '\t' || line[i-1] == '=') { + quote = b + continue + } + if b == '#' || b == ';' { + if !spaceBefore { + return i + } + if i > 0 && line[i-1] == ' ' { + return i - 1 + } + } + } + return -1 +} + func (p *parser) readValue(in []byte, bufferSize int) (string, error) { line := strings.TrimLeftFunc(string(in), unicode.IsSpace) @@ -278,22 +312,10 @@ func (p *parser) readValue(in []byte, bufferSize int) (string, error) { // Check if ignore inline comment if !p.options.IgnoreInlineComment { - var i int - if p.options.SpaceBeforeInlineComment { - i = strings.Index(line, " #") - if i == -1 { - i = strings.Index(line, " ;") - } - - } else { - i = strings.IndexAny(line, "#;") - } - - if i > -1 { + if i := inlineCommentIndex(line, p.options.SpaceBeforeInlineComment); i > -1 { p.comment.WriteString(line[i:]) line = strings.TrimSpace(line[:i]) } - } // Trim single and double quotes diff --git a/parser_test.go b/parser_test.go index 7016d67..24272e2 100644 --- a/parser_test.go +++ b/parser_test.go @@ -75,3 +75,23 @@ func TestBadLoad(t *testing.T) { }) }) } + +func TestInlineCommentMarkersInsideQuotedValues(t *testing.T) { + f, err := Load([]byte("[background]\nprimary = '#e18477'\nsecondary = \"#000000\"\nurl = 'https://example.test/#fragment' ; trailing comment\nplain = value # actual comment\n")) + require.NoError(t, err) + section := f.Section("background") + assert.Equal(t, "#e18477", section.Key("primary").String()) + assert.Equal(t, "#000000", section.Key("secondary").String()) + assert.Equal(t, "https://example.test/#fragment", section.Key("url").String()) + assert.Equal(t, "value", section.Key("plain").String()) +} + +func TestInlineCommentIndexRespectsQuoteAndSpaceOptions(t *testing.T) { + assert.Equal(t, -1, inlineCommentIndex(`'#a;b'`, false)) + assert.Equal(t, 7, inlineCommentIndex(`'#a;b' # trailing`, false)) + assert.Equal(t, 6, inlineCommentIndex(`'#a;b' # trailing`, true)) + assert.Equal(t, -1, inlineCommentIndex(`value#literal`, true)) + assert.Equal(t, 5, inlineCommentIndex(`value#comment`, false)) + assert.Equal(t, 5, inlineCommentIndex(`value ; comment`, true)) + assert.Equal(t, 5, inlineCommentIndex(`don't # comment`, true)) +}