legacy-dotfiles/emacs.d/nxhtml/util/wrap-to-fill.el

365 lines
14 KiB
EmacsLisp
Raw Normal View History

;;; wrap-to-fill.el --- Make a fill-column wide space for editing
;;
;; Author: Lennart Borgman (lennart O borgman A gmail O com)
;; Created: 2009-08-12 Wed
;; Version:
;; Last-Updated: x
;; URL:
;; Keywords:
;; Compatibility:
;;
;; Features that might be required by this library:
;;
;; None
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Change log:
;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 3, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:
(eval-when-compile (require 'mumamo))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Wrapping
;;;###autoload
(defgroup wrap-to-fill nil
"Customizing of `wrap-to-fill-column-mode'."
:group 'convenience)
;;;###autoload
(defcustom wrap-to-fill-left-marg nil
"Left margin handling for `wrap-to-fill-column-mode'.
Used by `wrap-to-fill-column-mode'. If nil then center the
display columns. Otherwise it should be a number which will be
the left margin."
:type '(choice (const :tag "Center" nil)
(integer :tag "Left margin"))
:group 'wrap-to-fill)
(make-variable-buffer-local 'wrap-to-fill-left-marg)
(defvar wrap-to-fill--saved-state nil)
;;(make-variable-buffer-local 'wrap-to-fill--saved-state)
(put 'wrap-to-fill--saved-state 'permanent-local t)
;;;###autoload
(defcustom wrap-to-fill-left-marg-modes
'(text-mode
fundamental-mode)
"Major modes where `wrap-to-fill-left-margin' may be nil."
:type '(repeat command)
:group 'wrap-to-fill)
;;ThisisaVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongWord ThisisaVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongWord
(defun wrap-to-fill-wider ()
"Increase `fill-column' with 10."
(interactive)
(setq fill-column (+ fill-column 10))
(wrap-to-fill-set-values-in-buffer-windows))
(defun wrap-to-fill-narrower ()
"Decrease `fill-column' with 10."
(interactive)
(setq fill-column (- fill-column 10))
(wrap-to-fill-set-values-in-buffer-windows))
(defun wrap-to-fill-normal ()
"Reset `fill-column' to global value."
(interactive)
;;(setq fill-column (default-value 'fill-column))
(kill-local-variable 'fill-column)
(wrap-to-fill-set-values-in-buffer-windows))
(defvar wrap-to-fill-column-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [(control ?c) ?+] 'wrap-to-fill-wider)
(define-key map [(control ?c) ?-] 'wrap-to-fill-narrower)
(define-key map [(control ?c) ?0] 'wrap-to-fill-normal)
map))
;; Fix-me: Maybe make the `wrap-prefix' behavior an option or separate
;; minor mode.
;; Fix-me: better handling of left-column in mumamo buffers (and other
;; if possible).
;;;###autoload
(define-minor-mode wrap-to-fill-column-mode
"Use `fill-column' display columns in buffer windows.
By default the display columns are centered, but see the option
`wrap-to-fill-left-marg'.
Fix-me:
Note 1: When turning this on `visual-line-mode' is also turned on. This
is not reset when turning off this mode.
Note 2: The text properties 'wrap-prefix and 'wrap-to-fill-prefix
is set by this mode to indent continuation lines.
Key bindings added by this minor mode:
\\{wrap-to-fill-column-mode-map}"
:lighter " WrapFill"
:group 'wrap-to-fill
;; (message "wrap-to-fill-column-mode %s, cb=%s, major=%s, multi=%s" wrap-to-fill-column-mode (current-buffer)
;; major-mode mumamo-multi-major-mode)
(if wrap-to-fill-column-mode
(progn
;; Old values (idea from visual-line-mode)
(set (make-local-variable 'wrap-to-fill--saved-state) nil)
(dolist (var '(visual-line-mode
;;left-margin-width
;;right-margin-width
))
(push (list var (symbol-value var) (local-variable-p var))
wrap-to-fill--saved-state))
;; Hooks
(add-hook 'window-configuration-change-hook 'wrap-to-fill-set-values nil t)
;; Wrapping
(visual-line-mode 1)
(wrap-to-fill-set-values-in-buffer-windows))
;; Hooks
(remove-hook 'window-configuration-change-hook 'wrap-to-fill-set-values t)
;; Old values
(dolist (saved wrap-to-fill--saved-state)
(let ((var (nth 0 saved))
(val (nth 1 saved))
(loc (nth 2 saved)))
(cond
((eq var 'visual-line-mode)
(unless val (visual-line-mode -1)))
(t
(if loc
(set (make-local-variable var) val)
(kill-local-variable var))))))
(kill-local-variable 'wrap-to-fill--saved-state)
;; Margins
(dolist (win (get-buffer-window-list (current-buffer)))
(set-window-margins win left-margin-width right-margin-width))
;; Indentation
(let ((here (point))
(inhibit-field-text-motion t)
beg-pos
end-pos)
(mumamo-with-buffer-prepared-for-jit-lock
(save-restriction
(widen)
(goto-char (point-min))
(while (< (point) (point-max))
(setq beg-pos (point))
(setq end-pos (line-end-position))
(when (equal (get-text-property beg-pos 'wrap-prefix)
(get-text-property beg-pos 'wrap-to-fill-prefix))
(remove-list-of-text-properties
beg-pos end-pos
'(wrap-prefix)))
(forward-line))
(remove-list-of-text-properties
(point-min) (point-max)
'(wrap-to-fill-prefix)))
(goto-char here))))
(wrap-to-fill-font-lock wrap-to-fill-column-mode))
(put 'wrap-to-fill-column-mode 'permanent-local t)
(defcustom wrap-to-fill-major-modes '(org-mode
html-mode
nxhtml-mode)
"Major modes where to turn on `wrap-to-fill-column-mode'"
;;:type '(repeat major-mode)
:type '(repeat command)
:group 'wrap-to-fill)
(defun wrap-to-fill-turn-on-in-buffer ()
"Turn on fun for globalization."
(when (catch 'turn-on
(dolist (m wrap-to-fill-major-modes)
(when (derived-mode-p m)
(throw 'turn-on t))))
(wrap-to-fill-column-mode 1)))
(define-globalized-minor-mode wrap-to-fill-column-global-mode wrap-to-fill-column-mode
wrap-to-fill-turn-on-in-buffer
:group 'wrap-to-fill)
;; Fix-me: There is a confusion between buffer and window margins
;; here. Also the doc says that left-margin-width and dito right may
;; be nil. However they seem to be 0 by default, but when displaying a
;; buffer in a window then window-margins returns (nil).
(defvar wrap-to-fill-timer nil)
(make-variable-buffer-local 'wrap-to-fill-timer)
(defun wrap-to-fill-set-values ()
(when (timerp wrap-to-fill-timer)
(cancel-timer wrap-to-fill-timer))
(setq wrap-to-fill-timer
(run-with-idle-timer 0 nil 'wrap-to-fill-set-values-in-timer
(selected-window) (current-buffer))))
(put 'wrap-to-fill-set-values 'permanent-local-hook t)
(defun wrap-to-fill-set-values-in-timer (win buf)
(condition-case err
(when (buffer-live-p buf)
(wrap-to-fill-set-values-in-buffer-windows buf))
(error (message "ERROR wrap-to-fill-set-values-in-timer: %s"
(error-message-string err)))))
(defun wrap-to-fill-set-values-in-timer-old (win buf)
(when (and (window-live-p win) (buffer-live-p buf)
(eq buf (window-buffer win)))
(condition-case err
(with-current-buffer buf
(when wrap-to-fill-column-mode
(wrap-to-fill-set-values-in-window win)))
(error (message "ERROR wrap-to-fill-set-values: %s"
(error-message-string err))))))
(defun wrap-to-fill-set-values-in-buffer-windows (&optional buffer)
"Use `fill-column' display columns in buffer windows."
(let ((buf-windows (get-buffer-window-list (or buffer
(current-buffer))
nil
t)))
(dolist (win buf-windows)
(if wrap-to-fill-column-mode
(wrap-to-fill-set-values-in-window win)
(set-window-buffer nil (current-buffer))))))
(defvar wrap-old-win-width nil)
(make-variable-buffer-local 'wrap-old-win-width)
;; Fix-me: compensate for left-margin-width etc
(defun wrap-to-fill-set-values-in-window (win)
(with-current-buffer (window-buffer win)
(when wrap-to-fill-column-mode
(let* ((win-width (window-width win))
(win-margs (window-margins win))
(win-full (+ win-width
(or (car win-margs) 0)
(or (cdr win-margs) 0)))
(extra-width (- win-full fill-column))
(fill-left-marg (unless (memq major-mode wrap-to-fill-left-marg-modes)
(or (when (> left-margin-width 0) left-margin-width)
wrap-to-fill-left-marg)))
(left-marg (if fill-left-marg
fill-left-marg
(- (/ extra-width 2) 1)))
;; Fix-me: Why do I have to subtract 1 here...???
(right-marg (- win-full fill-column left-marg 1))
(need-update nil)
)
;; (when wrap-old-win-width
;; (unless (= wrap-old-win-width win-width)
;; (message "-")
;; (message "win-width 0: %s => %s, win-full=%s, e=%s l/r=%s/%s %S %S %S" wrap-old-win-width win-width win-full extra-width left-marg right-marg (window-edges) (window-inside-edges) (window-margins))
;; ))
(setq wrap-old-win-width win-width)
(unless (> left-marg 0) (setq left-marg 0))
(unless (> right-marg 0) (setq right-marg 0))
(unless nil;(= left-marg (or left-margin-width 0))
;;(setq left-margin-width left-marg)
(setq need-update t))
(unless nil;(= right-marg (or right-margin-width 0))
;;(setq right-margin-width right-marg)
(setq need-update t))
;;(message "win-width a: %s => %s, win-full=%s, e=%s l/r=%s/%s %S %S %S" wrap-old-win-width win-width win-full extra-width left-margin-width right-margin-width (window-edges) (window-inside-edges) (window-margins))
(when need-update
;;(set-window-buffer win (window-buffer win))
;;(run-with-idle-timer 0 nil 'set-window-buffer win (window-buffer win))
;;(dolist (win (get-buffer-window-list (current-buffer)))
;; Fix-me: check window width...
(set-window-margins win left-marg right-marg)
;;)
;;(message "win-width b: %s => %s, win-full=%s, e=%s l/r=%s/%s %S %S %S" wrap-old-win-width win-width win-full extra-width left-marg right-marg (window-edges) (window-inside-edges) (window-margins))
)
))))
;; (add-hook 'post-command-hook 'my-win-post-command nil t)
;; (remove-hook 'post-command-hook 'my-win-post-command t)
(defun my-win-post-command ()
(message "win-post-command: l/r=%s/%s %S %S %S" left-margin-width right-margin-width (window-edges) (window-inside-edges) (window-margins))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Font lock
(defun wrap-to-fill-fontify (bound)
(save-restriction
(widen)
(while (< (point) bound)
(let ((this-bol (if (bolp) (point)
(1+ (line-end-position)))))
(unless (< this-bol bound) (setq this-bol nil))
(when this-bol
(goto-char (+ this-bol 0))
(let (ind-str
ind-str-fill
(beg-pos this-bol)
(end-pos (line-end-position)))
(when (equal (get-text-property beg-pos 'wrap-prefix)
(get-text-property beg-pos 'wrap-to-fill-prefix))
;; Find indentation
(skip-chars-forward "[:blank:]")
(setq ind-str (buffer-substring-no-properties beg-pos (point)))
;; Any special markers like -, * etc
(if (and (< (1+ (point)) (point-max))
(memq (char-after) '(?- ;; 45
? ;; 8211
?*
))
(eq (char-after (1+ (point))) ?\ ))
(setq ind-str-fill (concat " " ind-str))
(setq ind-str-fill ind-str))
;;(setq ind-str-fill (concat " " ind-str))
(mumamo-with-buffer-prepared-for-jit-lock
(put-text-property beg-pos end-pos 'wrap-prefix ind-str-fill)
(put-text-property beg-pos end-pos 'wrap-to-fill-prefix ind-str-fill))))))
(forward-line 1))
;; Note: doing it line by line and returning t gave problem in mumamo.
(when nil ;this-bol
(set-match-data (list (point) (point)))
t)))
(defun wrap-to-fill-font-lock (on)
;; See mlinks.el
(let* ((add-or-remove (if on 'font-lock-add-keywords 'font-lock-remove-keywords))
(fontify-fun 'wrap-to-fill-fontify)
(args (list nil `(( ,fontify-fun ( 0 'font-lock-warning-face t ))))))
(when fontify-fun
(when on (setq args (append args (list t))))
(apply add-or-remove args)
(font-lock-mode -1)
(font-lock-mode 1))))
(provide 'wrap-to-fill)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; wrap-to-fill.el ends here