rdoc-spellcheck 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/.autotest +17 -0
- data/.gemtest +0 -0
- data/History.rdoc +5 -0
- data/Manifest.txt +8 -0
- data/README.rdoc +80 -0
- data/Rakefile +23 -0
- data/lib/rdoc/discover.rb +2 -0
- data/lib/rdoc/generator/spellcheck.rb +714 -0
- data/test/test_rdoc_generator_spellcheck.rb +655 -0
- metadata +162 -0
- metadata.gz.sig +0 -0
data.tar.gz.sig
ADDED
Binary file
|
data/.autotest
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
|
5
|
+
Autotest.add_hook :initialize do |at|
|
6
|
+
at.testlib = 'minitest/autorun'
|
7
|
+
at.add_exception '.git'
|
8
|
+
|
9
|
+
def at.path_to_classname s
|
10
|
+
sep = File::SEPARATOR
|
11
|
+
f = s.sub(/^test#{sep}/, '').sub(/\.rb$/, '').split(sep)
|
12
|
+
f = f.map { |path| path.split(/_|(\d+)/).map { |seg| seg.capitalize }.join }
|
13
|
+
f = f.map { |path| path =~ /^Test/ ? path : "Test#{path}" }
|
14
|
+
f.join('::').gsub('Rdoc', 'RDoc')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
data/.gemtest
ADDED
File without changes
|
data/History.rdoc
ADDED
data/Manifest.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
= rdoc-spellcheck
|
2
|
+
|
3
|
+
home :: https://github.com/drbrain/rdoc-spellcheck
|
4
|
+
rdoc :: http://docs.seattlerb.org/rdoc-spellcheck
|
5
|
+
bugs :: https://github.com/drbrain/rdoc-spellcheck/issues
|
6
|
+
|
7
|
+
== Description
|
8
|
+
|
9
|
+
rdoc-spellcheck checks your documentation for spelling errors. File, class,
|
10
|
+
and method names are automatically excluded from the results and you can add
|
11
|
+
your own words to the default word list.
|
12
|
+
|
13
|
+
== Features
|
14
|
+
|
15
|
+
* Checks documentation for pages, classes, modules, methods, constants, etc.
|
16
|
+
* Prints a report showing each misspelled word and correction suggestions
|
17
|
+
|
18
|
+
== Problems
|
19
|
+
|
20
|
+
rdoc-spellcheck depends on aspell for which development has ceased despite
|
21
|
+
messages to the contrary. I've submitted two pull requests to the
|
22
|
+
hunspell-ffi gem that should allow it to become a usable replacement for the
|
23
|
+
raspell gem. (The currently released hunspell-ffi does not appear to support
|
24
|
+
private or session dictionaries.)
|
25
|
+
|
26
|
+
For some projects it seems there are too many false positives for names and
|
27
|
+
programming words. I've reduced this somewhat by automatically including many
|
28
|
+
programming words in the session dictionary. One solution involves switching
|
29
|
+
to hunspell which appears to have better-maintained word dictionaries. The
|
30
|
+
other involves exploring an upper threshold for the edit distance between a
|
31
|
+
given word and its suggestion.
|
32
|
+
|
33
|
+
== Synopsis
|
34
|
+
|
35
|
+
$ rdoc -f spellcheck lib *.rdoc
|
36
|
+
|
37
|
+
== Requirements
|
38
|
+
|
39
|
+
* rdoc
|
40
|
+
* libaspell -- See https://github.com/evan/raspell/blob/master/README.rdoc for
|
41
|
+
installation instructions for aspell
|
42
|
+
|
43
|
+
== Install
|
44
|
+
|
45
|
+
sudo gem install rdoc-spellcheck
|
46
|
+
|
47
|
+
== Developers
|
48
|
+
|
49
|
+
After checking out the source, run:
|
50
|
+
|
51
|
+
$ rake newb
|
52
|
+
|
53
|
+
This task will install any missing dependencies, run the tests/specs,
|
54
|
+
and generate the RDoc.
|
55
|
+
|
56
|
+
== License
|
57
|
+
|
58
|
+
(The MIT License)
|
59
|
+
|
60
|
+
Copyright (c) Eric Hodel
|
61
|
+
|
62
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
63
|
+
a copy of this software and associated documentation files (the
|
64
|
+
'Software'), to deal in the Software without restriction, including
|
65
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
66
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
67
|
+
permit persons to whom the Software is furnished to do so, subject to
|
68
|
+
the following conditions:
|
69
|
+
|
70
|
+
The above copyright notice and this permission notice shall be
|
71
|
+
included in all copies or substantial portions of the Software.
|
72
|
+
|
73
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
74
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
75
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
76
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
77
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
78
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
79
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
80
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.plugin :minitest
|
7
|
+
Hoe.plugin :git
|
8
|
+
Hoe.plugin :travis
|
9
|
+
|
10
|
+
Hoe.spec 'rdoc-spellcheck' do
|
11
|
+
developer 'Eric Hodel', 'drbrain@segment7.net'
|
12
|
+
|
13
|
+
rdoc_locations <<
|
14
|
+
'docs.seattlerb.org:/data/www/docs.seattlerb.org/rdoc-spellcheck/'
|
15
|
+
|
16
|
+
# Too lazy to make Unicode Regexps work on Ruby 1.8 and 1.9
|
17
|
+
spec_extras['required_ruby_version'] = '>= 1.9.2'
|
18
|
+
|
19
|
+
dependency 'raspell', '~> 1.3'
|
20
|
+
dependency 'rdoc', '~> 3.12'
|
21
|
+
end
|
22
|
+
|
23
|
+
# vim: syntax=ruby
|
@@ -0,0 +1,714 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
require 'raspell'
|
4
|
+
|
5
|
+
##
|
6
|
+
# A spell checking generator for RDoc.
|
7
|
+
#
|
8
|
+
# This generator creates a report of misspelled words. You can use it to find
|
9
|
+
# when you acidentally make a typo. For example, this line contains one.
|
10
|
+
|
11
|
+
class RDoc::Generator::Spellcheck
|
12
|
+
|
13
|
+
RDoc::RDoc.add_generator self
|
14
|
+
|
15
|
+
##
|
16
|
+
# This version of rdoc-spellcheck
|
17
|
+
|
18
|
+
VERSION = '1.0'
|
19
|
+
|
20
|
+
##
|
21
|
+
# A list of common words that aspell may not include, but are commonly used
|
22
|
+
# in ruby programs.
|
23
|
+
#--
|
24
|
+
# Please keep this list sorted in your pull requests
|
25
|
+
|
26
|
+
DEFAULT_WORDS = %w[
|
27
|
+
API
|
28
|
+
ArgumentError
|
29
|
+
CGI
|
30
|
+
DES
|
31
|
+
ECDSA
|
32
|
+
EOFError
|
33
|
+
ERb
|
34
|
+
Encoding::CompatibilityError
|
35
|
+
Encoding::ConverterNotFoundError
|
36
|
+
Encoding::InvalidByteSequenceError
|
37
|
+
Encoding::UndefinedConversionError
|
38
|
+
EncodingError
|
39
|
+
Errno::E2BIG
|
40
|
+
Errno::EACCES
|
41
|
+
Errno::EADDRINUSE
|
42
|
+
Errno::EADDRNOTAVAIL
|
43
|
+
Errno::EAFNOSUPPORT
|
44
|
+
Errno::EAGAIN
|
45
|
+
Errno::EALREADY
|
46
|
+
Errno::EAUTH
|
47
|
+
Errno::EBADF
|
48
|
+
Errno::EBADMSG
|
49
|
+
Errno::EBADRPC
|
50
|
+
Errno::EBUSY
|
51
|
+
Errno::ECANCELED
|
52
|
+
Errno::ECHILD
|
53
|
+
Errno::ECONNABORTED
|
54
|
+
Errno::ECONNREFUSED
|
55
|
+
Errno::ECONNRESET
|
56
|
+
Errno::EDEADLK
|
57
|
+
Errno::EDESTADDRREQ
|
58
|
+
Errno::EDOM
|
59
|
+
Errno::EDQUOT
|
60
|
+
Errno::EEXIST
|
61
|
+
Errno::EFAULT
|
62
|
+
Errno::EFBIG
|
63
|
+
Errno::EFTYPE
|
64
|
+
Errno::EHOSTDOWN
|
65
|
+
Errno::EHOSTUNREACH
|
66
|
+
Errno::EIDRM
|
67
|
+
Errno::EILSEQ
|
68
|
+
Errno::EINPROGRESS
|
69
|
+
Errno::EINTR
|
70
|
+
Errno::EINVAL
|
71
|
+
Errno::EIO
|
72
|
+
Errno::EISCONN
|
73
|
+
Errno::EISDIR
|
74
|
+
Errno::ELOOP
|
75
|
+
Errno::EMFILE
|
76
|
+
Errno::EMLINK
|
77
|
+
Errno::EMSGSIZE
|
78
|
+
Errno::EMULTIHOP
|
79
|
+
Errno::ENAMETOOLONG
|
80
|
+
Errno::ENEEDAUTH
|
81
|
+
Errno::ENETDOWN
|
82
|
+
Errno::ENETRESET
|
83
|
+
Errno::ENETUNREACH
|
84
|
+
Errno::ENFILE
|
85
|
+
Errno::ENOATTR
|
86
|
+
Errno::ENOBUFS
|
87
|
+
Errno::ENODATA
|
88
|
+
Errno::ENODEV
|
89
|
+
Errno::ENOENT
|
90
|
+
Errno::ENOEXEC
|
91
|
+
Errno::ENOLCK
|
92
|
+
Errno::ENOLINK
|
93
|
+
Errno::ENOMEM
|
94
|
+
Errno::ENOMSG
|
95
|
+
Errno::ENOPROTOOPT
|
96
|
+
Errno::ENOSPC
|
97
|
+
Errno::ENOSR
|
98
|
+
Errno::ENOSTR
|
99
|
+
Errno::ENOSYS
|
100
|
+
Errno::ENOTBLK
|
101
|
+
Errno::ENOTCONN
|
102
|
+
Errno::ENOTDIR
|
103
|
+
Errno::ENOTEMPTY
|
104
|
+
Errno::ENOTRECOVERABLE
|
105
|
+
Errno::ENOTSOCK
|
106
|
+
Errno::ENOTSUP
|
107
|
+
Errno::ENOTTY
|
108
|
+
Errno::ENXIO
|
109
|
+
Errno::EOPNOTSUPP
|
110
|
+
Errno::EOVERFLOW
|
111
|
+
Errno::EOWNERDEAD
|
112
|
+
Errno::EPERM
|
113
|
+
Errno::EPFNOSUPPORT
|
114
|
+
Errno::EPIPE
|
115
|
+
Errno::EPROCLIM
|
116
|
+
Errno::EPROCUNAVAIL
|
117
|
+
Errno::EPROGMISMATCH
|
118
|
+
Errno::EPROGUNAVAIL
|
119
|
+
Errno::EPROTO
|
120
|
+
Errno::EPROTONOSUPPORT
|
121
|
+
Errno::EPROTOTYPE
|
122
|
+
Errno::ERANGE
|
123
|
+
Errno::EREMOTE
|
124
|
+
Errno::EROFS
|
125
|
+
Errno::ERPCMISMATCH
|
126
|
+
Errno::ESHUTDOWN
|
127
|
+
Errno::ESOCKTNOSUPPORT
|
128
|
+
Errno::ESPIPE
|
129
|
+
Errno::ESRCH
|
130
|
+
Errno::ESTALE
|
131
|
+
Errno::ETIME
|
132
|
+
Errno::ETIMEDOUT
|
133
|
+
Errno::ETOOMANYREFS
|
134
|
+
Errno::ETXTBSY
|
135
|
+
Errno::EUSERS
|
136
|
+
Errno::EXDEV
|
137
|
+
Errno::NOERROR
|
138
|
+
Exception
|
139
|
+
FIXME
|
140
|
+
FQDN
|
141
|
+
FiberError
|
142
|
+
FileUtils
|
143
|
+
FloatDomainError
|
144
|
+
GPL
|
145
|
+
IETF
|
146
|
+
IOError
|
147
|
+
IndexError
|
148
|
+
Interrupt
|
149
|
+
KeyError
|
150
|
+
LoadError
|
151
|
+
LocalJumpError
|
152
|
+
MSDN
|
153
|
+
Math::DomainError
|
154
|
+
NTFS
|
155
|
+
NUL
|
156
|
+
NameError
|
157
|
+
NoMemoryError
|
158
|
+
NoMethodError
|
159
|
+
NoMethodError
|
160
|
+
NotImplementedError
|
161
|
+
O'Reilly
|
162
|
+
PHP
|
163
|
+
PNG
|
164
|
+
POSIX
|
165
|
+
PRNG
|
166
|
+
README
|
167
|
+
RangeError
|
168
|
+
RegexpError
|
169
|
+
RuntimeError
|
170
|
+
SIGABRT
|
171
|
+
SIGALRM
|
172
|
+
SIGBUS
|
173
|
+
SIGCHLD
|
174
|
+
SIGCLD
|
175
|
+
SIGCONT
|
176
|
+
SIGEMT
|
177
|
+
SIGEXIT
|
178
|
+
SIGFPE
|
179
|
+
SIGHUP
|
180
|
+
SIGILL
|
181
|
+
SIGINFO
|
182
|
+
SIGINT
|
183
|
+
SIGIO
|
184
|
+
SIGIOT
|
185
|
+
SIGKILL
|
186
|
+
SIGPIPE
|
187
|
+
SIGPROF
|
188
|
+
SIGQUIT
|
189
|
+
SIGSEGV
|
190
|
+
SIGSTOP
|
191
|
+
SIGSYS
|
192
|
+
SIGTERM
|
193
|
+
SIGTRAP
|
194
|
+
SIGTSTP
|
195
|
+
SIGTTIN
|
196
|
+
SIGTTOU
|
197
|
+
SIGURG
|
198
|
+
SIGUSR1
|
199
|
+
SIGUSR2
|
200
|
+
SIGVTALRM
|
201
|
+
SIGWINCH
|
202
|
+
SIGXCPU
|
203
|
+
SIGXFSZ
|
204
|
+
SMTP
|
205
|
+
SMTPS
|
206
|
+
ScriptError
|
207
|
+
SecurityError
|
208
|
+
SignalException
|
209
|
+
StandardError
|
210
|
+
StopIteration
|
211
|
+
StringIO
|
212
|
+
SyntaxError
|
213
|
+
SystemCallError
|
214
|
+
SystemExit
|
215
|
+
SystemStackError
|
216
|
+
ThreadError
|
217
|
+
TypeError
|
218
|
+
URI
|
219
|
+
UUCP
|
220
|
+
VCS
|
221
|
+
Wikipedia
|
222
|
+
XHTML
|
223
|
+
ZeroDivisionError
|
224
|
+
Zlib
|
225
|
+
accessor
|
226
|
+
accessors
|
227
|
+
argf
|
228
|
+
argv
|
229
|
+
ary
|
230
|
+
authenticators
|
231
|
+
baz
|
232
|
+
bom
|
233
|
+
bzip
|
234
|
+
canonicalization
|
235
|
+
cfg
|
236
|
+
cpp
|
237
|
+
crlf
|
238
|
+
cryptographic
|
239
|
+
csh
|
240
|
+
daemonizing
|
241
|
+
decrypt
|
242
|
+
decrypted
|
243
|
+
decrypter
|
244
|
+
decrypting
|
245
|
+
decrypts
|
246
|
+
deprecations
|
247
|
+
dereferenced
|
248
|
+
deserialization
|
249
|
+
deserialize
|
250
|
+
deserialized
|
251
|
+
deserializes
|
252
|
+
deserializing
|
253
|
+
dev
|
254
|
+
druby
|
255
|
+
dRuby
|
256
|
+
dup
|
257
|
+
duplexed
|
258
|
+
elsif
|
259
|
+
emacs
|
260
|
+
encodings
|
261
|
+
encrypter
|
262
|
+
endian
|
263
|
+
env
|
264
|
+
erb
|
265
|
+
finalized
|
266
|
+
finalizer
|
267
|
+
finalizers
|
268
|
+
globals
|
269
|
+
gsub
|
270
|
+
gzip
|
271
|
+
gzipped
|
272
|
+
http
|
273
|
+
https
|
274
|
+
img
|
275
|
+
incrementing
|
276
|
+
initializer
|
277
|
+
inlining
|
278
|
+
instantiation
|
279
|
+
irb
|
280
|
+
iso
|
281
|
+
ivar
|
282
|
+
kbd
|
283
|
+
klass
|
284
|
+
klasses
|
285
|
+
lang
|
286
|
+
lexing
|
287
|
+
lookup
|
288
|
+
lossy
|
289
|
+
mailto
|
290
|
+
matz
|
291
|
+
mktmpdir
|
292
|
+
natively
|
293
|
+
newb
|
294
|
+
nonces
|
295
|
+
perl
|
296
|
+
popup
|
297
|
+
proleptic
|
298
|
+
proxied
|
299
|
+
pwd
|
300
|
+
racc
|
301
|
+
radian
|
302
|
+
radians
|
303
|
+
radix
|
304
|
+
rbw
|
305
|
+
redistributions
|
306
|
+
refactor
|
307
|
+
refactored
|
308
|
+
reinitializes
|
309
|
+
resized
|
310
|
+
rhtml
|
311
|
+
rsync
|
312
|
+
serializable
|
313
|
+
startup
|
314
|
+
stderr
|
315
|
+
stdin
|
316
|
+
stdout
|
317
|
+
struct
|
318
|
+
succ
|
319
|
+
sudo
|
320
|
+
tmpdir
|
321
|
+
tokenizer
|
322
|
+
tokenizes
|
323
|
+
txt
|
324
|
+
unbuffered
|
325
|
+
unescape
|
326
|
+
unescapes
|
327
|
+
uniq
|
328
|
+
unmaintained
|
329
|
+
unmarshal
|
330
|
+
unmarshalled
|
331
|
+
unmarshalling
|
332
|
+
unordered
|
333
|
+
untagged
|
334
|
+
untrusted
|
335
|
+
utf
|
336
|
+
validator
|
337
|
+
validators
|
338
|
+
versioning
|
339
|
+
visibilities
|
340
|
+
www
|
341
|
+
yacc
|
342
|
+
]
|
343
|
+
|
344
|
+
##
|
345
|
+
# OptionParser validator for Aspell language dictionaries
|
346
|
+
|
347
|
+
SpellLanguage = Object.new
|
348
|
+
|
349
|
+
attr_accessor :minimum_word_length # :nodoc:
|
350
|
+
|
351
|
+
attr_reader :spell # :nodoc:
|
352
|
+
|
353
|
+
##
|
354
|
+
# Adds rdoc-spellcheck options to the rdoc command
|
355
|
+
|
356
|
+
def self.setup_options options
|
357
|
+
default_language, = ENV['LANG'].split '.'
|
358
|
+
|
359
|
+
options.spell_add_words = false
|
360
|
+
options.spell_language = default_language
|
361
|
+
options.spell_minimum_word_length = 4
|
362
|
+
options.spell_source_dir = Dir.pwd
|
363
|
+
options.quiet = true # suppress statistics
|
364
|
+
|
365
|
+
op = options.option_parser
|
366
|
+
|
367
|
+
op.accept SpellLanguage do |language|
|
368
|
+
found = Aspell.list_dicts.find do |dict|
|
369
|
+
dict.name == language
|
370
|
+
end
|
371
|
+
|
372
|
+
raise OptionParser::InvalidArgument,
|
373
|
+
"dictionary #{language} not installed" unless found
|
374
|
+
|
375
|
+
language
|
376
|
+
end
|
377
|
+
|
378
|
+
op.separator nil
|
379
|
+
op.separator 'Spellcheck options:'
|
380
|
+
op.separator nil
|
381
|
+
|
382
|
+
op.on('--spell-add-words [WORDLIST]',
|
383
|
+
'Adds words to the aspell personal wordlist.',
|
384
|
+
'The word list may be a comma-separated',
|
385
|
+
'list of words which must contain multiple',
|
386
|
+
'words, a file or empty to read words from',
|
387
|
+
'stdin') do |wordlist|
|
388
|
+
words = if wordlist.nil? then
|
389
|
+
$stdin.read.split
|
390
|
+
elsif wordlist =~ /,/ then
|
391
|
+
wordlist.split ','
|
392
|
+
else
|
393
|
+
open wordlist do |io|
|
394
|
+
io.read.split
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
options.spell_add_words = words
|
399
|
+
end
|
400
|
+
|
401
|
+
op.separator nil
|
402
|
+
|
403
|
+
op.on('--[no-]spell-aggregate-all',
|
404
|
+
'Show aggregate counts for all misspellings.') do |aggregate_all|
|
405
|
+
options.spell_aggregate_all = aggregate_all
|
406
|
+
end
|
407
|
+
|
408
|
+
op.separator nil
|
409
|
+
|
410
|
+
op.on('--spell-language=LANGUAGE', SpellLanguage,
|
411
|
+
'Language to use for spell checking.',
|
412
|
+
"The default language is #{default_language}") do |language|
|
413
|
+
options.spell_language = language
|
414
|
+
end
|
415
|
+
|
416
|
+
op.separator nil
|
417
|
+
|
418
|
+
op.on('--spell-minimum-word-length=LENGTH', Integer,
|
419
|
+
'Minimum length of a word to spell check.',
|
420
|
+
"The default is #{options.spell_minimum_word_length}") do |length|
|
421
|
+
options.spell_minimum_word_length = length
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
def initialize options # :not-new:
|
426
|
+
@options = options
|
427
|
+
|
428
|
+
@encoding = @options.encoding
|
429
|
+
@aggregate_all = @options.spell_aggregate_all
|
430
|
+
@minimum_word_length = @options.spell_minimum_word_length
|
431
|
+
@source_dir = @options.spell_source_dir
|
432
|
+
|
433
|
+
@misspellings = Hash.new 0
|
434
|
+
|
435
|
+
@spell = Aspell.new @options.spell_language, nil, nil, @encoding.name
|
436
|
+
@spell.suggestion_mode = Aspell::NORMAL
|
437
|
+
@spell.set_option 'run-together', 'true'
|
438
|
+
|
439
|
+
if words = @options.spell_add_words then
|
440
|
+
words.each do |word|
|
441
|
+
@spell.add_to_personal word
|
442
|
+
end
|
443
|
+
|
444
|
+
@spell.save_all_word_lists
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
##
|
449
|
+
# Adds +name+ to the dictionary, splitting the word on '_' (a character
|
450
|
+
# Aspell does not allow)
|
451
|
+
|
452
|
+
def add_name name
|
453
|
+
name.scan(/[a-z]+/i) do |part|
|
454
|
+
@spell.add_to_session part
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
##
|
459
|
+
# Returns a report of misspelled words in +comment+. The report contains
|
460
|
+
# each misspelled word and its offset in the comment's text.
|
461
|
+
|
462
|
+
def find_misspelled comment
|
463
|
+
report = []
|
464
|
+
|
465
|
+
comment.text.scan(/\p{L}[\p{L}']+\p{L}/i) do |word|
|
466
|
+
next if $&.length < @minimum_word_length
|
467
|
+
offset = $`.length # store
|
468
|
+
|
469
|
+
word = $` if word =~ /'s$/i
|
470
|
+
|
471
|
+
next if @spell.check word
|
472
|
+
|
473
|
+
offset = offset.zero? ? 0 : offset + 1
|
474
|
+
|
475
|
+
report << [word, offset]
|
476
|
+
|
477
|
+
@misspellings[word] += 1
|
478
|
+
end
|
479
|
+
|
480
|
+
report
|
481
|
+
end
|
482
|
+
|
483
|
+
##
|
484
|
+
# Creates the spelling report
|
485
|
+
|
486
|
+
def generate files
|
487
|
+
setup_dictionary
|
488
|
+
|
489
|
+
report = []
|
490
|
+
|
491
|
+
RDoc::TopLevel.all_classes_and_modules.each do |mod|
|
492
|
+
mod.comment_location.each do |comment, location|
|
493
|
+
report.concat misspellings_for(mod.definition, comment, location)
|
494
|
+
end
|
495
|
+
|
496
|
+
mod.each_include do |incl|
|
497
|
+
name = "#{incl.parent.full_name}.include #{incl.name}"
|
498
|
+
|
499
|
+
report.concat misspellings_for(name, incl.comment, incl.file)
|
500
|
+
end
|
501
|
+
|
502
|
+
mod.each_constant do |const|
|
503
|
+
# TODO add missing RDoc::Constant#full_name
|
504
|
+
name = const.parent ? const.parent.full_name : '(unknown)'
|
505
|
+
name = "#{name}::#{const.name}"
|
506
|
+
|
507
|
+
report.concat misspellings_for(name, const.comment, const.file)
|
508
|
+
end
|
509
|
+
|
510
|
+
mod.each_attribute do |attr|
|
511
|
+
name = "#{attr.parent.full_name}.#{attr.definition} :#{attr.name}"
|
512
|
+
|
513
|
+
report.concat misspellings_for(name, attr.comment, attr.file)
|
514
|
+
end
|
515
|
+
|
516
|
+
mod.each_method do |meth|
|
517
|
+
report.concat misspellings_for(meth.full_name, meth.comment, meth.file)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
RDoc::TopLevel.all_files.each do |file|
|
522
|
+
report.concat misspellings_for(nil, file.comment, file)
|
523
|
+
end
|
524
|
+
|
525
|
+
if @misspellings.empty? then
|
526
|
+
puts 'No misspellings found'
|
527
|
+
else
|
528
|
+
puts report.join "\n"
|
529
|
+
puts
|
530
|
+
|
531
|
+
num_width = @misspellings.values.max.to_s.length
|
532
|
+
order = @misspellings.sort_by do |word, count|
|
533
|
+
[-count, word]
|
534
|
+
end
|
535
|
+
|
536
|
+
order = order.first 10 unless @aggregate_all
|
537
|
+
|
538
|
+
puts 'Aggregate misspellings:'
|
539
|
+
order.each do |word, count|
|
540
|
+
puts "%*d %s" % [num_width, count, word]
|
541
|
+
end
|
542
|
+
|
543
|
+
total = @misspellings.values.inject :+
|
544
|
+
|
545
|
+
puts
|
546
|
+
puts "Total misspellings: #{total}"
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
##
|
551
|
+
# Determines the line and column of the misspelling in +comment+ at +offset+
|
552
|
+
# in the +file+.
|
553
|
+
|
554
|
+
def location_of text, offset, file
|
555
|
+
last_newline = text[0, offset].rindex "\n"
|
556
|
+
start_of_line = last_newline ? last_newline + 1 : 0
|
557
|
+
|
558
|
+
line_text = text[start_of_line..offset]
|
559
|
+
|
560
|
+
full_path = File.expand_path file.absolute_name, @source_dir
|
561
|
+
|
562
|
+
file_content = RDoc::Encoding.read_file full_path, @encoding
|
563
|
+
|
564
|
+
raise "[bug] Unable to read #{full_path}" unless file_content
|
565
|
+
|
566
|
+
file_content.each_line.with_index do |line, index|
|
567
|
+
if line =~ /#{Regexp.escape line_text}/ then
|
568
|
+
column = $`.length + line_text.length
|
569
|
+
return index, column
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
# TODO typos in include file
|
574
|
+
|
575
|
+
nil
|
576
|
+
end
|
577
|
+
|
578
|
+
##
|
579
|
+
# Returns a report of misspellings the +comment+ at +location+ for
|
580
|
+
# documentation item +name+
|
581
|
+
|
582
|
+
def misspellings_for name, comment, location
|
583
|
+
out = []
|
584
|
+
|
585
|
+
return out if comment.empty?
|
586
|
+
|
587
|
+
misspelled = find_misspelled comment
|
588
|
+
|
589
|
+
return out if misspelled.empty?
|
590
|
+
|
591
|
+
if name then
|
592
|
+
out << "#{name} in #{location.full_name}:"
|
593
|
+
else
|
594
|
+
out << "In #{location.full_name}:"
|
595
|
+
end
|
596
|
+
|
597
|
+
out << nil
|
598
|
+
|
599
|
+
out.concat misspelled.flat_map { |word, offset|
|
600
|
+
suggestion = suggestion_text comment.text, word, offset
|
601
|
+
line, column = location_of word, offset, location
|
602
|
+
|
603
|
+
if line then
|
604
|
+
["#{location.absolute_name}:#{line}:#{column}", suggestion]
|
605
|
+
else
|
606
|
+
["(via include)", suggestion]
|
607
|
+
end
|
608
|
+
}
|
609
|
+
|
610
|
+
out
|
611
|
+
end
|
612
|
+
|
613
|
+
##
|
614
|
+
# Adds file names, class names, module names, method names, etc. from the
|
615
|
+
# documentation tree to the session spelling dictionary.
|
616
|
+
|
617
|
+
def setup_dictionary
|
618
|
+
DEFAULT_WORDS.each do |word|
|
619
|
+
add_name word
|
620
|
+
end
|
621
|
+
|
622
|
+
RDoc::TopLevel.all_classes_and_modules.each do |mod|
|
623
|
+
add_name mod.name
|
624
|
+
|
625
|
+
mod.each_include do |incl|
|
626
|
+
add_name incl.name
|
627
|
+
end
|
628
|
+
|
629
|
+
mod.each_constant do |const|
|
630
|
+
add_name const.name
|
631
|
+
end
|
632
|
+
|
633
|
+
mod.each_attribute do |attr|
|
634
|
+
add_name attr.name
|
635
|
+
end
|
636
|
+
|
637
|
+
mod.each_method do |meth|
|
638
|
+
add_name meth.name
|
639
|
+
add_name meth.params if meth.params
|
640
|
+
add_name meth.block_params if meth.block_params
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
RDoc::TopLevel.all_files.each do |file|
|
645
|
+
file.absolute_name.split(%r%[/\\.]%).each do |part|
|
646
|
+
add_name part
|
647
|
+
end
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
##
|
652
|
+
# Creates suggestion text for the misspelled +word+ at +offset+ in +text+
|
653
|
+
|
654
|
+
def suggestion_text text, word, offset
|
655
|
+
prefix = offset - 10
|
656
|
+
prefix = 0 if prefix < 0
|
657
|
+
|
658
|
+
text =~ /\A.{#{prefix}}(.{0,10})#{Regexp.escape word}(.{0,10})/m
|
659
|
+
|
660
|
+
before = "#{prefix.zero? ? nil : '...'}#{$1}"
|
661
|
+
after = "#{$2}#{$2.length < 10 ? nil : '...'}"
|
662
|
+
|
663
|
+
highlight = "\e[1;31m#{word}\e[m"
|
664
|
+
|
665
|
+
suggestions = @spell.suggest(word).first 5
|
666
|
+
|
667
|
+
<<-TEXT
|
668
|
+
"#{before}#{highlight}#{after}"
|
669
|
+
|
670
|
+
"#{word}" suggestions:
|
671
|
+
\t#{suggestions.join ', '}
|
672
|
+
|
673
|
+
TEXT
|
674
|
+
rescue => e
|
675
|
+
$stderr.puts "[bug] #{e.class}: #{e.message}"
|
676
|
+
$stderr.puts
|
677
|
+
$stderr.puts "word: #{word}"
|
678
|
+
$stderr.puts "offset: #{offset}"
|
679
|
+
$stderr.puts ">>>> start text <<<<\n#{text}\n>>>>> end text <<<<<"
|
680
|
+
raise
|
681
|
+
end
|
682
|
+
|
683
|
+
end
|
684
|
+
|
685
|
+
class RDoc::Options
|
686
|
+
|
687
|
+
##
|
688
|
+
# Enables addition of words to the personal wordlist
|
689
|
+
|
690
|
+
attr_accessor :spell_add_words
|
691
|
+
|
692
|
+
##
|
693
|
+
# Display all found misspellings instead of the top ten.
|
694
|
+
|
695
|
+
attr_accessor :spell_aggregate_all
|
696
|
+
|
697
|
+
##
|
698
|
+
# The Aspell dictionary language to use. Defaults to the language in the
|
699
|
+
# LANG environment variable.
|
700
|
+
|
701
|
+
attr_accessor :spell_language
|
702
|
+
|
703
|
+
##
|
704
|
+
# The minimum length of a word for spell checking.
|
705
|
+
|
706
|
+
attr_accessor :spell_minimum_word_length
|
707
|
+
|
708
|
+
##
|
709
|
+
# The directory spellcheck was run from which contains all the source files.
|
710
|
+
|
711
|
+
attr_accessor :spell_source_dir
|
712
|
+
|
713
|
+
end
|
714
|
+
|