cr.rb 4.0.2 → 4.1.0

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 (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