cr.rb 4.0.2 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cr +428 -642
  3. data/lib/cr.rb +236 -12
  4. metadata +4 -4
data/bin/cr CHANGED
@@ -1,801 +1,587 @@
1
1
  #!/usr/bin/env ruby
2
- # coding: utf-8
3
2
  # ------------------------------------------------------
4
3
  # File : cr.rb
5
4
  # Authors : ccmywish <ccmywish@qq.com>
6
5
  # Created on : <2021-07-08>
7
- # Last modified : <2022-11-29>
6
+ # Last modified : <2023-02-12>
8
7
  #
9
8
  # cr:
10
9
  #
11
10
  # This file is used to explain a CRyptic command
12
- # or an acronym's real meaning in computer world or
13
- # other fields.
14
- #
11
+ # or an acronym's real meaning in computer world or
12
+ # other fields.
15
13
  # ------------------------------------------------------
16
14
 
17
15
  require 'cr'
18
- require 'tomlrb'
19
- require 'fileutils'
20
-
21
- CR_DEFAULT_LIBRARY = File.expand_path("~/.cryptic-resolver")
22
-
23
- $CR_DEFAULT_DICTS = [
24
- "https://github.com/cryptic-resolver/cryptic_common.git",
25
- "https://github.com/cryptic-resolver/cryptic_computer.git",
26
- "https://github.com/cryptic-resolver/cryptic_windows.git",
27
- "https://github.com/cryptic-resolver/cryptic_linux.git",
28
- "https://github.com/cryptic-resolver/cryptic_electronics"
29
- ]
30
-
31
- # The config file will override the default dicts, but not default library!
32
- if ENV['CRYPTIC_RESOLVER_CONFIG']
33
- file = ENV['CRYPTIC_RESOLVER_CONFIG']
34
- if test('f', file)
35
- config = Tomlrb.load_file file
36
-
37
- CR_EXTRA_LIBRARY ||= config['EXTRA_LIBRARY']
38
-
39
- if config['DEFAULT_DICTS']
40
- CR_DEFAULT_DICTS = config['DEFAULT_DICTS']
41
- else
42
- CR_DEFAULT_DICTS = $CR_DEFAULT_DICTS
43
- end
44
16
 
45
- else
46
- puts "FATAL: Your CRYPTIC_RESOLVER_CONFIG is NOT a file!"
47
- exit 1
48
- end
17
+ class CrypticResolver::CliHandler
49
18
 
50
- else
51
- # if user doesn't specify, we use the hard-coded defaults
52
- CR_EXTRA_LIBRARY = nil
53
- CR_DEFAULT_DICTS = $CR_DEFAULT_DICTS
54
- end
19
+ def initialize(args)
20
+ option = args.shift
55
21
 
22
+ case option when nil, '-h','--help' then help or exit end
56
23
 
57
- # Prevent runtime error
58
- if not CR_EXTRA_LIBRARY.nil?
59
- if ! test('d', CR_EXTRA_LIBRARY)
60
- puts "FATAL: Your CRYPTIC_RESOLVER_CONFIG's option 'EXTRA_LIBRARY' is NOT a legal directory!"
61
- exit 1
62
- end
63
- end
24
+ reslvr = CrypticResolver::Resolver.new
64
25
 
26
+ reslvr.add_default_dicts_if_none_exists
65
27
 
66
- # This is used to display what you are pulling when adding dicts
67
- CR_DEFAULT_DICTS_USER_AND_NAMES = CR_DEFAULT_DICTS.map do |e|
68
- user, repo = e.split('/').last(2)
69
- repo = repo.split('.').first
70
- user + '/' + repo
71
- end
28
+ case option
29
+ when '-l' then reslvr.list_dicts
30
+ when '-c' then reslvr.word_count
31
+ when '-u' then reslvr.update_dicts
32
+ when '-s' then reslvr.search_word args.shift
33
+ when '-a' then reslvr.add_dict args.shift
34
+ when '-d' then reslvr.del_dict args.shift
35
+ else reslvr.resolve_word option end
36
+ end
72
37
 
73
- # Same with the pulled repo dirs' names in CR_DEFAULT_LIBRARY
74
- CR_DEFAULT_DICTS_NAMES = CR_DEFAULT_DICTS.map do |e|
75
- e.split('/').last.split('.').first
76
- end
77
38
 
39
+ def help
40
+ puts <<~HELP
41
+ cr: Cryptic Resolver v#{CrypticResolver::GEM_VERSION}
78
42
 
79
- ####################
80
- # helper: for color
81
- ####################
43
+ Usage:
44
+ cr <keyword> Search the keyword
45
+ cr emacs => Edit macros: A feature-rich editor
82
46
 
83
- def bold(str) "\e[1m#{str}\e[0m" end
84
- def underline(str) "\e[4m#{str}\e[0m" end
85
- def red(str) "\e[31m#{str}\e[0m" end
86
- def green(str) "\e[32m#{str}\e[0m" end
87
- def yellow(str) "\e[33m#{str}\e[0m" end
88
- def blue(str) "\e[34m#{str}\e[0m" end
89
- def purple(str) "\e[35m#{str}\e[0m" end
90
- def cyan(str) "\e[36m#{str}\e[0m" end
47
+ Options:
48
+ -c Print word count
49
+ -s <pattern> Search words matched with pattern
50
+ -h Print this help
91
51
 
52
+ Dictionaries:
53
+ -l List local dicts and official dicts
54
+ -u Update all dicts in Default library
55
+ -a <https://repo[.git]> Add a new dict
56
+ -a <user/repo> Add a new dict from Github
57
+ -a <dict_name> Add an official dict, use -l to see
58
+ -d <dict_name> Delete a dict
92
59
 
