(text-mode-map): Remove the \t binding.
[bpt/emacs.git] / lisp / textmodes / paragraphs.el
CommitLineData
55535639 1;;; paragraphs.el --- paragraph and sentence parsing
6594deb0 2
3fe35f35 3;; Copyright (C) 1985, 86, 87, 91, 94, 95, 96, 1997, 1999, 2000, 2001
e4550233 4;; Free Software Foundation, Inc.
9750e079 5
4821e2af 6;; Maintainer: FSF
d7b4d18f 7;; Keywords: wp
4821e2af 8
a2535589
JA
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
29add8b9 13;; the Free Software Foundation; either version 2, or (at your option)
a2535589
JA
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b578f267
EN
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
a2535589 25
edbd2f74
ER
26;;; Commentary:
27
28;; This package provides the paragraph-oriented commands documented in the
29;; Emacs manual.
30
4821e2af 31;;; Code:
a2535589 32
e4550233
RS
33(defgroup paragraphs nil
34 "Paragraph and sentence parsing."
35 :group 'editing)
36
07187d55 37(define-minor-mode use-hard-newlines
965eb84a
RS
38 "Minor mode to distinguish hard and soft newlines.
39When active, the functions `newline' and `open-line' add the
40text-property `hard' to newlines that they insert, and a line is
55cc5677 41only considered as a candidate to match `paragraph-start' or
965eb84a 42`paragraph-separate' if it follows a hard newline.
55cc5677 43
965eb84a
RS
44Prefix argument says to turn mode on if positive, off if negative.
45When the mode is turned on, if there are newlines in the buffer but no hard
46newlines, ask the user whether to mark as hard any newlines preceeding a
47`paragraph-start' line. From a program, second arg INSERT specifies whether
48to do this; it can be `never' to change nothing, t or `always' to force
49marking, `guess' to try to do the right thing with no questions, nil
50or anything else to ask the user.
51
52Newlines not marked hard are called \"soft\", and are always internal
53to paragraphs. The fill functions insert and delete only soft newlines."
07187d55
SM
54 :extra-args (insert)
55 (when use-hard-newlines
965eb84a
RS
56 ;; Turn mode on
57 ;; Intuit hard newlines --
58 ;; mark as hard any newlines preceding a paragraph-start line.
59 (if (or (eq insert t) (eq insert 'always)
60 (and (not (eq 'never insert))
965eb84a
RS
61 (not (text-property-any (point-min) (point-max) 'hard t))
62 (save-excursion
63 (goto-char (point-min))
64 (search-forward "\n" nil t))
65 (or (eq insert 'guess)
66 (y-or-n-p "Make newlines between paragraphs hard? "))))
67 (save-excursion
68 (goto-char (point-min))
69 (while (search-forward "\n" nil t)
70 (let ((pos (point)))
71 (move-to-left-margin)
07187d55
SM
72 (when (looking-at paragraph-start)
73 (set-hard-newline-properties (1- pos) pos))
74 ;; If paragraph-separate, newline after it is hard too.
75 (when (looking-at paragraph-separate)
76 (set-hard-newline-properties (1- pos) pos)
77 (end-of-line)
78 (unless (eobp)
79 (set-hard-newline-properties (point) (1+ (point)))))))))))
55cc5677 80
07187d55 81(defcustom paragraph-start "\f\\|[ \t]*$" "\
1f2007b3
RS
82*Regexp for beginning of a line that starts OR separates paragraphs.
83This regexp should match lines that separate paragraphs
84and should also match lines that start a paragraph
85\(and are part of that paragraph).
a37669ec 86
55cc5677
BG
87This is matched against the text at the left margin, which is not necessarily
88the beginning of the line, so it should never use \"^\" as an anchor. This
89ensures that the paragraph functions will work equally well within a region
90of text indented by a margin setting.
91
1f2007b3 92The variable `paragraph-separate' specifies how to distinguish
a37669ec
RS
93lines that start paragraphs from lines that separate them.
94
3f5dc0b0 95If the variable `use-hard-newlines' is non-nil, then only lines following a
e4550233
RS
96hard newline are considered to match."
97 :group 'paragraphs
98 :type 'regexp)
6503cec3 99
55cc5677
BG
100;; paragraph-start requires a hard newline, but paragraph-separate does not:
101;; It is assumed that paragraph-separate is distinctive enough to be believed
102;; whenever it occurs, while it is reasonable to set paragraph-start to
103;; something very minimal, even including "." (which makes every hard newline
104;; start a new paragraph).
105
e4550233
RS
106(defcustom paragraph-separate "[ \t\f]*$"
107 "*Regexp for beginning of a line that separates paragraphs.
9d7c4eb5 108If you change this, you may have to change `paragraph-start' also.
a37669ec 109
55cc5677
BG
110This is matched against the text at the left margin, which is not necessarily
111the beginning of the line, so it should not use \"^\" as an anchor. This
112ensures that the paragraph functions will work equally within a region of
e4550233
RS
113text indented by a margin setting."
114 :group 'paragraphs
115 :type 'regexp)
6503cec3 116
e4550233
RS
117(defcustom sentence-end (purecopy "[.?!][]\"')}]*\\($\\| $\\|\t\\| \\)[ \t\n]*")
118 "*Regexp describing the end of a sentence.
ac1470eb 119The value includes the whitespace following the sentence.
51534471
JB
120All paragraph boundaries also end sentences, regardless.
121
220dae72
DL
122The default value specifies that in order to be recognized as the end
123of a sentence, the ending period, question mark, or exclamation point
124must be followed by two spaces, unless it's inside some sort of quotes
125or parenthesis.
126
8919155a
DL
127See also the variable `sentence-end-double-space', the variable
128`sentence-end-without-period' and Info node `Sentences'."
e4550233
RS
129 :group 'paragraphs
130 :type 'regexp)
131
132(defcustom page-delimiter "^\014"
133 "*Regexp describing line-beginnings that separate pages."
134 :group 'paragraphs
135 :type 'regexp)
136
137(defcustom paragraph-ignore-fill-prefix nil
138 "*Non-nil means the paragraph commands are not affected by `fill-prefix'.
139This is desirable in modes where blank lines are the paragraph delimiters."
140 :group 'paragraphs
141 :type 'boolean)
77176e73 142
a2535589
JA
143(defun forward-paragraph (&optional arg)
144 "Move forward to end of paragraph.
94d63a23
RS
145With argument ARG, do it ARG times;
146a negative argument ARG = -N means move backward N paragraphs.
a2535589
JA
147
148A line which `paragraph-start' matches either separates paragraphs
149\(if `paragraph-separate' matches it also) or is the first line of a paragraph.
150A paragraph end is the beginning of a line which is not part of the paragraph
151to which the end of the previous line belongs, or the end of the buffer."
152 (interactive "p")
153 (or arg (setq arg 1))
17cca868
GM
154 (let* ((opoint (point))
155 (fill-prefix-regexp
a2535589
JA
156 (and fill-prefix (not (equal fill-prefix ""))
157 (not paragraph-ignore-fill-prefix)
158 (regexp-quote fill-prefix)))
55cc5677
BG
159 ;; Remove ^ from paragraph-start and paragraph-sep if they are there.
160 ;; These regexps shouldn't be anchored, because we look for them
161 ;; starting at the left-margin. This allows paragraph commands to
162 ;; work normally with indented text.
163 ;; This hack will not find problem cases like "whatever\\|^something".
164 (paragraph-start (if (and (not (equal "" paragraph-start))
165 (equal ?^ (aref paragraph-start 0)))
166 (substring paragraph-start 1)
167 paragraph-start))
c4ac30da 168 (paragraph-separate (if (and (not (equal "" paragraph-separate))
55cc5677 169 (equal ?^ (aref paragraph-separate 0)))
c4ac30da 170 (substring paragraph-separate 1)
55cc5677 171 paragraph-separate))
a2535589
JA
172 (paragraph-separate
173 (if fill-prefix-regexp
55cc5677 174 (concat paragraph-separate "\\|"
a2535589 175 fill-prefix-regexp "[ \t]*$")
55cc5677
BG
176 paragraph-separate))
177 ;; This is used for searching.
178 (sp-paragraph-start (concat "^[ \t]*\\(" paragraph-start "\\)"))
eeb0f327 179 start found-start)
8a2a4ced 180 (while (and (< arg 0) (not (bobp)))
55cc5677 181 (if (and (not (looking-at paragraph-separate))
a37669ec 182 (re-search-backward "^\n" (max (1- (point)) (point-min)) t)
55cc5677 183 (looking-at paragraph-separate))
a2535589 184 nil
2be01738 185 (setq start (point))
8a2a4ced 186 ;; Move back over paragraph-separating lines.
a2535589 187 (forward-char -1) (beginning-of-line)
a37669ec 188 (while (and (not (bobp))
55cc5677
BG
189 (progn (move-to-left-margin)
190 (looking-at paragraph-separate)))
3f5dc0b0 191 (forward-line -1))
8a2a4ced
RS
192 (if (bobp)
193 nil
194 ;; Go to end of the previous (non-separating) line.
195 (end-of-line)
196 ;; Search back for line that starts or separates paragraphs.
197 (if (if fill-prefix-regexp
198 ;; There is a fill prefix; it overrides paragraph-start.
2be01738 199 (let (multiple-lines)
55cc5677
BG
200 (while (and (progn (beginning-of-line) (not (bobp)))
201 (progn (move-to-left-margin)
202 (not (looking-at paragraph-separate)))
203 (looking-at fill-prefix-regexp))
3f5dc0b0
SM
204 (unless (= (point) start)
205 (setq multiple-lines t))
55cc5677 206 (forward-line -1))
2be01738 207 (move-to-left-margin)
b53ce41d
RS
208;;; This deleted code caused a long hanging-indent line
209;;; not to be filled together with the following lines.
210;;; ;; Don't move back over a line before the paragraph
211;;; ;; which doesn't start with fill-prefix
212;;; ;; unless that is the only line we've moved over.
213;;; (and (not (looking-at fill-prefix-regexp))
214;;; multiple-lines
215;;; (forward-line 1))
2be01738 216 (not (bobp)))
55cc5677 217 (while (and (re-search-backward sp-paragraph-start nil 1)
eeb0f327 218 (setq found-start t)
55cc5677
BG
219 ;; Found a candidate, but need to check if it is a
220 ;; REAL paragraph-start.
55cc5677
BG
221 (progn (setq start (point))
222 (move-to-left-margin)
223 (not (looking-at paragraph-separate)))
eeb0f327
RS
224 (not (and (looking-at paragraph-start)
225 (not
226 (and use-hard-newlines
227 (not (bobp))
228 (not (get-text-property (1- start)
229 'hard)))))))
230 (setq found-start nil)
55cc5677 231 (goto-char start))
eeb0f327 232 found-start)
07187d55
SM
233 ;; Found one.
234 (progn
8a2a4ced
RS
235 ;; Move forward over paragraph separators.
236 ;; We know this cannot reach the place we started
237 ;; because we know we moved back over a non-separator.
55cc5677
BG
238 (while (and (not (eobp))
239 (progn (move-to-left-margin)
240 (looking-at paragraph-separate)))
8a2a4ced 241 (forward-line 1))
55cc5677
BG
242 ;; If line before paragraph is just margin, back up to there.
243 (end-of-line 0)
244 (if (> (current-column) (current-left-margin))
245 (forward-char 1)
246 (skip-chars-backward " \t")
247 (if (not (bolp))
248 (forward-line 1))))
8a2a4ced
RS
249 ;; No starter or separator line => use buffer beg.
250 (goto-char (point-min)))))
a2535589 251 (setq arg (1+ arg)))
8a2a4ced 252 (while (and (> arg 0) (not (eobp)))
88c2fc2c 253 ;; Move forward over separator lines, and one more line.
a2535589 254 (while (prog1 (and (not (eobp))
55cc5677
BG
255 (progn (move-to-left-margin) (not (eobp)))
256 (looking-at paragraph-separate))
a37669ec 257 (forward-line 1)))
a2535589
JA
258 (if fill-prefix-regexp
259 ;; There is a fill prefix; it overrides paragraph-start.
260 (while (and (not (eobp))
55cc5677
BG
261 (progn (move-to-left-margin) (not (eobp)))
262 (not (looking-at paragraph-separate))
a2535589
JA
263 (looking-at fill-prefix-regexp))
264 (forward-line 1))
55cc5677 265 (while (and (re-search-forward sp-paragraph-start nil 1)
55cc5677
BG
266 (progn (setq start (match-beginning 0))
267 (goto-char start)
4669fb3c
RS
268 (not (eobp)))
269 (progn (move-to-left-margin)
55cc5677
BG
270 (not (looking-at paragraph-separate)))
271 (or (not (looking-at paragraph-start))
272 (and use-hard-newlines
273 (not (get-text-property (1- start) 'hard)))))
a37669ec
RS
274 (forward-char 1))
275 (if (< (point) (point-max))
55cc5677 276 (goto-char start)))
779e6e56 277 (setq arg (1- arg)))
17cca868 278 (constrain-to-field nil opoint t)))
a2535589
JA
279
280(defun backward-paragraph (&optional arg)
281 "Move backward to start of paragraph.
94d63a23
RS
282With argument ARG, do it ARG times;
283a negative argument ARG = -N means move forward N paragraphs.
a2535589 284
23b34992
BP
285A paragraph start is the beginning of a line which is a
286`first-line-of-paragraph' or which is ordinary text and follows a
287paragraph-separating line; except: if the first real line of a
288paragraph is preceded by a blank line, the paragraph starts at that
289blank line.
290
291See `forward-paragraph' for more information."
a2535589
JA
292 (interactive "p")
293 (or arg (setq arg 1))
294 (forward-paragraph (- arg)))
295
296(defun mark-paragraph ()
297 "Put point at beginning of this paragraph, mark at end.
298The paragraph marked is the one that contains point or follows point."
299 (interactive)
300 (forward-paragraph 1)
0b108c01 301 (push-mark nil t t)
a2535589
JA
302 (backward-paragraph 1))
303
304(defun kill-paragraph (arg)
305 "Kill forward to end of paragraph.
306With arg N, kill forward to Nth end of paragraph;
307negative arg -N means kill backward to Nth start of paragraph."
275cf1b2 308 (interactive "p")
8d6eaa00 309 (kill-region (point) (progn (forward-paragraph arg) (point))))
a2535589
JA
310
311(defun backward-kill-paragraph (arg)
312 "Kill back to start of paragraph.
313With arg N, kill back to Nth start of paragraph;
314negative arg -N means kill forward to Nth end of paragraph."
275cf1b2 315 (interactive "p")
17cca868 316 (kill-region (point) (progn (backward-paragraph arg) (point))))
a2535589
JA
317
318(defun transpose-paragraphs (arg)
319 "Interchange this (or next) paragraph with previous one."
320 (interactive "*p")
321 (transpose-subr 'forward-paragraph arg))
322
323(defun start-of-paragraph-text ()
324 (let ((opoint (point)) npoint)
325 (forward-paragraph -1)
326 (setq npoint (point))
327 (skip-chars-forward " \t\n")
b4e6c391
RS
328 ;; If the range of blank lines found spans the original start point,
329 ;; try again from the beginning of it.
330 ;; Must be careful to avoid infinite loop
331 ;; when following a single return at start of buffer.
332 (if (and (>= (point) opoint) (< npoint opoint))
a2535589
JA
333 (progn
334 (goto-char npoint)
335 (if (> npoint (point-min))
336 (start-of-paragraph-text))))))
337
338(defun end-of-paragraph-text ()
339 (let ((opoint (point)))
340 (forward-paragraph 1)
341 (if (eq (preceding-char) ?\n) (forward-char -1))
342 (if (<= (point) opoint)
343 (progn
344 (forward-char 1)
345 (if (< (point) (point-max))
346 (end-of-paragraph-text))))))
347
348(defun forward-sentence (&optional arg)
51534471 349 "Move forward to next `sentence-end'. With argument, repeat.
23b34992 350With negative argument, move backward repeatedly to `sentence-beginning'.
a2535589 351
23b34992
BP
352The variable `sentence-end' is a regular expression that matches ends of
353sentences. Also, every paragraph boundary terminates sentences as well."
a2535589
JA
354 (interactive "p")
355 (or arg (setq arg 1))
17cca868
GM
356 (let ((opoint (point)))
357 (while (< arg 0)
ea2c6478
GM
358 (let ((pos (point))
359 (par-beg (save-excursion (start-of-paragraph-text) (point))))
360 (if (and (re-search-backward sentence-end par-beg t)
361 (or (< (match-end 0) pos)
362 (re-search-backward sentence-end par-beg t)))
363 (goto-char (match-end 0))
17cca868
GM
364 (goto-char par-beg)))
365 (setq arg (1+ arg)))
366 (while (> arg 0)
367 (let ((par-end (save-excursion (end-of-paragraph-text) (point))))
368 (if (re-search-forward sentence-end par-end t)
369 (skip-chars-backward " \t\n")
370 (goto-char par-end)))
371 (setq arg (1- arg)))
372 (constrain-to-field nil opoint t)))
a2535589
JA
373
374(defun backward-sentence (&optional arg)
375 "Move backward to start of sentence. With arg, do it arg times.
23b34992 376See `forward-sentence' for more information."
a2535589
JA
377 (interactive "p")
378 (or arg (setq arg 1))
379 (forward-sentence (- arg)))
380
381(defun kill-sentence (&optional arg)
382 "Kill from point to end of sentence.
383With arg, repeat; negative arg -N means kill back to Nth start of sentence."
275cf1b2 384 (interactive "p")
8d6eaa00 385 (kill-region (point) (progn (forward-sentence arg) (point))))
a2535589
JA
386
387(defun backward-kill-sentence (&optional arg)
388 "Kill back from point to start of sentence.
389With arg, repeat, or kill forward to Nth end of sentence if negative arg -N."
275cf1b2 390 (interactive "p")
17cca868 391 (kill-region (point) (progn (backward-sentence arg) (point))))
a2535589
JA
392
393(defun mark-end-of-sentence (arg)
23b34992 394 "Put mark at end of sentence. Arg works as in `forward-sentence'."
a2535589
JA
395 (interactive "p")
396 (push-mark
397 (save-excursion
398 (forward-sentence arg)
a524dc5b
RS
399 (point))
400 nil t))
a2535589
JA
401
402(defun transpose-sentences (arg)
403 "Interchange this (next) and previous sentence."
404 (interactive "*p")
405 (transpose-subr 'forward-sentence arg))
6594deb0
ER
406
407;;; paragraphs.el ends here