As wasamasa already pointed out word-wrapping only a part of the buffer requires most probably some re-plumping.
The following demonstrates an idea. It works with truncated lines and puts a newline with indent before the word reaching fill-column
.
I am aware that this is not perfect. Some things that are not handled:
- word wrapping does not adapt to
window-body-width
- it does also not adapt to changes of
fill-column
; one could advice set-fill-column
with jit-lock-redisplay
- no special point motion (e.g.
forward-line
jumps over the visual lines)
- other
jit-lock
handlers might interfere with visline-mode
If you want to try this stuff load it. There is an org-visline-mode
function at the end of this code which installs the filter for org headlines and switches on visline-mode
in org-mode-hook
.
(defvar-local visline-predicate-functions nil
"List of predicate functions that are run at the beginning of each line which is visually word-wrapped by `visline-mode'.
You should add your handlers before running `visline-mode'.
The predicate functions are called without arguments.")
(defmacro visline-expand-region (start end)
"Expand region between START and END such that START is at the beginning of a line and END at the end of a line."
(declare (debug (sexp sexp)))
`(progn
(setq ,start (save-excursion (goto-char ,start) (beginning-of-line) (point)))
(setq ,end (save-excursion (goto-char ,end) (end-of-line) (point)))))
(defun visline-clear (start end)
"Remove visual linebreaks in the region from START to END."
(with-silent-modifications
(while (setq start (text-property-any start end 'visline t))
(remove-text-properties start (1+ start) '(visline t display "\n")))))
(defsubst visline-put (pos indent)
"Put a visual linebreak at the character following position POS."
(add-text-properties pos (1+ pos) (list 'visline t 'display (concat "\n" (make-string indent ?\ )))))
(defun visline-fill-line ()
"Word-wrap current line with the help of visual linebreaks."
(beginning-of-line)
(forward-to-indentation 0)
(let* ((indent (current-column))
(last-col indent)
last-posn
(last-virtual-newline indent)
fitting-line)
(while (null (eolp))
(setq last-col (current-column))
(setq last-posn (point))
(forward-char)
(skip-chars-forward "^[[:space:]]\n")
(when (and (> (- (current-column) last-virtual-newline) fill-column)
(> last-col indent))
(visline-put last-posn indent)
(setq last-virtual-newline (+ last-col indent))))))
(defun visline-handler (start end)
"Visually word-wrap region between START and END."
(with-silent-modifications
(save-excursion
(visline-expand-region start end)
(visline-clear start end)
(goto-char start)
(while (< (point) end)
(when (run-hook-with-args-until-failure 'visline-predicate-functions)
(visline-fill-line))
(forward-line)))))
(define-minor-mode visline-mode
"Word-wrap via text-properties"
:keymap nil
(if visline-mode
(jit-lock-register 'visline-handler) ;; `jit-lock-register' also re-fontifies
(jit-lock-unregister 'visline-handler)
(with-silent-modifications
(visline-clear (point-min) (point-max)))))
(defun org-visline-dont-wrap-headlines ()
"Return only t if we are not on a headline."
(null (eq (char-after) ?*)))
(defun org-visline-mode ()
"Switches `visline-mode' on in `org-mode'.
Usage:
(require 'visline)
(add-hook 'org-mode-hook 'org-visline-mode)"
(add-hook 'visline-predicate-functions 'org-visline-dont-wrap-headlines)
(visline-mode t))
(provide 'visline)
(add-hook 'org-mode-hook 'org-visline-mode)