gogcli/internal/cmd/docs_sed_insert.go
Bill 4185e02a54
fix(docs): preserve sed formatting offsets
Use UTF-16 offsets for sed formatting ranges and preserve whole-match & replacements.\n\nThanks @bill492.
2026-04-20 13:29:35 +01:00

115 lines
3.2 KiB
Go

package cmd
import (
"context"
"fmt"
"google.golang.org/api/docs/v1"
"github.com/steipete/gogcli/internal/ui"
)
func (c *DocsSedCmd) doPositionalInsert(ctx context.Context, docsSvc *docs.Service, u *ui.UI, id string, idx int64, replacement string) error {
// Check for image syntax first
imgSpec := parseImageSyntax(replacement)
// Check for table creation (explicit |RxC| or pipe-table syntax)
tableSpec := parseTableCreate(replacement)
if tableSpec == nil {
tableSpec = parseTableFromPipes(replacement)
}
// Parse markdown formatting
plainText, formats := parseMarkdownReplacement(replacement)
var requests []*docs.Request
switch {
case tableSpec != nil:
// Insert a table at the position
requests = append(requests, &docs.Request{
InsertTable: &docs.InsertTableRequest{
Location: &docs.Location{Index: idx},
Rows: int64(tableSpec.rows),
Columns: int64(tableSpec.cols),
},
})
case imgSpec != nil:
// Insert an image
imgReq := &docs.InsertInlineImageRequest{
Uri: imgSpec.URL,
Location: &docs.Location{Index: idx},
ObjectSize: buildImageSizeSpec(imgSpec),
}
requests = append(requests, &docs.Request{InsertInlineImage: imgReq})
default:
// Insert plain text
requests = append(requests, &docs.Request{
InsertText: &docs.InsertTextRequest{
Location: &docs.Location{Index: idx},
Text: plainText,
},
})
// Apply text formatting if any
if len(formats) > 0 {
end := idx + utf16Len(plainText)
requests = append(requests, buildTextStyleRequests(formats, idx, end)...)
}
}
// After inserting text, reset paragraph styles and apply heading/bullet if requested
if tableSpec == nil && imgSpec == nil && plainText != "" {
end := idx + utf16Len(plainText)
// Reset paragraph to NORMAL_TEXT (removes inherited heading styles)
requests = append(requests, &docs.Request{
UpdateParagraphStyle: &docs.UpdateParagraphStyleRequest{
Range: &docs.Range{StartIndex: idx, EndIndex: end},
ParagraphStyle: &docs.ParagraphStyle{
NamedStyleType: "NORMAL_TEXT",
},
Fields: "namedStyleType",
},
})
// Remove inherited bullets
requests = append(requests, &docs.Request{
DeleteParagraphBullets: &docs.DeleteParagraphBulletsRequest{
Range: &docs.Range{StartIndex: idx, EndIndex: end},
},
})
// Apply heading/bullet formats from markdown
requests = append(requests, buildParagraphStyleRequests(formats, idx, end)...)
}
err := retryOnQuota(ctx, func() error {
_, e := docsSvc.Documents.BatchUpdate(id, &docs.BatchUpdateDocumentRequest{
Requests: requests,
}).Context(ctx).Do()
return e
})
if err != nil {
return fmt.Errorf("batch update (positional insert): %w", err)
}
// Fill pipe-table cells if content was provided
if tableSpec != nil && len(tableSpec.cells) > 0 {
if err := c.fillTableCells(ctx, docsSvc, id, idx, tableSpec); err != nil {
return fmt.Errorf("fill table cells: %w", err)
}
}
label := fmt.Sprintf("%d chars", len(plainText))
if tableSpec != nil {
label = fmt.Sprintf("%dx%d table", tableSpec.rows, tableSpec.cols)
if len(tableSpec.cells) > 0 {
label += " (filled)"
}
} else if imgSpec != nil {
label = "image"
}
return sedOutputOK(ctx, u, id, sedOutputKV{"inserted", label})
}