spellr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +186 -0
  5. data/.ruby-version +1 -0
  6. data/.spellr.yml +23 -0
  7. data/.spellr_wordlists/dictionary.txt +120 -0
  8. data/.spellr_wordlists/english.txt +3 -0
  9. data/.spellr_wordlists/lorem.txt +4 -0
  10. data/.spellr_wordlists/ruby.txt +2 -0
  11. data/.travis.yml +7 -0
  12. data/Gemfile +8 -0
  13. data/Gemfile.lock +67 -0
  14. data/LICENSE.txt +21 -0
  15. data/README.md +64 -0
  16. data/Rakefile +8 -0
  17. data/bin/console +8 -0
  18. data/bin/fetch_wordlist/english +65 -0
  19. data/bin/fetch_wordlist/ruby +150 -0
  20. data/bin/setup +3 -0
  21. data/exe/spellr +5 -0
  22. data/lib/.spellr.yml +93 -0
  23. data/lib/spellr.rb +26 -0
  24. data/lib/spellr/check.rb +56 -0
  25. data/lib/spellr/cli.rb +205 -0
  26. data/lib/spellr/column_location.rb +49 -0
  27. data/lib/spellr/config.rb +105 -0
  28. data/lib/spellr/file.rb +27 -0
  29. data/lib/spellr/file_list.rb +45 -0
  30. data/lib/spellr/interactive.rb +191 -0
  31. data/lib/spellr/language.rb +104 -0
  32. data/lib/spellr/line_location.rb +29 -0
  33. data/lib/spellr/line_tokenizer.rb +181 -0
  34. data/lib/spellr/reporter.rb +27 -0
  35. data/lib/spellr/string_format.rb +43 -0
  36. data/lib/spellr/token.rb +83 -0
  37. data/lib/spellr/tokenizer.rb +72 -0
  38. data/lib/spellr/version.rb +5 -0
  39. data/lib/spellr/wordlist.rb +100 -0
  40. data/lib/spellr/wordlist_reporter.rb +21 -0
  41. data/spellr.gemspec +35 -0
  42. data/wordlist +2 -0
  43. data/wordlists/dockerfile.txt +21 -0
  44. data/wordlists/html.txt +340 -0
  45. data/wordlists/javascript.txt +64 -0
  46. data/wordlists/ruby.txt +2344 -0
  47. data/wordlists/shell.txt +2 -0
  48. metadata +217 -0
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'string_format'
4
+
5
+ module Spellr
6
+ class Reporter
7
+ include Spellr::StringFormat
8
+
9
+ attr_accessor :total
10
+
11
+ def initialize
12
+ @total = 0
13
+ end
14
+
15
+ def finish(checked)
16
+ puts "\n"
17
+ puts "#{pluralize 'file', checked} checked"
18
+ puts "#{pluralize 'error', total} found"
19
+ end
20
+
21
+ def call(token)
22
+ puts "#{aqua token.location} #{token.line.highlight(token.char_range).strip}"
23
+
24
+ self.total += 1
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'config'
4
+
5
+ module Spellr
6
+ module StringFormat
7
+ module_function
8
+
9
+ def pluralize(word, count)
10
+ "#{count} #{word}#{'s' if count != 1}"
11
+ end
12
+
13
+ def color_enabled?
14
+ return $stdout.tty? if Spellr.config.color.nil?
15
+
16
+ Spellr.config.color
17
+ end
18
+
19
+ def aqua(text)
20
+ return text unless Spellr::StringFormat.color_enabled?
21
+
22
+ "\e[36m#{text}#{normal}"
23
+ end
24
+
25
+ def normal(text = '')
26
+ return text unless Spellr::StringFormat.color_enabled?
27
+
28
+ "\e[0m#{text}"
29
+ end
30
+
31
+ def bold(text)
32
+ return text unless Spellr::StringFormat.color_enabled?
33
+
34
+ "\e[1;39m#{text}#{normal}"
35
+ end
36
+
37
+ def red(text)
38
+ return text unless Spellr::StringFormat.color_enabled?
39
+
40
+ "\e[1;31m#{text}#{normal}"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ # frozen string_literal: true
4
+
5
+ require_relative 'column_location'
6
+ require_relative 'string_format'
7
+
8
+ module Spellr
9
+ class Token < String
10
+ attr_reader :location, :line
11
+ def self.normalize(value)
12
+ return value.normalize if value.is_a?(Spellr::Token)
13
+
14
+ value.strip.downcase.unicode_normalize.tr('’', "'") + "\n"
15
+ end
16
+
17
+ def self.wrap(value)
18
+ return value if value.is_a?(Spellr::Token)
19
+
20
+ Spellr::Token.new(value || '')
21
+ end
22
+
23
+ def initialize(string, line: string, location: ColumnLocation.new)
24
+ @location = location
25
+ @line = line
26
+ super(string)
27
+ end
28
+
29
+ def strip
30
+ @strip ||= begin
31
+ lstripped = lstrip
32
+ new_column_location = lstripped_column_location(lstripped)
33
+ Token.new(lstripped.rstrip, line: line, location: new_column_location)
34
+ end
35
+ end
36
+
37
+ def lstripped_column_location(lstripped)
38
+ ColumnLocation.new(
39
+ byte_offset: bytesize - lstripped.bytesize,
40
+ char_offset: length - lstripped.length,
41
+ line_location: location.line_location
42
+ )
43
+ end
44
+
45
+ def normalize
46
+ @normalize ||= self.class.normalize(to_s)
47
+ end
48
+
49
+ def inspect
50
+ "#<#{self.class.name} #{to_s.inspect} @#{location}>"
51
+ end
52
+
53
+ def char_range
54
+ @char_range ||= location.char_offset...(location.char_offset + length)
55
+ end
56
+
57
+ def byte_range
58
+ @byte_range ||= location.byte_offset...(location.byte_offset + bytesize)
59
+ end
60
+
61
+ def coordinates
62
+ location.coordinates
63
+ end
64
+
65
+ def highlight(range = char_range)
66
+ "#{slice(0...(range.first))}#{Spellr::StringFormat.red slice(range)}#{slice(range.last..-1)}"
67
+ end
68
+
69
+ def replace(replacement)
70
+ ::File.open(file_name, 'r+') do |f|
71
+ body = f.read
72
+ body[location.absolute_char_offset...(location.absolute_char_offset + length)] = replacement
73
+ f.rewind
74
+ f.truncate(0)
75
+ f.write(body)
76
+ end
77
+ end
78
+
79
+ def file_name
80
+ location.file_name
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spellr'
4
+ require_relative 'token'
5
+ require_relative 'column_location'
6
+ require_relative 'line_location'
7
+ require_relative 'line_tokenizer'
8
+
9
+ module Spellr
10
+ class Tokenizer
11
+ attr_reader :file
12
+ attr_reader :start_at
13
+
14
+ attr_accessor :disabled
15
+ alias_method :disabled?, :disabled
16
+
17
+ def initialize(file, start_at: nil, skip_uri: true, skip_key: true)
18
+ # $stderr.puts start_at if start_at
19
+ @start_at = start_at || ColumnLocation.new(line_location: LineLocation.new(file))
20
+ @file = file.is_a?(StringIO) || file.is_a?(IO) ? file : ::File.new(file)
21
+ @file.pos = @start_at.line_location.byte_offset
22
+
23
+ @line_tokenizer = LineTokenizer.new(skip_uri: skip_uri, skip_key: skip_key)
24
+ end
25
+
26
+ def terms
27
+ enum_for(:each_term).to_a
28
+ end
29
+
30
+ def map(&block)
31
+ enum_for(:each_token).map(&block)
32
+ end
33
+
34
+ def each_term(&block)
35
+ each_line_with_offset do |line, line_number|
36
+ prepare_tokenizer_for_line(line, line_number).each_term(&block)
37
+ end
38
+ end
39
+
40
+ def each_token(&block) # rubocop:disable Metrics/AbcSize
41
+ char_offset = @start_at.line_location.char_offset
42
+ byte_offset = @start_at.line_location.byte_offset
43
+
44
+ each_line_with_offset do |line, line_number|
45
+ line_location = LineLocation.new(file, line_number, byte_offset: byte_offset, char_offset: char_offset)
46
+ char_offset += line.length
47
+ byte_offset += line.bytesize
48
+ line = Token.new(line, location: ColumnLocation.new(line_location: line_location))
49
+ prepare_tokenizer_for_line(line, line_number).each_token(&block)
50
+ end
51
+ end
52
+
53
+ def normalized_terms
54
+ enum_for(:each_term).lazy.map { |t| Token.normalize(t) }.uniq.sort
55
+ end
56
+
57
+ private
58
+
59
+ attr_reader :line_tokenizer
60
+
61
+ def each_line_with_offset(&block)
62
+ file.each_line.with_index(@start_at.line_number, &block)
63
+ end
64
+
65
+ def prepare_tokenizer_for_line(line, _line_number)
66
+ line_tokenizer.string = line
67
+ line_tokenizer.pos = 0
68
+ # line_tokenizer.pos = @start_at.byte_offset if line_number == @start_at.line_number
69
+ line_tokenizer
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spellr
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require_relative '../spellr'
5
+ require_relative 'token'
6
+
7
+ module Spellr
8
+ class Wordlist
9
+ class NotFound < Spellr::Error; end
10
+
11
+ include Enumerable
12
+
13
+ attr_reader :path, :name
14
+
15
+ def initialize(file, name: file)
16
+ path = @file = file
17
+ @path = Pathname.pwd.join('.spellr_wordlists').join(path).expand_path
18
+ @name = name
19
+ end
20
+
21
+ def each(&block)
22
+ raise_unless_exists?
23
+
24
+ @path.each_line(&block)
25
+ end
26
+
27
+ def inspect
28
+ "#<#{self.class.name}:#{@path}>"
29
+ end
30
+
31
+ # significantly faster than default Enumerable#include?
32
+ # requires terms to be sorted
33
+ def include?(term)
34
+ include_cache[Spellr::Token.normalize(term)]
35
+ end
36
+
37
+ def include_cache
38
+ @include_cache ||= Hash.new do |cache, term|
39
+ cache[term] = to_a.bsearch do |value|
40
+ term <=> value
41
+ end
42
+ end
43
+ end
44
+
45
+ def to_a
46
+ @to_a ||= super
47
+ end
48
+
49
+ def clean(file = @path)
50
+ require_relative 'tokenizer'
51
+ write(Spellr::Tokenizer.new(file, skip_uri: false, skip_key: false).normalized_terms.join)
52
+ end
53
+
54
+ def write(content)
55
+ @path.write(content)
56
+
57
+ clear_cache
58
+ end
59
+
60
+ def read
61
+ raise_unless_exists?
62
+
63
+ @path.read
64
+ end
65
+
66
+ def clear_cache
67
+ @to_a = nil
68
+ @include = nil
69
+ end
70
+
71
+ def exist?
72
+ @path.exist?
73
+ end
74
+
75
+ def add(term)
76
+ touch
77
+ term = Spellr::Token.normalize(term)
78
+ include_cache[term] = true
79
+ to_a << term
80
+ to_a.sort!
81
+ write(@to_a.join)
82
+ Spellr.config.clear_cache if to_a.length == 1
83
+ end
84
+
85
+ private
86
+
87
+ def touch
88
+ return if exist?
89
+
90
+ @path.dirname.mkpath
91
+ @path.write('')
92
+ end
93
+
94
+ def raise_unless_exists?
95
+ return if exist?
96
+
97
+ raise Spellr::Wordlist::NotFound, "Wordlist file #{@file} doesn't exist at #{@path}"
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Spellr
6
+ class WordlistReporter
7
+ attr_reader :words
8
+
9
+ def initialize
10
+ @words = Set.new
11
+ end
12
+
13
+ def finish(_checked)
14
+ puts words.sort.join
15
+ end
16
+
17
+ def call(token)
18
+ words << token.normalize
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = ::File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'spellr/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'spellr'
9
+ spec.version = Spellr::VERSION
10
+ spec.authors = ['Dana Sherson']
11
+ spec.email = ['robot@dana.sh']
12
+
13
+ spec.summary = 'Spell check your source code'
14
+ spec.homepage = 'http://github.com/robotdana/spellr'
15
+ spec.license = 'MIT'
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(::File.expand_path(__dir__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = 'exe'
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ['lib']
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.17'
27
+ spec.add_development_dependency 'pry'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.0'
30
+ spec.add_development_dependency 'rspec-eventually'
31
+ spec.add_development_dependency 'rubocop'
32
+ spec.add_development_dependency 'rubocop-rspec'
33
+ spec.add_dependency 'fast_ignore'
34
+ spec.add_dependency 'parallel'
35
+ end
@@ -0,0 +1,2 @@
1
+ bar
2
+ foo
@@ -0,0 +1,21 @@
1
+ add
2
+ arg
3
+ as
4
+ cmd
5
+ copy
6
+ docker
7
+ dockerfile
8
+ entrypoint
9
+ env
10
+ expose
11
+ from
12
+ healthcheck
13
+ label
14
+ maintainer
15
+ onbuild
16
+ run
17
+ shell
18
+ stopsignal
19
+ user
20
+ volume
21
+ workdir
@@ -0,0 +1,340 @@
1
+ a
2
+ abbr
3
+ accept
4
+ accesskey
5
+ acronym
6
+ action
7
+ activedescendant
8
+ address
9
+ alert
10
+ alertdialog
11
+ align
12
+ allow
13
+ alt
14
+ applet
15
+ application
16
+ area
17
+ aria
18
+ article
19
+ aside
20
+ async
21
+ atomic
22
+ audio
23
+ autocapitalize
24
+ autocomplete
25
+ autofocus
26
+ autoplay
27
+ b
28
+ banner
29
+ base
30
+ basefont
31
+ bdi
32
+ bdo
33
+ bgcolor
34
+ bgsound
35
+ big
36
+ blink
37
+ blockquote
38
+ body
39
+ br
40
+ buffered
41
+ busy
42
+ button
43
+ canvas
44
+ caption
45
+ cell
46
+ center
47
+ challenge
48
+ charset
49
+ checkbox
50
+ checked
51
+ cite
52
+ class
53
+ code
54
+ codebase
55
+ col
56
+ colcount
57
+ colgroup
58
+ colindex
59
+ color
60
+ cols
61
+ colspan
62
+ columnheader
63
+ combobox
64
+ command
65
+ complementary
66
+ content
67
+ contenteditable
68
+ contentinfo
69
+ contextmenu
70
+ controls
71
+ coords
72
+ crossorigin
73
+ csp
74
+ current
75
+ data
76
+ datalist
77
+ datetime
78
+ dd
79
+ decoding
80
+ default
81
+ defer
82
+ definition
83
+ del
84
+ describedby
85
+ details
86
+ dfn
87
+ dialog
88
+ dir
89
+ directory
90
+ dirname
91
+ disabled
92
+ div
93
+ dl
94
+ document
95
+ download
96
+ draggable
97
+ dropeffect
98
+ dropzone
99
+ dt
100
+ element
101
+ em
102
+ embed
103
+ enctype
104
+ equiv
105
+ errormessage
106
+ expanded
107
+ feed
108
+ fieldset
109
+ figcaption
110
+ figure
111
+ flowto
112
+ font
113
+ footer
114
+ for
115
+ form
116
+ formaction
117
+ frame
118
+ frameset
119
+ grabbed
120
+ grid
121
+ gridcell
122
+ group
123
+ h
124
+ haspopup
125
+ head
126
+ header
127
+ headers
128
+ heading
129
+ height
130
+ hgroup
131
+ hidden
132
+ high
133
+ hr
134
+ href
135
+ hreflang
136
+ html
137
+ http
138
+ i
139
+ icon
140
+ id
141
+ iframe
142
+ image
143
+ img
144
+ importance
145
+ input
146
+ ins
147
+ integrity
148
+ invalid
149
+ isindex
150
+ ismap
151
+ itemprop
152
+ kbd
153
+ keygen
154
+ keyshortcuts
155
+ keytype
156
+ kind
157
+ label
158
+ labelledby
159
+ lang
160
+ language
161
+ lazyload
162
+ legend
163
+ level
164
+ li
165
+ link
166
+ list
167
+ listbox
168
+ listing
169
+ listitem
170
+ live
171
+ log
172
+ loop
173
+ low
174
+ main
175
+ manifest
176
+ map
177
+ mark
178
+ marquee
179
+ math
180
+ max
181
+ maxlength
182
+ media
183
+ menu
184
+ menubar
185
+ menuitem
186
+ menuitemcheckbox
187
+ menuitemradio
188
+ meta
189
+ meter
190
+ method
191
+ min
192
+ minlength
193
+ modal
194
+ multicol
195
+ multiline
196
+ multiple
197
+ multiselectable
198
+ muted
199
+ name
200
+ nav
201
+ navigation
202
+ nextid
203
+ nobr
204
+ noembed
205
+ noframes
206
+ none
207
+ noscript
208
+ note
209
+ novalidate
210
+ object
211
+ ol
212
+ open
213
+ optgroup
214
+ optimum
215
+ option
216
+ orientation
217
+ output
218
+ owns
219
+ p
220
+ param
221
+ pattern
222
+ picture
223
+ ping
224
+ placeholder
225
+ plaintext
226
+ posinset
227
+ poster
228
+ pre
229
+ preload
230
+ presentation
231
+ pressed
232
+ progress
233
+ progressbar
234
+ q
235
+ radio
236
+ radiogroup
237
+ rb
238
+ readonly
239
+ referrerpolicy
240
+ region
241
+ rel
242
+ relevant
243
+ required
244
+ reversed
245
+ role
246
+ roledescription
247
+ row
248
+ rowcount
249
+ rowgroup
250
+ rowheader
251
+ rowindex
252
+ rows
253
+ rowspan
254
+ rp
255
+ rt
256
+ rtc
257
+ ruby
258
+ s
259
+ samp
260
+ sandbox
261
+ scope
262
+ scoped
263
+ script
264
+ scrollbar
265
+ search
266
+ searchbox
267
+ section
268
+ select
269
+ selected
270
+ separator
271
+ setsize
272
+ shadow
273
+ shape
274
+ size
275
+ sizes
276
+ slider
277
+ slot
278
+ small
279
+ sort
280
+ source
281
+ spacer
282
+ span
283
+ spellcheck
284
+ spinbutton
285
+ src
286
+ srcdoc
287
+ srclang
288
+ srcset
289
+ start
290
+ status
291
+ step
292
+ strike
293
+ strong
294
+ style
295
+ sub
296
+ summary
297
+ sup
298
+ switch
299
+ tab
300
+ tabindex
301
+ table
302
+ tablist
303
+ tabpanel
304
+ target
305
+ tbody
306
+ td
307
+ template
308
+ term
309
+ textarea
310
+ textbox
311
+ tfoot
312
+ th
313
+ thead
314
+ time
315
+ timer
316
+ title
317
+ toolbar
318
+ tooltip
319
+ tr
320
+ track
321
+ translate
322
+ tree
323
+ treegrid
324
+ treeitem
325
+ tt
326
+ type
327
+ u
328
+ ul
329
+ usemap
330
+ value
331
+ valuemax
332
+ valuemin
333
+ valuenow
334
+ valuetext
335
+ var
336
+ video
337
+ wbr
338
+ width
339
+ wrap
340
+ xmp