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

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

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

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

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

;; 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
23
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
24

25 26 27 28 29
;;; Commentary:

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

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

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

36
(put 'use-hard-newlines 'permanent-local t)
37
(define-minor-mode use-hard-newlines
38 39 40
  "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
41
only considered as a candidate to match `paragraph-start' or
42
`paragraph-separate' if it follows a hard newline.
43

44 45
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
46
newlines, ask the user whether to mark as hard any newlines preceeding a
47 48
`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
49
marking, `guess' to try to do the right thing with no questions, nil
50 51 52 53
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
54
  :group 'paragraphs
55 56
  :extra-args (insert)
  (when use-hard-newlines
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
    ;; 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)
73 74 75 76 77 78 79 80
	      (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)))))))))))
81

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

88 89 90 91 92
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.

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

Stefan Monnier's avatar
Typo.  
Stefan Monnier committed
96
If the variable `use-hard-newlines' is non-nil, then only lines following a
Richard M. Stallman's avatar
Richard M. Stallman committed
97 98 99
hard newline are considered to match."
  :group 'paragraphs
  :type 'regexp)
100
(put 'paragraph-start 'safe-local-variable 'stringp)
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
(defcustom paragraph-separate "[ \t\f]*$"
109
  "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)
118
(put 'paragraph-separate 'safe-local-variable 'stringp)
Jim Blandy's avatar
Jim Blandy committed
119

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

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

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

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

144
(defcustom sentence-end-without-space
145
  "。.?!"
146
  "String of characters that end sentence without following spaces.
147 148

This value is used by the function `sentence-end' to construct the
Luc Teirlinck's avatar
Luc Teirlinck committed
149 150
regexp describing the end of a sentence, when the value of the variable
`sentence-end' is nil.  See Info node `(elisp)Standard Regexps'."
151 152
  :group 'paragraphs
  :type 'string)
