1
#| ispell.jl -- ispell wrapper
3
$Id: ispell.jl,v 1.5 2000/09/10 20:03:17 john Exp $
5
Copyright (C) 2000 John Harper <john@dcs.warwick.ac.uk>
7
This file is part of librep.
9
librep is free software; you can redistribute it and/or modify it
10
under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2, or (at your option)
14
librep is distributed in the hope that it will be useful, but
15
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.
19
You should have received a copy of the GNU General Public License
20
along with librep; see the file COPYING. If not, write to
21
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24
(define-structure rep.util.ispell
31
ispell-add-word-to-dictionary
32
ispell-add-word-for-session
33
ispell-save-dictionary)
40
(defvar *ispell-program* "ispell"
41
"Filename of program used to start ispell(1).")
43
(defvar *ispell-options* nil
44
"List of options to pass to Ispell")
46
(defvar *ispell-dictionary* nil
47
"Name of dictionary to pass to Ispell, or nil for the default.")
49
(defvar *ispell-timeout* 5
50
"Seconds to wait for ispell output before giving up.")
52
(defvar *ispell-echo-output* nil
53
"Use for debugging only.")
56
"Subprocess that ispell is running in, or nil if ispell isn't running.")
58
(define process-busy nil
59
"When t, the process is being used to check a word, but not all
60
results have been received.")
63
"String sent by ispell identifying itself when it started executing.")
65
(define pending-output nil
66
"String of output received from ispell but not processed.")
68
(define line-callback (make-fluid nil)
69
"Function to call asynchronously with a single line of output from ispell.")
71
;;; Process management
73
;; Function to buffer output from Ispell
74
(define (output-filter output)
75
(when (integerp output)
76
(setq output (make-string 1 output)))
77
(and *ispell-echo-output*
79
(let ((print-escape t))
80
(format standard-error "Ispell: %S\n" output)))
81
(setq pending-output (concat pending-output output))
82
(while (and (fluid line-callback)
84
(string-match "\n" pending-output))
85
(let ((line (substring pending-output 0 (match-end))))
86
(setq pending-output (substring pending-output (match-end)))
87
((fluid line-callback) line))))
89
;; Start the process if it isn't already
90
(define (ispell-start)
92
(setq process (make-process output-filter))
93
(set-process-function process (lambda ()
95
(setq id-string nil)))
96
;; Use a pty if possible. This allow EOF to be sent via ^D
97
(set-process-connection-type process 'pty)
98
(apply start-process process *ispell-program* "-a"
99
(nconc (and *ispell-dictionary*
100
(list "-d" *ispell-dictionary*))
102
(setq pending-output nil)
103
(fluid-set line-callback nil)
104
(setq id-string (ispell-read-line))
105
(unless (string-match "ispell version" id-string 0 t)
107
(error "Ispell: %s" id-string))))
109
(define (ispell-stop)
110
"Kill any subprocesses being used internally to run Ispell."
111
(accept-process-output-1 process 0) ;in case the process already died
113
(ispell-save-dictionary)
114
(if (eq (process-connection-type process) 'pty)
116
;; Not so successful..
117
(interrupt-process process))
119
(while (and (accept-process-output-1 process *ispell-timeout*) process)
121
(interrupt-process process)
122
(kill-process process))
123
(setq counter (1+ counter))))))
125
;; Read one whole line from the process (including newline)
126
(define (ispell-read-line)
128
(let-fluids ((line-callback (lambda (l)
130
;; Only want the first line
131
(fluid-set line-callback nil))))
132
;; Flush any pending output
134
(while (and (not out) process
135
(not (accept-process-output-1 process *ispell-timeout*))))
136
(or out (error "Ispell timed out waiting for output")))))
138
;; put in the before-exit-hook
139
(define (before-exit)
143
(add-hook 'before-exit-hook before-exit)
145
;; Arbitrate access to the Ispell process, the mutex must be obtained
146
;; before sending a command that generates output. An error is signalled
147
;; if the process is busy
151
(error "Ispell process is busy!")
153
(setq process-busy t))
154
(setq process-busy nil)))
156
;; Check a word with Ispell. Returns the raw (single-line) output
157
;; see ispell(1) for details (under the -a option)
158
(define (ispell-word word)
163
(format process "%s\n" word)
164
(setq response (ispell-read-line))
165
(if (eq (aref response 0) ?\n)
166
;; This shouldn't happen
167
(error "Null output from Ispell")
168
;; Gobble following blank line
169
(setq tem (ispell-read-line))
170
(unless (eq (aref tem 0) ?\n)
171
(error "Non-null trailing line from Ispell"))))
175
;; return true if WORD is spelt correctly
176
(define (ispell-test-word word)
177
(let ((response (ispell-word word)))
178
(string-looking-at "^[*+-]" response)))
180
;;; Dictionary management
182
(define (ispell-set-dictionary dict-name)
183
"Set the name of the dictionary used by Ispell to DICT-NAME."
184
(setq *ispell-dictionary* dict-name)
188
(call-hook '*ispell-dictionary-changed*))
190
(define (ispell-add-word-to-dictionary word)
191
"Add the string WORD to your personal Ispell dictionary."
193
(format process "*%s\n" word)
194
(call-hook '*ispell-dictionary-changed*))
196
(define (ispell-add-word-for-session word)
197
"Add the string WORD to Ispell's per-session dictionary."
199
(format process "@%s\n" word)
200
(call-hook '*ispell-dictionary-changed*))
202
(define (ispell-save-dictionary)
203
"Make Ispell save the current personal dictionary to its file."
205
(write process "#\n"))))