Skip to content

Commit

Permalink
Improve manual line wrapping in long prompts
Browse files Browse the repository at this point in the history
* ivy.el (ivy--break-lines): New function.
(ivy--insert-prompt): Use it to break long lines based on
string-width rather than length (#2869).
* ivy-test.el (ivy--break-lines): New test.
  • Loading branch information
basil-conto committed May 18, 2021
1 parent e8ac2e2 commit 7c5d49f
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 8 deletions.
35 changes: 35 additions & 0 deletions ivy-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,41 @@ a buffer visiting a file."
"k789"))
(should (equal ivy-text "k7"))))

(ert-deftest ivy--break-lines ()
"Test `ivy--break-lines' behavior."
(dolist (width '(-1 0))
(dolist (str '("" "\n" "a" "a\nb"))
(should (equal (ivy--break-lines str width) str))))
(should (equal (ivy--break-lines "" 1) ""))
(should (equal (ivy--break-lines "a" 1) "a"))
(should (equal (ivy--break-lines "a" 2) "a"))
(should (equal (ivy--break-lines "ab" 1) "a\nb"))
(should (equal (ivy--break-lines "ab" 2) "ab"))
(should (equal (ivy--break-lines "abc" 1) "a\nb\nc"))
(should (equal (ivy--break-lines "abc" 2) "ab\nc"))
(should (equal (ivy--break-lines "abc" 3) "abc"))
(should (equal (ivy--break-lines "\^X" 1) "\^X"))
(should (equal (ivy--break-lines "\^X" 2) "\^X"))
(should (equal (ivy--break-lines "a\^X" 1) "a\n\^X"))
(should (equal (ivy--break-lines "a\^X" 2) "a\n\^X"))
(should (equal (ivy--break-lines "a\^X" 3) "a\^X"))
(should (equal (ivy--break-lines "\^X\^X" 1) "\^X\n\^X"))
(should (equal (ivy--break-lines "\^X\^X" 2) "\^X\n\^X"))
(should (equal (ivy--break-lines "\^X\^X" 3) "\^X\n\^X"))
(should (equal (ivy--break-lines "\^X\^X" 4) "\^X\^X"))
(should (equal (ivy--break-lines "\nfoo\n\^X\^X\^X\nbar\n" 1)
"\nf\no\no\n\^X\n\^X\n\^X\nb\na\nr\n"))
(should (equal (ivy--break-lines "\nfoo\n\^X\^X\^X\nbar\n" 2)
"\nfo\no\n\^X\n\^X\n\^X\nba\nr\n"))
(should (equal (ivy--break-lines "\nfoo\n\^X\^X\^X\nbar\n" 3)
"\nfoo\n\^X\n\^X\n\^X\nbar\n"))
(should (equal (ivy--break-lines "\nfoo\n\^X\^X\^X\nbar\n" 4)
"\nfoo\n\^X\^X\n\^X\nbar\n"))
(should (equal (ivy--break-lines "\nfoo\n\^X\^X\^X\nbar\n" 5)
"\nfoo\n\^X\^X\n\^X\nbar\n"))
(should (equal (ivy--break-lines "\nfoo\n\^X\^X\^X\nbar\n" 6)
"\nfoo\n\^X\^X\^X\nbar\n")))

(defun ivy-test-run-tests ()
(let ((test-sets
'(
Expand Down
29 changes: 21 additions & 8 deletions ivy.el
Original file line number Diff line number Diff line change
Expand Up @@ -3068,6 +3068,26 @@ parts beyond their respective faces `ivy-confirm-face' and
(funcall fn (ivy-state-prompt ivy-last))))
ivy--prompt)))

(defun ivy--break-lines (str width)
"Break each line in STR with newlines to fit into WIDTH columns."
(if (<= width 0)
str
(let (lines)
(dolist (line (split-string str "\n"))
(while (and line (> (string-width line) width))
(let ((prefix "") (extra 0))
(while (string-empty-p prefix)
;; Grow `width' until it fits at least one char from `line'.
(setq prefix (truncate-string-to-width line (+ width extra)))
(setq extra (1+ extra)))
;; Avoid introducing spurious newline if `prefix' and `line' are
;; equal, i.e., if `line' couldn't be truncated to `width'.
(setq line (and (> (length line) (length prefix))
(substring line (length prefix))))
(push prefix lines)))
(when line (push line lines)))
(string-join (nreverse lines) "\n"))))

(defun ivy--insert-prompt ()
"Update the prompt according to `ivy--prompt'."
(when (setq ivy--prompt (ivy-prompt))
Expand Down Expand Up @@ -3122,14 +3142,7 @@ parts beyond their respective faces `ivy-confirm-face' and
(setq n-str (concat (funcall ivy-pre-prompt-function) n-str)))
(when ivy-add-newline-after-prompt
(setq n-str (concat n-str "\n")))
;; FIXME: This does not take character widths into account!
;; Should ideally let the display engine wrap text, otherwise
;; use `window-text-pixel-size'. See e.g. #2869.
(let ((regex (format "\\([^\n]\\{%d\\}\\)[^\n]" (window-width))))
(while (string-match regex n-str)
(setq n-str (replace-match
(concat (match-string 1 n-str) "\n")
nil t n-str 1))))
(setq n-str (ivy--break-lines n-str (window-width)))
(set-text-properties 0 (length n-str)
`(face minibuffer-prompt ,@std-props)
n-str)
Expand Down

0 comments on commit 7c5d49f

Please sign in to comment.