Skip to content

fix: a delimiter inside a quoted field at line start splits the column - #1156

Merged
noborus merged 2 commits into
noborus:masterfrom
VXNCXNX:fix/leading-quoted-field-delimiter
Aug 17, 2026
Merged

fix: a delimiter inside a quoted field at line start splits the column#1156
noborus merged 2 commits into
noborus:masterfrom
VXNCXNX:fix/leading-quoted-field-delimiter

Conversation

@VXNCXNX

@VXNCXNX VXNCXNX commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

A delimiter inside a double-quoted field is correctly ignored, unless the quoted
field is the first one on the line.

al.csv:

"a,b",xx,y
longer,c,dd

with --column-delimiter=, --column-mode --align:

before:
"a    ,b",xx,y
longer,c ,dd

after:
"a,b" ,xx,y
longer,c ,dd

Align mode makes it visible: the padding is inserted inside the quoted field,
because "a and b" are being treated as two columns. In column mode the cursor
column is the truncated "a rather than the whole field.

Cause

allStringIndex skips a quoted field only after it has already found a
delimiter:

for pos, offSet := strings.Index(s, substr), 0; pos != -1; {
    s = s[pos+width:]
    ...
    if len(s) > 0 && s[0] == '"' {   // only reached after the first match

A quote at position 0 is never examined, so the delimiter inside the first field
is counted.

The fix

Check for a leading quote before the first search, and pull the skip into a
skipQuoted helper so both places do the same thing.

An unclosed quote keeps its current behaviour: strings.Index returns -1 and
s[qpos+2:] becomes s[1:], so only the opening quote is skipped and the rest
of the line is scanned as usual. That matches what the original code did for a
mid-line unclosed quote, and keeps this change to the one bug.

Verification

Two cases in Test_allStringIndex, one for a line that is only a quoted field
and one for a quoted field followed by more columns.

Removing the leading-quote skip fails both:

--- FAIL: Test_allStringIndex/testLeadingDoubleQuoteOnly
    utils_test.go:550: allIndex() = [[2 3]], want []
--- FAIL: Test_allStringIndex/testLeadingDoubleQuote
    utils_test.go:550: allIndex() = [[2 3] [5 6] [7 8]], want [[5 6] [7 8]]

The existing mid-line quoted cases pass under that mutation, which is what pins
the behaviour the change must not alter.

go test ./... is ok across all packages. gofumpt -l is clean on both files I
touched. No new fixture files, so the t.TempDir() and Windows CI hazard does
not arise.

golangci-lint reports 9 issues repo-wide and gofumpt -l flags draw.go and
move_updown.go, all pre-existing on master in files I did not touch.

Disclosure: written with AI assistance (Claude Code). The before and after come from running binaries built from each tree in a real terminal under tmux, and I ran the mutation check myself.

@noborus

noborus commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Thank you.
I think supporting fields enclosed in quotation marks is a good idea, but modifying allStringIndex is not advisable.
I believe it would be better to add a separate function.

allStringIndex is also used by the plain-text search highlighting, where
quotes have no special meaning. The delimiter path now has its own
allDelimiterIndex, called from allIndex, and allStringIndex is back to
what it was on master.
@VXNCXNX

VXNCXNX commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Agreed, and there is a better reason than I realised: allStringIndex is also used by the search highlighting, where a quote is just a character.

Pushed a second commit adding allDelimiterIndex, called from allIndex. allStringIndex is now byte-identical to master again.

@noborus

noborus commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Thank you for making the changes.

However, regarding the corrections, I would like you to create a separate wrap function instead of modifying allIndex directly. Please update DelimiterRange and maxWidthsDelem in prepare_draw.go to call this new function.

@noborus

noborus commented Aug 17, 2026

Copy link
Copy Markdown
Owner

However, regarding the corrections, I would like you to create a separate wrap function instead of modifying allIndex directly. Please update DelimiterRange and maxWidthsDelem in prepare_draw.go to call this new function.

Oh, I'm sorry. Actually, this is fine for now.

@noborus

noborus commented Aug 17, 2026

Copy link
Copy Markdown
Owner

I'll go ahead and merge this for now.
Thank you very much.

@noborus
noborus merged commit febdc0a into noborus:master Aug 17, 2026
6 checks passed
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.

2 participants