Commit | Line | Data |
---|---|---|
6594deb0 ER |
1 | ;;; paragraphs.el --- paragraph and sentence parsing. |
2 | ||
8f1204db | 3 | ;; Copyright (C) 1985, 86, 87, 91, 94 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 | |
21 | ;; along with GNU Emacs; see the file COPYING. If not, write to | |
22 | ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
23 | ||
edbd2f74 ER |
24 | ;;; Commentary: |
25 | ||
26 | ;; This package provides the paragraph-oriented commands documented in the | |
27 | ;; Emacs manual. | |
28 | ||
4821e2af | 29 | ;;; Code: |
a2535589 | 30 | |
6503cec3 | 31 | (defconst paragraph-start "^[ \t\n\f]" "\ |
1f2007b3 RS |
32 | *Regexp for beginning of a line that starts OR separates paragraphs. |
33 | This regexp should match lines that separate paragraphs | |
34 | and should also match lines that start a paragraph | |
35 | \(and are part of that paragraph). | |
a37669ec | 36 | |
1f2007b3 | 37 | The variable `paragraph-separate' specifies how to distinguish |
a37669ec RS |
38 | lines that start paragraphs from lines that separate them. |
39 | ||
40 | If the variable `use-hard-newlines' is nonnil, then only lines following a | |
41 | hard newline are considered to match.") | |
6503cec3 | 42 | |
6503cec3 JB |
43 | (defconst paragraph-separate "^[ \t\f]*$" "\ |
44 | *Regexp for beginning of a line that separates paragraphs. | |
a37669ec RS |
45 | If you change this, you may have to change paragraph-start also. |
46 | ||
47 | If the variable `use-hard-newlines' is nonnil, then only lines following a | |
48 | hard newline are considered to match.") | |
6503cec3 | 49 | |
6503cec3 JB |
50 | (defconst sentence-end (purecopy "[.?!][]\"')}]*\\($\\| $\\|\t\\| \\)[ \t\n]*") "\ |
51 | *Regexp describing the end of a sentence. | |
51534471 JB |
52 | All paragraph boundaries also end sentences, regardless. |
53 | ||
54 | In order to be recognized as the end of a sentence, the ending period, | |
55 | question mark, or exclamation point must be followed by two spaces, | |
56 | unless it's inside some sort of quotes or parenthesis.") | |
6503cec3 | 57 | |
6503cec3 JB |
58 | (defconst page-delimiter "^\014" "\ |
59 | *Regexp describing line-beginnings that separate pages.") | |
60 | ||
6503cec3 JB |
61 | (defvar paragraph-ignore-fill-prefix nil "\ |
62 | Non-nil means the paragraph commands are not affected by `fill-prefix'. | |
63 | This is desirable in modes where blank lines are the paragraph delimiters.") | |
77176e73 | 64 | |
a37669ec RS |
65 | (defsubst looking-at-hard (re) |
66 | ;; Just for convenience in writing the function below. | |
67 | (and (or (null use-hard-newlines) | |
68 | (bobp) | |
69 | (get-text-property (1- (point)) 'hard)) | |
70 | (looking-at re))) | |
a2535589 JA |
71 | |
72 | (defun forward-paragraph (&optional arg) | |
73 | "Move forward to end of paragraph. | |
85de1612 | 74 | With arg N, do it N times; negative arg -N means move backward N paragraphs. |
a2535589 JA |
75 | |
76 | A line which `paragraph-start' matches either separates paragraphs | |
77 | \(if `paragraph-separate' matches it also) or is the first line of a paragraph. | |
78 | A paragraph end is the beginning of a line which is not part of the paragraph | |
79 | to which the end of the previous line belongs, or the end of the buffer." | |
80 | (interactive "p") | |
81 | (or arg (setq arg 1)) | |
82 | (let* ((fill-prefix-regexp | |
83 | (and fill-prefix (not (equal fill-prefix "")) | |
84 | (not paragraph-ignore-fill-prefix) | |
85 | (regexp-quote fill-prefix))) | |
86 | (paragraph-separate | |
87 | (if fill-prefix-regexp | |
88 | (concat paragraph-separate "\\|^" | |
89 | fill-prefix-regexp "[ \t]*$") | |
90 | paragraph-separate))) | |
8a2a4ced | 91 | (while (and (< arg 0) (not (bobp))) |
a37669ec RS |
92 | (if (and (not (looking-at-hard paragraph-separate)) |
93 | (re-search-backward "^\n" (max (1- (point)) (point-min)) t) | |
94 | (looking-at-hard paragraph-separate)) | |
a2535589 | 95 | nil |
8a2a4ced | 96 | ;; Move back over paragraph-separating lines. |
a2535589 | 97 | (forward-char -1) (beginning-of-line) |
a37669ec RS |
98 | (while (and (not (bobp)) |
99 | (looking-at-hard paragraph-separate)) | |
a2535589 | 100 | (forward-line -1)) |
8a2a4ced RS |
101 | (if (bobp) |
102 | nil | |
103 | ;; Go to end of the previous (non-separating) line. | |
104 | (end-of-line) | |
105 | ;; Search back for line that starts or separates paragraphs. | |
106 | (if (if fill-prefix-regexp | |
107 | ;; There is a fill prefix; it overrides paragraph-start. | |
108 | (progn | |
109 | (while (progn (beginning-of-line) | |
110 | (and (not (bobp)) | |
a37669ec RS |
111 | (not (looking-at-hard |
112 | paragraph-separate)) | |
8a2a4ced RS |
113 | (looking-at fill-prefix-regexp))) |
114 | (forward-line -1)) | |
115 | (not (bobp))) | |
a37669ec RS |
116 | (while (and (re-search-backward paragraph-start nil 1) |
117 | use-hard-newlines | |
118 | (not (bobp)) | |
119 | (null (get-text-property (1- (point)) 'hard))) | |
120 | (if (not (bobp)) (backward-char 1))) | |
121 | (> (point) (point-min))) | |
8a2a4ced RS |
122 | ;; Found one. |
123 | (progn | |
124 | ;; Move forward over paragraph separators. | |
125 | ;; We know this cannot reach the place we started | |
126 | ;; because we know we moved back over a non-separator. | |
a37669ec | 127 | (while (and (not (eobp)) (looking-at-hard paragraph-separate)) |
8a2a4ced RS |
128 | (forward-line 1)) |
129 | (if (eq (char-after (- (point) 2)) ?\n) | |
130 | (forward-line -1))) | |
131 | ;; No starter or separator line => use buffer beg. | |
132 | (goto-char (point-min))))) | |
a2535589 | 133 | (setq arg (1+ arg))) |
8a2a4ced | 134 | (while (and (> arg 0) (not (eobp))) |
a2535589 JA |
135 | (beginning-of-line) |
136 | (while (prog1 (and (not (eobp)) | |
a37669ec RS |
137 | (looking-at-hard paragraph-separate)) |
138 | (forward-line 1))) | |
a2535589 JA |
139 | (if fill-prefix-regexp |
140 | ;; There is a fill prefix; it overrides paragraph-start. | |
141 | (while (and (not (eobp)) | |
a37669ec | 142 | (not (looking-at-hard paragraph-separate)) |
a2535589 JA |
143 | (looking-at fill-prefix-regexp)) |
144 | (forward-line 1)) | |
650f194c RS |
145 | (while (and (re-search-forward paragraph-start nil 1) |
146 | (not (eobp)) | |
a37669ec RS |
147 | use-hard-newlines |
148 | (null (get-text-property (1- (match-beginning 0)) 'hard))) | |
149 | (forward-char 1)) | |
150 | (if (< (point) (point-max)) | |
151 | (goto-char (match-beginning 0)))) | |
a2535589 JA |
152 | (setq arg (1- arg))))) |
153 | ||
154 | (defun backward-paragraph (&optional arg) | |
155 | "Move backward to start of paragraph. | |
156 | With arg N, do it N times; negative arg -N means move forward N paragraphs. | |
157 | ||
23b34992 BP |
158 | A paragraph start is the beginning of a line which is a |
159 | `first-line-of-paragraph' or which is ordinary text and follows a | |
160 | paragraph-separating line; except: if the first real line of a | |
161 | paragraph is preceded by a blank line, the paragraph starts at that | |
162 | blank line. | |
163 | ||
164 | See `forward-paragraph' for more information." | |
a2535589 JA |
165 | (interactive "p") |
166 | (or arg (setq arg 1)) | |
167 | (forward-paragraph (- arg))) | |
168 | ||
169 | (defun mark-paragraph () | |
170 | "Put point at beginning of this paragraph, mark at end. | |
171 | The paragraph marked is the one that contains point or follows point." | |
172 | (interactive) | |
173 | (forward-paragraph 1) | |
0b108c01 | 174 | (push-mark nil t t) |
a2535589 JA |
175 | (backward-paragraph 1)) |
176 | ||
177 | (defun kill-paragraph (arg) | |
178 | "Kill forward to end of paragraph. | |
179 | With arg N, kill forward to Nth end of paragraph; | |
180 | negative arg -N means kill backward to Nth start of paragraph." | |
23b34992 | 181 | (interactive "p") |
8d6eaa00 | 182 | (kill-region (point) (progn (forward-paragraph arg) (point)))) |
a2535589 JA |
183 | |
184 | (defun backward-kill-paragraph (arg) | |
185 | "Kill back to start of paragraph. | |
186 | With arg N, kill back to Nth start of paragraph; | |
187 | negative arg -N means kill forward to Nth end of paragraph." | |
23b34992 | 188 | (interactive "p") |
8d6eaa00 | 189 | (kill-region (point) (progn (backward-paragraph arg) (point)))) |
a2535589 JA |
190 | |
191 | (defun transpose-paragraphs (arg) | |
192 | "Interchange this (or next) paragraph with previous one." | |
193 | (interactive "*p") | |
194 | (transpose-subr 'forward-paragraph arg)) | |
195 | ||
196 | (defun start-of-paragraph-text () | |
197 | (let ((opoint (point)) npoint) | |
198 | (forward-paragraph -1) | |
199 | (setq npoint (point)) | |
200 | (skip-chars-forward " \t\n") | |
b4e6c391 RS |
201 | ;; If the range of blank lines found spans the original start point, |
202 | ;; try again from the beginning of it. | |
203 | ;; Must be careful to avoid infinite loop | |
204 | ;; when following a single return at start of buffer. | |
205 | (if (and (>= (point) opoint) (< npoint opoint)) | |
a2535589 JA |
206 | (progn |
207 | (goto-char npoint) | |
208 | (if (> npoint (point-min)) | |
209 | (start-of-paragraph-text)))))) | |
210 | ||
211 | (defun end-of-paragraph-text () | |
212 | (let ((opoint (point))) | |
213 | (forward-paragraph 1) | |
214 | (if (eq (preceding-char) ?\n) (forward-char -1)) | |
215 | (if (<= (point) opoint) | |
216 | (progn | |
217 | (forward-char 1) | |
218 | (if (< (point) (point-max)) | |
219 | (end-of-paragraph-text)))))) | |
220 | ||
221 | (defun forward-sentence (&optional arg) | |
51534471 | 222 | "Move forward to next `sentence-end'. With argument, repeat. |
23b34992 | 223 | With negative argument, move backward repeatedly to `sentence-beginning'. |
a2535589 | 224 | |
23b34992 BP |
225 | The variable `sentence-end' is a regular expression that matches ends of |
226 | sentences. Also, every paragraph boundary terminates sentences as well." | |
a2535589 JA |
227 | (interactive "p") |
228 | (or arg (setq arg 1)) | |
229 | (while (< arg 0) | |
230 | (let ((par-beg (save-excursion (start-of-paragraph-text) (point)))) | |
231 | (if (re-search-backward (concat sentence-end "[^ \t\n]") par-beg t) | |
232 | (goto-char (1- (match-end 0))) | |
233 | (goto-char par-beg))) | |
234 | (setq arg (1+ arg))) | |
235 | (while (> arg 0) | |
236 | (let ((par-end (save-excursion (end-of-paragraph-text) (point)))) | |
237 | (if (re-search-forward sentence-end par-end t) | |
238 | (skip-chars-backward " \t\n") | |
239 | (goto-char par-end))) | |
240 | (setq arg (1- arg)))) | |
241 | ||
242 | (defun backward-sentence (&optional arg) | |
243 | "Move backward to start of sentence. With arg, do it arg times. | |
23b34992 | 244 | See `forward-sentence' for more information." |
a2535589 JA |
245 | (interactive "p") |
246 | (or arg (setq arg 1)) | |
247 | (forward-sentence (- arg))) | |
248 | ||
249 | (defun kill-sentence (&optional arg) | |
250 | "Kill from point to end of sentence. | |
251 | With arg, repeat; negative arg -N means kill back to Nth start of sentence." | |
b0e1e38f | 252 | (interactive "p") |
8d6eaa00 | 253 | (kill-region (point) (progn (forward-sentence arg) (point)))) |
a2535589 JA |
254 | |
255 | (defun backward-kill-sentence (&optional arg) | |
256 | "Kill back from point to start of sentence. | |
257 | With arg, repeat, or kill forward to Nth end of sentence if negative arg -N." | |
b0e1e38f | 258 | (interactive "p") |
8d6eaa00 | 259 | (kill-region (point) (progn (backward-sentence arg) (point)))) |
a2535589 JA |
260 | |
261 | (defun mark-end-of-sentence (arg) | |
23b34992 | 262 | "Put mark at end of sentence. Arg works as in `forward-sentence'." |
a2535589 JA |
263 | (interactive "p") |
264 | (push-mark | |
265 | (save-excursion | |
266 | (forward-sentence arg) | |
a524dc5b RS |
267 | (point)) |
268 | nil t)) | |
a2535589 JA |
269 | |
270 | (defun transpose-sentences (arg) | |
271 | "Interchange this (next) and previous sentence." | |
272 | (interactive "*p") | |
273 | (transpose-subr 'forward-sentence arg)) | |
6594deb0 ER |
274 | |
275 | ;;; paragraphs.el ends here |