summaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2024-06-25 17:08:47 +0900
committerGitHub <noreply@github.com>2024-06-25 17:08:47 +0900
commit70bf8bc35dfb31eb1963c92fa72e38261fa0056a (patch)
tree02d7481e3f208bc41923dc776ca6278882ed66e8 /src/util
parent724f8a1d450e4929554cd882a0d25f05e336ba63 (diff)
downloadfzf-70bf8bc35dfb31eb1963c92fa72e38261fa0056a.tar.gz
Add --wrap option and 'toggle-wrap' action (#3887)
* `--wrap` * `--wrap-sign` * `toggle-wrap` Close #3619 Close #2236 Close #577 Close #461
Diffstat (limited to 'src/util')
-rw-r--r--src/util/chars.go82
-rw-r--r--src/util/chars_test.go39
2 files changed, 120 insertions, 1 deletions
diff --git a/src/util/chars.go b/src/util/chars.go
index 82773f40..a0ea9436 100644
--- a/src/util/chars.go
+++ b/src/util/chars.go
@@ -226,3 +226,85 @@ func (chars *Chars) Prepend(prefix string) {
chars.slice = append([]byte(prefix), chars.slice...)
}
}
+
+func (chars *Chars) Lines(multiLine bool, maxLines int, wrapCols int, wrapSignWidth int, tabstop int) ([][]rune, bool) {
+ text := make([]rune, chars.Length())
+ copy(text, chars.ToRunes())
+
+ lines := [][]rune{}
+ overflow := false
+ if !multiLine {
+ lines = append(lines, text)
+ } else {
+ from := 0
+ for off := 0; off < len(text); off++ {
+ if text[off] == '\n' {
+ lines = append(lines, text[from:off+1]) // Include '\n'
+ from = off + 1
+ if len(lines) >= maxLines {
+ break
+ }
+ }
+ }
+
+ var lastLine []rune
+ if from < len(text) {
+ lastLine = text[from:]
+ }
+
+ overflow = false
+ if len(lines) >= maxLines {
+ overflow = true
+ } else {
+ lines = append(lines, lastLine)
+ }
+ }
+
+ // If wrapping is disabled, we're done
+ if wrapCols == 0 {
+ return lines, overflow
+ }
+
+ wrapped := [][]rune{}
+ for _, line := range lines {
+ // Remove trailing '\n' and remember if it was there
+ newline := len(line) > 0 && line[len(line)-1] == '\n'
+ if newline {
+ line = line[:len(line)-1]
+ }
+
+ for {
+ cols := wrapCols
+ if len(wrapped) > 0 {
+ cols -= wrapSignWidth
+ }
+ _, overflowIdx := RunesWidth(line, 0, tabstop, cols)
+ if overflowIdx >= 0 {
+ // Might be a wide character
+ if overflowIdx == 0 {
+ overflowIdx = 1
+ }
+ if len(wrapped) >= maxLines {
+ return wrapped, true
+ }
+ wrapped = append(wrapped, line[:overflowIdx])
+ line = line[overflowIdx:]
+ continue
+ }
+
+ // Restore trailing '\n'
+ if newline {
+ line = append(line, '\n')
+ }
+
+ if len(wrapped) >= maxLines {
+ return wrapped, true
+ }
+
+ wrapped = append(wrapped, line)
+ break
+ }
+ }
+
+ return wrapped, false
+}
diff --git a/src/util/chars_test.go b/src/util/chars_test.go
index b7983f30..0d3e4f37 100644
--- a/src/util/chars_test.go
+++ b/src/util/chars_test.go
@@ -1,6 +1,9 @@
package util
-import "testing"
+import (
+ "fmt"
+ "testing"
+)
func TestToCharsAscii(t *testing.T) {
chars := ToChars([]byte("foobar"))
@@ -44,3 +47,37 @@ func TestTrimLength(t *testing.T) {
check(" h o ", 5)
check(" ", 0)
}
+
+func TestCharsLines(t *testing.T) {
+ chars := ToChars([]byte("abcdef\n가나다\n\tdef"))
+ check := func(multiLine bool, maxLines int, wrapCols int, wrapSignWidth int, tabstop int, expectedNumLines int, expectedOverflow bool) {
+ lines, overflow := chars.Lines(multiLine, maxLines, wrapCols, wrapSignWidth, tabstop)
+ fmt.Println(lines, overflow)
+ if len(lines) != expectedNumLines || overflow != expectedOverflow {
+ t.Errorf("Invalid result: %d %v (expected %d %v)", len(lines), overflow, expectedNumLines, expectedOverflow)
+ }
+ }
+
+ // No wrap
+ check(true, 1, 0, 0, 8, 1, true)
+ check(true, 2, 0, 0, 8, 2, true)
+ check(true, 3, 0, 0, 8, 3, false)
+
+ // Wrap (2)
+ check(true, 4, 2, 0, 8, 4, true)
+ check(true, 5, 2, 0, 8, 5, true)
+ check(true, 6, 2, 0, 8, 6, true)
+ check(true, 7, 2, 0, 8, 7, true)
+ check(true, 8, 2, 0, 8, 8, true)
+ check(true, 9, 2, 0, 8, 9, false)
+ check(true, 9, 2, 0, 1, 8, false) // Smaller tab size
+
+ // With wrap sign (3 + 1)
+ check(true, 100, 3, 1, 1, 8, false)
+
+ // With wrap sign (3 + 2)
+ check(true, 100, 3, 2, 1, 12, false)
+
+ // With wrap sign (3 + 2) and no multi-line
+ check(false, 100, 3, 2, 1, 13, false)
+}