93
- ####################
94
- # core: logic
95
- ####################
96
-
97
- def is_there_any_dict?
98
- unless Dir.exist? CR_DEFAULT_LIBRARY
99
- Dir.mkdir CR_DEFAULT_LIBRARY
60
+ HELP
100
61
  end
101
62
 
102
- !Dir.empty? CR_DEFAULT_LIBRARY
103
63
  end
104
64
 
105
65
 
106
- def add_default_dicts_if_none_exists
107
- unless is_there_any_dict?
108
- puts "cr: Adding default dicts..."
66
+ class CrypticResolver::Resolver
67
+
68
+ # Notice that we only update the Default library, not Extra library
69
+ def update_dicts
70
+
71
+ @counter.count_def_lib(display: false)
72
+ old_wc = @counter.word_count_of_def_lib
73
+
74
+ puts "cr: Updating all dicts in Default library..."
109
75
 
110
76
  begin
111
- if RUBY_PLATFORM.include? "mingw"
112
- # Windows doesn't have fork
113
- CR_DEFAULT_DICTS_USER_AND_NAMES.each_with_index do |name, i|
114
- puts "cr: Pulling #{name}..."
115
- `git -C #{CR_DEFAULT_LIBRARY} clone #{CR_DEFAULT_DICTS[i]} -q`
116
- end
117
- else
118
- # *nix-like
119
- CR_DEFAULT_DICTS_USER_AND_NAMES.each_with_index do |name, i|
120
- fork do
121
- puts "cr: Pulling #{name}..."
122
- `git -C #{CR_DEFAULT_LIBRARY} clone #{CR_DEFAULT_DICTS[i]} -q`
77
+ Dir.chdir DEFAULT_LIB_PATH do
78
+ if Gem.win_platform?
79
+ # Windows doesn't have fork
80
+ Dir.children(DEFAULT_LIB_PATH).each do |dict|
81
+ next if File.file? dict
82
+ puts "cr: Wait to update #{dict}..."
83
+ `git -C ./#{dict} pull -q`
123
84
  end
124
- end
125
- Process.waitall
85
+ else
86
+ # *nix
87
+ Dir.children(DEFAULT_LIB_PATH).each do |dict|
88
+ next if File.file? dict
89
+ fork do
90
+ puts "cr: Wait to update #{dict}..."
91
+ `git -C ./#{dict} pull -q`
92
+ end
93
+ end
94
+ Process.waitall
95
+ end # end if/else
126
96
  end
127
97
 
128
98
  rescue Interrupt
129
- puts "cr: Cancel add default dicts"
130
- exit 1
99
+ abort "cr: Cancel update"
131
100
  end
132
-
133
- puts "cr: Add done"
134
- word_count(p: false)
135
- puts
136
- puts "#{$DefaultLibWordCount} words added"
137
-
138
- # Really added
139
- return true
140
- end
141
- # Not added
142
- return false
143
- end
144
-
145
-
146
- # Notice that we only update the Default library, not Extra library
147
- def update_dicts()
148
- return if add_default_dicts_if_none_exists
149
101
 
150
- word_count(p: false)
151
- old_wc = $DefaultLibWordCount
152
-
153
- puts "cr: Updating all dicts in Default library..."
154
-
155
- begin
156
- Dir.chdir CR_DEFAULT_LIBRARY do
157
-
158
- if RUBY_PLATFORM.include? "mingw"
159
- # Windows doesn't have fork
160
- Dir.children(CR_DEFAULT_LIBRARY).each do |dict|
161
- puts "cr: Wait to update #{dict}..."
162
- `git -C ./#{dict} pull -q`
163
- end
164
- else
165
- # *nix
166
- Dir.children(CR_DEFAULT_LIBRARY).each do |dict|
167
- fork do
168
- puts "cr: Wait to update #{dict}..."
169
- `git -C ./#{dict} pull -q`
170
- end
171
- end
172
- Process.waitall
102
+ puts "cr: Update done"
173
103
 
174
- end # end if/else
175
- end
104
+ # clear
105
+ @counter.word_count_of_def_lib = 0
106
+ # recount
107
+ @counter.count_def_lib(display: false)
176
108
 
177
- rescue Interrupt
178
- puts "cr: Cancel update"
179
- exit 1
109
+ new_wc = @counter.word_count_of_def_lib
110
+ puts ; puts "#{new_wc - old_wc} words added in Default library"
180
111
  end
181
112
 
182
- puts "cr: Update done"
183
-
184
- # clear
185
- $DefaultLibWordCount = 0
186
- # recount
187
- word_count(p: false)
188
113
 
189
- new_wc = $DefaultLibWordCount
190
-
191
- puts
192
- puts "#{new_wc - old_wc} words added in Default library"
193
-
194
- end
195
-
196
-
197
- def add_dict(repo)
198
- if repo.nil?
199
- puts bold(red("cr: Need an argument!"))
200
- exit -1
201
- end
202
-
203
- # Ensure the cr home dir exists
204
- FileUtils.mkdir_p(CR_DEFAULT_LIBRARY)
114
+ def add_dict(repo)
115
+ if repo.nil?
116
+ abort bold(red("cr: Need an argument!"))
117
+ end
205
118
 
