paragraphs.el 18.4 KB
Newer Older
1
;;; paragraphs.el --- paragraph and sentence parsing
Eric S. Raymond's avatar
Eric S. Raymond committed
2

3 4
;; Copyright (C) 1985, 1986, 1987, 1991, 1994, 1995, 1996, 1997, 1999, 2000,
;;   2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Eric S. Raymond's avatar
Eric S. Raymond committed
5

Eric S. Raymond's avatar
Eric S. Raymond committed
6
;; Maintainer: FSF
Eric S. Raymond's avatar
Eric S. Raymond committed
7
;; Keywords: wp
Eric S. Raymond's avatar
Eric S. Raymond committed
8

Joseph Arceneaux's avatar
Joseph Arceneaux committed
9 10 11 12
;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
Roland McGrath's avatar
Roland McGrath committed
13
;; the Free Software Foundation; either version 2, or (at your option)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
14 15 16 17 18 19 20 21
;; any later version.

;; GNU Emacs 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
Erik Naggum's avatar
Erik Naggum committed
22
;; along with GNU Emacs; see the file COPYING.  If not, write to the
Lute Kamstra's avatar
Lute Kamstra committed
23 24
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
25

26 27 28 29 30
;;; Commentary:

;; This package provides the paragraph-oriented commands documented in the
;; Emacs manual.

Eric S. Raymond's avatar
Eric S. Raymond committed
31
;;; Code:
Joseph Arceneaux's avatar
Joseph Arceneaux committed
32

