X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/650f194c5accb990c199fd802fd6778ca5b394c8..9d7c4eb5547e75fdda8cf70758deeb45a1362994:/lisp/textmodes/paragraphs.el diff --git a/lisp/textmodes/paragraphs.el b/lisp/textmodes/paragraphs.el index 0edfe88c87..4f373763bc 100644 --- a/lisp/textmodes/paragraphs.el +++ b/lisp/textmodes/paragraphs.el @@ -1,6 +1,7 @@ ;;; paragraphs.el --- paragraph and sentence parsing. -;; Copyright (C) 1985, 86, 87, 91, 94 Free Software Foundation, Inc. +;; Copyright (C) 1985, 86, 87, 91, 94, 95, 96, 1997 +;; Free Software Foundation, Inc. ;; Maintainer: FSF ;; Keywords: wp @@ -18,8 +19,9 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to -;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. ;;; Commentary: @@ -28,50 +30,129 @@ ;;; Code: -(defconst paragraph-start "^[ \t\n\f]" "\ +(defgroup paragraphs nil + "Paragraph and sentence parsing." + :group 'editing) + +;; It isn't useful to use defcustom for this variable +;; because it is always buffer-local. +(defvar use-hard-newlines nil + "Non-nil means to distinguish hard and soft newlines. +See also the documentation for the function `use-hard-newlines'.") +(make-variable-buffer-local 'use-hard-newlines) + +(defun use-hard-newlines (&optional arg insert) + "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 +only considered as a candidate to match `paragraph-start' or +`paragraph-separate' if it follows a hard newline. + +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 +newlines, ask the user whether to mark as hard any newlines preceeding 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. + +Newlines not marked hard are called \"soft\", and are always internal +to paragraphs. The fill functions insert and delete only soft newlines." + (interactive (list current-prefix-arg nil)) + (if (or (<= (prefix-numeric-value arg) 0) + (and use-hard-newlines (null arg))) + ;; Turn mode off + (setq use-hard-newlines nil) + ;; 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 use-hard-newlines) + (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) + (if (looking-at paragraph-start) + (progn + (set-hard-newline-properties (1- pos) pos) + ;; If paragraph-separate, newline after it is hard too. + (if (looking-at paragraph-separate) + (progn + (end-of-line) + (if (not (eobp)) + (set-hard-newline-properties + (point) (1+ (point)))))))))))) + (setq use-hard-newlines t))) + +(defcustom paragraph-start "[ \t\n\f]" "\ *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). +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. + The variable `paragraph-separate' specifies how to distinguish lines that start paragraphs from lines that separate them. If the variable `use-hard-newlines' is nonnil, then only lines following a -hard newline are considered to match.") - -(defconst paragraph-separate "^[ \t\f]*$" "\ -*Regexp for beginning of a line that separates paragraphs. -If you change this, you may have to change paragraph-start also. - -If the variable `use-hard-newlines' is nonnil, then only lines following a -hard newline are considered to match.") - -(defconst sentence-end (purecopy "[.?!][]\"')}]*\\($\\| $\\|\t\\| \\)[ \t\n]*") "\ -*Regexp describing the end of a sentence. +hard newline are considered to match." + :group 'paragraphs + :type 'regexp) + +;; 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). + +(defcustom paragraph-separate "[ \t\f]*$" + "*Regexp for beginning of a line that separates paragraphs. +If you change this, you may have to change `paragraph-start' also. + +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 +text indented by a margin setting." + :group 'paragraphs + :type 'regexp) + +(defcustom sentence-end (purecopy "[.?!][]\"')}]*\\($\\| $\\|\t\\| \\)[ \t\n]*") + "*Regexp describing the end of a sentence. All paragraph boundaries also end sentences, regardless. 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, -unless it's inside some sort of quotes or parenthesis.") +unless it's inside some sort of quotes or parenthesis." + :group 'paragraphs + :type 'regexp) -(defconst page-delimiter "^\014" "\ -*Regexp describing line-beginnings that separate pages.") +(defcustom page-delimiter "^\014" + "*Regexp describing line-beginnings that separate pages." + :group 'paragraphs + :type 'regexp) -(defvar 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.") - -(defsubst looking-at-hard (re) - ;; Just for convenience in writing the function below. - (and (or (null use-hard-newlines) - (bobp) - (get-text-property (1- (point)) 'hard)) - (looking-at re))) +(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) (defun forward-paragraph (&optional arg) "Move forward to end of paragraph. -With arg N, do it N times; negative arg -N means move backward N paragraphs. +With argument ARG, do it ARG times; +a negative argument ARG = -N means move backward N paragraphs. A line which `paragraph-start' matches either separates paragraphs \(if `paragraph-separate' matches it also) or is the first line of a paragraph. @@ -83,21 +164,39 @@ to which the end of the previous line belongs, or the end of the buffer." (and fill-prefix (not (equal fill-prefix "")) (not paragraph-ignore-fill-prefix) (regexp-quote fill-prefix))) + ;; 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". + (paragraph-start (if (and (not (equal "" paragraph-start)) + (equal ?^ (aref paragraph-start 0))) + (substring paragraph-start 1) + paragraph-start)) + (paragraph-separate (if (and (not (equal "" paragraph-separate)) + (equal ?^ (aref paragraph-separate 0))) + (substring paragraph-separate 1) + paragraph-separate)) (paragraph-separate (if fill-prefix-regexp - (concat paragraph-separate "\\|^" + (concat paragraph-separate "\\|" fill-prefix-regexp "[ \t]*$") - paragraph-separate))) + paragraph-separate)) + ;; This is used for searching. + (sp-paragraph-start (concat "^[ \t]*\\(" paragraph-start "\\)")) + start found-start) (while (and (< arg 0) (not (bobp))) - (if (and (not (looking-at-hard paragraph-separate)) + (if (and (not (looking-at paragraph-separate)) (re-search-backward "^\n" (max (1- (point)) (point-min)) t) - (looking-at-hard paragraph-separate)) + (looking-at paragraph-separate)) nil + (setq start (point)) ;; Move back over paragraph-separating lines. (forward-char -1) (beginning-of-line) (while (and (not (bobp)) - (looking-at-hard paragraph-separate)) - (forward-line -1)) + (progn (move-to-left-margin) + (looking-at paragraph-separate))) + (forward-line -1)) (if (bobp) nil ;; Go to end of the previous (non-separating) line. @@ -105,55 +204,90 @@ to which the end of the previous line belongs, or the end of the buffer." ;; Search back for line that starts or separates paragraphs. (if (if fill-prefix-regexp ;; There is a fill prefix; it overrides paragraph-start. - (progn - (while (progn (beginning-of-line) - (and (not (bobp)) - (not (looking-at-hard - paragraph-separate)) - (looking-at fill-prefix-regexp))) - (forward-line -1)) - (not (bobp))) - (while (and (re-search-backward paragraph-start nil 1) - use-hard-newlines - (not (bobp)) - (null (get-text-property (1- (point)) 'hard))) - (if (not (bobp)) (backward-char 1))) - (> (point) (point-min))) + (let (multiple-lines) + (while (and (progn (beginning-of-line) (not (bobp))) + (progn (move-to-left-margin) + (not (looking-at paragraph-separate))) + (looking-at fill-prefix-regexp)) + (if (not (= (point) start)) + (setq multiple-lines t)) + (forward-line -1)) + (move-to-left-margin) +;;; 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)) + (not (bobp))) + (while (and (re-search-backward sp-paragraph-start nil 1) + (setq found-start t) + ;; Found a candidate, but need to check if it is a + ;; REAL paragraph-start. + (progn (setq start (point)) + (move-to-left-margin) + (not (looking-at paragraph-separate))) + (not (and (looking-at paragraph-start) + (not + (and use-hard-newlines + (not (bobp)) + (not (get-text-property (1- start) + 'hard))))))) + (setq found-start nil) + (goto-char start)) + found-start) ;; Found one. (progn ;; Move forward over paragraph separators. ;; We know this cannot reach the place we started ;; because we know we moved back over a non-separator. - (while (and (not (eobp)) (looking-at-hard paragraph-separate)) + (while (and (not (eobp)) + (progn (move-to-left-margin) + (looking-at paragraph-separate))) (forward-line 1)) - (if (eq (char-after (- (point) 2)) ?\n) - (forward-line -1))) + ;; 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)))) ;; No starter or separator line => use buffer beg. (goto-char (point-min))))) (setq arg (1+ arg))) (while (and (> arg 0) (not (eobp))) - (beginning-of-line) + ;; Move forward over separator lines, and one more line. (while (prog1 (and (not (eobp)) - (looking-at-hard paragraph-separate)) + (progn (move-to-left-margin) (not (eobp))) + (looking-at paragraph-separate)) (forward-line 1))) (if fill-prefix-regexp ;; There is a fill prefix; it overrides paragraph-start. (while (and (not (eobp)) - (not (looking-at-hard paragraph-separate)) + (progn (move-to-left-margin) (not (eobp))) + (not (looking-at paragraph-separate)) (looking-at fill-prefix-regexp)) (forward-line 1)) - (while (and (re-search-forward paragraph-start nil 1) - (not (eobp)) - use-hard-newlines - (null (get-text-property (1- (match-beginning 0)) 'hard))) + (while (and (re-search-forward sp-paragraph-start nil 1) + (progn (setq start (match-beginning 0)) + (goto-char start) + (not (eobp))) + (progn (move-to-left-margin) + (not (looking-at paragraph-separate))) + (or (not (looking-at paragraph-start)) + (and use-hard-newlines + (not (get-text-property (1- start) 'hard))))) (forward-char 1)) (if (< (point) (point-max)) - (goto-char (match-beginning 0)))) + (goto-char start))) (setq arg (1- arg))))) (defun backward-paragraph (&optional arg) "Move backward to start of paragraph. -With arg N, do it N times; negative arg -N means move forward N paragraphs. +With argument ARG, do it ARG times; +a negative argument ARG = -N means move forward N paragraphs. A paragraph start is the beginning of a line which is a `first-line-of-paragraph' or which is ordinary text and follows a