BUYER BEWARE: This is a prototype code, and during the development of this snippet, I had to re-start my emacs
multiple times.
If the custom fontifier doesn't move the point
(or doesn't behave like a good citizen, in the sense that font-lock
machinery expects it to) then emacs
locks up the cpu, and becomes usable. So, use this snippet only after good amount of testing.
So, don't this snippet on a buffer which has your dissertation thesis, or some such important thing.
emacs -Q -nw
- Copy the snippet to
*scratch*
buffer, and do M-x eval-buffer
. The snippet adds soft wrap
indicators to visual-line-mode
buffer, just the way you want it---that is, right within the buffer, and immediately at the end of the soft wrap
-ped line The snippet binds C-c C-c
global
-ly to visual-line-mode
for quick testing.
;; testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode
;; testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode testing soft-wrap mode
(global-set-key (kbd "C-c C-c") 'visual-line-mode)
(defface soft-wrap-face
'((t :foreground "red"))
"Face for hi-lock mode."
:group 'hi-lock-faces)
(defvar soft-wrap)
(put 'soft-wrap 'display "\N{DOWN ARROW WITH TIP LEFT}")
(put 'soft-wrap 'soft-wrap t)
(put 'soft-wrap 'face 'soft-wrap-face)
;; (put 'soft-wrap 'face nil)
(defun make-soft-wrap-overlay (&optional p)
(let* ((p (or p (point)))
(ov (make-overlay p (1+ p))))
(overlay-put ov 'category 'soft-wrap)))
(defun remove-soft-wrap-overlays (&optional beg end)
(let* ((beg (or beg (point-min)))
(end (or end (point-max))))
(remove-overlays beg end 'soft-wrap t)))
(defun my-font-lock-matcher (limit)
(let* ((points '()))
(end-of-visual-line)
(while (< (point) limit)
(cond
((looking-back "\n" (1- (point)))
(forward-char 2)
(end-of-visual-line))
((looking-at "\n")
(skip-chars-forward "\n"))
(t
(push (point) points)
(forward-char 2)
(end-of-visual-line))))
(while points
(let ((p (pop points)))
(make-soft-wrap-overlay p)))
(goto-char limit)
t))
(defun my-extend-region-function (beg end old-len)
(let* ((end (progn (goto-char end)
(re-search-forward "\n" nil 'move)
(point)))
(cursor beg))
(remove-soft-wrap-overlays beg end)
(cons beg end)))
(defun turn-on-soft-wrap ()
(font-lock-add-keywords
nil (list '(my-font-lock-matcher)))
(setq-local font-lock-extend-after-change-region-function
#'my-extend-region-function)
(font-lock-flush))
(defun turn-off-soft-wrap ()
(remove-soft-wrap-overlays)
(font-lock-remove-keywords
nil (list '(my-font-lock-matcher))))
(add-hook 'visual-line-mode-hook
(defun my-visual-line-mode-hook-1 ()
(cond
(visual-line-mode
(auto-fill-mode -1)
(turn-on-soft-wrap))
(t (turn-off-soft-wrap)))))
(add-hook 'visual-line-mode-hook
(defun my-visual-line-mode-hook ()
(unless (display-graphic-p)
;; UTF-8 support
;; (prefer-coding-system 'utf-8)
;; (set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
;; (set-keyboard-coding-system 'utf-8)
;; (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))
;; (info "(elisp) Truncation")
;; [Truncation (GNU Emacs Lisp Reference Manual)](https://www.gnu.org/software/emacs/manual/html_node/elisp/Truncation.html#index-line_002dprefix)
(setq line-prefix
(propertize "\N{SPACE}" 'face 'highlight))
(setq wrap-prefix
(propertize "\N{DOWN ARROW WITH TIP RIGHT}" 'face 'highlight))
;; This is for wrap at the window edge
;; (info "(elisp) Display Tables")
;; [Display Tables (GNU Emacs Lisp Reference Manual)](https://www.gnu.org/software/emacs/manual/html_node/elisp/Display-Tables.html#index-display_002dtable_002dslot)
(set-display-table-slot standard-display-table
'wrap (make-glyph-code ?\N{DOWN ARROW WITH TIP LEFT} 'highlight)))))
;; Local Variables:
;; byte-compile-warnings: all
;; End:
- Do
M-x visual-line-mode
, and this is what you will see.
Custom soft-wrap
indicators in visual-line-mode
; also shows wrap indicators--seen at the beginning of line--which comes by default with emacs
Edit the long lines; Monkey around by deleting large chunk of test, or inserting large chuck of text, or by breaking a long line by inserting newlines. Satisfies yourself that the soft-wrap
markers at the right
side of text, are redrawn, along the expected lines (with old soft-wrap
markers vanishing in to thin air)
FWIW, this is how the buffer looks in NON visual-line-mode
Buffer with long lines; and visual-line-mode
is OFF
Apropos (forward-char 2)
---the code that moves the point
to the next visual line: Intuitively movement by 1
char is sufficient to get past the end of visual line space
or tab
character (displayed as soft wrap
). But once these space
characters have a display
property, then the point
moves in a counter-intuitive way. (Difficult to explain by words, and interested parties can experiment on their own, make sense of what I say here)
There are plenty of reports on official emacs
channel and in stackexchange
, about display
property on soft wrap
spaces influencing the vertical-motion
in a counter-intuitive way.
Initially, I went with 1
and the point
was not moving in the MATCHER
routine. So, emacs
was locking up. (Lockups are a nightmare, and difficult to debug.)
Let me repeat, consider this code as a prototype; I recommend that you don't use it on very important buffers.
REFERENCES
Instead of continuation
indicator---indicator that appears on the right window edge, right next to the point where the line wraps---what you instead get is prefix
indicators---line-prefix
and wrap-prefix
. That is the prefix
indicators appear AFTER the line has wrapped, and appears on the left window edge. (They are are NOT "user-friendly", and forces you to "wrap" your brains).
(info "(elisp) Truncation")
(info "(elisp) Display Tables")