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