Revisión | 4b95b5b6d767f14d17265b6c81804fe36c8b661e (tree) |
---|---|
Tiempo | 2010-12-02 20:39:40 |
Autor | lorenzo |
Commiter | lorenzo |
I added another file needed to obtain support for multiple dictionaries in latex documents.
@@ -0,0 +1,445 @@ | ||
1 | +;; ispell-multi.el -- multiple ispell processes and multiple flyspell languages | |
2 | +;; | |
3 | +;; Copyright (C) 2005 P J Heslin | |
4 | +;; | |
5 | +;; Author: Peter Heslin <p.j.heslin@dur.ac.uk> | |
6 | +;; URL: http://www.dur.ac.uk/p.j.heslin/Software/Emacs/Download/ispell-multi.el | |
7 | +;; Version: 1.2 | |
8 | +;; | |
9 | +;; This program is free software; you can redistribute it and/or modify | |
10 | +;; it under the terms of the GNU General Public License as published by | |
11 | +;; the Free Software Foundation; either version 2, or (at your option) | |
12 | +;; any later version. | |
13 | +;; | |
14 | +;; This program is distributed in the hope that it will be useful, | |
15 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | +;; GNU General Public License for more details. | |
18 | +;; | |
19 | +;; If you do not have a copy of the GNU General Public License, you | |
20 | +;; can obtain one by writing to the Free Software Foundation, Inc., 59 | |
21 | +;; Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
22 | + | |
23 | +;;; Overview: | |
24 | +;; | |
25 | +;; ispell-multi.el enables Emacs to keep a number of ispell processes | |
26 | +;; alive in order to spell-check text efficiently in multiple | |
27 | +;; languages, and it provides a hook that tells flyspell to switch | |
28 | +;; languages depending on the value of a particular text property. | |
29 | +;; | |
30 | +;; Normally, ispell.el only ever keeps one ispell/aspell process | |
31 | +;; alive. So if you have one buffer in which an English local | |
32 | +;; dictionary is used and another in which a German dictionary is | |
33 | +;; used, the ispell process will be killed and restarted every time | |
34 | +;; you run ispell in the other buffer. This is not really a problem, | |
35 | +;; since doing a spellcheck is infrequent and slow anyway. If you are | |
36 | +;; using flyspell-mode, however, the ispell process will be restarted | |
37 | +;; every time you switch buffers. Even this may not matter too much | |
38 | +;; to many people, since switching buffers is also a somewhat slow | |
39 | +;; operation. | |
40 | +;; | |
41 | +;; Where the need for multiple ispell processes becomes really acute | |
42 | +;; is in buffers that have multiple languages in them and a way of | |
43 | +;; telling flyspell to switch local dictionaries depending on where | |
44 | +;; point is. In this case, the starting and stopping of ispell | |
45 | +;; processes very visibly impedes the fluid movement of the cursor. | |
46 | +;; | |
47 | +;; I have written two packages that provide this sort of behavior. | |
48 | +;; One is flyspell-xml-lang.el, which tells flyspell what the local | |
49 | +;; language is in xml files depending on xml:lang attributes, and | |
50 | +;; another is flyspell-babel.el, which does the same with Babel | |
51 | +;; commands in LaTeX files. | |
52 | +;; | |
53 | +;; The present package modifies ispell.el (via defadvice) so that | |
54 | +;; multiple ispell processes are kept alive to check different | |
55 | +;; languages. It requires version 3.6 of ispell, so users of Emacs | |
56 | +;; 21.3 and earlier will have to upgrade. This has only been tested | |
57 | +;; with GNU Emacs. | |
58 | +;; | |
59 | +;; To install this package, just put it somewhere in your load-path | |
60 | +;; and put a (require 'ispell-multi) statement in your .emacs file. | |
61 | + | |
62 | +;;; Using ispell-multi | |
63 | +;; | |
64 | +;; If all you want to do is to change the behavior of ispell so that | |
65 | +;; it uses multiple ispell processes for different buffers, then | |
66 | +;; (require 'ispell-multi) is all you need to do. The rest of this | |
67 | +;; section is for those who want to modify flyspell to switch | |
68 | +;; languages within a buffer. See flyspell-xml-lang.el and | |
69 | +;; flyspell-babel.el for examples of the usage of all of the | |
70 | +;; facilities described below. | |
71 | +;; | |
72 | +;; Flyspell-mode provides a hook that runs before checking each word; | |
73 | +;; this allows you to change the value of ispell-local-dictionary to a | |
74 | +;; different language, depending on the context. If you have a | |
75 | +;; package that parses a buffer and figures out what languages are in | |
76 | +;; it and where they are, you can tell flyspell about it by setting | |
77 | +;; the text property `ispell-multi-lang' to the correct ispell | |
78 | +;; language (this can be any value that ispell-change-dictionary | |
79 | +;; accepts). Your package should set the value of the buffer-local | |
80 | +;; variable `flyspell-generic-check-word-predicate' to the symbol | |
81 | +;; `ispell-multi-verify'; do this *after* you have turned on | |
82 | +;; flyspell-mode. | |
83 | + | |
84 | +;; If you the set the buffer-local variable | |
85 | +;; `ispell-multi-nil-callback' to a symbol, the associated | |
86 | +;; function will be called every time flyspell is called to check a | |
87 | +;; word, and the `ispell-multi-lang' text property returns nil. This | |
88 | +;; function can be used to parse the buffer incrementally and set the | |
89 | +;; text-property lazily as the user moves through the buffer. | |
90 | +;; | |
91 | +;; Since the parser callback is only invoked when the text-property is | |
92 | +;; nil, there is a possibility that the already-set text-property and | |
93 | +;; the changed contents of the buffer will get out of sync. To fix | |
94 | +;; this you will probably also want to arrange for the parser to be | |
95 | +;; run by an idle-timer. You can arrange for this by calling the | |
96 | +;; function `ispell-multi-idler-setup' with a single argument, giving | |
97 | +;; the delay. This will run the function specified by | |
98 | +;; `ispell-multi-idler-callback' after the specified idle time. | |
99 | +;; | |
100 | +;; If you want to indicate that some text should not be spell-checked, | |
101 | +;; set the `ispell-multi-lang' text property to the string "void". To | |
102 | +;; indicate a reversion to the default ispell dictionary, use the | |
103 | +;; string "default". | |
104 | + | |
105 | +;;; Aspell vs. Ispell | |
106 | +;; | |
107 | +;; ispell.el and ispell-multi.el will work happily with aspell instead | |
108 | +;; of ispell, since the former can emulate the latter. Aspell has | |
109 | +;; many advantages over ispell, including a very large selection of | |
110 | +;; language dictionaries, and it is much better able to suggest the | |
111 | +;; correct spelling (which is quite handy when using | |
112 | +;; flyspell-auto-correct-previous-word). If you prefer to use aspell, | |
113 | +;; you can put the following into your .emacs: | |
114 | +;; | |
115 | +;; (setq ispell-program-name "aspell") | |
116 | +;; (setq ispell-really-aspell t) | |
117 | +;; (setq ispell-extra-args '("--sug-mode=fast")) | |
118 | +;; | |
119 | +;; The first two lines tell ispell.el to run aspell instead of ispell, | |
120 | +;; and the third line tells aspell not to use its default algorithm | |
121 | +;; for suggesting spellings, but to use a faster one; the default is | |
122 | +;; very accurate, but can be a bit slow for use with flyspell. If | |
123 | +;; this is not fast enough, try "ultra" instead of "fast" (but even | |
124 | +;; ultra mode is still two times slower than ispell). | |
125 | +;; | |
126 | +;; If you are installing a new aspell dictionary that ispell.el does | |
127 | +;; not know about, you will have to add it to | |
128 | +;; ispell-local-dictionary-alist; see the documentation of | |
129 | +;; flyspell-xml-lang.el for an example. | |
130 | + | |
131 | +;;; Bugs and Limitations | |
132 | +;; | |
133 | +;; If you change ispell dictionaries by using the function | |
134 | +;; `ispell-change-dictionary', then an ispell process will be killed, | |
135 | +;; where it would not have been if you had simply set | |
136 | +;; ispell-local-dictionary. That's because this package just modifies | |
137 | +;; the way ispell deals with local variables like | |
138 | +;; ispell-local-dictionary; it doesn't touch the | |
139 | +;; ispell-change-dictionary function. Maybe it should. The necessary | |
140 | +;; ispell process will be re-started next time you need it, so this is | |
141 | +;; not really a bug so much as a slight performance issue. | |
142 | +;; | |
143 | +;; We try to share ispell processes between buffers, so that a single | |
144 | +;; process can service all buffers or regions in a given language. | |
145 | +;; But if you put buffer-local variables that modify the behavior of | |
146 | +;; ispell for a given buffer (such as LocalWords), then that buffer's | |
147 | +;; ispell processes will not be shared. | |
148 | +;; | |
149 | +;; flyspell-large-region, which is the fast mode of flyspell that it | |
150 | +;; uses when checking the entirety of a large buffer, does not work at | |
151 | +;; all, since it depends on launching a single ispell process for this | |
152 | +;; purpose and so cannot cope with multiple languages. For this | |
153 | +;; reason, flyspell-large-region should be disabled in buffers using | |
154 | +;; this package. | |
155 | +;; | |
156 | +;; It might have been nice to put in here the code to inspect a | |
157 | +;; text-property to find out the language of the text, so that ispell | |
158 | +;; (as opposed to flyspell) would obey the property and change | |
159 | +;; dictionary accordingly. This won't work, though, since | |
160 | +;; ispell-region works on a line-by-line basis, which would fail in | |
161 | +;; the case of a mid-line language-switch. | |
162 | + | |
163 | +;;; Changes | |
164 | +;; | |
165 | +;; 1.0 Pre-release | |
166 | +;; 1.1 Worked around ispell-current-dictionary / ispell-dictionary | |
167 | +;; inconsistency in Emacs CVS / stand-alone ispell.el | |
168 | +;; 1.2 Protect better against errors when switching to an undefined or | |
169 | +;; uninstalled dictionary -- errors in post-command-hook will | |
170 | +;; switch off flyspell, cua, etc. | |
171 | +;; 1.3 Changed process management so that an ispell process without | |
172 | +;; buffer-local modifications will only be killed when all buffers | |
173 | +;; that have used that process have been killed. | |
174 | + | |
175 | +(require 'ispell) | |
176 | +;; For Emacs 21.3, we have to use an updated ispell.el (3.6 or from | |
177 | +;; Emacs CVS), and for some reason we may have to load it again to get | |
178 | +;; ispell-dictionary-alist set properly. | |
179 | +(unless (assoc "english" ispell-dictionary-alist) | |
180 | + (load "ispell")) | |
181 | +;; For updated ispell.el with emacs < 21.3.5 | |
182 | +(when (not (fboundp 'set-process-query-on-exit-flag)) | |
183 | + (defun set-process-query-on-exit-flag (one two) ())) | |
184 | + | |
185 | +; In current Emacs CVS, the variable ispell-current-dictionary is used | |
186 | +; to indicate the dictionary associated with the current ispell | |
187 | +; process, while in the current, separately distributed version of | |
188 | +; ispell.el (3.7beta), this variable does not exist, and | |
189 | +; ispell-dictionary serves this purpose. | |
190 | +(defvar ispell-multi-current-dictionary-var | |
191 | + (if (boundp 'ispell-current-dictionary) | |
192 | + 'ispell-current-dictionary | |
193 | + 'ispell-dictionary)) | |
194 | + | |
195 | +(defvar ispell-multi-dict nil | |
196 | + "The language that this package thinks is current in this | |
197 | + buffer. We don't set ispell-local-dictionary directly, since | |
198 | + we want that to contain the default fall-back in case | |
199 | + ispell-multi-dict is not set.") | |
200 | +(make-variable-buffer-local 'ispell-multi-dict) | |
201 | + | |
202 | + | |
203 | +(defvar ispell-multi-lang-process nil | |
204 | + "Alist mapping languages to ispell processes. Only for | |
205 | + processes without any buffer-local modifications") | |
206 | + | |
207 | +(defvar ispell-multi-lang-process-local nil | |
208 | + "As ispell-multi-lang-process, but a buffer-local alist, to use | |
209 | + for processes with buffer-local modifications") | |
210 | +(make-variable-buffer-local 'ispell-multi-lang-process-local) | |
211 | + | |
212 | +(defvar ispell-multi-flyspell-verify-default nil | |
213 | + "The original value of `flyspell-generic-check-word-predicate', | |
214 | + before it was overridden in order to invoke this package; taken | |
215 | + from the the `flyspell-mode-predicate' property of the major | |
216 | + mode name.") | |
217 | +(make-variable-buffer-local 'ispell-multi-flyspell-verify-default) | |
218 | + | |
219 | +(defvar ispell-multi-nil-callback nil | |
220 | + "Buffer local variable that indicates a function to call when | |
221 | + flyspell is checking a word and the text property | |
222 | + `ispell-multi-lang' is nil. This function will normally set | |
223 | + that property at point and for some of the text in the | |
224 | + neighborhood") | |
225 | +(make-variable-buffer-local 'ispell-multi-nil-callback) | |
226 | + | |
227 | +(defvar ispell-multi-idler-callback nil | |
228 | + "Buffer local variable that indicates a function to call when | |
229 | + idle. Can be used to parse part or all of the buffer.") | |
230 | +(make-variable-buffer-local 'ispell-multi-idler-callback) | |
231 | + | |
232 | +(defvar ispell-multi-verbose nil | |
233 | + "If non-nil, print diagnostic messages about switching dictionaries") | |
234 | + | |
235 | +(defvar ispell-multi-valid-dictionary-list nil | |
236 | + "Cached value of ispell-valid-dictionary-list.") | |
237 | +(when (fboundp 'ispell-valid-dictionary-list) | |
238 | + (setq ispell-multi-valid-dictionary-list | |
239 | + (ispell-valid-dictionary-list))) | |
240 | + | |
241 | +(defvar ispell-multi-bad-language nil | |
242 | + "A list of languages that ispell has choked on. After the | |
243 | + first attempt, we stop trying to start ispell processes for | |
244 | + this language, since flyspell will try to do so incessantly and | |
245 | + cursor motion will get sluggish") | |
246 | + | |
247 | +;; Reset on re-load | |
248 | +(setq ispell-multi-bad-language nil) | |
249 | + | |
250 | +;; This is our hook into ispell.el. | |
251 | +(defadvice ispell-accept-buffer-local-defs (around ispell-multi-advice activate) | |
252 | + "Advice that changes ispell to enable multiple ispell processes." | |
253 | + ;; Bind ispell-local-dictionary | |
254 | + (let* ((ispell-local-dictionary | |
255 | + (if (or (equal ispell-multi-dict "void") | |
256 | + (equal ispell-multi-dict "default") | |
257 | + (null ispell-multi-dict)) | |
258 | + ispell-local-dictionary | |
259 | + ispell-multi-dict)) | |
260 | + (local-mods (ispell-multi-buffer-local-modifications-p)) | |
261 | + (alist (if local-mods | |
262 | + 'ispell-multi-lang-process-local | |
263 | + 'ispell-multi-lang-process)) | |
264 | + (stored-process (cdr (assoc ispell-local-dictionary (symbol-value alist))))) | |
265 | + | |
266 | + ;; Store the currently running process if we haven't already | |
267 | + (when (and ispell-process | |
268 | + (eq (process-status ispell-process) 'run)) | |
269 | + (when (not (rassq ispell-process (symbol-value alist))) | |
270 | + (set alist (cons (cons (symbol-value ispell-multi-current-dictionary-var) ispell-process) | |
271 | + (symbol-value alist)))) | |
272 | + ;; Make a note that this buffer has used this process | |
273 | + (when (not (memq (current-buffer) | |
274 | + (process-get ispell-process 'ispell-multi-buffers))) | |
275 | + (process-put ispell-process 'ispell-multi-buffers | |
276 | + (cons (current-buffer) | |
277 | + (process-get ispell-process 'ispell-multi-buffers))))) | |
278 | + | |
279 | + ;; Do we already have a process for this language? | |
280 | + (if (and stored-process | |
281 | + (eq (process-status stored-process) 'run)) | |
282 | + (progn | |
283 | + (setq ispell-process stored-process) | |
284 | + ;; When ispell-current-dictionary / ispell-dictionary is | |
285 | + ;; the same as ispell-local-dictionary, ispell.el will | |
286 | + ;; refrain from killing the process | |
287 | + (set ispell-multi-current-dictionary-var ispell-local-dictionary)) | |
288 | + | |
289 | + ;; This is to fool ispell into not killing the old process when | |
290 | + ;; it starts the new one. But we don't want a new process if | |
291 | + ;; the current one is correct, or if the default dict is void. | |
292 | + (unless (or (equal (symbol-value ispell-multi-current-dictionary-var) ispell-local-dictionary) | |
293 | + (equal ispell-local-dictionary "void")) | |
294 | + (setq ispell-process nil)) | |
295 | + | |
296 | + ;; Possibly start a new process | |
297 | + (unless (equal ispell-local-dictionary "void") ; ensure against error | |
298 | + ad-do-it)))) | |
299 | + | |
300 | + | |
301 | +(defun ispell-multi-kill-processes-hook () | |
302 | + "Kill orphaned ispell processes." | |
303 | + (dolist (proc (process-list)) | |
304 | + (let* ((old-list (process-get proc 'ispell-multi-buffers)) | |
305 | + (new-list (delete (current-buffer) old-list))) | |
306 | + (when (and old-list (not new-list) | |
307 | + (eq (process-status proc) 'run)) | |
308 | + (setq ispell-process proc) | |
309 | + (ispell-kill-ispell)))) | |
310 | + (ispell-multi-processes-alist-cleanup)) | |
311 | + | |
312 | +(add-hook 'kill-buffer-hook 'ispell-multi-kill-processes-hook) | |
313 | + | |
314 | +(defun ispell-multi-processes-alist-cleanup () | |
315 | + "Remove any defunct processes from the global alist" | |
316 | + (let ((newlist)) | |
317 | + (while ispell-multi-lang-process | |
318 | + (when (eq (process-status (cdar ispell-multi-lang-process)) 'run) | |
319 | + (setq newlist (cons (car ispell-multi-lang-process) newlist))) | |
320 | + (setq ispell-multi-lang-process (cdr ispell-multi-lang-process))) | |
321 | + (setq ispell-multi-lang-process (nreverse newlist)))) | |
322 | + | |
323 | +(defvar ispell-multi-local-regexp | |
324 | + (mapconcat 'regexp-quote (list ispell-dictionary-keyword | |
325 | + ispell-pdict-keyword | |
326 | + ispell-words-keyword) "\\|")) | |
327 | + | |
328 | +(defun ispell-multi-buffer-local-modifications-p () | |
329 | + (save-excursion | |
330 | + (goto-char (point-max)) | |
331 | + (re-search-backward ispell-multi-local-regexp nil t))) | |
332 | + | |
333 | +(defun ispell-multi-verify () | |
334 | + ;; Possibly initialize value of default function | |
335 | + (unless ispell-multi-flyspell-verify-default | |
336 | + (setq ispell-multi-flyspell-verify-default | |
337 | + (or (get major-mode 'flyspell-mode-predicate) | |
338 | + 'none))) | |
339 | + (let ((do-check t) | |
340 | + ;; NB. Adding text properties via the callback will call | |
341 | + ;; after-change-functions, to which flyspell adds a function | |
342 | + ;; that can trigger an infinite loop: flyspell-word changes | |
343 | + ;; the buffer, which can add a value to flyspell-changes, so | |
344 | + ;; that list never goes to nil | |
345 | + (after-change-functions nil)) | |
346 | + ;; Don't switch language if we're not supposed to check this bit anyway | |
347 | + (when (not (eq ispell-multi-flyspell-verify-default 'none)) | |
348 | + (setq do-check (funcall ispell-multi-flyspell-verify-default))) | |
349 | + (when do-check | |
350 | + (let* ((current-position (point)) | |
351 | + (lang (get-text-property current-position 'ispell-multi-lang))) | |
352 | + (when (and (not lang) | |
353 | + ispell-multi-nil-callback) | |
354 | + (ispell-multi-message "parsing ...") | |
355 | + (save-excursion | |
356 | + (funcall ispell-multi-nil-callback)) | |
357 | + (ispell-multi-message "finished parsing.") | |
358 | + (setq lang (get-text-property current-position 'ispell-multi-lang))) | |
359 | + (when lang | |
360 | + (unless (string= ispell-multi-dict lang) | |
361 | + (let ((old-lang ispell-multi-dict)) | |
362 | + (cond | |
363 | + ((string= lang "void") | |
364 | + (setq do-check nil) | |
365 | +; (ispell-multi-message "current dictionary is void: not checking") | |
366 | + ) | |
367 | + ((and ispell-multi-valid-dictionary-list | |
368 | + (member lang ispell-multi-valid-dictionary-list) | |
369 | + (not (member lang ispell-multi-bad-language))) | |
370 | + (ispell-multi-message (concat "dictionary changing to: " lang)) | |
371 | + (setq ispell-multi-dict lang) | |
372 | + ;; Be paranoid, since this can be called from a post-command hook | |
373 | + (condition-case nil | |
374 | + (ispell-accept-buffer-local-defs) | |
375 | + (error (progn | |
376 | + (ispell-multi-message | |
377 | + (concat "Error: ispell didn't like language " lang) t) | |
378 | + (setq ispell-multi-bad-language (cons lang ispell-multi-bad-language)) | |
379 | + (setq do-check nil) | |
380 | + (setq ispell-multi-dict old-lang))))) | |
381 | + (t | |
382 | + (ispell-multi-message | |
383 | + (concat "Warning: no dictionary defined for " lang)) | |
384 | + (setq do-check nil)))))))) | |
385 | + do-check)) | |
386 | + | |
387 | +(defvar ispell-multi-ticker 0) | |
388 | +(defvar ispell-multi-old-point 0) | |
389 | +(make-variable-buffer-local 'ispell-multi-ticker) | |
390 | +(make-variable-buffer-local 'ispell-multi-old-point) | |
391 | + | |
392 | +(defun ispell-multi-idler () | |
393 | + (when (and flyspell-mode | |
394 | + (eq flyspell-generic-check-word-predicate 'ispell-multi-verify) | |
395 | + (not (= (buffer-modified-tick) ispell-multi-ticker)) | |
396 | + (not (= (point) ispell-multi-old-point))) | |
397 | + (let ((old-lang (get-text-property (point) 'ispell-multi-lang)) | |
398 | + new-lang) | |
399 | + (when ispell-multi-idler-callback | |
400 | + (ispell-multi-message "parsing (idle) ...") | |
401 | + (let ((after-change-functions nil)) | |
402 | + (save-excursion | |
403 | + (funcall ispell-multi-idler-callback))) | |
404 | + (ispell-multi-message "finished parsing.") | |
405 | + (setq new-lang (get-text-property (point) 'ispell-multi-lang)) | |
406 | + (unless (or (equal old-lang new-lang) | |
407 | + (equal new-lang "void")) | |
408 | + (setq ispell-multi-dict new-lang) | |
409 | + (ispell-accept-buffer-local-defs))) | |
410 | + (setq ispell-multi-ticker (buffer-modified-tick)) | |
411 | + (setq ispell-multi-old-point (point))))) | |
412 | + | |
413 | +(defvar ispell-multi-idle-timer nil) | |
414 | +(defun ispell-multi-idler-setup (delay) | |
415 | + (unless ispell-multi-idle-timer | |
416 | + (setq ispell-multi-idle-timer | |
417 | + (run-with-idle-timer 5 t 'ispell-multi-idler)))) | |
418 | + | |
419 | +(defun ispell-multi-idler-cancel () | |
420 | + (cancel-timer ispell-multi-idle-timer) | |
421 | + (setq ispell-multi-idle-timer nil)) | |
422 | + | |
423 | +(defun ispell-multi-unhack-flyspell-modeline () | |
424 | + "Remove the flyspell modeline entry" | |
425 | + (setq minor-mode-alist | |
426 | + (delq (assq 'flyspell-mode minor-mode-alist) minor-mode-alist))) | |
427 | + | |
428 | +(defun ispell-multi-hack-flyspell-modeline () | |
429 | + "Add a modeline entry for flyspell that indicates the current | |
430 | + language in parentheses." | |
431 | + (ispell-multi-unhack-flyspell-modeline) | |
432 | + (setq minor-mode-alist | |
433 | + (cons '(flyspell-mode | |
434 | + (:eval | |
435 | + (let ((lang (get-text-property (point) 'ispell-multi-lang))) | |
436 | + (concat flyspell-mode-line-string | |
437 | + (when lang | |
438 | + (concat " (" (capitalize lang) ")")))))) minor-mode-alist))) | |
439 | + | |
440 | +(defun ispell-multi-message (mess &optional force) | |
441 | + (when (or ispell-multi-verbose force) | |
442 | + (message "ispell-multi -- %s" mess))) | |
443 | + | |
444 | +(provide 'ispell-multi) | |
445 | + |