206
- # Simplify adding dictionary
207
- if !repo.start_with?("https://") and !repo.start_with?("git@")
208
- if repo.include?('/')
209
- repo = "https://github.com/#{repo}.git"
210
- else
211
- repo = "https://github.com/cryptic-resolver/cryptic_#{repo}.git"
119
+ # Simplify adding dictionary
120
+ if !repo.start_with?("https://") and !repo.start_with?("git@")
121
+ if repo.include?('/')
122
+ repo = "https://github.com/#{repo}.git"
123
+ else
124
+ repo = "https://github.com/cryptic-resolver/cryptic_#{repo}.git"
125
+ end
212
126
  end
213
- end
214
127
 
215
- begin
128
+ begin
216
129
  puts "cr: Adding new dictionary..."
217
- `git -C #{CR_DEFAULT_LIBRARY} clone #{repo} -q`
218
- rescue Interrupt
219
- puts "cr: Cancel add dict"
220
- exit 1
221
- end
222
-
223
- puts "cr: Add new dictionary done"
130
+ `git -C #{DEFAULT_LIB_PATH} clone #{repo} -q`
131
+ rescue Interrupt
132
+ abort "cr: Cancel add dict"
133
+ end
224
134
 
225
- # github/com/ccmywish/ruby_knowledge(.git)
226
- dict = repo.split('/')[-1].delete_suffix('.git')
227
- wc = count_dict_words(CR_DEFAULT_LIBRARY ,dict)
228
- puts
229
- puts "#{wc} words added"
135
+ puts "cr: Add new dictionary done"
230
136
 
231
- end
137
+ # github/com/ccmywish/ruby_knowledge(.git)
138
+ dict = repo.split('/')[-1].delete_suffix('.git')
139
+ wc = @counter.count_dict_words(DEFAULT_LIB_PATH ,dict)
140
+ puts ; puts "#{wc} words added"
141
+ end
232
142
 
233
143
 
234
- def del_dict(repo)
235
- if repo.nil?
236
- puts bold(red("cr: Need an argument!"))
237
- exit -1
238
- end
239
- Dir.chdir CR_DEFAULT_LIBRARY do
240
- begin
144
+ def del_dict(repo)
145
+ if repo.nil?
146
+ abort bold(red("cr: Need an argument!"))
147
+ end
148
+ Dir.chdir DEFAULT_LIB_PATH do
149
+ begin
241
150
  # Dir.rmdir repo # Can't rm a filled dir
242
151
  # FileUtils.rmdir repo # Can't rm a filled dir
243
152
  FileUtils.rm_rf repo
244
153
  puts "cr: Delete dictionary #{bold(green(repo))} done"
245
- rescue Exception => e
154
+ rescue Exception => e
246
155
  puts bold(red("cr: #{e}"))
247
156
  list_dicts
157
+ end
248
158
  end
249
159
  end
250
- end
251
160
 
252
161
 
253
- def load_sheet(library, dict, sheet_name)
254
- file = library + "/#{dict}/#{sheet_name}.toml"
255
-
256
- if File.exist? file
257
- return Tomlrb.load_file file # gem 'tomlrb'
258
- # return TOML.load_file file # gem 'toml'
259
- else
260
- nil
162
+ def load_sheet(library, dict, sheet_name)
163
+ file = library + "/#{dict}/#{sheet_name}.toml"
164
+ if File.exist? file
165
+ return Tomlrb.load_file file # gem 'tomlrb'
166
+ # return TOML.load_file file # gem 'toml'
167
+ else nil end
261
168
  end
262
- end
263
169
 
264
170
 
265
- #
266
- # Pretty print the info of the given word
267
- #
268
- # A info looks like this
269
- # emacs = {
270
- # name = "Emacs"
271
- # desc = "edit macros"
272
- # more = "a feature-rich editor"
273
- # see = ["Vim"]
274
- # }
275
- #
276
- # @param info [Hash] the information of the given word (mapped to a keyword in TOML)
277
- #
278
- def pp_info(info)
279
- name = info['name'] || red("No name!") # keyword `or` is invalid here in Ruby
280
-
281
- desc = info['desc']
282
- more = info['more']
283
-
284
- if desc
285
- puts "\n #{name}: #{desc}"
286
- print "\n ",more,"\n" if more
287
- else
288
- puts "\n #{name}"
289
- print "\n ",more,"\n" if more
290
- end
171
+ =begin
172
+ Pretty print the info of the given word
173
+
174
+ A info looks like this
175
+ emacs = {
176
+ name = "Emacs"
177
+ desc = "edit macros"
178
+ more = "a feature-rich editor"
179
+ see = ["Vim"]
180
+ }
181
+
182
+ @param info [Hash] the information of the given word (mapped to a keyword in TOML)
183
+ =end
184
+ def pp_info(info)
185
+ name = info['name'] || red("No name!") # keyword `or` is invalid here in Ruby
291
186
 
292
- if see_also = info['see']
293
- print "\n", purple("SEE ALSO ")
294
- if see_also.is_a?(Array)
295
- see_also.each {|x| print underline(x),' '}
187
+ desc = info['desc']
188
+ more = info['more']
189
+
190
+ if desc
191
+ puts "\n #{name}: #{desc}"
192
+ print "\n ",more,"\n" if more
296
193
  else
297
- print underline(see_also),' '
194
+ puts "\n #{name}"
195
+ print "\n ",more,"\n" if more
196
+ end
197
+
198
+ if see_also = info['see']
199
+ print "\n", purple("SEE ALSO ")
200
+ if see_also.is_a?(Array)
201
+ last_ndx = see_also.size - 1
202
+ see_also.each_with_index do |x,i|
203
+ if last_ndx == i
204
+ print underline(x) # Last word doesn't show space
205
+ else
206
+ print underline(x),' '
207
+ end
208
+ end
209
+ else
210
+ print underline(see_also)
211
+ end
212
+ puts
298
213
  end
