aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/emacs/basic.texi11
-rw-r--r--etc/NEWS12
-rw-r--r--lisp/visual-wrap.el203
3 files changed, 226 insertions, 0 deletions
diff --git a/doc/emacs/basic.texi b/doc/emacs/basic.texi
index f64b3995d25..a6b71db4bea 100644
--- a/doc/emacs/basic.texi
+++ b/doc/emacs/basic.texi
@@ -630,6 +630,17 @@ before they get too long, by inserting newlines. If you prefer, you
630can make Emacs insert a newline automatically when a line gets too 630can make Emacs insert a newline automatically when a line gets too
631long, by using Auto Fill mode. @xref{Filling}. 631long, by using Auto Fill mode. @xref{Filling}.
632 632
633@cindex continuation lines, wrapping with prefix
634@findex visual-wrap-prefix-mode
635 Normally, the first character of each continuation line is
636positioned at the beginning of the screen line where it is displayed.
637The minor mode @code{visual-wrap-prefix-mode} arranges that
638continuation lines be prefixed by slightly adjusted versions of the
639fill prefixes (@pxref{Fill Prefix}) of their respective logical lines,
640so that indentation characters or the prefixes of source code comments
641are replicated across every continuation line, and the appearance of
642such comments or indentation is not broken.
643
633 Sometimes, you may need to edit files containing many long logical 644 Sometimes, you may need to edit files containing many long logical
634lines, and it may not be practical to break them all up by adding 645lines, and it may not be practical to break them all up by adding
635newlines. In that case, you can use Visual Line mode, which enables 646newlines. In that case, you can use Visual Line mode, which enables
diff --git a/etc/NEWS b/etc/NEWS
index 37264f2f1f1..37a017c4db1 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -318,6 +318,18 @@ name detection.
318* Editing Changes in Emacs 30.1 318* Editing Changes in Emacs 30.1
319 319
320+++ 320+++
321** New minor mode 'visual-wrap-prefix-mode'.
322
323When enabled, continuation lines displayed for a folded long line will
324receive a 'wrap-prefix' automatically computed from the line's
325surrounding context by the function 'fill-context-prefix', which
326generally indents continuation lines as if the line were filled with
327'M-q', or similar.
328
329This minor mode is the 'adaptive-wrap' ELPA package renamed and
330lightly edited for inclusion in Emacs.
331
332+++
321** New user option 'gud-highlight-current-line'. 333** New user option 'gud-highlight-current-line'.
322When enabled, Gud will visually emphasize the line being executed upon 334When enabled, Gud will visually emphasize the line being executed upon
323pauses in the debugee's execution, such as those occasioned by 335pauses in the debugee's execution, such as those occasioned by
diff --git a/lisp/visual-wrap.el b/lisp/visual-wrap.el
new file mode 100644
index 00000000000..9f52a1868c1
--- /dev/null
+++ b/lisp/visual-wrap.el
@@ -0,0 +1,203 @@
1;;; visual-wrap.el --- Smart line-wrapping with wrap-prefix
2
3;; Copyright (C) 2011-2021, 2024 Free Software Foundation, Inc.
4
5;; Author: Stephen Berman <stephen.berman@gmx.net>
6;; Stefan Monnier <monnier@iro.umontreal.ca>
7;; Maintainer: emacs-devel@gnu.org
8;; Keywords: convenience
9;; Package: emacs
10
11;; This file is part of GNU Emacs.
12
13;; This program is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
17
18;; This program is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
24;; along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26;;; Commentary:
27
28;; This package provides the `visual-wrap-prefix-mode' minor mode
29;; which sets the wrap-prefix property on the fly so that
30;; single-long-line paragraphs get word-wrapped in a way similar to
31;; what you'd get with M-q using visual-fill-mode, but without
32;; actually changing the buffer's text.
33
34;;; Code:
35
36(defcustom visual-wrap-extra-indent 0
37 "Number of extra spaces to indent in `visual-wrap-prefix-mode'.
38
39`visual-wrap-prefix-mode' indents the visual lines to the level
40of the actual line plus `visual-wrap-extra-indent'. A negative
41value will do a relative de-indent.
42
43Examples:
44
45actual indent = 2
46extra indent = -1
47
48 Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
49 do eiusmod tempor incididunt ut labore et dolore magna
50 aliqua. Ut enim ad minim veniam, quis nostrud exercitation
51 ullamco laboris nisi ut aliquip ex ea commodo consequat.
52
53actual indent = 2
54extra indent = 2
55
56 Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
57 do eiusmod tempor incididunt ut labore et dolore magna
58 aliqua. Ut enim ad minim veniam, quis nostrud exercitation
59 ullamco laboris nisi ut aliquip ex ea commodo consequat."
60 :type 'integer
61 :safe 'integerp
62 :group 'visual-line)
63
64(defun visual-wrap--face-extend-p (face)
65 ;; Before Emacs 27, faces always extended beyond EOL, so we check
66 ;; for a non-default background instead.
67 (cond
68 ((listp face)
69 (plist-get face (if (fboundp 'face-extend-p) :extend :background)))
70 ((symbolp face)
71 (if (fboundp 'face-extend-p)
72 (face-extend-p face nil t)
73 (face-background face nil t)))))
74
75(defun visual-wrap--prefix-face (fcp _beg end)
76 ;; If the fill-context-prefix already specifies a face, just use that.
77 (cond ((get-text-property 0 'face fcp))
78 ;; Else, if the last character is a newline and has a face
79 ;; that extends beyond EOL, assume that this face spans the
80 ;; whole line and apply it to the prefix to preserve the
81 ;; "block" visual effect.
82 ;;
83 ;; NB: the face might not actually span the whole line: see
84 ;; for example removed lines in diff-mode, where the first
85 ;; character has the diff-indicator-removed face, while the
86 ;; rest of the line has the diff-removed face.
87 ((= (char-before end) ?\n)
88 (let ((eol-face (get-text-property (1- end) 'face)))
89 ;; `eol-face' can be a face, a "face value"
90 ;; (plist of face properties) or a list of one of those.
91 (if (or (not (consp eol-face)) (keywordp (car eol-face)))
92 ;; A single face.
93 (if (visual-wrap--face-extend-p eol-face) eol-face)
94 ;; A list of faces. Keep the ones that extend beyond EOL.
95 (delq nil (mapcar (lambda (f)
96 (if (visual-wrap--face-extend-p f) f))
97 eol-face)))))))
98
99(defun visual-wrap--prefix (fcp)
100 (let ((fcp-len (string-width fcp)))
101 (cond
102 ((= 0 visual-wrap-extra-indent)
103 fcp)
104 ((< 0 visual-wrap-extra-indent)
105 (concat fcp (make-string visual-wrap-extra-indent ?\s)))
106 ((< 0 (+ visual-wrap-extra-indent fcp-len))
107 (substring fcp
108 0
109 (+ visual-wrap-extra-indent fcp-len)))
110 (t
111 ""))))
112
113(defun visual-wrap-fill-context-prefix (beg end)
114 "Like `fill-context-prefix', but with length adjusted by
115`visual-wrap-extra-indent'."
116 (let* ((fcp
117 ;; `fill-context-prefix' ignores prefixes that look like
118 ;; paragraph starts, in order to avoid inadvertently
119 ;; creating a new paragraph while filling, but here we're
120 ;; only dealing with single-line "paragraphs" and we don't
121 ;; actually modify the buffer, so this restriction doesn't
122 ;; make much sense (and is positively harmful in
123 ;; taskpaper-mode where paragraph-start matches everything).
124 (or (let ((paragraph-start "\\`\\'a"))
125 (fill-context-prefix beg end))
126 ;; Note: fill-context-prefix may return nil; See:
127 ;; http://article.gmane.org/gmane.emacs.devel/156285
128 ""))
129 (prefix (visual-wrap--prefix fcp))
130 (face (visual-wrap--prefix-face fcp beg end)))
131 (if face
132 (propertize prefix 'face face)
133 prefix)))
134
135(defun visual-wrap-prefix-function (beg end)
136 "Indent the region between BEG and END with visual filling."
137 ;; Any change at the beginning of a line might change its wrap
138 ;; prefix, which affects the whole line. So we need to "round-up"
139 ;; `end' to the nearest end of line. We do the same with `beg'
140 ;; although it's probably not needed.
141 (goto-char end)
142 (unless (bolp) (forward-line 1))
143 (setq end (point))
144 (goto-char beg)
145 (forward-line 0)
146 (setq beg (point))
147 (while (< (point) end)
148 (let ((lbp (point)))
149 (put-text-property
150 (point) (progn (search-forward "\n" end 'move) (point))
151 'wrap-prefix
152 (let ((pfx (visual-wrap-fill-context-prefix
153 lbp (point))))
154 ;; Remove any `wrap-prefix' property that might have been
155 ;; added earlier. Otherwise, we end up with a string
156 ;; containing a `wrap-prefix' string containing a
157 ;; `wrap-prefix' string ...
158 (remove-text-properties
159 0 (length pfx) '(wrap-prefix) pfx)
160 (let ((dp (get-text-property 0 'display pfx)))
161 (when (and dp (eq dp (get-text-property (1- lbp) 'display)))
162 ;; There's a `display' property which covers not just the
163 ;; prefix but also the previous newline. So it's not
164 ;; just making the prefix more pretty and could interfere
165 ;; or even defeat our efforts (e.g. it comes from
166 ;; `visual-fill-mode').
167 (remove-text-properties
168 0 (length pfx) '(display) pfx)))
169 pfx))))
170 `(jit-lock-bounds ,beg . ,end))
171
172;;;###autoload
173(define-minor-mode visual-wrap-prefix-mode
174 "Wrap the buffer text with visual filling."
175 :lighter ""
176 :group 'visual-line
177 (if visual-wrap-prefix-mode
178 (progn
179 ;; HACK ATTACK! We want to run after font-lock (so our
180 ;; wrap-prefix includes the faces applied by font-lock), but
181 ;; jit-lock-register doesn't accept an `append' argument, so
182 ;; we add ourselves beforehand, to make sure we're at the end
183 ;; of the hook (bug#15155).
184 (add-hook 'jit-lock-functions
185 #'visual-wrap-prefix-function 'append t)
186 (jit-lock-register #'visual-wrap-prefix-function))
187 (jit-lock-unregister #'visual-wrap-prefix-function)
188 (with-silent-modifications
189 (save-restriction
190 (widen)
191 (remove-text-properties (point-min) (point-max) '(wrap-prefix nil))))))
192
193;;;###autoload
194(define-key-after (lookup-key menu-bar-options-menu [line-wrapping])
195 [visual-wrap]
196 '(menu-item "Visual Wrap" visual-wrap-prefix-mode
197 :visible (menu-bar-menu-frame-live-and-visible-p)
198 :help "Display continuation lines with prefix derived from context"
199 :button (:toggle . (bound-and-true-p visual-wrap-prefix-mode)))
200 word-wrap)
201
202(provide 'visual-wrap)
203;;; visual-wrap.el ends here