Richard M. Stallman's avatar
Richard M. Stallman committed
33 34 35 36
(defgroup paragraphs nil
  "Paragraph and sentence parsing."
  :group 'editing)

37
(put 'use-hard-newlines 'permanent-local t)
38
(define-minor-mode use-hard-newlines
39 40 41
  "Minor mode to distinguish hard and soft newlines.
When active, the functions `newline' and `open-line' add the
text-property `hard' to newlines that they insert, and a line is
42
only considered as a candidate to match `paragraph-start' or
43
`paragraph-separate' if it follows a hard newline.
44

45 46
Prefix argument says to turn mode on if positive, off if negative.
When the mode is turned on, if there are newlines in the buffer but no hard
47
newlines, ask the user whether to mark as hard any newlines preceeding a
48 49
`paragraph-start' line.  From a program, second arg INSERT specifies whether
to do this; it can be `never' to change nothing, t or `always' to force
50
marking, `guess' to try to do the right thing with no questions, nil
51 52 53 54
or anything else to ask the user.

Newlines not marked hard are called \"soft\", and are always internal
to paragraphs.  The fill functions insert and delete only soft newlines."
Markus Rost's avatar
Markus Rost committed
55
  :group 'paragraphs
56 57
  :extra-args (insert)
  (when use-hard-newlines
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
    ;; Turn mode on
    ;; Intuit hard newlines --
    ;;   mark as hard any newlines preceding a paragraph-start line.
    (if (or (eq insert t) (eq insert 'always)
	    (and (not (eq 'never insert))
		 (not (text-property-any (point-min) (point-max) 'hard t))
		 (save-excursion
		   (goto-char (point-min))
		   (search-forward "\n" nil t))
		 (or (eq insert 'guess)
		     (y-or-n-p "Make newlines between paragraphs hard? "))))
	(save-excursion
	  (goto-char (point-min))
	  (while (search-forward "\n" nil t)
	    (let ((pos (point)))
	      (move-to-left-margin)
74 75 76 77 78 79 80 81
	      (when (looking-at paragraph-start)
		(set-hard-newline-properties (1- pos) pos))
	      ;; If paragraph-separate, newline after it is hard too.
	      (when (looking-at paragraph-separate)
		(set-hard-newline-properties (1- pos) pos)
		(end-of-line)
		(unless (eobp)
		  (set-hard-newline-properties (point) (1+ (point)))))))))))
82

83
(defcustom paragraph-start "\f\\|[ \t]*$" "\
84 85 86 87
*Regexp for beginning of a line that starts OR separates paragraphs.
This regexp should match lines that separate paragraphs
and should also match lines that start a paragraph
\(and are part of that paragraph).
88

89 90 91 92 93
This is matched against the text at the left margin, which is not necessarily
the beginning of the line, so it should never use \"^\" as an anchor.  This
ensures that the paragraph functions will work equally well within a region
of text indented by a margin setting.

94
The variable `paragraph-separate' specifies how to distinguish
95 96
lines that start paragraphs from lines that separate them.

Stefan Monnier's avatar
Typo.  
Stefan Monnier committed
97
If the variable `use-hard-newlines' is non-nil, then only lines following a
Richard M. Stallman's avatar
Richard M. Stallman committed
98 99 100
hard newline are considered to match."
  :group 'paragraphs
  :type 'regexp)
Jim Blandy's avatar
Jim Blandy committed
101

102 103 104 105 106 107
;; paragraph-start requires a hard newline, but paragraph-separate does not:
;; It is assumed that paragraph-separate is distinctive enough to be believed
;; whenever it occurs, while it is reasonable to set paragraph-start to
;; something very minimal, even including "." (which makes every hard newline
;; start a new paragraph).

Richard M. Stallman's avatar
Richard M. Stallman committed
108 109
(defcustom paragraph-separate "[ \t\f]*$"
  "*Regexp for beginning of a line that separates paragraphs.
110
If you change this, you may have to change `paragraph-start' also.
111

112 113 114
This is matched against the text at the left margin, which is not necessarily
the beginning of the line, so it should not use \"^\" as an anchor.  This
ensures that the paragraph functions will work equally within a region of
Richard M. Stallman's avatar
Richard M. Stallman committed
115 116 117
text indented by a margin setting."
  :group 'paragraphs
  :type 'regexp)
Jim Blandy's avatar
Jim Blandy committed
118

119 120 121 122 123
(defcustom sentence-end-double-space t
  "*Non-nil means a single space does not end a sentence.
This is relevant for filling.  See also `sentence-end-without-period'
and `colon-double-space'.

124
This value is used by the function `sentence-end' to construct the
Luc Teirlinck's avatar
Luc Teirlinck committed
125 126
regexp describing the end of a sentence, when the value of the variable
`sentence-end' is nil.  See Info node `(elisp)Standard Regexps'."
127 128 129 130 131 132
  :type 'boolean
  :group 'fill)

(defcustom sentence-end-without-period nil
  "*Non-nil means a sentence will end without a period.
For example, a sentence in Thai text ends with double space but
133 134 135
without a period.

This value is used by the function `sentence-end' to construct the
Luc Teirlinck's avatar
Luc Teirlinck committed
136 137
regexp describing the end of a sentence, when the value of the variable
`sentence-end' is nil.  See Info node `(elisp)Standard Regexps'."
138 139 140
  :type 'boolean
  :group 'fill)

141 142
(defcustom sentence-end-without-space
  "$B!#!%!)!*$A!##.#?#!$(0!$!%!)!*$(G!$!%!)!*(B"
Luc Teirlinck's avatar
Luc Teirlinck committed
143
  "*String of characters that end sentence without following spaces.
144 145

This value is used by the function `sentence-end' to construct the
Luc Teirlinck's avatar
Luc Teirlinck committed
146 147
regexp describing the end of a sentence, when the value of the variable
`sentence-end' is nil.  See Info node `(elisp)Standard Regexps'."
148 149 150
  :group 'paragraphs
  :type 'string)

151
(defcustom sentence-end nil
Richard M. Stallman's avatar
Richard M. Stallman committed
152
  "*Regexp describing the end of a sentence.
Eli Zaretskii's avatar
Eli Zaretskii committed
153
The value includes the whitespace following the sentence.
154 155
All paragraph boundaries also end sentences, regardless.

156 157 158
The value nil means to use the default value defined by the
function `sentence-end'.  You should always use this function
to obtain the value of this variable."
Richard M. Stallman's avatar
Richard M. Stallman committed
159
  :group 'paragraphs
160 161
  :type '(choice regexp (const :tag "Use default value" nil)))

162 163 164 165 166 167
(defcustom sentence-end-base "[.?!][]\"'\xd0c9\x5397d)}]*"
  "*Regexp matching the basic end of a sentence, not including following space."
  :group 'paragraphs
  :type 'string
  :version "22.1")

168 169 170 171 172
(defun sentence-end ()
  "Return the regexp describing the end of a sentence.

This function returns either the value of the variable `sentence-end'
if it is non-nil, or the default value constructed from the
173 174 175 176 177 178 179
variables `sentence-end-base', `sentence-end-double-space',
`sentence-end-without-period' and `sentence-end-without-space'.

The default value specifies that in order to be recognized as the
end of a sentence, the ending period, question mark, or exclamation point
must be followed by two spaces, with perhaps some closing delimiters
in between.  See Info node `(elisp)Standard Regexps'."
180 181
  (or sentence-end
      (concat (if sentence-end-without-period "\\w  \\|")
182 183
	      "\\("
	      sentence-end-base
184 185
              (if sentence-end-double-space
                  "\\($\\| $\\|\t\\|  \\)" "\\($\\|[\t ]\\)")
186 187
              "\\|[" sentence-end-without-space "]+"
	      "\\)"
188
              "[ \t\n]*")))
Richard M. Stallman's avatar
Richard M. Stallman committed
189 190 191 192 193 194 195 196 197 198 199

(defcustom page-delimiter "^\014"
  "*Regexp describing line-beginnings that separate pages."
  :group 'paragraphs
  :type 'regexp)

(defcustom paragraph-ignore-fill-prefix nil
  "*Non-nil means the paragraph commands are not affected by `fill-prefix'.
This is desirable in modes where blank lines are the paragraph delimiters."
  :group 'paragraphs
  :type 'boolean)
Roland McGrath's avatar
Roland McGrath committed
200

Joseph Arceneaux's avatar
Joseph Arceneaux committed
201 202
(defun forward-paragraph (&optional arg)
  "Move forward to end of paragraph.
203 204
With argument ARG, do it ARG times;
a negative argument ARG = -N means move backward N paragraphs.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
205 206 207 208

A line which `paragraph-start' matches either separates paragraphs
\(if `paragraph-separate' matches it also) or is the first line of a paragraph.
A paragraph end is the beginning of a line which is not part of the paragraph
209 210
to which the end of the previous line belongs, or the end of the buffer.
Returns the count of paragraphs left to move."
Joseph Arceneaux's avatar
Joseph Arceneaux committed
211 212
  (interactive "p")
  (or arg (setq arg 1))
213 214
  (let* ((opoint (point))
	 (fill-prefix-regexp
Joseph Arceneaux's avatar
Joseph Arceneaux committed
215 216 217
	  (and fill-prefix (not (equal fill-prefix ""))
	       (not paragraph-ignore-fill-prefix)
	       (regexp-quote fill-prefix)))
218 219 220 221 222
	 ;; Remove ^ from paragraph-start and paragraph-sep if they are there.
	 ;; These regexps shouldn't be anchored, because we look for them
	 ;; starting at the left-margin.  This allows paragraph commands to
	 ;; work normally with indented text.
	 ;; This hack will not find problem cases like "whatever\\|^something".
223 224 225 226 227 228 229 230 231
	 (parstart (if (and (not (equal "" paragraph-start))
			    (equal ?^ (aref paragraph-start 0)))
		       (substring paragraph-start 1)
		     paragraph-start))
	 (parsep (if (and (not (equal "" paragraph-separate))
			  (equal ?^ (aref paragraph-separate 0)))
		     (substring paragraph-separate 1)
		   paragraph-separate))
	 (parsep
Joseph Arceneaux's avatar
Joseph Arceneaux committed
232
	  (if fill-prefix-regexp
233
	      (concat parsep "\\|"
Joseph Arceneaux's avatar
Joseph Arceneaux committed
234
		      fill-prefix-regexp "[ \t]*$")
235
	    parsep))
236
	 ;; This is used for searching.
237
	 (sp-parstart (concat "^[ \t]*\\(?:" parstart "\\|" parsep "\\)"))
238
	 start found-start)
239
    (while (and (< arg 0) (not (bobp)))
240
      (if (and (not (looking-at parsep))
241
	       (re-search-backward "^\n" (max (1- (point)) (point-min)) t)
242
	       (looking-at parsep))
243
	  (setq arg (1+ arg))
244
	(setq start (point))
245
	;; Move back over paragraph-separating lines.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
246
	(forward-char -1) (beginning-of-line)
247
	(while (and (not (bobp))
248
		    (progn (move-to-left-margin)
249
			   (looking-at parsep)))
Stefan Monnier's avatar
Typo.  
Stefan Monnier committed
250
	  (forward-line -1))
251 252
	(if (bobp)
	    nil
253
	  (setq arg (1+ arg))
254 255 256 257
	  ;; Go to end of the previous (non-separating) line.
	  (end-of-line)
	  ;; Search back for line that starts or separates paragraphs.
	  (if (if fill-prefix-regexp
258
		  ;; There is a fill prefix; it overrides parstart.
259
		  (let (multiple-lines)
260 261
		    (while (and (progn (beginning-of-line) (not (bobp)))
				(progn (move-to-left-margin)
262
				       (not (looking-at parsep)))
263
				(looking-at fill-prefix-regexp))
Stefan Monnier's avatar
Typo.  
Stefan Monnier committed
264 265
		      (unless (= (point) start)
			(setq multiple-lines t))
266
		      (forward-line -1))
267
		    (move-to-left-margin)
268 269 270 271 272 273 274 275
		    ;; This deleted code caused a long hanging-indent line
		    ;; not to be filled together with the following lines.
		    ;; ;; Don't move back over a line before the paragraph
		    ;; ;; which doesn't start with fill-prefix
		    ;; ;; unless that is the only line we've moved over.
		    ;; (and (not (looking-at fill-prefix-regexp))
		    ;;      multiple-lines
		    ;;      (forward-line 1))
276
		    (not (bobp)))
277
		(while (and (re-search-backward sp-parstart nil 1)
278
			    (setq found-start t)
279
			    ;; Found a candidate, but need to check if it is a
280
			    ;; REAL parstart.
281 282
			    (progn (setq start (point))
				   (move-to-left-margin)
283 284 285
				   (not (looking-at parsep)))
			    (not (and (looking-at parstart)
				      (or (not use-hard-newlines)
286 287 288
					  (bobp)
					  (get-text-property
					   (1- start) 'hard)))))
289
		  (setq found-start nil)
290
		  (goto-char start))
291
		found-start)
292 293
	      ;; Found one.
	      (progn
294 295 296
		;; Move forward over paragraph separators.
		;; We know this cannot reach the place we started
		;; because we know we moved back over a non-separator.
297 298
		(while (and (not (eobp))
			    (progn (move-to-left-margin)
299
				   (looking-at parsep)))
300
		  (forward-line 1))
301 302 303 304 305 306 307
		;; If line before paragraph is just margin, back up to there.
		(end-of-line 0)
		(if (> (current-column) (current-left-margin))
		    (forward-char 1)
		  (skip-chars-backward " \t")
		  (if (not (bolp))
		      (forward-line 1))))
308
	    ;; No starter or separator line => use buffer beg.
309
	    (goto-char (point-min))))))
310

311
    (while (and (> arg 0) (not (eobp)))
312 313 314 315 316 317 318 319
      ;; Move forward over separator lines...
      (while (and (not (eobp))
		  (progn (move-to-left-margin) (not (eobp)))
		  (looking-at parsep))
	(forward-line 1))
      (unless (eobp) (setq arg (1- arg)))
      ;; ... and one more line.
      (forward-line 1)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
320
      (if fill-prefix-regexp
321
	  ;; There is a fill prefix; it overrides parstart.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
322
	  (while (and (not (eobp))
323
		      (progn (move-to-left-margin) (not (eobp)))
324
		      (not (looking-at parsep))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
325 326
		      (looking-at fill-prefix-regexp))
	    (forward-line 1))
327
	(while (and (re-search-forward sp-parstart nil 1)
328 329
		    (progn (setq start (match-beginning 0))
			   (goto-char start)
330 331
			   (not (eobp)))
		    (progn (move-to-left-margin)
332 333
			   (not (looking-at parsep)))
		    (or (not (looking-at parstart))
334 335
			(and use-hard-newlines
			     (not (get-text-property (1- start) 'hard)))))
336 337
	  (forward-char 1))
	(if (< (point) (point-max))
338
	    (goto-char start))))
339 340 341
    (constrain-to-field nil opoint t)
    ;; Return the number of steps that could not be done.
    arg))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
342 343 344

(defun backward-paragraph (&optional arg)
  "Move backward to start of paragraph.
345 346
With argument ARG, do it ARG times;
a negative argument ARG = -N means move forward N paragraphs.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
347

Brian Preble's avatar
Brian Preble committed
348 349 350 351 352 353 354
A paragraph start is the beginning of a line which is a
`first-line-of-paragraph' or which is ordinary text and follows a
paragraph-separating line; except: if the first real line of a
paragraph is preceded by a blank line, the paragraph starts at that
blank line.

See `forward-paragraph' for more information."
Joseph Arceneaux's avatar
Joseph Arceneaux committed
355 356 357 358
  (interactive "p")
  (or arg (setq arg 1))
  (forward-paragraph (- arg)))

359
(defun mark-paragraph (&optional arg allow-extend)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
360
  "Put point at beginning of this paragraph, mark at end.
361 362 363 364 365 366
The paragraph marked is the one that contains point or follows point.

With argument ARG, puts mark at end of a following paragraph, so that
the number of paragraphs marked equals ARG.

If ARG is negative, point is put at end of this paragraph, mark is put
367 368
at beginning of this or a previous paragraph.

369
Interactively, if this command is repeated
370
or (in Transient Mark mode) if the mark is active,
371 372
it marks the next ARG paragraphs after the ones already marked."
  (interactive "p\np")
373 374 375
  (unless arg (setq arg 1))
  (when (zerop arg)
    (error "Cannot mark zero paragraphs"))
376 377 378
  (cond ((and allow-extend
	      (or (and (eq last-command this-command) (mark t))
		  (and transient-mark-mode mark-active)))
379 380 381 382 383 384 385 386 387
	 (set-mark
	  (save-excursion
	    (goto-char (mark))
	    (forward-paragraph arg)
	    (point))))
	(t
	 (forward-paragraph arg)
	 (push-mark nil t t)
	 (backward-paragraph arg))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
388 389 390 391 392

(defun kill-paragraph (arg)
  "Kill forward to end of paragraph.
With arg N, kill forward to Nth end of paragraph;
negative arg -N means kill backward to Nth start of paragraph."
393
  (interactive "p")
394
  (kill-region (point) (progn (forward-paragraph arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
395 396 397 398 399

(defun backward-kill-paragraph (arg)
  "Kill back to start of paragraph.
With arg N, kill back to Nth start of paragraph;
negative arg -N means kill forward to Nth end of paragraph."
400
  (interactive "p")
401
  (kill-region (point) (progn (backward-paragraph arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
402 403 404 405 406 407 408 409 410 411 412

(defun transpose-paragraphs (arg)
  "Interchange this (or next) paragraph with previous one."
  (interactive "*p")
  (transpose-subr 'forward-paragraph arg))

(defun start-of-paragraph-text ()
  (let ((opoint (point)) npoint)
    (forward-paragraph -1)
    (setq npoint (point))
    (skip-chars-forward " \t\n")
413 414 415 416 417
    ;; If the range of blank lines found spans the original start point,
    ;; try again from the beginning of it.
    ;; Must be careful to avoid infinite loop
    ;; when following a single return at start of buffer.
    (if (and (>= (point) opoint) (< npoint opoint))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
	(progn
	  (goto-char npoint)
	  (if (> npoint (point-min))
	      (start-of-paragraph-text))))))

(defun end-of-paragraph-text ()
  (let ((opoint (point)))
    (forward-paragraph 1)
    (if (eq (preceding-char) ?\n) (forward-char -1))
    (if (<= (point) opoint)
	(progn
	  (forward-char 1)
	  (if (< (point) (point-max))
	      (end-of-paragraph-text))))))

(defun forward-sentence (&optional arg)
434
  "Move forward to next `sentence-end'.  With argument, repeat.
Brian Preble's avatar
Brian Preble committed
435
With negative argument, move backward repeatedly to `sentence-beginning'.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
436

Brian Preble's avatar
Brian Preble committed
437 438
The variable `sentence-end' is a regular expression that matches ends of
sentences.  Also, every paragraph boundary terminates sentences as well."
Joseph Arceneaux's avatar
Joseph Arceneaux committed
439 440
  (interactive "p")
  (or arg (setq arg 1))
441 442
  (let ((opoint (point))
        (sentence-end (sentence-end)))
443
    (while (< arg 0)
444 445 446 447 448 449
      (let ((pos (point))
	    (par-beg (save-excursion (start-of-paragraph-text) (point))))
       (if (and (re-search-backward sentence-end par-beg t)
		(or (< (match-end 0) pos)
		    (re-search-backward sentence-end par-beg t)))
	   (goto-char (match-end 0))
450 451 452 453 454 455 456 457 458
	 (goto-char par-beg)))
      (setq arg (1+ arg)))
    (while (> arg 0)
      (let ((par-end (save-excursion (end-of-paragraph-text) (point))))
       (if (re-search-forward sentence-end par-end t)
	   (skip-chars-backward " \t\n")
	 (goto-char par-end)))
      (setq arg (1- arg)))
    (constrain-to-field nil opoint t)))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
459

460 461 462
(defun repunctuate-sentences ()
  "Put two spaces at the end of sentences from point to the end of buffer.
It works using `query-replace-regexp'."
463
  (interactive)
464 465 466 467
  (query-replace-regexp "\\([]\"')]?\\)\\([.?!]\\)\\([]\"')]?\\) +"
			"\\1\\2\\3  "))


Joseph Arceneaux's avatar
Joseph Arceneaux committed
468 469
(defun backward-sentence (&optional arg)
  "Move backward to start of sentence.  With arg, do it arg times.
Brian Preble's avatar
Brian Preble committed
470
See `forward-sentence' for more information."
Joseph Arceneaux's avatar
Joseph Arceneaux committed
471 472 473 474 475 476 477
  (interactive "p")
  (or arg (setq arg 1))
  (forward-sentence (- arg)))

(defun kill-sentence (&optional arg)
  "Kill from point to end of sentence.
With arg, repeat; negative arg -N means kill back to Nth start of sentence."
478
  (interactive "p")
479
  (kill-region (point) (progn (forward-sentence arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
480 481 482 483

(defun backward-kill-sentence (&optional arg)
  "Kill back from point to start of sentence.
With arg, repeat, or kill forward to Nth end of sentence if negative arg -N."
484
  (interactive "p")
485
  (kill-region (point) (progn (backward-sentence arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
486 487

(defun mark-end-of-sentence (arg)
488 489 490
  "Put mark at end of sentence.  Arg works as in `forward-sentence'.
If this command is repeated, it marks the next ARG sentences after the
ones already marked."
Joseph Arceneaux's avatar
Joseph Arceneaux committed
491 492
  (interactive "p")
  (push-mark
493 494 495 496 497 498
   (save-excursion
     (if (and (eq last-command this-command) (mark t))
	 (goto-char (mark)))
     (forward-sentence arg)
     (point))
   nil t))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
499 500 501 502 503

(defun transpose-sentences (arg)
  "Interchange this (next) and previous sentence."
  (interactive "*p")
  (transpose-subr 'forward-sentence arg))
Eric S. Raymond's avatar
Eric S. Raymond committed
504

505 506 507 508
;;; Local Variables:
;;; coding: iso-2022-7bit
;;; End:

Miles Bader's avatar
Miles Bader committed
509
;;; arch-tag: e727eb1a-527a-4464-b9d7-9d3ec0d1a575
Eric S. Raymond's avatar
Eric S. Raymond committed
510
;;; paragraphs.el ends here