299
214
  puts
300
- end
301
- puts
302
- end
215
+ end
216
+
303
217
 
304
- # Print default cryptic_ dicts
305
- def pp_dict(dict)
218
+ # Print default cryptic_ dicts
219
+ def pp_dict(dict)
306
220
  puts green("From: #{dict}")
307
- end
221
+ end
308
222
 
309
223
 
310
- #
311
- # Used for synonym jump
312
- # Because we absolutely jump to a must-have word
313
- # So we can directly lookup to it
314
- #
315
- # Notice that, we must jump to a specific word definition
316
- # So in the toml file, you must specify the precise word.
317
- # If it has multiple meanings, for example
318
- #
319
- # [blah]
320
- # same = "xdg" # this is wrong, because xdg has multiple
321
- # # definitions, and all of them specify a
322
- # # category
323
- #
324
- # [blah]
325
- # same = "XDG downloader =>xdg.Download" # this is correct
326
- #
327
- # [blah]
328
- # name = "BlaH" # You may want to display a better name first
329
- # same = "XDG downloader =>xdg.Download" # this is correct
330
- #
331
- #
332
- def pp_same_info(library, dict, word, cache_content, same_key, own_name)
333
-
334
- # If it's a synonym for anther word,
335
- # we should lookup into this dict again, but maybe with a different file
336
-
337
- # file name
338
- x = word.chr.downcase
339
-
340
- #
341
- # dictionary maintainer must obey the rule for xxx.yyy word:
342
- # xxx should be lower case
343
- # yyy can be any case
344
- #
345
- # Because yyy should clearly explain the category info, IBM is better than ibm
346
- # Implementation should not be too simple if we want to stress the function we
347
- # expect.
348
- #
349
- # 'jump to' will output to user, so this is important not only inside our sheet.
350
- #
351
- # same = "XDM downloader=>xdm.Download"
352
- #
353
- # We split 'same' key into two parts via spaceship symbol `=>`, first part will
354
- # output to user, the second part is for internal jump.
355
- #
356
-
357
- jump_to, same = same_key.split("=>")
358
- same = jump_to if same.nil?
224
+ =begin
225
+ Used for synonym jump
226
+ Because we absolutely jump to a must-have word
227
+ So we can directly lookup to it
359
228
 
360
- unless own_name
361
- own_name = word
362
- end
363
- puts blue(bold(own_name)) + ' redirects to ' + blue(bold(jump_to))
229
+ Notice that, we must jump to a specific word definition
230
+ So in the toml file, you must specify the precise word.
231
+ If it has multiple meanings, for example
364
232
 
365
- #
366
- # As '.' is used to delimit a word and a category, what if
367
- # we jump to a dotted word?
368
- #
369
- # [eg]
370
- # same = "e.g." # this must lead to a wrong resolution to
371
- # # word 'e', category 'g'
372
- #
373
- # All you need is to be like this:
374
- #
375
- # [eg]
376
- # same = "'e.g.'" # cr will notice the single quote
377
- #
233
+ [blah]
234
+ same = "xdg" # this is wrong, because xdg has multiple
235
+ # definitions, and all of them specify a
236
+ # category
378
237
 
379
- if same =~ /^'(.*)'$/
380
- same, category = $1, nil
381
- else
382
- same, category = same.split('.')
383
- end
384
-
385
- if same.chr == x
386
- # No need to load another dictionary if match
387
- sheet_content = cache_content
388
- else
389
- sheet_content = load_sheet(library, dict, same.chr.downcase)
390
- end
238
+ [blah]
239
+ same = "XDG downloader =>xdg.Download" # this is correct
391
240
 
392
- if category.nil?
393
- info = sheet_content[same]
394
- else
395
- info = sheet_content[same][category]
396
- end
241
+ [blah]
242
+ name = "BlaH" # You may want to display a better name first
243
+ same = "XDG downloader =>xdg.Download" # this is correct
244
+ =end
245
+ def pp_same_info(library, dict, word, cache_content, same_key, own_name)
397
246
 
