aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/lispref/display.texi6
-rw-r--r--etc/NEWS12
-rw-r--r--lisp/emacs-lisp/subr-x.el11
-rw-r--r--lisp/visual-wrap.el113
4 files changed, 102 insertions, 40 deletions
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 195464ef7f5..d28ff9ead26 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2385,9 +2385,11 @@ The optional arguments @var{x-limit} and @var{y-limit} have the same
2385meaning as with @code{window-text-pixel-size}. 2385meaning as with @code{window-text-pixel-size}.
2386@end defun 2386@end defun
2387 2387
2388@defun string-pixel-width string 2388@defun string-pixel-width string &optional buffer
2389This is a convenience function that uses @code{window-text-pixel-size} 2389This is a convenience function that uses @code{window-text-pixel-size}
2390to compute the width of @var{string} (in pixels). 2390to compute the width of @var{string} (in pixels). If @var{buffer} is
2391non-@code{nil}, use any face remappings (@pxref{Face Remapping}) from
2392that buffer when computing the width of @var{string}.
2391@end defun 2393@end defun
2392 2394
2393@defun line-pixel-height 2395@defun line-pixel-height
diff --git a/etc/NEWS b/etc/NEWS
index 02007830bfc..b89a80aa14d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -83,6 +83,12 @@ aggressively rather than switching to some other buffer in it.
83*** New language-environment and input method for Tifinagh. 83*** New language-environment and input method for Tifinagh.
84The Tifinagh script is used to write the Berber languages. 84The Tifinagh script is used to write the Berber languages.
85 85
86---
87** 'visual-wrap-prefix-mode' now supports variable-pitch fonts.
88When using 'visual-wrap-prefix-mode' in buffers with variable-pitch
89fonts, the wrapped text will now be lined up correctly so that it's
90exactly below the text after the prefix on the first line.
91
86 92
87* Changes in Specialized Modes and Packages in Emacs 31.1 93* Changes in Specialized Modes and Packages in Emacs 31.1
88 94
@@ -245,6 +251,12 @@ language A will be applied to language B instead.
245This is useful for reusing font-lock rules and indentation rules of 251This is useful for reusing font-lock rules and indentation rules of
246language A for language B, when language B is a strict superset of 252language A for language B, when language B is a strict superset of
247language A. 253language A.
254
255+++
256** New optional BUFFER argument for 'string-pixel-width'.
257If supplied, 'string-pixel-width' will use any face remappings from
258BUFFER when computing the string's width.
259
248 260
249* Changes in Emacs 31.1 on Non-Free Operating Systems 261* Changes in Emacs 31.1 on Non-Free Operating Systems
250 262
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index e725c490aba..058c06bc5f6 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -337,8 +337,10 @@ This construct can only be used with lexical binding."
337 . ,aargs))) 337 . ,aargs)))
338 338
339;;;###autoload 339;;;###autoload
340(defun string-pixel-width (string) 340(defun string-pixel-width (string &optional buffer)
341 "Return the width of STRING in pixels." 341 "Return the width of STRING in pixels.
342If BUFFER is non-nil, use the face remappings from that buffer when
343determining the width."
342 (declare (important-return-value t)) 344 (declare (important-return-value t))
343 (if (zerop (length string)) 345 (if (zerop (length string))
344 0 346 0
@@ -352,6 +354,11 @@ This construct can only be used with lexical binding."
352 ;; Disable line-prefix and wrap-prefix, for the same reason. 354 ;; Disable line-prefix and wrap-prefix, for the same reason.
353 (setq line-prefix nil 355 (setq line-prefix nil
354 wrap-prefix nil) 356 wrap-prefix nil)
357 (if buffer
358 (setq-local face-remapping-alist
359 (with-current-buffer buffer
360 face-remapping-alist))
361 (kill-local-variable 'face-remapping-alist))
355 (insert (propertize string 'line-prefix nil 'wrap-prefix nil)) 362 (insert (propertize string 'line-prefix nil 'wrap-prefix nil))
356 (car (buffer-text-pixel-size nil nil t))))) 363 (car (buffer-text-pixel-size nil nil t)))))
357 364
diff --git a/lisp/visual-wrap.el b/lisp/visual-wrap.el
index d95cf4bb569..cac3bc767b8 100644
--- a/lisp/visual-wrap.el
+++ b/lisp/visual-wrap.el
@@ -97,24 +97,85 @@ extra indent = 2
97 (if (visual-wrap--face-extend-p f) f)) 97 (if (visual-wrap--face-extend-p f) f))
98 eol-face))))))) 98 eol-face)))))))
99 99
100(defun visual-wrap--prefix (fcp) 100(defun visual-wrap--adjust-prefix (prefix)
101 (let ((fcp-len (string-width fcp))) 101 "Adjust PREFIX with `visual-wrap-extra-indent'."
102 (cond 102 (if (numberp prefix)
103 ((= 0 visual-wrap-extra-indent) 103 (+ visual-wrap-extra-indent prefix)
104 fcp) 104 (let ((prefix-len (string-width prefix)))
105 ((< 0 visual-wrap-extra-indent) 105 (cond
106 (concat fcp (make-string visual-wrap-extra-indent ?\s))) 106 ((= 0 visual-wrap-extra-indent)
107 ((< 0 (+ visual-wrap-extra-indent fcp-len)) 107 prefix)
108 (substring fcp 108 ((< 0 visual-wrap-extra-indent)
109 0 109 (concat prefix (make-string visual-wrap-extra-indent ?\s)))
110 (+ visual-wrap-extra-indent fcp-len))) 110 ((< 0 (+ visual-wrap-extra-indent prefix-len))
111 (t 111 (substring prefix
112 "")))) 112 0 (+ visual-wrap-extra-indent prefix-len)))
113 (t
114 "")))))
115
116(defun visual-wrap--apply-to-line (position)
117 "Apply visual-wrapping properties to the logical line starting at POSITION."
118 (save-excursion
119 (goto-char position)
120 (when-let ((first-line-prefix (fill-match-adaptive-prefix))
121 (next-line-prefix (visual-wrap--content-prefix
122 first-line-prefix position)))
123 (when (numberp next-line-prefix)
124 (put-text-property
125 position (+ position (length first-line-prefix)) 'display
126 `(min-width ((,next-line-prefix . width)))))
127 (setq next-line-prefix (visual-wrap--adjust-prefix next-line-prefix))
128 (put-text-property
129 position (line-end-position) 'wrap-prefix
130 (if (numberp next-line-prefix)
131 `(space :align-to (,next-line-prefix . width))
132 next-line-prefix)))))
133
134(defun visual-wrap--content-prefix (prefix position)
135 "Get the next-line prefix for the specified first-line PREFIX.
136POSITION is the position in the buffer where PREFIX is located.
137
138This returns a string prefix to use for subsequent lines; an integer,
139indicating the number of canonical-width spaces to use; or nil, if
140PREFIX was empty."
141 (cond
142 ((string= prefix "")
143 nil)
144 ((string-match (rx bos (+ blank) eos) prefix)
145 ;; If the first-line prefix is all spaces, return its width in
146 ;; characters. This way, we can set the prefix for all lines to use
147 ;; the canonical-width of the font, which helps for variable-pitch
148 ;; fonts where space characters are usually quite narrow.
149 (string-width prefix))
150 ((or (and adaptive-fill-first-line-regexp
151 (string-match adaptive-fill-first-line-regexp prefix))
152 (and comment-start-skip
153 (string-match comment-start-skip prefix)))
154 ;; If we want to repeat the first-line prefix on subsequent lines,
155 ;; return its string value. However, we remove any `wrap-prefix'
156 ;; property that might have been added earlier. Otherwise, we end
157 ;; up with a string containing a `wrap-prefix' string containing a
158 ;; `wrap-prefix' string...
159 (remove-text-properties 0 (length prefix) '(wrap-prefix) prefix)
160 prefix)
161 (t
162 ;; Otherwise, we want the prefix to be whitespace of the same width
163 ;; as the first-line prefix. If possible, compute the real pixel
164 ;; width of the first-line prefix in canonical-width characters.
165 ;; This is useful if the first-line prefix uses some very-wide
166 ;; characters.
167 (if-let ((font (font-at position))
168 (info (query-font font)))
169 (max (string-width prefix)
170 (ceiling (string-pixel-width prefix (current-buffer))
171 (aref info 7)))
172 (string-width prefix)))))
113 173
114(defun visual-wrap-fill-context-prefix (beg end) 174(defun visual-wrap-fill-context-prefix (beg end)
115 "Compute visual wrap prefix from text between BEG and END. 175 "Compute visual wrap prefix from text between BEG and END.
116This is like `fill-context-prefix', but with prefix length adjusted 176This is like `fill-context-prefix', but with prefix length adjusted
117by `visual-wrap-extra-indent'." 177by `visual-wrap-extra-indent'."
178 (declare (obsolete nil "31.1"))
118 (let* ((fcp 179 (let* ((fcp
119 ;; `fill-context-prefix' ignores prefixes that look like 180 ;; `fill-context-prefix' ignores prefixes that look like
120 ;; paragraph starts, in order to avoid inadvertently 181 ;; paragraph starts, in order to avoid inadvertently
@@ -128,7 +189,7 @@ by `visual-wrap-extra-indent'."
128 ;; Note: fill-context-prefix may return nil; See: 189 ;; Note: fill-context-prefix may return nil; See:
129 ;; http://article.gmane.org/gmane.emacs.devel/156285 190 ;; http://article.gmane.org/gmane.emacs.devel/156285
130 "")) 191 ""))
131 (prefix (visual-wrap--prefix fcp)) 192 (prefix (visual-wrap--adjust-prefix fcp))
132 (face (visual-wrap--prefix-face fcp beg end))) 193 (face (visual-wrap--prefix-face fcp beg end)))
133 (if face 194 (if face
134 (propertize prefix 'face face) 195 (propertize prefix 'face face)
@@ -147,28 +208,8 @@ by `visual-wrap-extra-indent'."
147 (forward-line 0) 208 (forward-line 0)
148 (setq beg (point)) 209 (setq beg (point))
149 (while (< (point) end) 210 (while (< (point) end)
150 (let ((lbp (point))) 211 (visual-wrap--apply-to-line (point))
151 (put-text-property 212 (forward-line))
152 (point) (progn (search-forward "\n" end 'move) (point))
153 'wrap-prefix
154 (let ((pfx (visual-wrap-fill-context-prefix
155 lbp (point))))
156 ;; Remove any `wrap-prefix' property that might have been
157 ;; added earlier. Otherwise, we end up with a string
158 ;; containing a `wrap-prefix' string containing a
159 ;; `wrap-prefix' string ...
160 (remove-text-properties
161 0 (length pfx) '(wrap-prefix) pfx)
162 (let ((dp (get-text-property 0 'display pfx)))
163 (when (and dp (eq dp (get-text-property (1- lbp) 'display)))
164 ;; There's a `display' property which covers not just the
165 ;; prefix but also the previous newline. So it's not
166 ;; just making the prefix more pretty and could interfere
167 ;; or even defeat our efforts (e.g. it comes from
168 ;; `adaptive-fill-mode').
169 (remove-text-properties
170 0 (length pfx) '(display) pfx)))
171 pfx))))
172 `(jit-lock-bounds ,beg . ,end)) 213 `(jit-lock-bounds ,beg . ,end))
173 214
174;;;###autoload 215;;;###autoload