1 ;;; kfill.el --- Fill and justify koutline cells (adapted from Kyle Jones' filladapt).
3 ;; Copyright (C) 1995 Free Software Foundation, Inc.
4 ;; Developed with support from Motorola Inc.
6 ;; Author: Bob Weiner, Brown U.
7 ;; Maintainer: Mats Lidell <matsl@contactor.se>
8 ;; Keywords: outlines, wp
10 ;; This file is part of GNU Hyperbole.
12 ;; GNU Hyperbole is free software; you can redistribute it and/or
13 ;; modify it under the terms of the GNU General Public License as
14 ;; published by the Free Software Foundation; either version 3, or (at
15 ;; your option) any later version.
17 ;; GNU Hyperbole is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ;; General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ;; Boston, MA 02110-1301, USA.
31 (defvar kfill:function-table
33 (if (featurep 'filladapt)
34 (progn (load "fill") ;; Save basic fill-paragraph function.
35 (load "simple"))) ;; Save basic do-auto-fill function.
36 (list (cons 'fill-paragraph (symbol-function 'fill-paragraph))
37 (cons 'do-auto-fill (symbol-function 'do-auto-fill))))
38 "Table containing the old function definitions that kfill overrides.")
40 (defvar kfill:prefix-table
42 ;; Lists with hanging indents, e.g.
43 ;; 1. xxxxx or 1) xxxxx etc.
46 ;; Be sure pattern does not match to: (last word in parens starts
48 (" *(?\\([0-9][0-9a-z.]*\\|[a-z][0-9a-z.]\\)) +" . kfill:hanging-list)
49 (" *\\([0-9]+[a-z.]+[0-9a-z.]*\\|[0-9]+\\|[a-z]\\)\\([.>] +\\| +\\)"
51 ;; Included text in news or mail replies
52 ("[ \t]*\\(>+ *\\)+" . kfill:normal-included-text)
53 ;; Included text generated by SUPERCITE. We can't hope to match all
54 ;; the possible variations, your mileage may vary.
55 ("[ \t]*[A-Za-z0-9][^'`\"< \t\n]*>[ \t]*" . kfill:supercite-included-text)
57 ("[ \t]*\\(;+[ \t]*\\)+" . kfill:lisp-comment)
58 ;; UNIX shell comments
59 ("[ \t]*\\(#+[ \t]*\\)+" . kfill:sh-comment)
60 ;; Postscript comments
61 ("[ \t]*\\(%+[ \t]*\\)+" . kfill:postscript-comment)
63 ("[ \t]*//[/ \t]*" . kfill:c++-comment)
64 ("[?!~*+ -]+ " . kfill:hanging-list)
65 ;; This keeps normal paragraphs from interacting unpleasantly with
66 ;; the types given above.
67 ("[^ \t/#%?!~*+-]" . kfill:normal)
69 "Value is an alist of the form
71 ((REGXP . FUNCTION) ...)
73 When fill-paragraph or do-auto-fill is called, the REGEXP of each alist
74 element is compared with the beginning of the current line. If a match
75 is found the corresponding FUNCTION is called. FUNCTION is called with
76 one argument, which is non-nil when invoked on the behalf of
77 fill-paragraph, nil for do-auto-fill. It is the job of FUNCTION to set
78 the values of the paragraph-* variables (or set a clipping region, if
79 paragraph-start and paragraph-separate cannot be made discerning enough)
80 so that fill-paragraph and do-auto-fill work correctly in various
87 (defun do-auto-fill ()
89 (if (null fill-prefix)
90 (let ((paragraph-ignore-fill-prefix nil)
91 ;; Need this or Emacs 19 ignores fill-prefix when
93 (comment-multi-line t)
96 (kfill:funcall 'do-auto-fill))
97 (kfill:funcall 'do-auto-fill))))
99 (defun fill-paragraph (arg &optional skip-prefix-remove)
100 "Fill paragraph at or after point. Prefix ARG means justify as well."
102 ;; Emacs 19 expects a specific symbol here.
103 (if (and arg (not (symbolp arg))) (setq arg 'full))
104 (or skip-prefix-remove (kfill:remove-paragraph-prefix))
107 (if (null fill-prefix)
108 (let ((paragraph-ignore-fill-prefix nil)
109 ;; Need this or Emacs 19 ignores fill-prefix when
111 (comment-multi-line t)
112 (paragraph-start paragraph-start)
113 (paragraph-separate paragraph-separate)
116 (throw 'done (kfill:funcall 'fill-paragraph arg)))))
117 ;; Kfill:adapt failed or fill-prefix is set, so do a basic
118 ;; paragraph fill as adapted from par-align.el.
119 (kfill:fill-paragraph arg skip-prefix-remove))))
122 ;;; Redefine this function so that it sets 'fill-prefix-prev' also.
124 (defun set-fill-prefix (&optional turn-off)
125 "Set the fill-prefix to the current line up to point.
126 Also sets fill-prefix-prev to previous value of fill-prefix.
127 Filling expects lines to start with the fill prefix and reinserts the fill
128 prefix in each resulting line."
130 (setq fill-prefix-prev fill-prefix
131 fill-prefix (if turn-off
134 (save-excursion (beginning-of-line) (point))
136 (if (equal fill-prefix-prev "")
137 (setq fill-prefix-prev nil))
138 (if (equal fill-prefix "")
139 (setq fill-prefix nil))
141 (message "fill-prefix: \"%s\"" fill-prefix)
142 (message "fill-prefix cancelled")))
145 ;;; Private functions
148 (defun kfill:adapt (paragraph)
149 (let ((table kfill:prefix-table)
155 (if (not (looking-at (car (car table))))
156 (setq table (cdr table))
157 (funcall (cdr (car table)) paragraph)
158 (setq success t table nil))))
161 (defun kfill:c++-comment (paragraph)
162 (setq fill-prefix (buffer-substring (match-beginning 0) (match-end 0)))
164 (setq paragraph-separate "^[^ \t/]")))
166 (defun kfill:fill-paragraph (justify-flag &optional leave-prefix)
169 ;; Backward to para begin
170 (re-search-backward (concat "\\`\\|" paragraph-separate))
172 (let ((region-start (point)))
174 (let ((from (point)))
176 ;; Forward to real paragraph end
177 (re-search-forward (concat "\\'\\|" paragraph-separate))
178 (or (= (point) (point-max)) (beginning-of-line))
180 (kfill:replace-string
181 (or fill-prefix fill-prefix-prev)
182 "" nil region-start (point)))
183 (fill-region-as-paragraph from (point) justify-flag)))))
185 (defun kfill:funcall (function &rest args)
186 (apply (cdr (assq function kfill:function-table)) args))
188 (defun kfill:hanging-list (paragraph)
189 (let (prefix match beg end)
190 (setq prefix (make-string (- (match-end 0) (match-beginning 0)) ?\ ))
193 (setq match (buffer-substring (match-beginning 0) (match-end 0)))
194 (if (string-match "^ +$" match)
196 (while (and (not (bobp)) (looking-at prefix))
199 (cond ((kfill:hanging-p)
201 (t (setq beg (progn (forward-line 1) (point))))))
205 (while (and (looking-at prefix)
206 (not (equal (char-after (match-end 0)) ?\ )))
209 (narrow-to-region beg end)))
210 (setq fill-prefix prefix)))
212 (defun kfill:hanging-p ()
213 "Return non-nil iff point is in front of a hanging list."
214 (eval kfill:hanging-expression))
216 (defun kfill:lisp-comment (paragraph)
217 (setq fill-prefix (buffer-substring (match-beginning 0) (match-end 0)))
219 (setq paragraph-separate
220 (concat "^" fill-prefix " *;\\|^"
221 (kfill:negate-string fill-prefix)))))
223 (defun kfill:negate-string (string)
224 (let ((len (length string))
226 (setq string-list (cons "\\(" nil))
229 (cons (if (= i (1- len)) "" "\\|")
231 (cons (substring string i (1+ i))
233 (cons (regexp-quote (substring string 0 i))
236 (setq string-list (cons "\\)" string-list))
237 (apply 'concat (nreverse string-list))))
239 (defun kfill:normal (paragraph)
241 (setq paragraph-separate
242 (concat paragraph-separate "\\|^[ \t/#%?!~*+-]"))))
244 (defun kfill:normal-included-text (paragraph)
245 (setq fill-prefix (buffer-substring (match-beginning 0) (match-end 0)))
247 (setq paragraph-separate
248 (concat "^" fill-prefix " *>\\|^"
249 (kfill:negate-string fill-prefix)))))
251 (defun kfill:postscript-comment (paragraph)
252 (setq fill-prefix (buffer-substring (match-beginning 0) (match-end 0)))
254 (setq paragraph-separate
255 (concat "^" fill-prefix " *%\\|^"
256 (kfill:negate-string fill-prefix)))))
258 (defun kfill:remove-paragraph-prefix (&optional indent-str)
259 "Remove fill prefix from current paragraph."
262 ;; Backward to para begin
263 (re-search-backward (concat "\\`\\|" paragraph-separate))
265 (let ((region-start (point)))
268 ;; Forward to real paragraph end
269 (re-search-forward (concat "\\'\\|" paragraph-separate))
270 (or (= (point) (point-max)) (beginning-of-line))
271 (kfill:replace-string (or fill-prefix fill-prefix-prev)
272 (if (eq major-mode 'kotl-mode)
274 (make-string (kcell-view:indent) ? ))
276 nil region-start (point)))))
278 (defun kfill:replace-string (fill-str-prev fill-str &optional suffix start end)
279 "Replace whitespace separated FILL-STR-PREV with FILL-STR.
280 Optional SUFFIX non-nil means replace at ends of lines, default is beginnings.
281 Optional arguments START and END specify the replace region, default is the
285 (let ((s (min start end)))
286 (setq end (max start end)
288 (setq start (region-beginning)
290 (if (not fill-str) (setq fill-str ""))
293 (narrow-to-region start end)
294 (goto-char (point-min))
300 ;; Get non-whitespace separated fill-str-prev
303 (or (string-match "[^ \t]" fill-str-prev) 0)
305 "[ \t]*\\(.*[^ \t]\\)[ \t]*$"
310 (while (re-search-forward prefix nil t)
311 (replace-match fill-str nil t))))))))
313 (defun kfill:sh-comment (paragraph)
314 (setq fill-prefix (buffer-substring (match-beginning 0) (match-end 0)))
316 (setq paragraph-separate
317 (concat "^" fill-prefix " *#\\|^"
318 (kfill:negate-string fill-prefix)))))
320 (defun kfill:supercite-included-text (paragraph)
321 (setq fill-prefix (buffer-substring (match-beginning 0) (match-end 0)))
323 (setq paragraph-separate
324 (concat "^" (kfill:negate-string fill-prefix)))))
327 ;;; Private variables
330 (defconst kfill:hanging-expression
332 (delq nil (mapcar (function
333 (lambda (pattern-type)
334 (if (eq (cdr pattern-type) 'kfill:hanging-list)
335 (list 'looking-at (car pattern-type)))))
336 kfill:prefix-table)))
337 "Conditional expression used to test for hanging indented lists.")
339 (defvar fill-prefix-prev nil
340 "Prior string inserted at front of new line during filling, or nil for none.
341 Setting this variable automatically makes it local to the current buffer.")
342 (make-variable-buffer-local 'fill-prefix-prev)
347 ;;; kfill.el ends here