Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Release: update the Homebrew handoff to publish through `openclaw/tap`.
- Version: `gog --version` now reports an informative fallback (for example, `v0.17.0-dev`) when built from source with plain `go build` instead of returning `dev`.
- Docs: `gog docs insert` now defaults to end-of-doc when `--index` is omitted, instead of always inserting at position 1 (which silently reversed iterative inserts across multiple calls). Pass `--index 1` explicitly to keep the previous behaviour. (#606)
- Docs: `docs write --append --markdown` with three or more markdown tables in a single render no longer drifts the per-table insertion offset by one character per table — the trailing punctuation of the paragraph immediately before the third (and any subsequent) table is preserved instead of being split into a standalone paragraph after the table. (#607)
- Docs: `docs write --append --markdown` now expands inline markdown markers (`**bold**`, `*italic*`, `` `code` ``, `[link](url)`) inside table cells into character runs, matching the behaviour outside of tables — previously the markers rendered as literal characters because the table inserter bypassed the inline-formatting pass. (#608)
- Docs: markdown empty-header table rows (e.g. `| | |`) no longer collide with the separator detection — previously `docs write --append --markdown` swallowed both the empty header and the real `|---|---|` separator, leaving the last data row re-parsed as a literal pipe paragraph after the table. (#609)

Expand Down
63 changes: 63 additions & 0 deletions internal/cmd/docs_multi_table_offset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cmd

import "testing"

// Regression for #607: with three or more tables in a single markdown render
// pass, the running placeholder offset under-counted by one character per
// table, causing the trailing punctuation of the paragraph immediately before
// the third (and any subsequent) table to be re-ordered as a standalone
// paragraph after the table. The fix in nextTableInsertOffset accumulates the
// full (tableEnd - tableIndex) instead of (tableEnd - tableIndex) - 1.

func TestNextTableInsertOffset_AccumulatesFullTableSize(t *testing.T) {
off := int64(0)

// Table 1: spans [10, 30), size 20.
off = nextTableInsertOffset(off, 10, 30)
if off != 20 {
t.Fatalf("after table 1: offset = %d, want 20", off)
}

// Table 2 was originally at placeholder 50; after shift it is at 50+20=70.
// It spans [70, 90), size 20. Offset must accumulate to 40.
off = nextTableInsertOffset(off, 70, 90)
if off != 40 {
t.Fatalf("after table 2: offset = %d, want 40 (previous bug: 38)", off)
}

// Table 3 was originally at placeholder 80; after shift it is at 80+40=120.
// It spans [120, 135), size 15. Offset accumulates to 55.
off = nextTableInsertOffset(off, 120, 135)
if off != 55 {
t.Fatalf("after table 3: offset = %d, want 55 (previous bug: 52)", off)
}
}

func TestNextTableInsertOffset_ZeroSizedTableLeavesOffsetUnchanged(t *testing.T) {
// If InsertNativeTable failed to grow the doc (tableEnd <= tableIndex), the
// offset must not change so subsequent placeholders stay at their plainText
// positions.
if got := nextTableInsertOffset(7, 10, 10); got != 7 {
t.Fatalf("equal indices: offset = %d, want 7 unchanged", got)
}
if got := nextTableInsertOffset(7, 10, 5); got != 7 {
t.Fatalf("tableEnd < tableIndex: offset = %d, want 7 unchanged", got)
}
}

func TestNextTableInsertOffset_MatchesIssueRepro(t *testing.T) {
// The repro from #607 is three identical 2x2 tables interleaved with
// paragraphs. The exact API-side table size depends on the Docs service,
// but for ANY table size G > 0, the cumulative offset after N tables of
// size G must equal N*G. The previous (G-1) formula produced N*(G-1),
// undercounting by N and explaining why the corruption only became
// visually obvious starting at the 3rd table (when drift = 2).
const G = int64(17)
off := int64(0)
off = nextTableInsertOffset(off, 100, 100+G)
off = nextTableInsertOffset(off, 200, 200+G)
off = nextTableInsertOffset(off, 300, 300+G)
if off != 3*G {
t.Fatalf("3 tables of size %d: offset %d, want %d", G, off, 3*G)
}
}
8 changes: 2 additions & 6 deletions internal/cmd/docs_mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,7 @@ func replaceDocsMarkdownRange(ctx context.Context, svc *docs.Service, doc *docs.
if tableErr != nil {
return fmt.Errorf("insert native table: %w", tableErr)
}
if tableEnd > tableIndex {
tableOffset += (tableEnd - tableIndex) - 1
}
tableOffset = nextTableInsertOffset(tableOffset, tableIndex, tableEnd)
}
}

Expand Down Expand Up @@ -234,9 +232,7 @@ func insertDocsMarkdownAt(ctx context.Context, svc *docs.Service, docID string,
if tableErr != nil {
return len(requests), len(textToInsert), fmt.Errorf("insert native table: %w", tableErr)
}
if tableEnd > tableIndex {
tableOffset += (tableEnd - tableIndex) - 1
}
tableOffset = nextTableInsertOffset(tableOffset, tableIndex, tableEnd)
}
}

Expand Down
15 changes: 15 additions & 0 deletions internal/cmd/docs_table_inserter.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,18 @@ func (ti *TableInserter) updateIndicesAfter(afterIndex, length int64, cellIndice
*tableEndIndex += length
}
}

// nextTableInsertOffset returns the running offset to apply to subsequent
// markdown-table placeholder positions after inserting a native table that
// spans [tableIndex, tableEnd). InsertTable inserts the new table before the
// existing character at tableIndex, so the placeholder "\n" we wrote into
// plainText for that table position stays in the doc; every subsequent
// placeholder therefore shifts forward by (tableEnd - tableIndex). The
// previous formula subtracted an extra 1, which accumulated one missing
// character of drift per table; see #607.
func nextTableInsertOffset(currentOffset, tableIndex, tableEnd int64) int64 {
if tableEnd <= tableIndex {
return currentOffset
}
return currentOffset + (tableEnd - tableIndex)
}
Loading