• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

charmbracelet / bubbles / 32939514982
59%
main: 58%

Build:
Build:
LAST BUILD BRANCH: dependabot/go_modules/all-a21795bff9
DEFAULT BRANCH: main
Ran 26 Aug 2026 06:45AM UTC
Jobs 1
Files 28
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

26 Aug 2026 05:49AM UTC coverage: 58.505% (+0.4%) from 58.059%
32939514982

Pull #1030

github

joestump
feat(textarea): add an optional line highlighter

There is no way to style parts of a line in a textarea. Apps that want
token highlighting in their prompt — @file mentions, /command names,
anything lexical — have to either post-process the rendered string
(fighting ANSI escapes the textarea just emitted) or fork the render
loop wholesale.

Add a LineHighlighter hook:

    type LineHighlighter interface {
        Highlight(lineIdx int, line []rune) []lipgloss.Range
    }

    func (m *Model) SetHighlighter(h LineHighlighter)

The hook is consulted once per logical line per render and returns style
ranges over that line's runes. The renderer intersects those ranges with
each wrapped segment, so implementations never think about wrapping and
never see a segment boundary.

Keeping the interface range-based, rather than a func(string) string
that rewrites ANSI, means implementations never parse or preserve escape
sequences, and the textarea's wrap and cursor math keeps operating on
raw runes.

Neither the cell under the virtual cursor nor a selected segment is
styled by ranges. The renderer already splits cursor-line content into
pre- and post-cursor segments, and an active selection short-circuits
the segment entirely, so no two span-styling features can fight over the
same cell.

With no highlighter installed the render path is byte-identical to
before — styleSegment short-circuits to the plain render when there are
no ranges.

Two subtleties worth stating, because both are easy to get wrong:

Rune offsets are converted to display cells with ansi.StringWidth rather
than by summing per-rune widths. lipgloss.StyleRanges resolves the cell
offsets it is handed via ansi.Cut, which segments by grapheme cluster.
Per-rune summing disagrees on every cluster built from more than one
rune — an emoji with a skin-tone modifier scores 4 instead of 2, a ZWJ
family 7 instead of 2 — and the highlight then lands that many cells
right of its token.

StyleRanges also walks... (continued)
Pull Request #1030: feat(textarea): add an optional line highlighter

67 of 67 new or added lines in 2 files covered. (100.0%)

3577 of 6114 relevant lines covered (58.51%)

207.02 hits per line

Jobs
ID Job ID Ran Files Coverage
1 32939514982.1 26 Aug 2026 06:45AM UTC 28
58.51
GitHub Action Run
Source Files on build 32939514982
  • Tree
  • List 28
  • Changed 1
  • Source Changed 0
  • Coverage Changed 1
Coverage ∆ File Lines Relevant Covered Missed Hits/Line
  • Back to Repo
  • Pull Request #1030
  • PR Base - main (#32710429639)
  • Delete
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc