clausewitz-spelling 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4014a78444b4229f57cc23e9bc8d02fbdb31acae
4
- data.tar.gz: b7fe59713b71f23c3ffed9d297c1af91a8d667b3
3
+ metadata.gz: 928fb347a0cf9627f9d5fe15bf41cdcde863bfc6
4
+ data.tar.gz: c953a1f747632c9068f953fb49d6f849cff88c9b
5
5
  SHA512:
6
- metadata.gz: 6f9f4a9c0cf05519b35122469c938a1a2bc99a059222c19007d86afc59a7bbdf9bcc9bcea510a9295df0c73886a45859e68cea4b19fcdb52cfcfd58b2c7dc843
7
- data.tar.gz: e247e1f39f9867fd518525860e67975e172474f0a23a754fa4412e66c35c42c504452650dc8dbb2bf2ea331e1e89d45e3077e9030a5282e8ffb9a1426b2c42ab
6
+ metadata.gz: 7d3a8fac9a0f8600406a7d6a074eeee3620da864ed4f5bfc2c8e7b59431f88232959cb45858e6fa78cf27a7347a6d6b0c7753ba63da63a9e55d66a967ee70922
7
+ data.tar.gz: 564fc2d7c4b478b96bb18157fc27bbf67919d8d849bee0bad349376a28f911cb09f76f0014a2c969543a24156130198edd2964c76c7d3b72c57ad0e9a61324bf
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- clausewitz-spelling (0.1.9)
4
+ clausewitz-spelling (0.1.10)
5
5
  colorize
6
6
  damerau-levenshtein
7
7
  ffi-aspell
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "bundler/setup"
4
4
  require "clausewitz/spelling"
5
+ require "clausewitz/localisation"
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -9,14 +9,8 @@ class Main
9
9
 
10
10
  def parse_args(args)
11
11
  opts = Optimist::options(args) do
12
- opt :custom_wordlist,
13
- "Text file containing newline-delimited list of custom words",
14
- type: :string
15
- opt :english_dialect,
16
- "Two-letter code indicating dialect of English to use",
17
- type: :string
18
- opt :spanish_dialect,
19
- "Two-letter code indicating dialect of Spanish to use",
12
+ opt :dictionary_root,
13
+ "Directory containing per-language custom word lists",
20
14
  type: :string
21
15
  opt :suggestion_count,
22
16
  "How many suggestions to display",
@@ -27,9 +21,9 @@ class Main
27
21
 
28
22
  def run
29
23
  spellchecker = Clausewitz::Spelling::Checker.new(@opts)
30
- results = spellchecker.check_files(@args)
31
- results.render
32
- !results.failed?
24
+ results = @args.map { |arg| spellchecker.check_file(arg) }
25
+ results.each { |r| puts r }
26
+ !results.any?(&:failed?)
33
27
  end
34
28
  end
35
29
 
@@ -0,0 +1,64 @@
1
+ require 'yaml'
2
+
3
+ module Clausewitz
4
+ module Localisation
5
+ LANG_MAP = {
6
+ 'english' => {
7
+ base: 'en'
8
+ },
9
+ 'french' => {
10
+ base: 'fr'
11
+ },
12
+ 'german' => {
13
+ base: 'de'
14
+ },
15
+ 'portuguese' => {
16
+ base: 'pt'
17
+ },
18
+ 'russian' => {
19
+ base: 'ru'
20
+ },
21
+ 'spanish' => {
22
+ base: 'es'
23
+ }
24
+ }
25
+
26
+ def self.parse(text)
27
+ smudged_text = text.lines.map do |line|
28
+ self.smudge_key(line)
29
+ end
30
+ contents = YAML.load(smudged_text.join("\n"))
31
+ self.validate_localisation!(contents)
32
+ contents.each do |lang, entries|
33
+ entry_keys = entries ? entries.keys : []
34
+ entry_keys.each do |key|
35
+ entries[unsmudge_key(key)] = entries.delete(key)
36
+ end
37
+ end
38
+ contents
39
+ end
40
+
41
+ def self.parse_file(filepath)
42
+ self.parse(File.read(filepath))
43
+ end
44
+
45
+ def self.smudge_key(line)
46
+ line.sub(/\:([0-9]+) /, '!!!\1: ')
47
+ end
48
+
49
+ def self.validate_localisation!(contents)
50
+ fail("Unknown language keys!") unless contents.keys.all? do |lang|
51
+ self.valid_lang?(lang)
52
+ end
53
+ end
54
+
55
+ VALID_LANG_REGEX = /^l_(#{LANG_MAP.keys.join('|')})/
56
+ def self.valid_lang?(lang)
57
+ lang =~ VALID_LANG_REGEX
58
+ end
59
+
60
+ def self.unsmudge_key(line)
61
+ line.sub(/!!!([0-9]+)$/, ':\1')
62
+ end
63
+ end
64
+ end
@@ -2,6 +2,5 @@ require "clausewitz/spelling/version"
2
2
 
3
3
  module Clausewitz
4
4
  module Spelling
5
- # Your code goes here...
6
5
  end
7
6
  end
@@ -1,140 +1,149 @@
1
1
  require 'ffi/aspell'
2
2
  require 'open3'
3
+ require 'pathname'
3
4
  require 'set'
4
5
  require 'tmpdir'
5
6
  require 'yaml'
6
7
  require 'damerau-levenshtein'
8
+ require 'clausewitz/localisation'
7
9
  require 'clausewitz/spelling/results'
8
10
 
9
11
  module Clausewitz; module Spelling
10
12
  class Checker
11
13
  attr_accessor :dict_words
12
14
  def initialize(opts = {})
13
-
15
+ @dictionary_root = opts[:dictionary_root]
14
16
  @suggestion_count = opts[:suggestion_count] || 3
15
17
 
16
- @english_dialect = opts[:english_dialect] || 'GB'
17
- @english_dialect = "en_#{@english_dialect}"
18
- @en_speller = FFI::Aspell::Speller.new(
19
- @english_dialect,
20
- encoding: 'UTF-8'
21
- )
22
-
23
- @spanish_dialect = opts[:spanish_dialect] || 'es'
24
- @spanish_dialect = "es_#{@spanish_dialect}" if opts[:spanish_dialect]
25
- @es_speller = FFI::Aspell::Speller.new(
26
- @spanish_dialect,
27
- encoding: 'UTF-8'
28
- )
29
-
30
- dict_path = opts[:custom_wordlist]
31
-
32
- if dict_path
33
- fail("No such file #{dict_path}!") unless File.exist?(dict_path)
34
- @dict_words = Set.new(File.read(dict_path).lines.map(&:chomp).to_a)
35
- @custom_words_filepath = generate_word_list
36
- @en_speller.set('extra-dicts', @custom_words_filepath)
37
- @en_speller.set('ignore-accents', true)
38
- else
39
- @dict_words = Set.new([])
40
- @en_speller.set('ignore-accents', true)
18
+ if @dictionary_root
19
+ @dictionary_root = Pathname.new(@dictionary_root)
41
20
  end
21
+
22
+ @loaded_spellcheckers = {}
23
+ @loaded_wordlists = {}
42
24
  end
43
25
 
44
- def check_file(file_path)
45
- $stderr.puts "Checking #{file_path}..."
46
- loc = load_file(file_path)
47
- # Poorly formatted YAML files often lack proper indentation; you can
48
- # easily discover this by checking to make sure all top level keys are
49
- # actual language names.
50
- bad_keys = loc.keys.select { |key| bad_lang_key(key) }
51
- bad_keys.map! { |key| unsmudge_key(key) }
52
- if !bad_keys.empty?
53
- UnknownLangsFileResult.new(file_path, bad_keys)
54
- else
55
- results = loc.map do |lang, entries|
56
- check_entries(lang, entries)
57
- end
58
- FileResults.new(file_path, results)
26
+ def check_file(filepath)
27
+ results = []
28
+ begin
29
+ filepath = Pathname.new(filepath)
30
+ validate_filepath!(filepath)
31
+ rescue => e
32
+ return InvalidFilepathResult.new(filepath, e)
59
33
  end
60
- rescue Psych::SyntaxError => e
61
- # If we fail to load the file it's probably busted.
62
- BadFormatFileResult.new(file_path, e)
63
- end
64
34
 
65
- def check_files(file_paths)
66
- results = Array(file_paths).map do |file_path|
67
- check_file(file_path) unless File.directory?(file_path)
35
+ $stderr.puts "Skipping #{filepath}..." if filepath.directory?
36
+
37
+ begin
38
+ contents = Clausewitz::Localisation.parse_file(filepath)
39
+ rescue => e
40
+ return UnparseableFileResult.new(filepath, e)
68
41
  end
69
- OverallResults.new(results.compact)
70
- end
71
42
 
72
- def check_entries(lang, entries)
73
- misspellings = []
74
- if entries && !entries.empty?
75
- entries.each do |key, text|
76
- result = check_entry(key, text)
77
- misspellings << result unless result.check_results.empty?
78
- end
43
+ checks = contents.map do |lang, entries|
44
+ check_entries(lang, entries)
79
45
  end
80
- LangResults.new(lang, misspellings)
46
+ FileResults.new(filepath, checks)
81
47
  end
82
48
 
83
- def check_entry(key, text)
84
- misspellings = []
85
- text = preprocess_entry(text)
86
- text.split(/(\s|—)/).each_with_index do |word, index|
87
- unless check_word(word) || misspellings.any? { |ms| ms.misspelled_word == word }
88
- misspellings << CheckResult.new(word, suggest_words(word))
49
+ private
50
+
51
+ def check_entries(lang, entries)
52
+ wordlist = load_wordlist(lang)
53
+ aspell_checker = load_aspell_checker(lang)
54
+ spellcheck_ignore = entries&.delete('spellcheck_ignore')
55
+ ignored_keys = spellcheck_ignore ? spellcheck_ignore.split(',') : []
56
+ return IgnoredLangResult.new(lang) if ignored_keys.include?('all')
57
+ return LangResults.new(lang, []) unless entries
58
+ checks = entries.map do |key, entry|
59
+ if ignored_keys.include?(key)
60
+ IgnoredEntryResult.new(key)
61
+ else
62
+ check_entry(aspell_checker, wordlist, key, entry)
89
63
  end
90
64
  end
91
- KeyResults.new(unsmudge_key(key), misspellings)
92
- end
65
+ LangResults.new(lang, checks)
66
+ end
67
+
68
+ def check_entry(checker, wordlist, key, entry)
69
+ # We don't want to pay attention to scripted localisation, so we'll strip
70
+ # it out before we start.
71
+ # TODO: Look into supporting escaped square brackets as part of the
72
+ # string.
73
+ entry.gsub!(/\[.+\]/, '')
74
+
75
+ # Remove other localisation bits we don't care about.
76
+ entry.gsub!(/§(%|\*|=|\d|W|G|R|B|Y|b|M|g|T|l|H|\+|-|!)/, '')
77
+
78
+ # We should also remove punctuation that is never part of words, like
79
+ # exclamation points, commas, semi-colons, and question marks.
80
+ # We should be using proper apostrophes for possessives in our loc.
81
+ entry.gsub!(/(!|;|\?|'|"|“|”|:|\$|\(|\))/, '')
82
+
83
+ # If a word has one full stop at the end with no other full stops
84
+ # elsewhere in the word, it's probably an acronym or initialism like
85
+ # U.S.A. and so we should avoid stripping it. Otherwise, it's probably
86
+ # the end of a sentence and can be ignored.
87
+ words = entry.split(/\s|—/)
88
+ words.map! do |word|
89
+ if word.end_with?('...')
90
+ word.sub(/\.\.\.$/, '')
91
+ elsif word =~ /\.$/ && word.chars.count('.') == 1
92
+ word.sub(/\.$/, '')
93
+ elsif word =~ /,$/
94
+ word.sub(/,$/, '')
95
+ else
96
+ word
97
+ end
98
+ end.join(" ")
93
99
 
94
- SQUIGGLE = '§'
95
- def check_word(word)
96
- return true if word.chars.count('.') > 1
97
- word.gsub!(/^(#{SQUIGGLE}.|[[:punct:]])+/, '')
98
- word.gsub!(/(#{SQUIGGLE}.|[[:punct:]])+$/, '')
99
- not_word?(word) ||
100
- @dict_words.include?(word) ||
101
- @en_speller.correct?(word) ||
102
- @es_speller.correct?(word)
100
+ checks = words.map { |word| check_word(checker, wordlist, word) }.compact
101
+ EntryResults.new(key, checks)
103
102
  end
104
103
 
105
- def load_file(file_path)
106
- contents = nil
107
- File.open(file_path, 'r:UTF-8') do |f|
108
- contents = f.read
104
+ def check_word(checker, wordlist, word)
105
+ return if is_number?(word)
106
+ return if is_ordinal?(word)
107
+ return if is_percentage?(word)
108
+ return if is_icon?(word)
109
+ return if wordlist.include?(word)
110
+
111
+ if !checker.correct?(word)
112
+ MisspelledWordResult.new(word, suggest_words(checker, wordlist, word))
109
113
  end
110
- contents = contents.lines.map do |line|
111
- smudge_key(line)
112
- end.join("\n")
113
- YAML.load(contents)
114
114
  end
115
115
 
116
- def not_word?(word)
117
- is_percentage?(word) ||
118
- is_number?(word) ||
119
- is_ordinal?(word) ||
120
- word =~ /^£/
121
- end
116
+ def suggest_words(checker, wordlist, word)
117
+ return [] if word.size < 3
122
118
 
123
- private
119
+ suggestions = Set.new
120
+
121
+ aspell_suggestions = checker.suggestions(word)
122
+
123
+ custom_suggestions = wordlist.select do |dict_word|
124
+ DamerauLevenshtein.distance(word, dict_word) < word.size
125
+ end
126
+
127
+ aspell_suggestions.each { |sug| suggestions.add(sug) }
128
+ custom_suggestions.each { |sug| suggestions.add(sug) }
124
129
 
125
- def bad_lang_key(key)
126
- key !~ /^l_.+/
130
+ suggestions.to_a.sort_by do |sug|
131
+ DamerauLevenshtein.distance(sug, word)
132
+ end.first(@suggestion_count)
127
133
  end
128
134
 
129
- def is_ordinal?(word)
130
- word =~ /[0-9]+(th|st|nd|rd)/
135
+ def is_icon?(word)
136
+ word =~ /^£\w+/
131
137
  end
132
138
 
133
139
  def is_number?(word)
134
140
  Float(word) != nil rescue false
135
141
  end
136
142
 
137
- # Tries to detect if a word is a percentage and can be skipped.
143
+ def is_ordinal?(word)
144
+ word =~ /[0-9]+(th|st|nd|rd)/
145
+ end
146
+
138
147
  def is_percentage?(word)
139
148
  word =~ /(-|\+)?[0-9]+(\.[0-9]+)?%/ ||
140
149
  word =~ /%(-|\+)?[0-9]+(\.[0-9]+)?/
@@ -143,55 +152,59 @@ module Clausewitz; module Spelling
143
152
  # Loads our custom wordlist into a temporary Aspell dictionary.
144
153
  # This way Aspell won't yell at us for custom words and will also
145
154
  # potentially select from this list as suggestions for misspelled words.
146
- def generate_word_list
147
- dir = Dir.mktmpdir('custom-wordlist-')
148
- output = File.join(dir, 'fallout_words.wlst')
155
+ def load_custom_dictionary(lang)
156
+ dir = Dir.mktmpdir("custom-wordlist-#{lang}-")
157
+ output = File.join(dir, "#{lang}-custom.wlst")
149
158
  cmd = %W[
150
159
  aspell --lang=en --encoding=UTF-8 create master #{output}
151
160
  ]
152
161
  value = nil
153
162
  Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thr|
154
- @dict_words.each do |word|
163
+ contents = File.read(File.join(@dictionary_root, lang, 'dict.txt'))
164
+ words = contents.lines.map(&:chomp)
165
+ words.each do |word|
155
166
  stdin.puts(word)
156
167
  end
157
168
  stdin.close
158
169
  value = wait_thr.value
159
170
  end
160
- fail("Could not generate custom word list!") unless value.success?
171
+ unless value.success?
172
+ fail("Could not generate custom word list for #{lang}!")
173
+ end
161
174
  output
162
175
  end
163
176
 
164
- def preprocess_entry(entry)
165
- entry.gsub(/\[.+\]/, '')
166
- end
167
-
168
- def smudge_key(key)
169
- key.sub(/\:([0-9]+) /, "!!!1: ")
177
+ def load_aspell_checker(lang)
178
+ return @loaded_spellcheckers[lang] if @loaded_spellcheckers[lang]
179
+ aspell_lang_config = Localisation::LANG_MAP.select do |config_key, _|
180
+ lang.sub(/^l_/, '') == config_key
181
+ end.first
182
+ lang_code = aspell_lang_config.last[:base]
183
+ aspell_checker = FFI::Aspell::Speller.new(lang_code, encoding: 'UTF-8')
184
+ aspell_checker.set('ignore-accents', true)
185
+ if @dictionary_root && @dictionary_root.join(lang_code).exist?
186
+ custom_words = load_custom_dictionary(lang_code)
187
+ aspell_checker.set('extra-dicts', custom_words)
188
+ end
189
+ @loaded_spellcheckers[lang] = aspell_checker
170
190
  end
171
191
 
172
- def suggest_words(word)
173
- return [] if word.size < 5
174
-
175
- suggestions = Set.new
176
-
177
- aspell_suggestions = []
178
- aspell_suggestions.concat(@en_speller.suggestions(word))
179
- aspell_suggestions.concat(@es_speller.suggestions(word))
180
-
181
- custom_suggestions = @dict_words.select do |dict_word|
182
- DamerauLevenshtein.distance(word, dict_word) < word.size
192
+ def load_wordlist(lang)
193
+ return @loaded_wordlists[lang] if @loaded_wordlists[lang]
194
+ aspell_lang_config = Localisation::LANG_MAP.select do |config_key, _|
195
+ lang.sub(/^l_/, '') == config_key
196
+ end.first
197
+ lang_code = aspell_lang_config.last[:base]
198
+ if @dictionary_root && @dictionary_root.join(lang_code).exist?
199
+ contents = File.read(@dictionary_root.join(lang_code, 'dict.txt'))
183
200
  end
184
-
185
- aspell_suggestions.each { |sug| suggestions.add(sug) }
186
- custom_suggestions.each { |sug| suggestions.add(sug) }
187
-
188
- suggestions.to_a.sort_by do |sug|
189
- DamerauLevenshtein.distance(sug, word)
190
- end.first(@suggestion_count)
201
+ @loaded_wordlists[lang] = Set.new(contents.lines.to_a.map(&:chomp))
191
202
  end
192
203
 
193
- def unsmudge_key(key)
194
- key.gsub(/!!!([0-9]+)$/, ":1")
204
+ # Make sure a file to be checked is actually present and readable.
205
+ def validate_filepath!(filepath)
206
+ fail("No such file #{filepath}!") unless filepath.exist?
207
+ fail("Cannot read #{filepath}!") unless filepath.readable?
195
208
  end
196
209
  end
197
- end; end # Clausewitz::Spelling
210
+ end; end
@@ -1,137 +1,174 @@
1
1
  require 'colorize'
2
2
 
3
3
  module Clausewitz; module Spelling
4
- # Stores the results of a run over a set of files.
5
- class OverallResults
6
- attr_reader :file_results
7
- def initialize(file_results)
8
- @file_results = file_results
4
+ class FileResults
5
+ def initialize(filepath, lang_results)
6
+ @filepath = filepath
7
+ @lang_results = lang_results
9
8
  end
10
9
 
11
10
  def failed?
12
- @file_results.any?(&:failed?)
11
+ @lang_results.any?(&:failed?)
13
12
  end
14
13
 
15
- def render
16
- @file_results.each(&:render)
14
+ def to_s
15
+ outfile = failed? ? @filepath.to_s.red : "#{@filepath} passed".green
16
+ failures = @lang_results.select(&:failed?)
17
+ "#{outfile}\n" + failures.map { |l| " #{l}" }.join("\n")
17
18
  end
18
19
  end
19
20
 
20
- class FileResults
21
- attr_reader :file_path, :lang_results
22
- def initialize(file_path, lang_results)
23
- @file_path = file_path
24
- @lang_results = lang_results
21
+ class LangResults
22
+ def initialize(lang, entry_results)
23
+ @lang = lang
24
+ @entry_results = entry_results
25
25
  end
26
26
 
27
27
  def failed?
28
- @lang_results.any?(&:failed?)
28
+ @entry_results.any?(&:failed?)
29
29
  end
30
30
 
31
- def render
32
- successful, failed = @lang_results.partition do |lang|
33
- lang.key_results.empty?
34
- end
35
- successful.each do |lang|
36
- puts " #{@file_path.yellow}:#{lang.lang.yellow} #{'passed'.green}"
37
- end
38
- failed.each do |lang|
39
- failed_count = lang.key_results.size.to_s.red
40
- puts " #{@file_path.yellow}:#{lang.lang.yellow} has #{failed_count} keys with errors:"
41
- lang.key_results.each(&:render)
42
- end
31
+ def to_s
32
+ to_str
33
+ end
34
+
35
+ def to_str(indent = 0)
36
+ firstspacer = ' ' * indent
37
+ spacer = ' ' * (indent + 2)
38
+ failures = @entry_results.select(&:failed?)
39
+ outlines = failures.map { |e| "#{spacer}#{e.to_str(indent + 2)}" }
40
+ outlines = outlines.join("\n")
41
+ outlang = failed? ? @lang.red : @lang.green
42
+ out = "#{firstspacer}#{outlang}\n#{outlines}"
43
43
  end
44
44
  end
45
45
 
46
- class BadFormatFileResult
47
- attr_reader :file_path, :error
48
- def initialize(file_path, error)
49
- @file_path = file_path
50
- @error = error
46
+ class IgnoredLangResult
47
+ def initialize(lang)
48
+ @lang = lang
51
49
  end
52
50
 
53
51
  def failed?
54
- true
52
+ false
55
53
  end
56
54
 
57
- def render
58
- puts " #{@file_path.yellow} #{'appears to be formatted incorrectly:'.red}"
59
- puts " #{@error.message.red}"
55
+ def to_s
56
+ to_str
57
+ end
58
+
59
+ def to_str(indent = 0)
60
+ firstspacer = ' ' * indent
61
+ "#{firstspacer}#{@lang} ignored".yellow
60
62
  end
61
63
  end
62
64
 
63
- class UnknownLangsFileResult
64
- attr_reader :file_path, :bad_langs
65
- def initialize(file_path, bad_langs)
66
- @file_path = file_path
67
- @bad_langs = bad_langs
65
+ class EntryResults
66
+ def initialize(key, word_results)
67
+ @key = key
68
+ @word_results = word_results
68
69
  end
69
70
 
70
71
  def failed?
71
- true
72
+ !@word_results.empty?
72
73
  end
73
74
 
74
- def render
75
- puts " #{@file_path.yellow} #{'has unknown language keys!'.red}"
76
- puts " Make sure that each loc key has *two* leading *spaces*.".yellow
75
+ def to_s
76
+ to_str
77
+ end
78
+
79
+ def to_str(indent = 0)
80
+ spacer = ' ' * indent
81
+ if failed?
82
+ outlines = @word_results.map { |w| "#{spacer}#{w.to_str(indent + 2)}" }
83
+ outlines = outlines.join("\n")
84
+ "#{spacer}#{@key.red}\n#{outlines}"
85
+ else
86
+ "#{spacer}#{@key} passed".green
87
+ end
77
88
  end
78
89
  end
79
90
 
80
- class LangResults
81
- attr_reader :lang, :key_results
82
- def initialize(lang, key_results)
83
- @lang = lang
84
- @key_results = key_results
91
+ class IgnoredEntryResult
92
+ def initialize(key)
93
+ @key = key
85
94
  end
86
95
 
87
96
  def failed?
88
- !@key_results.empty?
97
+ false
89
98
  end
90
99
 
91
- def render
92
- puts " #{@lang} has #{@key_results.size.to_s.red} keys with errors:"
93
- @key_results.each(&:render)
100
+ def to_s
101
+ to_str
102
+ end
103
+
104
+ def to_str(indent = 0)
105
+ firstspacer = ' ' * indent
106
+ "#{firstspacer}#{@key} ignored".yellow
94
107
  end
95
108
  end
96
109
 
97
- class KeyResults
98
- attr_reader :key, :check_results
99
- def initialize(key, check_results)
100
- @key = key
101
- @check_results = check_results
110
+ class MisspelledWordResult
111
+ def initialize(word, suggestions)
112
+ @word = word
113
+ @suggestions = suggestions
102
114
  end
103
115
 
104
116
  def failed?
105
- !@check_results.empty?
117
+ true
118
+ end
119
+
120
+ def to_s
121
+ to_str
122
+ end
123
+
124
+ def to_str(indent = 0)
125
+ spacer = ' ' * indent
126
+ msg = "#{spacer}#{@word}".red
127
+
128
+ if @suggestions && !@suggestions.empty?
129
+ msg += " (#{@suggestions.join(', ')})".yellow
130
+ else
131
+ msg += " (-- no suggestions --)".yellow
132
+ end
133
+
134
+ msg
106
135
  end
136
+ end
107
137
 
108
- def render
109
- puts " #{@key.yellow} has #{@check_results.size.to_s.red} unrecognized words:"
110
- @check_results.each(&:render)
138
+ # Result capturing basic problems interacting with the file before parsing.
139
+ class InvalidFilepathResult
140
+ attr_reader :filepath, :error
141
+ def initialize(filepath, error)
142
+ @filepath = filepath
143
+ @error = error
144
+ end
145
+
146
+ def failed?
147
+ true
111
148
  end
112
149
  end
113
150
 
114
- class CheckResult
115
- attr_reader :misspelled_word, :suggestions
116
- def initialize(misspelled_word, suggestions)
117
- @misspelled_word = misspelled_word
118
- @suggestions = suggestions
151
+ # Result capturing problems parsing a readable file.
152
+ class UnparseableFileResult
153
+ attr_reader :filepath, :error
154
+ def initialize(filepath, error)
155
+ @filepath = filepath
156
+ @error = error
119
157
  end
120
158
 
121
159
  def failed?
122
160
  true
123
161
  end
124
162
 
125
- def render
126
- print " Unrecognized word '#{@misspelled_word.red}'"
127
- if @suggestions.empty?
128
- puts
129
- else
130
- puts "; did you mean:"
131
- @suggestions.each do |sug|
132
- puts " '#{sug.green}'"
133
- end
134
- end
163
+ def to_s
164
+ to_str
165
+ end
166
+
167
+ def to_str(indent = 0)
168
+ spacer = ' ' * indent
169
+ secondspacer = ' ' * (indent + 2)
170
+ "#{spacer}#{@filepath} could not be parsed\n".red +
171
+ "#{secondspacer}#{@error.message}".red
135
172
  end
136
173
  end
137
174
  end; end # Clausewitz::Spelling
@@ -1,5 +1,5 @@
1
1
  module Clausewitz
2
2
  module Spelling
3
- VERSION = "0.1.9"
3
+ VERSION = "0.1.10"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clausewitz-spelling
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Will Chappell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-13 00:00:00.000000000 Z
11
+ date: 2019-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -144,9 +144,9 @@ files:
144
144
  - bin/setup
145
145
  - clausewitz-spelling.gemspec
146
146
  - exe/clausewitz-spellcheck
147
+ - lib/clausewitz/localisation.rb
147
148
  - lib/clausewitz/spelling.rb
148
149
  - lib/clausewitz/spelling/checker.rb
149
- - lib/clausewitz/spelling/formatter.rb
150
150
  - lib/clausewitz/spelling/results.rb
151
151
  - lib/clausewitz/spelling/version.rb
152
152
  homepage: http://github.com/wtchappell/clausewitz-spelling
@@ -1,36 +0,0 @@
1
- module Clausewitz; module Spelling
2
- class Formatter
3
- def render(overall_results)
4
- render_overall_results(overall_results)
5
- end
6
-
7
- private
8
-
9
- def render_overall_results(overall_results)
10
- overall_results.files.each do |file_results|
11
- render_file_results(file_results)
12
- end
13
- end
14
-
15
- def render_file_results(file_results)
16
- file_results.langs.each do |lang_results|
17
- render_lang_results(lang_results)
18
- end
19
- end
20
-
21
- def render_lang_results(lang_results)
22
- lang_results.keys.each do |key_results|
23
- render_key_results(key_results)
24
- end
25
- end
26
-
27
- def render_key_results(key_results)
28
- key_results.spellchecks.each do |spellcheck_result|
29
- render_spellcheck_result(spellcheck_result)
30
- end
31
- end
32
-
33
- def render_spellcheck_result(spellcheck_result)
34
- end
35
- end
36
- end; end # Clausewitz::Spelling