153
(put 'sentence-end-without-space 'safe-local-variable 'stringp)
154

155
(defcustom sentence-end nil
156
  "Regexp describing the end of a sentence.
Eli Zaretskii's avatar
Eli Zaretskii committed
157
The value includes the whitespace following the sentence.
158 159
All paragraph boundaries also end sentences, regardless.

160 161 162
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
163
  :group 'paragraphs
164
  :type '(choice regexp (const :tag "Use default value" nil)))
165
(put 'sentence-end 'safe-local-variable 'string-or-null-p)
166

167
(defcustom sentence-end-base "[.?!][]\"'”)}]*"
168
  "Regexp matching the basic end of a sentence, not including following space."
169 170 171
  :group 'paragraphs
  :type 'string
  :version "22.1")
172
(put 'sentence-end-base 'safe-local-variable 'stringp)
173

174 175 176 177 178
(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
179 180 181 182 183 184 185
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'."
186
  (or sentence-end
187 188
      ;; We accept non-break space along with space.
      (concat (if sentence-end-without-period "\\w[ \u00a0][ \u00a0]\\|")
189 190
	      "\\("
	      sentence-end-base
191
              (if sentence-end-double-space
192
                  "\\($\\|[ \u00a0]$\\|\t\\|[ \u00a0][ \u00a0]\\)" "\\($\\|[\t \u00a0]\\)")
193 194
              "\\|[" sentence-end-without-space "]+"
	      "\\)"
195
              "[ \u00a0\t\n]*")))
Richard M. Stallman's avatar
Richard M. Stallman committed
196 197

(defcustom page-delimiter "^\014"
198
  "Regexp describing line-beginnings that separate pages."
Richard M. Stallman's avatar
Richard M. Stallman committed
199 200
  :group 'paragraphs
  :type 'regexp)
201
(put 'page-delimiter 'safe-local-variable 'stringp)
Richard M. Stallman's avatar
Richard M. Stallman committed
202 203

(defcustom paragraph-ignore-fill-prefix nil
204
  "Non-nil means the paragraph commands are not affected by `fill-prefix'.
Richard M. Stallman's avatar
Richard M. Stallman committed
205 206 207
This is desirable in modes where blank lines are the paragraph delimiters."
  :group 'paragraphs
  :type 'boolean)
208
(put 'paragraph-ignore-fill-prefix 'safe-local-variable 'booleanp)
Roland McGrath's avatar
Roland McGrath committed
209

Joseph Arceneaux's avatar
Joseph Arceneaux committed
210 211
(defun forward-paragraph (&optional arg)
  "Move forward to end of paragraph.
212 213
With argument ARG, do it ARG times;
a negative argument ARG = -N means move backward N paragraphs.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
214 215 216 217

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
218 219
to which the end of the previous line belongs, or the end of the buffer.
Returns the count of paragraphs left to move."
220
  (interactive "^p")
Joseph Arceneaux's avatar
Joseph Arceneaux committed
221
  (or arg (setq arg 1))
222 223
  (let* ((opoint (point))
	 (fill-prefix-regexp
Joseph Arceneaux's avatar
Joseph Arceneaux committed
224 225 226
	  (and fill-prefix (not (equal fill-prefix ""))
	       (not paragraph-ignore-fill-prefix)
	       (regexp-quote fill-prefix)))
227 228 229 230 231
	 ;; 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".
232 233 234 235 236 237 238 239 240
	 (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
241
	  (if fill-prefix-regexp
242
	      (concat parsep "\\|"
Joseph Arceneaux's avatar
Joseph Arceneaux committed
243
		      fill-prefix-regexp "[ \t]*$")
244
	    parsep))
245
	 ;; This is used for searching.
246
	 (sp-parstart (concat "^[ \t]*\\(?:" parstart "\\|" parsep "\\)"))
247
	 start found-start)
248
    (while (and (< arg 0) (not (bobp)))
249
      (if (and (not (looking-at parsep))
250
	       (re-search-backward "^\n" (max (1- (point)) (point-min)) t)
251
	       (looking-at parsep))
252
	  (setq arg (1+ arg))
253
	(setq start (point))
254
	;; Move back over paragraph-separating lines.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
255
	(forward-char -1) (beginning-of-line)
256
	(while (and (not (bobp))
257
		    (progn (move-to-left-margin)
258
			   (looking-at parsep)))
Stefan Monnier's avatar
Typo.  
Stefan Monnier committed
259
	  (forward-line -1))
260 261
	(if (bobp)
	    nil
262
	  (setq arg (1+ arg))
263 264 265 266
	  ;; 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
267
		  ;; There is a fill prefix; it overrides parstart.
268
		  (let (multiple-lines)
269 270
		    (while (and (progn (beginning-of-line) (not (bobp)))
				(progn (move-to-left-margin)
271
				       (not (looking-at parsep)))
272
				(looking-at fill-prefix-regexp))
Stefan Monnier's avatar
Typo.  
Stefan Monnier committed
273 274
		      (unless (= (point) start)
			(setq multiple-lines t))
275
		      (forward-line -1))
276
		    (move-to-left-margin)
277 278 279 280 281 282 283 284
		    ;; 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))
285
		    (not (bobp)))
286
		(while (and (re-search-backward sp-parstart nil 1)
287
			    (setq found-start t)
288
			    ;; Found a candidate, but need to check if it is a
289
			    ;; REAL parstart.
290 291
			    (progn (setq start (point))
				   (move-to-left-margin)
292 293 294
				   (not (looking-at parsep)))
			    (not (and (looking-at parstart)
				      (or (not use-hard-newlines)
295 296 297
					  (bobp)
					  (get-text-property
					   (1- start) 'hard)))))
298
		  (setq found-start nil)
299
		  (goto-char start))
300
		found-start)
301 302
	      ;; Found one.
	      (progn
303 304 305
		;; Move forward over paragraph separators.
		;; We know this cannot reach the place we started
		;; because we know we moved back over a non-separator.
306 307
		(while (and (not (eobp))
			    (progn (move-to-left-margin)
308
				   (looking-at parsep)))
309
		  (forward-line 1))
310 311 312 313 314 315 316
		;; 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))))
317
	    ;; No starter or separator line => use buffer beg.
318
	    (goto-char (point-min))))))
319

320
    (while (and (> arg 0) (not (eobp)))
321 322 323 324 325 326 327 328
      ;; 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
329
      (if fill-prefix-regexp
330
	  ;; There is a fill prefix; it overrides parstart.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
331
	  (while (and (not (eobp))
332
		      (progn (move-to-left-margin) (not (eobp)))
333
		      (not (looking-at parsep))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
334 335
		      (looking-at fill-prefix-regexp))
	    (forward-line 1))
336
	(while (and (re-search-forward sp-parstart nil 1)
337 338
		    (progn (setq start (match-beginning 0))
			   (goto-char start)
339 340
			   (not (eobp)))
		    (progn (move-to-left-margin)
341 342
			   (not (looking-at parsep)))
		    (or (not (looking-at parstart))
343 344
			(and use-hard-newlines
			     (not (get-text-property (1- start) 'hard)))))
345 346
	  (forward-char 1))
	(if (< (point) (point-max))
347
	    (goto-char start))))
348 349 350
    (constrain-to-field nil opoint t)
    ;; Return the number of steps that could not be done.
    arg))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
351 352 353

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

Brian Preble's avatar
Brian Preble committed
357
A paragraph start is the beginning of a line which is a
Stefan Monnier's avatar
Stefan Monnier committed
358 359
`paragraph-start' or which is ordinary text and follows a
`paragraph-separate'ing line; except: if the first real line of a
Brian Preble's avatar
Brian Preble committed
360 361 362 363
paragraph is preceded by a blank line, the paragraph starts at that
blank line.

See `forward-paragraph' for more information."
364
  (interactive "^p")
Joseph Arceneaux's avatar
Joseph Arceneaux committed
365 366 367
  (or arg (setq arg 1))
  (forward-paragraph (- arg)))

368
(defun mark-paragraph (&optional arg allow-extend)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
369
  "Put point at beginning of this paragraph, mark at end.
370 371 372 373 374 375
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
376 377
at beginning of this or a previous paragraph.

378
Interactively, if this command is repeated
379
or (in Transient Mark mode) if the mark is active,
380 381
it marks the next ARG paragraphs after the ones already marked."
  (interactive "p\np")
382 383 384
  (unless arg (setq arg 1))
  (when (zerop arg)
    (error "Cannot mark zero paragraphs"))
385 386 387
  (cond ((and allow-extend
	      (or (and (eq last-command this-command) (mark t))
		  (and transient-mark-mode mark-active)))
388 389 390 391 392 393 394 395 396
	 (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
397 398 399 400 401

(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."
402
  (interactive "p")
403
  (kill-region (point) (progn (forward-paragraph arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
404 405 406 407 408

(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."
409
  (interactive "p")
410
  (kill-region (point) (progn (backward-paragraph arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
411 412

(defun transpose-paragraphs (arg)
413 414 415 416 417 418
  "Interchange the current paragraph with the next one.
With prefix argument ARG a non-zero integer, moves the current
paragraph past ARG paragraphs, leaving point after the current paragraph.
If ARG is positive, moves the current paragraph forwards, if
ARG is negative moves it backwards.  If ARG is zero, exchanges
the current paragraph with the one containing the mark."
Joseph Arceneaux's avatar
Joseph Arceneaux committed
419 420 421 422 423 424 425 426
  (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")
427 428 429 430 431
    ;; 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
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
	(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)
448 449
  "Move forward to next end of sentence.  With argument, repeat.
With negative argument, move backward repeatedly to start of sentence.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
450

Brian Preble's avatar
Brian Preble committed
451 452
The variable `sentence-end' is a regular expression that matches ends of
sentences.  Also, every paragraph boundary terminates sentences as well."
453
  (interactive "^p")
Joseph Arceneaux's avatar
Joseph Arceneaux committed
454
  (or arg (setq arg 1))
455 456
  (let ((opoint (point))
        (sentence-end (sentence-end)))
457
    (while (< arg 0)
458
      (let ((pos (point))
459 460 461 462
	    ;; We used to use (start-of-paragraph-text), but this can
	    ;; prevent sentence-end from matching if it is anchored at
	    ;; BOL and the paragraph starts indented.
	    (par-beg (save-excursion (backward-paragraph) (point))))
463 464 465 466
       (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))
467 468 469 470 471 472 473 474 475
	 (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
476

477 478 479
(defun repunctuate-sentences ()
  "Put two spaces at the end of sentences from point to the end of buffer.
It works using `query-replace-regexp'."
480
  (interactive)
481 482 483 484
  (query-replace-regexp "\\([]\"')]?\\)\\([.?!]\\)\\([]\"')]?\\) +"
			"\\1\\2\\3  "))


Joseph Arceneaux's avatar
Joseph Arceneaux committed
485 486
(defun backward-sentence (&optional arg)
  "Move backward to start of sentence.  With arg, do it arg times.
Brian Preble's avatar
Brian Preble committed
487
See `forward-sentence' for more information."
488
  (interactive "^p")
Joseph Arceneaux's avatar
Joseph Arceneaux committed
489 490 491 492 493 494
  (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."
495
  (interactive "p")
496
  (kill-region (point) (progn (forward-sentence arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
497 498 499 500

(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."
501
  (interactive "p")
502
  (kill-region (point) (progn (backward-sentence arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
503 504

(defun mark-end-of-sentence (arg)
505 506 507
  "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
508 509
  (interactive "p")
  (push-mark
510 511 512 513 514 515
   (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
516 517

(defun transpose-sentences (arg)
518 519 520 521 522 523
  "Interchange the current sentence with the next one.
With prefix argument ARG a non-zero integer, moves the current
sentence past ARG sentences, leaving point after the current sentence.
If ARG is positive, moves the current sentence forwards, if
ARG is negative moves it backwards.  If ARG is zero, exchanges
the current sentence with the one containing the mark."
Joseph Arceneaux's avatar
Joseph Arceneaux committed
524 525
  (interactive "*p")
  (transpose-subr 'forward-sentence arg))
Eric S. Raymond's avatar
Eric S. Raymond committed
526

527
;; Local Variables:
528
;; coding: utf-8
529
;; End:
530

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