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

Paul Eggert's avatar
Paul Eggert committed
3
;; Copyright (C) 1985-1987, 1991, 1994-1997, 1999-2015 Free Software
4
;; Foundation, Inc.
Eric S. Raymond's avatar
Eric S. Raymond committed
5

6
;; Maintainer: emacs-devel@gnu.org
Eric S. Raymond's avatar
Eric S. Raymond committed
7
;; Keywords: wp
8
;; Package: emacs
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
Chong Yidong's avatar
Chong Yidong committed
38 39 40 41 42 43
  "Toggle distinguishing between hard and soft newlines.
With a prefix argument ARG, enable the feature if ARG is
positive, and disable it otherwise.  If called from Lisp, enable
it if ARG is omitted or nil.

When enabled, the functions `newline' and `open-line' add the
44
text-property `hard' to newlines that they insert, and a line is
45
only considered as a candidate to match `paragraph-start' or
46
`paragraph-separate' if it follows a hard newline.
47

Chong Yidong's avatar
Chong Yidong committed
48 49 50 51 52 53 54
When enabling, if there are newlines in the buffer but no hard
newlines, ask the user whether to mark as hard any newlines
preceding a `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 marking, `guess' to try to do the
right thing with no questions, nil or anything else to ask the
user.
55 56 57

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
58
  :group 'paragraphs
59 60
  :extra-args (insert)
  (when use-hard-newlines
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
    ;; 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)
77 78 79 80 81 82 83 84
	      (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)))))))))))
85

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

92 93 94 95 96
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.

97
The variable `paragraph-separate' specifies how to distinguish
98 99
lines that start paragraphs from lines that separate them.

Stefan Monnier's avatar
Typo.  
Stefan Monnier committed
100
If the variable `use-hard-newlines' is non-nil, then only lines following a
Richard M. Stallman's avatar
Richard M. Stallman committed
101 102 103
hard newline are considered to match."
  :group 'paragraphs
  :type 'regexp)
104
(put 'paragraph-start 'safe-local-variable 'stringp)
Jim Blandy's avatar
Jim Blandy committed
105

106 107 108 109 110 111
;; 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
112
(defcustom paragraph-separate "[ \t\f]*$"
113
  "Regexp for beginning of a line that separates paragraphs.
114
If you change this, you may have to change `paragraph-start' also.
115

116 117 118
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
119 120 121
text indented by a margin setting."
  :group 'paragraphs
  :type 'regexp)
122
(put 'paragraph-separate 'safe-local-variable 'stringp)
Jim Blandy's avatar
Jim Blandy committed
123

124
(defcustom sentence-end-double-space t
125
  "Non-nil means a single space does not end a sentence.
126 127 128
This is relevant for filling.  See also `sentence-end-without-period'
and `colon-double-space'.

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

(defcustom sentence-end-without-period nil
137
  "Non-nil means a sentence will end without a period.
138
For example, a sentence in Thai text ends with double space but
139 140 141
without a period.

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

148
(defcustom sentence-end-without-space
149
  "。.?!"
150
  "String of characters that end sentence without following spaces.
151 152

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

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

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

171
(defcustom sentence-end-base "[.?!…‽][]\"'”’)}]*"
172
  "Regexp matching the basic end of a sentence, not including following space."
173 174
  :group 'paragraphs
  :type 'string
175
  :version "25.1")
176
(put 'sentence-end-base 'safe-local-variable 'stringp)
177

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

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

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

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

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

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

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

Brian Preble's avatar
Brian Preble committed
361
A paragraph start is the beginning of a line which is a
Stefan Monnier's avatar
Stefan Monnier committed
362 363
`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
364 365 366 367
paragraph is preceded by a blank line, the paragraph starts at that
blank line.

See `forward-paragraph' for more information."
368
  (interactive "^p")
Joseph Arceneaux's avatar
Joseph Arceneaux committed
369 370 371
  (or arg (setq arg 1))
  (forward-paragraph (- arg)))

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

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

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

(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."
413
  (interactive "p")
414
  (kill-region (point) (progn (backward-paragraph arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
415 416

(defun transpose-paragraphs (arg)
417 418 419 420 421 422
  "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
423 424 425 426 427 428 429 430
  (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")
431 432 433 434 435
    ;; 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
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
	(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)
452 453
  "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
454

Brian Preble's avatar
Brian Preble committed
455 456
The variable `sentence-end' is a regular expression that matches ends of
sentences.  Also, every paragraph boundary terminates sentences as well."
457
  (interactive "^p")
Joseph Arceneaux's avatar
Joseph Arceneaux committed
458
  (or arg (setq arg 1))
459 460
  (let ((opoint (point))
        (sentence-end (sentence-end)))
461
    (while (< arg 0)
462
      (let ((pos (point))
463 464 465 466 467 468 469 470
	    par-beg par-text-beg)
	(save-excursion
	  (start-of-paragraph-text)
	  ;; Start of real text in the paragraph.
	  ;; We move back to here if we don't see a sentence-end.
	  (setq par-text-beg (point))
	  ;; Start of the first line of the paragraph.
	  ;; We use this as the search limit
Paul Eggert's avatar
Paul Eggert committed
471
	  ;; to allow sentence-end to match if it is anchored at
472 473 474
	  ;; BOL and the paragraph starts indented.
	  (beginning-of-line)
	  (setq par-beg (point)))
475 476 477 478
	(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))
479
	  (goto-char par-text-beg)))
480 481 482
      (setq arg (1+ arg)))
    (while (> arg 0)
      (let ((par-end (save-excursion (end-of-paragraph-text) (point))))
483 484 485
	(if (re-search-forward sentence-end par-end t)
	    (skip-chars-backward " \t\n")
	  (goto-char par-end)))
486 487
      (setq arg (1- arg)))
    (constrain-to-field nil opoint t)))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
488

489 490 491
(defun repunctuate-sentences ()
  "Put two spaces at the end of sentences from point to the end of buffer.
It works using `query-replace-regexp'."
492
  (interactive)
493 494 495 496
  (query-replace-regexp "\\([]\"')]?\\)\\([.?!]\\)\\([]\"')]?\\) +"
			"\\1\\2\\3  "))


Joseph Arceneaux's avatar
Joseph Arceneaux committed
497 498
(defun backward-sentence (&optional arg)
  "Move backward to start of sentence.  With arg, do it arg times.
Brian Preble's avatar
Brian Preble committed
499
See `forward-sentence' for more information."
500
  (interactive "^p")
Joseph Arceneaux's avatar
Joseph Arceneaux committed
501 502 503 504 505 506
  (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."
507
  (interactive "p")
508
  (kill-region (point) (progn (forward-sentence arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
509 510 511 512

(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."
513
  (interactive "p")
514
  (kill-region (point) (progn (backward-sentence arg) (point))))
Joseph Arceneaux's avatar
Joseph Arceneaux committed
515 516

(defun mark-end-of-sentence (arg)
517 518 519
  "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
520 521
  (interactive "p")
  (push-mark
522 523 524 525 526 527
   (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
528 529

(defun transpose-sentences (arg)
530 531 532 533 534 535
  "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
536 537
  (interactive "*p")
  (transpose-subr 'forward-sentence arg))
Eric S. Raymond's avatar
Eric S. Raymond committed
538

539
;; Local Variables:
540
;; coding: utf-8
541
;; End:
542

Eric S. Raymond's avatar
Eric S. Raymond committed
543
;;; paragraphs.el ends here