@@ -56,6 +56,7 @@ type List[T Item] interface {
5656 SelectWord(col, line int)
5757 SelectParagraph(col, line int)
5858 GetSelectedText(paddingLeft int) string
59+ HasSelection() bool
5960}
6061
6162type direction int
@@ -286,30 +287,10 @@ func (l *list[T]) handleMouseWheel(msg tea.MouseWheelMsg) (tea.Model, tea.Cmd) {
286287 return l, cmd
287288}
288289
289−// View implements List.
290−func (l *list[T]) View() string {
291− if l.height <= 0 || l.width <= 0 {
292− return ""
293− }
290+// selectionView renders the highlighted selection in the view and returns it
291+// as a string. If textOnly is true, it won't render any styles.
292+func (l *list[T]) selectionView(view string, textOnly bool) string {
294293 t := styles.CurrentTheme()
295− view := l.rendered
296− lines := strings.Split(view, "\n")
297−
298− start, end := l.viewPosition()
299− viewStart := max(0, start)
300− viewEnd := min(len(lines), end+1)
301− lines = lines[viewStart:viewEnd]
302− if l.resize {
303− return strings.Join(lines, "\n")
304− }
305− view = t.S().Base.
306− Height(l.height).
307− Width(l.width).
308− Render(strings.Join(lines, "\n"))
309−
310− if !l.hasSelection() {
311− return view
312− }
313294 area := uv.Rect(0, 0, l.width, l.height)
314295 scr := uv.NewScreenBuffer(area.Dx(), area.Dy())
315296 uv.NewStyledString(view).Draw(scr, area)
@@ -397,6 +378,8 @@ func (l *list[T]) View() string {
397378 lineTextBounds[y] = bounds
398379 }
399380
381+ var selectedText strings.Builder
382+
400383 // Second pass: apply selection highlighting
401384 for y := range scr.Height() {
402385 selBounds := lineSelections[y]
@@ -406,6 +389,11 @@ func (l *list[T]) View() string {
406389
407390 textBounds := lineTextBounds[y]
408391 if textBounds.start < 0 {
392+ if textOnly {
393+ // We don't want to get rid of all empty lines in text-only mode
394+ selectedText.WriteByte('\n')
395+ }
396+
409397 continue // No text on this line
410398 }
411399
@@ -421,16 +409,63 @@ func (l *list[T]) View() string {
421409
422410 cellStr := cell.String()
423411 if len(cellStr) > 0 && !specialChars[cellStr] {
412+ if textOnly {
413+ // Collect selected text without styles
414+ selectedText.WriteString(cell.String())
415+ continue
416+ }
417+
418+ // Text selection styling, which is a Lip Gloss style. We must
419+ // extract the values to use in a UV style, below.
420+ ts := t.TextSelection
421+
424422 cell = cell.Clone()
425− cell.Style = cell.Style.Background(t.BgOverlay).Foreground(t.White)
423+ cell.Style = cell.Style.Background(ts.GetBackground()).Foreground(ts.GetForeground())
426424 scr.SetCell(x, y, cell)
427425 }
428426 }
427+
428+ if textOnly {
429+ // Make sure we add a newline after each line of selected text
430+ selectedText.WriteByte('\n')
431+ }
432+ }
433+
434+ if textOnly {
435+ return strings.TrimSpace(selectedText.String())
429436 }
430437
431438 return scr.Render()
432439}
433440
441+// View implements List.
442+func (l *list[T]) View() string {
443+ if l.height <= 0 || l.width <= 0 {
444+ return ""
445+ }
446+ t := styles.CurrentTheme()
447+ view := l.rendered
448+ lines := strings.Split(view, "\n")
449+
450+ start, end := l.viewPosition()
451+ viewStart := max(0, start)
452+ viewEnd := min(len(lines), end+1)
453+ lines = lines[viewStart:viewEnd]
454+ if l.resize {
455+ return strings.Join(lines, "\n")
456+ }
457+ view = t.S().Base.
458+ Height(l.height).
459+ Width(l.width).
460+ Render(strings.Join(lines, "\n"))
461+
462+ if !l.hasSelection() {
463+ return view
464+ }
465+
466+ return l.selectionView(view, false)
467+}
468+
434469func (l *list[T]) viewPosition() (int, int) {
435470 start, end := 0, 0
436471 renderedLines := lipgloss.Height(l.rendered) - 1
@@ -1374,69 +1409,16 @@ func (l *list[T]) SelectParagraph(col, line int) {
13741409 l.selectionActive = false // Not actively selecting, just selected
13751410}
13761411
1412+// HasSelection returns whether there is an active selection.
1413+func (l *list[T]) HasSelection() bool {
1414+ return l.hasSelection()
1415+}
1416+
13771417// GetSelectedText returns the currently selected text.
13781418func (l *list[T]) GetSelectedText(paddingLeft int) string {
1379− return ""
1380− // if !l.hasSelection() {
1381− // return ""
1382− // }
1383− //
1384− // startLine := l.selectionStartLine
1385− // endLine := l.selectionEndLine
1386− // startCol := l.selectionStartCol
1387− // endCol := l.selectionEndCol
1388− //
1389− // if l.direction == DirectionBackward {
1390− // startLine = (lipgloss.Height(l.rendered) - 1) - startLine
1391− // endLine = (lipgloss.Height(l.rendered) - 1) - endLine
1392− // }
1393− //
1394− // if l.offset > 0 {
1395− // if l.direction == DirectionBackward {
1396− // startLine += l.offset
1397− // endLine += l.offset
1398− // } else {
1399− // startLine -= l.offset
1400− // endLine -= l.offset
1401− // }
1402− // }
1403− //
1404− // lines := strings.Split(l.rendered, "\n")
1405− //
1406− // if startLine < 0 || endLine < 0 || startLine >= len(lines) || endLine >= len(lines) {
1407− // return ""
1408− // }
1409− //
1410− // var result strings.Builder
1411− // for i := range lines {
1412− // lines[i] = ansi.Strip(lines[i])
1413− // for _, icon := range styles.SelectionIgnoreIcons {
1414− // lines[i] = strings.ReplaceAll(lines[i], icon, " ")
1415− // }
1416− //
1417− // if i == startLine {
1418− // if startCol < 0 || startCol >= len(lines[i]) {
1419− // startCol = 0
1420− // }
1421− // if startCol < paddingLeft {
1422− // startCol = paddingLeft
1423− // }
1424− // if i != endLine {
1425− // endCol = len(lines[i])
1426− // }
1427− // result.WriteString(strings.TrimRightFunc(lines[i][startCol:endCol], unicode.IsSpace))
1428− // } else if i > startLine && i < endLine {
1429− // result.WriteString(strings.TrimRightFunc(lines[i][paddingLeft:], unicode.IsSpace))
1430− // } else if i == endLine {
1431− // if endCol < 0 || endCol >= len(lines[i]) {
1432− // endCol = len(lines[i])
1433− // }
1434− // if endCol < paddingLeft {
1435− // endCol = paddingLeft
1436− // }
1437− // result.WriteString(strings.TrimRightFunc(lines[i][paddingLeft:endCol], unicode.IsSpace))
1438− // }
1439− // }
1440− //
1441− // return result.String()
1419+ if !l.hasSelection() {
1420+ return ""
1421+ }
1422+
1423+ return l.selectionView(l.View(), true)
14421424}