cr.rb 4.0.3 → 4.1.1

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