@@ -77,6 +77,8 @@ type highlightableMessageItem struct {
highlighter list.Highlighter
}
+var _ list.Highlightable = (*highlightableMessageItem)(nil)
+
// isHighlighted returns true if the item has a highlight range set.
func (h *highlightableMessageItem) isHighlighted() bool {
return h.startLine != -1 || h.endLine != -1
@@ -91,8 +93,8 @@ func (h *highlightableMessageItem) renderHighlighted(content string, width, heig
return list.Highlight(content, area, h.startLine, h.startCol, h.endLine, h.endCol, h.highlighter)
}
-// Highlight implements MessageItem.
-func (h *highlightableMessageItem) Highlight(startLine int, startCol int, endLine int, endCol int) {
+// SetHighlight implements [MessageItem].
+func (h *highlightableMessageItem) SetHighlight(startLine int, startCol int, endLine int, endCol int) {
// Adjust columns for the style's left inset (border + padding) since we
// highlight the content only.
offset := messageLeftPaddingTotal
@@ -106,6 +108,11 @@ func (h *highlightableMessageItem) Highlight(startLine int, startCol int, endLin
}
}
+// Highlight implements [MessageItem].
+func (h *highlightableMessageItem) Highlight() (startLine int, startCol int, endLine int, endCol int) {
+ return h.startLine, h.startCol, h.endLine, h.endCol
+}
+
func defaultHighlighter(sty *styles.Styles) *highlightableMessageItem {
return &highlightableMessageItem{
startLine: -1,
@@ -2,25 +2,60 @@ package list
import (
"image"
+ "strings"
"charm.land/lipgloss/v2"
uv "github.com/charmbracelet/ultraviolet"
)
// DefaultHighlighter is the default highlighter function that applies inverse style.
-var DefaultHighlighter Highlighter = func(s uv.Style) uv.Style {
- s.Attrs |= uv.AttrReverse
- return s
+var DefaultHighlighter Highlighter = func(x, y int, c *uv.Cell) *uv.Cell {
+ if c == nil {
+ return c
+ }
+ c.Style.Attrs |= uv.AttrReverse
+ return c
}
// Highlighter represents a function that defines how to highlight text.
-type Highlighter func(uv.Style) uv.Style
+type Highlighter func(x, y int, c *uv.Cell) *uv.Cell
+
+// HighlightContent returns the content with highlighted regions based on the specified parameters.
+func HighlightContent(content string, area image.Rectangle, startLine, startCol, endLine, endCol int) string {
+ var sb strings.Builder
+ pos := image.Pt(-1, -1)
+ HighlightBuffer(content, area, startLine, startCol, endLine, endCol, func(x, y int, c *uv.Cell) *uv.Cell {
+ pos.X = x
+ if pos.Y == -1 {
+ pos.Y = y
+ } else if y > pos.Y {
+ sb.WriteString(strings.Repeat("\n", y-pos.Y))
+ pos.Y = y
+ }
+ sb.WriteString(c.Content)
+ return c
+ })
+ if sb.Len() > 0 {
+ sb.WriteString("\n")
+ }
+ return sb.String()
+}
// Highlight highlights a region of text within the given content and region.
func Highlight(content string, area image.Rectangle, startLine, startCol, endLine, endCol int, highlighter Highlighter) string {
- if startLine < 0 || startCol < 0 {
+ buf := HighlightBuffer(content, area, startLine, startCol, endLine, endCol, highlighter)
+ if buf == nil {
return content
}
+ return buf.Render()
+}
+
+// HighlightBuffer highlights a region of text within the given content and
+// region, returning a [uv.ScreenBuffer].
+func HighlightBuffer(content string, area image.Rectangle, startLine, startCol, endLine, endCol int, highlighter Highlighter) *uv.ScreenBuffer {
+ if startLine < 0 || startCol < 0 {
+ return nil
+ }
if highlighter == nil {
highlighter = DefaultHighlighter
@@ -87,17 +122,22 @@ func Highlight(content string, area image.Rectangle, startLine, startCol, endLin
continue
}
cell := line.At(x)
- cell.Style = highlighter(cell.Style)
+ if cell != nil {
+ line.Set(x, highlighter(x, y, cell))
+ }
}
}
- return buf.Render()
+ return &buf
}
// ToHighlighter converts a [lipgloss.Style] to a [Highlighter].
func ToHighlighter(lgStyle lipgloss.Style) Highlighter {
- return func(uv.Style) uv.Style {
- return ToStyle(lgStyle)
+ return func(_ int, _ int, c *uv.Cell) *uv.Cell {
+ if c != nil {
+ c.Style = ToStyle(lgStyle)
+ }
+ return c
}
}
@@ -21,9 +21,11 @@ type Focusable interface {
// Highlightable represents an item that can highlight a portion of its content.
type Highlightable interface {
- // Highlight highlights the content from the given start to end positions.
- // Use -1 for no highlight.
- Highlight(startLine, startCol, endLine, endCol int)
+ // SetHighlight highlights the content from the given start to end
+ // positions. Use -1 for no highlight.
+ SetHighlight(startLine, startCol, endLine, endCol int)
+ // Highlight returns the current highlight positions within the item.
+ Highlight() (startLine, startCol, endLine, endCol int)
}
// MouseClickable represents an item that can handle mouse click events.
@@ -515,7 +515,7 @@ func (m *Chat) applyHighlightRange(idx, selectedIdx int, item list.Item) list.It
}
}
- hi.Highlight(sLine, sCol, eLine, eCol)
+ hi.SetHighlight(sLine, sCol, eLine, eCol)
return hi.(list.Item)
}