package editor import ( "regexp" "github.com/charmbracelet/lipgloss" ) // tkRe matches the journalism "TK" placeholder ("to come"): a whole word made of // one or more "tk" pairs, any case — "TK", "tk", "TKTK". The \b anchors keep it // from firing inside real words (catkin, Atkins, outkast), where no word boundary // precedes the "tk". var tkRe = regexp.MustCompile(`(?i)\b(?:tk)+\b`) // tkPass paints TK placeholder markers as a bold filled badge so they pop as // fill-in reminders. It runs after the spell and grammar passes (and wins over // them on the same runes), splitting each prose span so only the "TK" text is // restyled; the concatenation of span texts still equals the raw line, preserving // the markup-visible invariant. Non-prose spans (code, URLs, markup) are untouched. func (e *Editor) tkPass(all [][]Span) [][]Span { for li, spans := range all { var out []Span changed := false for _, sp := range spans { if !sp.Prose { out = append(out, sp) continue } split := e.splitTK(sp) if len(split) != 1 { changed = true } out = append(out, split...) } if changed { all[li] = out } } return all } // splitTK partitions one prose span's text, re-emitting each TK marker as a badge // span (page-colored bold text on the loud theme.TK background). URLs are skipped. // When no marker is present the original span is returned unchanged. func (e *Editor) splitTK(sp Span) []Span { text := sp.Text locs := tkRe.FindAllStringIndex(text, -1) if len(locs) == 0 { return []Span{sp} } skips := urlRe.FindAllStringIndex(text, -1) badge := lipgloss.NewStyle().Foreground(e.theme.Background).Background(e.theme.TK).Bold(true) var out []Span last := 0 emit := func(s string) { if s == "" { return } ns := sp ns.Text = s out = append(out, ns) } for _, m := range locs { if overlapsAny(m, skips) { continue } emit(text[last:m[0]]) badgeSpan := sp badgeSpan.Text = text[m[0]:m[1]] badgeSpan.Style = badge badgeSpan.Wavy = false // a badge never also carries a spell/grammar underline badgeSpan.Prose = false // and is inert to any later prose pass out = append(out, badgeSpan) last = m[1] } if last == 0 { return []Span{sp} // every match was inside a URL } emit(text[last:]) return out }