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