65 lines
1.6 KiB
Go
65 lines
1.6 KiB
Go
package utils
|
|
|
|
import (
|
|
"strings"
|
|
)
|
|
|
|
// WrapText wraps text to a limit, forcing mid-word breaks if a word is too long.
|
|
// It uses runes to correctly handle UTF-8 multi-byte characters.
|
|
func WrapText(text string, limit int) string {
|
|
if limit <= 0 {
|
|
return text
|
|
}
|
|
|
|
var result strings.Builder
|
|
paragraphs := strings.Split(text, "\n")
|
|
|
|
for i, paragraph := range paragraphs {
|
|
words := strings.Fields(paragraph)
|
|
currentLineLen := 0
|
|
|
|
for _, word := range words {
|
|
// Convert to runes to handle UTF-8 characters correctly
|
|
wordRunes := []rune(word)
|
|
wordLen := len(wordRunes)
|
|
|
|
// Determine if we need a space before the word
|
|
if currentLineLen > 0 {
|
|
// If adding the word + space exceeds the limit, push to next line
|
|
if currentLineLen+1+wordLen <= limit {
|
|
result.WriteString(" ")
|
|
currentLineLen++
|
|
} else {
|
|
result.WriteString("\n")
|
|
currentLineLen = 0
|
|
}
|
|
}
|
|
|
|
// Write the word, chunking it if it exceeds the remaining line limit
|
|
for len(wordRunes) > 0 {
|
|
spaceLeft := limit - currentLineLen
|
|
|
|
// If the line is full, wrap to the next line
|
|
if spaceLeft <= 0 {
|
|
result.WriteString("\n")
|
|
currentLineLen = 0
|
|
spaceLeft = limit
|
|
}
|
|
|
|
// Take as much of the word as fits on the current line
|
|
take := min(len(wordRunes), spaceLeft)
|
|
|
|
result.WriteString(string(wordRunes[:take]))
|
|
currentLineLen += take
|
|
wordRunes = wordRunes[take:] // Advance the rune slice
|
|
}
|
|
}
|
|
|
|
// Preserve original paragraph breaks
|
|
if i < len(paragraphs)-1 {
|
|
result.WriteString("\n")
|
|
}
|
|
}
|
|
|
|
return result.String()
|
|
}
|