94d2fc1815
* Added nxhtml, mostly for django support. * Changed some org settings.
364 lines
14 KiB
EmacsLisp
364 lines
14 KiB
EmacsLisp
;;; 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
|