Line data Source code
1 : ;;; text-mode.el --- text mode, and its idiosyncratic commands
2 :
3 : ;; Copyright (C) 1985, 1992, 1994, 2001-2017 Free Software Foundation,
4 : ;; Inc.
5 :
6 : ;; Maintainer: emacs-devel@gnu.org
7 : ;; Keywords: wp
8 : ;; Package: emacs
9 :
10 : ;; This file is part of GNU Emacs.
11 :
12 : ;; GNU Emacs is free software: you can redistribute it and/or modify
13 : ;; it under the terms of the GNU General Public License as published by
14 : ;; the Free Software Foundation, either version 3 of the License, or
15 : ;; (at your option) any later version.
16 :
17 : ;; GNU Emacs is distributed in the hope that it will be useful,
18 : ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : ;; GNU General Public License for more details.
21 :
22 : ;; 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/>.
24 :
25 : ;;; Commentary:
26 :
27 : ;; This package provides the fundamental text mode documented in the
28 : ;; Emacs user's manual.
29 :
30 : ;;; Code:
31 :
32 : ;; Normally non-nil defaults for hooks are bad, but since this file is
33 : ;; preloaded it's ok/better, and avoids this showing up in customize-rogue.
34 : (defcustom text-mode-hook '(text-mode-hook-identify)
35 : "Normal hook run when entering Text mode and many related modes."
36 : :type 'hook
37 : :options '(turn-on-auto-fill turn-on-flyspell)
38 : :group 'text)
39 :
40 : (defvar text-mode-variant nil
41 : "Non-nil if this buffer's major mode is a variant of Text mode.
42 : Use (derived-mode-p \\='text-mode) instead.")
43 :
44 : (defvar text-mode-syntax-table
45 : (let ((st (make-syntax-table)))
46 : (modify-syntax-entry ?\" ". " st)
47 : (modify-syntax-entry ?\\ ". " st)
48 : ;; We add `p' so that M-c on 'hello' leads to 'Hello' rather than 'hello'.
49 : (modify-syntax-entry ?' "w p" st)
50 : ;; UAX #29 says HEBREW PUNCTUATION GERESH behaves like a letter
51 : ;; for the purposes of finding word boundaries.
52 : (modify-syntax-entry #x5f3 "w ") ; GERESH
53 : ;; UAX #29 says HEBREW PUNCTUATION GERSHAYIM should not be a word
54 : ;; boundary when surrounded by letters. Our infrastructure for
55 : ;; finding a word boundary doesn't support 3-character
56 : ;; definitions, so for now simply make this a word-constituent
57 : ;; character. This leaves a problem of having GERSHAYIM at the
58 : ;; beginning or end of a word, where it should be a boundary;
59 : ;; FIXME.
60 : (modify-syntax-entry #x5f4 "w ") ; GERSHAYIM
61 : ;; These all should not be a word boundary when between letters,
62 : ;; according to UAX #29, so they again are prone to the same
63 : ;; problem as GERSHAYIM; FIXME.
64 : (modify-syntax-entry #xb7 "w ") ; MIDDLE DOT
65 : (modify-syntax-entry #x2027 "w ") ; HYPHENATION POINT
66 : (modify-syntax-entry #xff1a "w ") ; FULLWIDTH COLON
67 : st)
68 : "Syntax table used while in `text-mode'.")
69 :
70 : (defvar text-mode-map
71 : (let ((map (make-sparse-keymap)))
72 : (define-key map "\e\t" 'ispell-complete-word)
73 : (define-key map [menu-bar text]
74 : (cons "Text" (make-sparse-keymap "Text")))
75 : (bindings--define-key map [menu-bar text toggle-text-mode-auto-fill]
76 : '(menu-item "Auto Fill" toggle-text-mode-auto-fill
77 : :button (:toggle . (memq 'turn-on-auto-fill text-mode-hook))
78 : :help "Automatically fill text while typing in text modes (Auto Fill mode)"))
79 : (bindings--define-key map [menu-bar text paragraph-indent-minor-mode]
80 : '(menu-item "Paragraph Indent" paragraph-indent-minor-mode
81 : :button (:toggle . (bound-and-true-p paragraph-indent-minor-mode))
82 : :help "Toggle paragraph indent minor mode"))
83 : (bindings--define-key map [menu-bar text sep] menu-bar-separator)
84 : (bindings--define-key map [menu-bar text center-region]
85 : '(menu-item "Center Region" center-region
86 : :help "Center the marked region"
87 : :enable (region-active-p)))
88 : (bindings--define-key map [menu-bar text center-paragraph]
89 : '(menu-item "Center Paragraph" center-paragraph
90 : :help "Center the current paragraph"))
91 : (bindings--define-key map [menu-bar text center-line]
92 : '(menu-item "Center Line" center-line
93 : :help "Center the current line"))
94 : map)
95 : "Keymap for `text-mode'.
96 : Many other modes, such as `mail-mode', `outline-mode' and `indented-text-mode',
97 : inherit all the commands defined in this map.")
98 :
99 :
100 : (define-derived-mode text-mode nil "Text"
101 : "Major mode for editing text written for humans to read.
102 : In this mode, paragraphs are delimited only by blank or white lines.
103 : You can thus get the full benefit of adaptive filling
104 : (see the variable `adaptive-fill-mode').
105 : \\{text-mode-map}
106 : Turning on Text mode runs the normal hook `text-mode-hook'."
107 0 : (set (make-local-variable 'text-mode-variant) t)
108 0 : (set (make-local-variable 'require-final-newline)
109 0 : mode-require-final-newline)
110 0 : (set (make-local-variable 'indent-line-function) 'indent-relative))
111 :
112 : (define-derived-mode paragraph-indent-text-mode text-mode "Parindent"
113 : "Major mode for editing text, with leading spaces starting a paragraph.
114 : In this mode, you do not need blank lines between paragraphs
115 : when the first line of the following paragraph starts with whitespace.
116 : `paragraph-indent-minor-mode' provides a similar facility as a minor mode.
117 : Special commands:
118 : \\{text-mode-map}
119 : Turning on Paragraph-Indent Text mode runs the normal hooks
120 : `text-mode-hook' and `paragraph-indent-text-mode-hook'."
121 : :abbrev-table nil :syntax-table nil
122 0 : (paragraph-indent-minor-mode))
123 :
124 : (define-minor-mode paragraph-indent-minor-mode
125 : "Minor mode for editing text, with leading spaces starting a paragraph.
126 : In this mode, you do not need blank lines between paragraphs when the
127 : first line of the following paragraph starts with whitespace, as with
128 : `paragraph-indent-text-mode'.
129 : Turning on Paragraph-Indent minor mode runs the normal hook
130 : `paragraph-indent-text-mode-hook'."
131 : :initial-value nil
132 : ;; Change the definition of a paragraph start.
133 0 : (let ((ps-re "[ \t\n\f]\\|"))
134 0 : (if (eq t (compare-strings ps-re nil nil
135 0 : paragraph-start nil (length ps-re)))
136 0 : (if (not paragraph-indent-minor-mode)
137 0 : (set (make-local-variable 'paragraph-start)
138 0 : (substring paragraph-start (length ps-re))))
139 0 : (if paragraph-indent-minor-mode
140 0 : (set (make-local-variable 'paragraph-start)
141 0 : (concat ps-re paragraph-start)))))
142 : ;; Change the indentation function.
143 0 : (if paragraph-indent-minor-mode
144 0 : (add-function :override (local 'indent-line-function)
145 0 : #'indent-to-left-margin)
146 0 : (remove-function (local 'indent-line-function)
147 0 : #'indent-to-left-margin)))
148 :
149 : (defalias 'indented-text-mode 'text-mode)
150 :
151 : ;; This can be made a no-op once all modes that use text-mode-hook
152 : ;; are "derived" from text-mode. (As of 2015/04, and probably well before,
153 : ;; the only one I can find that doesn't so derive is rmail-edit-mode.)
154 : (defun text-mode-hook-identify ()
155 : "Mark that this mode has run `text-mode-hook'.
156 : This is how `toggle-text-mode-auto-fill' knows which buffers to operate on."
157 0 : (set (make-local-variable 'text-mode-variant) t))
158 :
159 : (defun toggle-text-mode-auto-fill ()
160 : "Toggle whether to use Auto Fill in Text mode and related modes.
161 : This command affects all buffers that use modes related to Text mode,
162 : both existing buffers and buffers that you subsequently create."
163 : (interactive)
164 0 : (let ((enable-mode (not (memq 'turn-on-auto-fill text-mode-hook))))
165 0 : (if enable-mode
166 0 : (add-hook 'text-mode-hook 'turn-on-auto-fill)
167 0 : (remove-hook 'text-mode-hook 'turn-on-auto-fill))
168 0 : (dolist (buffer (buffer-list))
169 0 : (with-current-buffer buffer
170 0 : (if (or (derived-mode-p 'text-mode) text-mode-variant)
171 0 : (auto-fill-mode (if enable-mode 1 0)))))
172 0 : (message "Auto Fill %s in Text modes"
173 0 : (if enable-mode "enabled" "disabled"))))
174 :
175 :
176 : (define-key facemenu-keymap "\eS" 'center-paragraph)
177 :
178 : (defun center-paragraph ()
179 : "Center each nonblank line in the paragraph at or after point.
180 : See `center-line' for more info."
181 : (interactive)
182 0 : (save-excursion
183 0 : (forward-paragraph)
184 0 : (or (bolp) (newline 1))
185 0 : (let ((end (point)))
186 0 : (backward-paragraph)
187 0 : (center-region (point) end))))
188 :
189 : (defun center-region (from to)
190 : "Center each nonblank line starting in the region.
191 : See `center-line' for more info."
192 : (interactive "r")
193 0 : (if (> from to)
194 0 : (let ((tem to))
195 0 : (setq to from from tem)))
196 0 : (save-excursion
197 0 : (save-restriction
198 0 : (narrow-to-region from to)
199 0 : (goto-char from)
200 0 : (while (not (eobp))
201 0 : (or (save-excursion (skip-chars-forward " \t") (eolp))
202 0 : (center-line))
203 0 : (forward-line 1)))))
204 :
205 : (define-key facemenu-keymap "\es" 'center-line)
206 :
207 : (defun center-line (&optional nlines)
208 : "Center the line point is on, within the width specified by `fill-column'.
209 : This means adjusting the indentation so that it equals
210 : the distance between the end of the text and `fill-column'.
211 : The argument NLINES says how many lines to center."
212 : (interactive "P")
213 0 : (if nlines (setq nlines (prefix-numeric-value nlines)))
214 0 : (while (not (eq nlines 0))
215 0 : (save-excursion
216 0 : (let ((lm (current-left-margin))
217 : line-length)
218 0 : (beginning-of-line)
219 0 : (delete-horizontal-space)
220 0 : (end-of-line)
221 0 : (delete-horizontal-space)
222 0 : (setq line-length (current-column))
223 0 : (if (> (- fill-column lm line-length) 0)
224 0 : (indent-line-to
225 0 : (+ lm (/ (- fill-column lm line-length) 2))))))
226 0 : (cond ((null nlines)
227 0 : (setq nlines 0))
228 0 : ((> nlines 0)
229 0 : (setq nlines (1- nlines))
230 0 : (forward-line 1))
231 0 : ((< nlines 0)
232 0 : (setq nlines (1+ nlines))
233 0 : (forward-line -1)))))
234 :
235 : (provide 'text-mode)
236 :
237 : ;;; text-mode.el ends here
|