398
- if info.nil?
399
- puts red("WARN: Synonym jumps to the wrong place `#{same}`,
400
- Please consider fixing this in `#{x}.toml` of the dictionary `#{dict}`")
401
- # exit
402
- return false
403
- # double or more jumps
404
- elsif same_key = info['same']
405
- own_name = info['name']
406
- return pp_same_info(library, dict, same, cache_content, same_key, own_name)
407
- else
408
- pp_info(info)
409
- return true
410
- end
411
- end
247
+ # If it's a synonym for anther word,
248
+ # we should lookup into this dict again, but maybe with a different file
412
249
 
250
+ # file name
251
+ x = word.chr.downcase
413
252
 
253
+ =begin
254
+ dictionary maintainer must obey the rule for xxx.yyy word:
255
+ xxx should be lower case
256
+ yyy can be any case
414
257
 
415
- #
416
- # Lookup the given word in a sheet (a toml file) and also print.
417
- # The core idea is that:
418
- #
419
- # 1. if the word is `same` with another synonym, it will directly jump to
420
- # a word in this dictionary, but maybe a different sheet.
421
- #
422
- # 2. load the toml file and check the given word
423
- # 2.1 with no category specifier
424
- # [abcd]
425
- # 2.2 with category specifier
426
- # [abcd.tYPe]
427
- #
428
- def lookup(library, dict, file, word)
429
- sheet_content = load_sheet(library, dict, file)
430
- return false if sheet_content.nil?
258
+ Because yyy should clearly explain the category info, IBM is better than ibm
259
+ Implementation should not be too simple if we want to stress the function we
260
+ expect.
431
261
 
432
- info = sheet_content[word]
433
- return false if info.nil?
262
+ 'jump to' will output to user, so this is important not only inside our sheet.
434
263
 
435
- # Warn if the info is empty. For example:
436
- # emacs = { }
437
- if info.size == 0
438
- puts red("WARN: Lack of everything of the given word
439
- Please consider fixing this in the dict `#{dict}`")
440
- exit
441
- end
264
+ same = "XDM downloader=>xdm.Download"
442
265
 
266
+ We split 'same' key into two parts via spaceship symbol `=>`, first part will
267
+ output to user, the second part is for internal jump.
268
+ =end
269
+ jump_to, same = same_key.split("=>")
270
+ same = jump_to if same.nil?
443
271
 
444
- # Word with no category specifier
445
- # We call this meaning as type 1
446
- type_1_exist_flag = false
272
+ unless own_name
273
+ own_name = word
274
+ end
275
+ puts blue(bold(own_name)) + ' redirects to ' + blue(bold(jump_to))
447
276
 
448
- # if already jump, don't check the word itself
449
- is_jump = false
277
+ =begin
278
+ As '.' is used to delimit a word and a category, what if
279
+ we jump to a dotted word?
450
280
 
451
- # synonym info print
452
- if same_key = info['same']
453
- own_name = info['name']
454
- pp_dict(dict)
455
- pp_same_info(library, dict, word, sheet_content, same_key, own_name)
456
- # It's also a type 1
457
- type_1_exist_flag = true
458
- is_jump = true
459
- end
281
+ [eg]
282
+ same = "e.g." # this must lead to a wrong resolution to
283
+ # word 'e', category 'g'
460
284
 
461
- # normal info print
462
- # To developer:
463
- # The word should at least has one of `desc` and `more`
464
- # But when none exists, this may not be considered wrong,
465
- # Because the type2 make the case too.
466
- #
467
- # So, just ignore it, even if it's really a mistake(insignificant)
468
- # by dictionary maintainers.
469
- #
470
- if !is_jump && (info.has_key?('desc') || info.has_key?('more'))
471
- pp_dict(dict)
472
- pp_info(info)
473
- type_1_exist_flag = true
474
- end
285
+ All you need is to be like this:
475
286
 
476
- # Meanings with category specifier
477
- # We call this meaning as type 2
478
- categories = info.keys - ["name", "desc", "more", "same", "see"]
287
+ [eg]
288
+ same = "'e.g.'" # cr will notice the single quote
289
+ =end
290
+ if same =~ /^'(.*)'$/
291
+ same, category = $1, nil
292
+ else
293
+ same, category = same.split('.')
294
+ end
479
295
 
480
- if !categories.empty?
296
+ if same.chr == x
297
+ # No need to load another dictionary if match
298
+ sheet_content = cache_content
299
+ else
300
+ sheet_content = load_sheet(library, dict, same.chr.downcase)
301
+ end
481
302
 
482
- if type_1_exist_flag
483
- print blue(bold("OR")),"\n"
303
+ if category.nil?
304
+ info = sheet_content[same]
484
305
  else
485
- pp_dict(dict)
306
+ info = sheet_content[same][category]
486
307
  end
487
308
 
488
- categories.each do |meaning|
489
- info0 = sheet_content[word][meaning]
490
- if same_key = info0['same']
491
- own_name = info0['name']
492
- pp_same_info(library, dict, word, sheet_content, same_key, own_name)
493
- else
494
- pp_info(info0)
495
- end
496
-
497
- # last meaning doesn't show this separate line
498
- print blue(bold("OR")),"\n" unless categories.last == meaning
309
+ if info.nil?
310
+ puts red("WARN: Synonym jumps to the wrong place `#{same}`,
311
+ Please consider fixing this in `#{x}.toml` of the dictionary `#{dict}`")
312
+ # exit
313
+ return false
314
+ # double or more jumps
315
+ elsif same_key = info['same']
316
+ own_name = info['name']
317
+ return pp_same_info(library, dict, same, cache_content, same_key, own_name)
318
+ else
319
+ pp_info(info)
320
+ return true
499
321
  end
500
- return true
501
- elsif type_1_exist_flag
502
- return true
503
- else
504
- return false
505
322
  end
506
- end
507
323
 
508
324
 
509
- #
510
- # The main procedure of `cr`
511
- #
512
- # 1. Search the default library first
513
- # 2. Search the extra library if it does exist
514
- #
515
- # The `search` is done via the `lookup` function. It will print
516
- # the info while finding. If `lookup` always return false then
517
- # means lacking of this word in our dicts. So a welcomed
518
- # contribution is printed on the screen.
519
- #
520
- def resolve_word(word)
521
-
522
- add_default_dicts_if_none_exists
523
-
524
- word = word.downcase # downcase! would lead to frozen error in Ruby 2.7.2
525
- # The index is the toml file we'll look into
526
- index = word.chr
527
- case index
528
- when '0'..'9'
529
- index = '0-9'
530
- end
531
325
 
532
- # cache lookup's results
533
- results = []
534
-
535
- # First consider the default library
536
- default = Dir.children(CR_DEFAULT_LIBRARY)
537
- default.each do |dict|
538
- results << lookup(CR_DEFAULT_LIBRARY,dict,index,word)
539
- end
540
-
541
- # Then is the extra library
542
- if CR_EXTRA_LIBRARY
543
- extra = Dir.children(CR_EXTRA_LIBRARY)
544
- extra.each do |dict|
545
- results << lookup(CR_EXTRA_LIBRARY,dict,index,word)
546
- end
547
- end
326
+ #
327
+ # Lookup the given word in a sheet (a toml file) and also print.
328
+ # The core idea is that:
329
+ #
330
+ # 1. if the word is `same` with another synonym, it will directly jump to
331
+ # a word in this dictionary, but maybe a different sheet.
332
+ #
333
+ # 2. load the toml file and check the given word
334
+ # 2.1 with no category specifier
335
+ # [abcd]
336
+ # 2.2 with category specifier
337
+ # [abcd.tYPe]
338
+ #
339
+ def lookup(library, dict, file, word)
340
+ sheet_content = load_sheet(library, dict, file)
341
+ return false if sheet_content.nil?
548
342
 
549
- unless results.include? true
550
- puts <<-NotFound
551
- cr: Not found anything.
343
+ info = sheet_content[word]
344
+ return false if info.nil?
552
345
 
553
- You may
554
- 1. Use `cr -u` to update all dicts
346
+ # Warn if the info is empty. For example:
347
+ # emacs = { }
348
+ if info.size == 0
349
+ abort red("WARN: Lack of everything of the given word
350
+ Please consider fixing this in the dict `#{dict}`")
351
+ end
555
352
 
556
- 2. Use
557
-
558
- `cr -l` to list available official dicts
559
-
560
- `cr -a repo` to add some dicts
561
-
562
- 3. Contribute to theses dicts
353
+ # Word with no category specifier
354
+ # We call this meaning as type 1
355
+ type_1_exist_flag = false
563
356
 
564
- NotFound
357
+ # if already jump, don't check the word itself
358
+ is_jump = false
565
359
 
566
- else
567
- return
568
- end
569
-
570
- end
360
+ # synonym info print
361
+ if same_key = info['same']
362
+ own_name = info['name']
363
+ pp_dict(dict)
364
+ pp_same_info(library, dict, word, sheet_content, same_key, own_name)
365
+ # It's also a type 1
366
+ type_1_exist_flag = true
367
+ is_jump = true
368
+ end
571
369
 
370
+ # normal info print
371
+ # To developer:
372
+ # The word should at least has one of `desc` and `more`
373
+ # But when none exists, this may not be considered wrong,
374
+ # Because the type2 make the case too.
375
+ #
376
+ # So, just ignore it, even if it's really a mistake(insignificant)
377
+ # by dictionary maintainers.
378
+ #
379
+ if !is_jump && (info.has_key?('desc') || info.has_key?('more'))
380
+ pp_dict(dict)
381
+ pp_info(info)
382
+ type_1_exist_flag = true
383
+ end
572
384
 
573
- #
574
- # Delegate to `search_word_internal`
575
- #
576
- def search_word(pattern)
577
- found_or_not1 = false
578
- found_or_not2 = false
385
+ # Meanings with category specifier
386
+ # We call this meaning as type 2
387
+ categories = info.keys - ["name", "desc", "more", "same", "see"]
579
388
 
580
- found_or_not1 = search_word_internal(pattern, CR_DEFAULT_LIBRARY)
581
- if CR_EXTRA_LIBRARY
582
- found_or_not2 = search_word_internal(pattern, CR_EXTRA_LIBRARY)
583
- end
389
+ if !categories.empty?
584
390
 
585
- if (found_or_not1 == false) && (found_or_not2 == false)
586
- puts red("cr: No words match with #{pattern.inspect}")
587
- puts
588
- end
589
- end
391
+ if type_1_exist_flag
392
+ print blue(bold("OR")),"\n"
393
+ else
394
+ pp_dict(dict)
395
+ end
590
396
 
591
- #
592
- # This `search_word_internal` routine is quite like `resolve_word`
593
- # Notice:
594
- # We handle two cases
595
- #
596
- # 1. the 'pattern' is the regexp itself
597
- # 2. the 'pattern' is like '/blah/'
598
- #
599
- # The second is what Ruby and Perl users like to do, handle it!
600
- #
601
- def search_word_internal(pattern, library)
397
+ categories.each do |meaning|
398
+ info0 = sheet_content[word][meaning]
399
+ if same_key = info0['same']
400
+ own_name = info0['name']
401
+ pp_same_info(library, dict, word, sheet_content, same_key, own_name)
402
+ else
403
+ pp_info(info0)
404
+ end
602
405
 
603
- if pattern.nil?
604
- puts bold(red("cr: Need an argument!"))
605
- exit -1
406
+ # last meaning doesn't show this separate line
407
+ print blue(bold("OR")),"\n" unless categories.last == meaning
408
+ end
409
+ return true
410
+ elsif type_1_exist_flag then return true
411
+ else return false end
606
412
  end
607
413
 
608
- add_default_dicts_if_none_exists
609
-
610
- if pattern =~ /^\/(.*)\/$/
611
- regexp = %r[#$1]
612
- else
613
- regexp = %r[#{pattern}]
614
- end
615
-
616
- found_or_not = false
617
414
 
618
415
  #
619
- # Try to match every word in all dicts
416
+ # The main procedure of `cr`
620
417
  #
621
- Dir.children(library).each do |dict|
622
- sheets = Dir.children(File.join(library, dict)).select do
623
- _1.end_with?('.toml')
418
+ # 1. Search the default library first
419
+ # 2. Search the extra library if it does exist
420
+ #
421
+ # The `search` is done via the `lookup` function. It will print
422
+ # the info while finding. If `lookup` always return false then
423
+ # means lacking of this word in our dicts. So a welcomed
424
+ # contribution is printed on the screen.
425
+ #
426
+ def resolve_word(word)
427
+
428
+ word = word.downcase # downcase! would lead to frozen error in Ruby 2.7.2
429
+ # The index is the toml file we'll look into
430
+ index = word.chr
431
+ case index
432
+ when '0'..'9'
433
+ index = '0-9'
624
434
  end
625
435
 
626
- similar_words_in_a_dict = []
627
-
628
- sheets.each do |sheet|
629
- sheet_content = load_sheet(library, dict, File.basename(sheet,'.toml'))
436
+ # cache lookup's results
437
+ results = []
630
438
 
631
- sheet_content.keys.each do
632
- if _1 =~ regexp
633
- found_or_not = true
634
- similar_words_in_a_dict << _1
635
- end
636
- end
637
- # end of each sheet in a dict
439
+ # First consider the default library
440
+ default = Dir.children(DEFAULT_LIB_PATH)
441
+ default.each do |dict|
442
+ results << lookup(DEFAULT_LIB_PATH,dict,index,word)
638
443
  end
639
444
 
640
- unless similar_words_in_a_dict.empty?
641
- pp_dict(dict)
642
- require 'ls_table'
643
- LsTable.ls(similar_words_in_a_dict) do |e|
644
- puts blue(e)
445
+ # Then is the extra library
446
+ if @extra_lib_path
447
+ extra = Dir.children(@extra_lib_path)
448
+ extra.each do |dict|
449
+ results << lookup(@extra_lib_path,dict,index,word)
645
450
  end
646
- puts
647
451
  end
648
- end
649
- return found_or_not
650
- end
651
452
 
453
+ unless results.include? true
454
+ puts <<~NotFound
455
+ cr: Not found anything.
652
456
 
653
- def help
654
- word_count(p: false)
655
- puts <<-HELP
656
- cr: Cryptic Resolver v#{CR_GEM_VERSION} (#{$TwoLibWordCount} words: Default lib/#{$DefaultLibWordCount} && Extra lib/#{$ExtraLibWordCount})
457
+ You could
657
458
 
658
- Usage:
459
+ #{blue("case 1: Update all dicts")}
659
460
 
660
- cr emacs => Edit macros: A feature-rich editor
661
- cr -c => Print word count
662
- cr -l => List local dicts and official dicts
663
- cr -u => Update all dicts in Default library
664
-
665
- cr -a repo.git => Add a new dict
666
- cr -a user/repo => Add a new dict from Github
667
- cr -a repo => Add an official dict from Github
461
+ #{yellow("$cr -u")}
668
462
 
669
- cr -d cryptic_xx => Delete a dict
670
- cr -s pattern => Search words matched with pattern
671
- cr -v => Print version
672
- cr -h => Print this help
463
+ #{blue("case 2: List available official and feature dicts")}
673
464
 
674
- HELP
465
+ #{yellow("$cr -l")}
675
466
 
676
- add_default_dicts_if_none_exists
467
+ #{yellow("$cr -a repo")} (Add a specific dict to default lib)
677
468
 
678
- end
469
+ #{blue("case 3: Contribute to theses dicts")}
679
470
 
471
+ Visit: https://github.com/cryptic-resolver
680
472
 
681
- def print_version
682
- puts "cr: Cryptic Resolver version #{CR_GEM_VERSION} in Ruby "
683
- end
473
+ NotFound
474
+ else return end
475
+ end
684
476
 
685
477
 
686
- #
687
- # 1. List Default library's dicts
688
- # 2. List Extra library's dicts
689
- # 3. List official dicts
690
- #
691
- def list_dicts
692
- Dir.chdir CR_DEFAULT_LIBRARY do
693
- puts blue("=> Default library: #{CR_DEFAULT_LIBRARY}")
694
- Dir.children(CR_DEFAULT_LIBRARY).each_with_index do |dict,i|
695
- puts "#{blue(i+1)}. #{bold(green(dict))}"
478
+ #
479
+ # Delegate to `search_word_internal`
480
+ #
481
+ def search_word(pattern)
482
+ found_or_not1 = false
483
+ found_or_not2 = false
484
+
485
+ found_or_not1 = search_word_internal(pattern, DEFAULT_LIB_PATH)
486
+ if @extra_lib_path
487
+ found_or_not2 = search_word_internal(pattern, @extra_lib_path)
696
488
  end
697
- end
698
489
 
699
- if CR_EXTRA_LIBRARY
700
- puts
701
- Dir.chdir CR_EXTRA_LIBRARY do
702
- puts blue("=> Extra library: #{CR_EXTRA_LIBRARY}")
703
- Dir.children(CR_EXTRA_LIBRARY).each_with_index do |dict,i|
704
- puts "#{blue(i+1)}. #{bold(green(dict))}"
705
- end
490
+ if (found_or_not1 == false) && (found_or_not2 == false)
491
+ puts red("cr: No words match with #{pattern.inspect}") ; puts
706
492
  end
707
493
  end
708
494
 
709
- puts
710
- puts blue("=> Official dicts: (Add it by 'cr -a xxx')")
711
- puts CR_OFFICIAL_DICTS
712
-
713
- end
714
-
495
+ #
496
+ # This `search_word_internal` routine is quite like `resolve_word`
497
+ # Notice:
498
+ # We handle two cases
499
+ #
500
+ # 1. the 'pattern' is the regexp itself
501
+ # 2. the 'pattern' is like '/blah/'
502
+ #
503
+ # The second is what Ruby and Perl users like to do, handle it!
504
+ #
505
+ def search_word_internal(pattern, library)
715
506
 
716
- # Two libraries word count (Default library + Extra library)
717
- $TwoLibWordCount = 0
718
- # Default library word count
719
- $DefaultLibWordCount = 0
720
- # Extra library word count
721
- $ExtraLibWordCount = 0
507
+ if pattern.nil?
508
+ abort bold(red("cr: Need an argument!"))
509
+ end
722
510
 
723
- def count_dict_words(library, dict)
724
-
725
- dict_dir = library + "/#{dict}"
726
-
727
- wc = 0
511
+ if pattern =~ /^\/(.*)\/$/
512
+ regexp = %r[#$1]
513
+ else
514
+ regexp = %r[#{pattern}]
515
+ end
728
516
 
729
- Dir.children(dict_dir).each do |entry|
730
- next unless entry.end_with?('.toml')
731
- sheet_content = load_sheet(library, dict, entry.delete_suffix('.toml'))
732
- count = sheet_content.keys.count
517
+ found_or_not = false
733
518
 
734
- wc = wc + count
735
- end
736
- return wc
737
- end
519
+ # Try to match every word in all dicts
520
+ Dir.children(library).each do |dict|
521
+ sheets = Dir.children(File.join(library, dict)).select do
522
+ _1.end_with?('.toml')
523
+ end
738
524
 
525
+ similar_words_in_a_dict = []
739
526
 
740
- def word_count(p:)
741
- # Always check before Dir.children (this method creates dir if not exists)
742
- is_there_any_dict?
527
+ sheets.each do |sheet|
528
+ sheet_content = load_sheet(library, dict, File.basename(sheet,'.toml'))
743
529
 
744
- default_lib = Dir.children(CR_DEFAULT_LIBRARY)
530
+ sheet_content.keys.each do
531
+ if _1 =~ regexp
532
+ found_or_not = true
533
+ similar_words_in_a_dict << _1
534
+ end
535
+ end
536
+ # end of each sheet in a dict
537
+ end
745
538
 
746
- # Count default library
747
- unless default_lib.empty?
748
- puts(bold(green("Default library: "))) if p
749
- default_lib.each do |s|
750
- wc = count_dict_words(CR_DEFAULT_LIBRARY,s)
751
- $DefaultLibWordCount += wc
752
- # With color, ljust not works, so we disable color
753
- puts(" #{s.ljust(17)}: #{wc}") if p
539
+ unless similar_words_in_a_dict.empty?
540
+ pp_dict(dict)
541
+ require 'ls_table'
542
+ LsTable.ls(similar_words_in_a_dict) do |e|
543
+ puts blue(e)
544
+ end
545
+ puts
546
+ end
754
547
  end
548
+ return found_or_not
755
549
  end
756
550
 
757
551
 
758
- if CR_EXTRA_LIBRARY
759
- extra_lib = Dir.children(CR_EXTRA_LIBRARY)
760
- # Count extra library
761
- unless extra_lib.empty?
762
- wc = 0
763
- puts(bold(blue("\nExtra library:"))) if p
764
- extra_lib.each do |s|
765
- wc = count_dict_words(CR_EXTRA_LIBRARY,s)
766
- $ExtraLibWordCount += wc
767
- puts(" #{s.ljust(17)}: #{wc}") if p
552
+ # 1. List Default library's dicts
553
+ # 2. List Extra library's dicts
554
+ # 3. List official dicts
555
+ def list_dicts
556
+ Dir.chdir DEFAULT_LIB_PATH do
557
+ puts blue("=> Default library: #{DEFAULT_LIB_PATH}")
558
+ Dir.children(DEFAULT_LIB_PATH).each_with_index do |dict,i|
559
+ puts "#{blue(i+1)}. #{bold(green(dict))}"
560
+ end
561
+ end
562
+
563
+ if @extra_lib_path
564
+ puts
565
+ Dir.chdir @extra_lib_path do
566
+ puts blue("=> Extra library: #{@extra_lib_path}")
567
+ Dir.children(@extra_lib_path).each_with_index do |dict,i|
568
+ puts "#{blue(i+1)}. #{bold(green(dict))}"
569
+ end
768
570
  end
769
571
  end
572
+
573
+ puts ; puts blue("=> Official dicts: (Add it by 'cr -a xxx')")
574
+ puts RECOMMENDED_DICTS
770
575
  end
771
- $TwoLibWordCount = $DefaultLibWordCount + $ExtraLibWordCount
772
576
 
773
- if p
774
- puts
775
- puts "#{$DefaultLibWordCount.to_s.rjust(4)} words in Default library"
776
- puts "#{$ExtraLibWordCount.to_s.rjust(4) } words in Extra library"
777
- puts "#{$TwoLibWordCount.to_s.rjust(4) } words altogether"
577
+
578
+ def word_count
579
+ @counter.count!(display: true)
778
580
  end
779
- end
780
581
 
582
+ end
781
583
 
782
584
  ####################
783
585
  # main: CLI Handling
784
586
  ####################
785
- arg = ARGV.shift
786
-
787
- case arg
788
- when nil then help
789
- when '-v' then print_version
790
- when '-h' then help
791
- when '-l' then list_dicts
792
- when '-c' then word_count(p: true)
793
- when '-u' then update_dicts
794
- when '-s' then search_word ARGV.shift
795
- when '-a' then add_dict ARGV.shift
796
- when '-d' then del_dict ARGV.shift
797
- when '--help'
798
- help
799
- else
800
- resolve_word arg
801
- end
587
+ cli = CrypticResolver::CliHandler.new ARGV