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.
- checksums.yaml +4 -4
- data/bin/cr +418 -646
- data/lib/cr/version.rb +16 -0
- data/lib/cr.rb +235 -14
- 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-
|
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
|
-
|
17
|
+
class CrypticResolver::CliHandler
|
21
18
|
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
-
|
24
|
+
reslvr = CrypticResolver::Resolver.new
|
37
25
|
|
38
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
73
|
-
|
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
|
-
|
97
|
-
unless Dir.exist? CR_DEFAULT_LIBRARY
|
98
|
-
Dir.mkdir CR_DEFAULT_LIBRARY
|
99
|
-
end
|
66
|
+
class CrypticResolver::Resolver
|
100
67
|
|
101
|
-
|
102
|
-
|
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
|
-
|
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
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
124
|
-
|
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
|
-
|
129
|
-
exit 1
|
99
|
+
abort "cr: Cancel update"
|
130
100
|
end
|
131
101
|
|
132
|
-
puts "cr:
|
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
|
-
#
|
146
|
-
|
147
|
-
|
104
|
+
# clear
|
105
|
+
@counter.word_count_of_def_lib = 0
|
106
|
+
# recount
|
107
|
+
@counter.count_def_lib(display: false)
|
148
108
|
|
149
|
-
|
150
|
-
|
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
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
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
|
-
|
128
|
+
begin
|
215
129
|
puts "cr: Adding new dictionary..."
|
216
|
-
`git -C #{
|
217
|
-
|
218
|
-
|
219
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
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
|
-
|
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
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
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
|
-
|
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
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
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
|
-
|
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
|
-
#
|
344
|
-
|
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
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
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
|
-
|
392
|
-
|
393
|
-
|
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
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
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
|
-
|
405
|
-
|
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
|
-
|
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
|
-
|
439
|
-
|
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
|
-
|
442
|
-
|
443
|
-
|
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
|
-
|
451
|
-
# We call this meaning as type 1
|
452
|
-
type_1_exist_flag = false
|
264
|
+
same = "XDM downloader=>xdm.Download"
|
453
265
|
|
454
|
-
|
455
|
-
|
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
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
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
|
-
|
468
|
-
|
469
|
-
|
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
|
-
|
483
|
-
|
484
|
-
|
281
|
+
[eg]
|
282
|
+
same = "e.g." # this must lead to a wrong resolution to
|
283
|
+
# word 'e', category 'g'
|
485
284
|
|
486
|
-
|
285
|
+
All you need is to be like this:
|
487
286
|
|
488
|
-
|
489
|
-
|
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
|
-
|
293
|
+
same, category = same.split('.')
|
492
294
|
end
|
493
295
|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
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
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
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
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
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
|
-
|
570
|
-
|
571
|
-
#
|
572
|
-
|
573
|
-
|
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
|
-
|
343
|
+
info = sheet_content[word]
|
344
|
+
return false if info.nil?
|
576
345
|
|
577
|
-
|
578
|
-
|
579
|
-
|
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
|
-
|
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
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
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
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
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
|
-
|
597
|
-
|
598
|
-
|
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
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
391
|
+
if type_1_exist_flag
|
392
|
+
print blue(bold("OR")),"\n"
|
393
|
+
else
|
394
|
+
pp_dict(dict)
|
395
|
+
end
|
618
396
|
|
619
|
-
|
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
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
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
|
-
#
|
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
|
-
|
633
|
-
|
634
|
-
|
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
|
-
|
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
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
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
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
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
|
-
|
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
|
-
|
459
|
+
#{blue("case 1: Update all dicts")}
|
670
460
|
|
671
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
467
|
+
#{yellow("$cr -a repo")} (Add a specific dict to default lib)
|
686
468
|
|
687
|
-
|
469
|
+
#{blue("case 3: Contribute to theses dicts")}
|
688
470
|
|
689
|
-
|
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
|
-
|
699
|
-
|
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
|
-
|
711
|
-
|
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
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
#
|
728
|
-
|
729
|
-
#
|
730
|
-
|
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
|
-
|
507
|
+
if pattern.nil?
|
508
|
+
abort bold(red("cr: Need an argument!"))
|
509
|
+
end
|
737
510
|
|
738
|
-
|
511
|
+
if pattern =~ /^\/(.*)\/$/
|
512
|
+
regexp = %r[#$1]
|
513
|
+
else
|
514
|
+
regexp = %r[#{pattern}]
|
515
|
+
end
|
739
516
|
|
740
|
-
|
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
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
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
|
-
|
752
|
-
|
753
|
-
is_there_any_dict?
|
527
|
+
sheets.each do |sheet|
|
528
|
+
sheet_content = load_sheet(library, dict, File.basename(sheet,'.toml'))
|
754
529
|
|
755
|
-
|
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
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
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
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
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
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
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
|
-
|
788
|
-
|
789
|
-
|
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
|
-
|
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
|