Class: CrypticResolver::Resolver
- Inherits:
-
Object
- Object
- CrypticResolver::Resolver
- Defined in:
- lib/cr.rb,
lib/cr.rb
Defined Under Namespace
Classes: Counter
Constant Summary collapse
- DEFAULT_LIB_PATH =
attr_accessor :default_dicts # Default dictionaries lists
StandardPath.app_data 'CrypticResolver'
- ORIGINAL_DEFAULT_DICTS =
[ "https://github.com/CrypticResolver/cryptic-common.git", "https://github.com/CrypticResolver/cryptic-computer.git", "https://github.com/CrypticResolver/cryptic-windows.git", "https://github.com/CrypticResolver/cryptic-linux.git", ]
- RECOMMENDED_DICTS =
<<~EOF #{"Default:".yellow} common computer windows linux EOF
Instance Attribute Summary collapse
-
#counter ⇒ Object
default dictionaries lists.
-
#def_dicts ⇒ Object
default dictionaries lists.
-
#extra_lib_path ⇒ Object
default dictionaries lists.
Instance Method Summary collapse
- #_do_the_same_thing ⇒ Object
- #add_default_dicts_if_none_exists ⇒ Object
- #add_dict(repo) ⇒ Object
- #count_words ⇒ Object
- #del_dict(repo) ⇒ Object
-
#initialize ⇒ Resolver
constructor
A new instance of Resolver.
- #is_there_any_dict? ⇒ Boolean
-
#list_dicts ⇒ Object
1.
- #load_sheet(library, dict, sheet_name) ⇒ Object
-
#lookup(library, dict, file, word) ⇒ Object
Lookup the given word in a sheet (a toml file) and also print.
-
#pp_dict(dict) ⇒ Object
Print default cryptic_ dicts.
-
#pp_info(info) ⇒ Object
Pretty print the info of the given word.
-
#pp_same_info(library, dict, word, cache_content, same_key, own_name) ⇒ Object
Used for synonym jump Because we absolutely jump to a must-have word So we can directly lookup to it.
-
#resolve_word(word) ⇒ Object
The main procedure of ‘cr`.
-
#search_related_words(pattern) ⇒ Object
Delegate to ‘search_word_internal`.
-
#search_related_words_internal(pattern, library) ⇒ Object
This routine is quite like ‘resolve_word`.
-
#update_dicts ⇒ Object
Notice that we only update the Default library, not Extra library.
Constructor Details
#initialize ⇒ Resolver
Returns a new instance of Resolver.
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 |
# File 'lib/cr.rb', line 570 def initialize @counter = Counter.new(self) # if user doesn't specify, we use the hard-coded defaults @def_dicts = ORIGINAL_DEFAULT_DICTS # The config file will override the default dicts, but not default library! if file = ENV['CRYPTIC_RESOLVER_CONFIG'] if !test('f', file) abort "FATAL: Your CRYPTIC_RESOLVER_CONFIG is NOT a file!" end config = Tomlrb.load_file file if ret = config['DEFAULT_DICTS'] @def_dicts = ret end @extra_lib_path ||= config['EXTRA_LIBRARY'] # Prevent runtime error if ! test('d', @extra_lib_path) abort "FATAL: Your CRYPTIC_RESOLVER_CONFIG's option 'EXTRA_LIBRARY' is NOT a legal directory!" end else @extra_lib_path = nil end # Same with the pulled repo dirs' names in DEFAULT_LIB_PATH # @def_dicts_names = @def_dicts.map do |e| # e.split('/').last.split('.').first # end end |
Instance Attribute Details
#counter ⇒ Object
default dictionaries lists
566 567 568 |
# File 'lib/cr.rb', line 566 def counter @counter end |
#def_dicts ⇒ Object
default dictionaries lists
566 567 568 |
# File 'lib/cr.rb', line 566 def def_dicts @def_dicts end |
#extra_lib_path ⇒ Object
default dictionaries lists
566 567 568 |
# File 'lib/cr.rb', line 566 def extra_lib_path @extra_lib_path end |
Instance Method Details
#_do_the_same_thing ⇒ Object
504 505 506 507 508 509 510 511 512 |
# File 'lib/cr.rb', line 504 def _do_the_same_thing count = 0 Dir.children(".").each do |dict| next if File.file? dict count += 1 prefix = (count.to_s + '.').ljust 4 puts " #{prefix}#{dict}" end end |
#add_default_dicts_if_none_exists ⇒ Object
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 |
# File 'lib/cr.rb', line 617 def add_default_dicts_if_none_exists unless is_there_any_dict? puts "cr: Adding default dicts..." # This is used to display what you are pulling when adding dicts dicts_user_and_names = @def_dicts.map do |e| user, repo = e.split('/').last(2) repo = repo.split('.').first user + '/' + repo end begin if Gem.win_platform? # Windows doesn't have fork dicts_user_and_names.each_with_index do |name, i| puts "cr: Pulling #{name}..." `git -C "#{DEFAULT_LIB_PATH}" clone #{@def_dicts[i]} -q` end else # *nix-like dicts_user_and_names.each_with_index do |name, i| fork do puts "cr: Pulling #{name}..." `git -C "#{DEFAULT_LIB_PATH}" clone #{@def_dicts[i]} -q` end end Process.waitall end rescue Interrupt abort "cr: Cancel add default dicts" end puts "cr: Add done" ; @counter.count_def_lib(display: false) puts ; puts "#{@counter.word_count_of_def_lib} words added" @counter.reset! # Really added return true end # Not added return false end |
#add_dict(repo) ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/cr.rb', line 67 def add_dict(repo) if repo.nil? abort "cr: Need an argument!".bold.red end # Simplify adding dictionary if !repo.start_with?("https://") and !repo.start_with?("git@") if repo.include?('/') repo = "https://github.com/#{repo}.git" else repo = "https://github.com/CrypticResolver/cryptic-#{repo}.git" end end begin puts "cr: Adding new dictionary..." # Note that, we must add "" to surround the dir # Because some path (e.g. macOS) will have spaces `git -C "#{DEFAULT_LIB_PATH}" clone #{repo} -q` rescue Interrupt abort "cr: Cancel add dict" end puts "cr: Add new dictionary done" # github/com/ccmywish/ruby_knowledge(.git) dict = repo.split('/')[-1].delete_suffix('.git') wc = @counter.count_dict_words(DEFAULT_LIB_PATH ,dict) puts ; puts "#{wc} words added" end |
#count_words ⇒ Object
532 533 534 |
# File 'lib/cr.rb', line 532 def count_words @counter.count!(display: true) end |
#del_dict(repo) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/cr.rb', line 99 def del_dict(repo) if repo.nil? abort "cr: Need an argument!".bold.red end Dir.chdir DEFAULT_LIB_PATH do begin # Dir.rmdir repo # Can't rm a filled dir # FileUtils.rmdir repo # Can't rm a filled dir FileUtils.rm_rf repo puts "cr: Delete dictionary #{repo.bold.green} done" rescue Exception => e puts "cr: #{e}".bold.red list_dicts end end end |
#is_there_any_dict? ⇒ Boolean
607 608 609 610 611 612 613 614 |
# File 'lib/cr.rb', line 607 def is_there_any_dict? # Ensure the cr home dir exists FileUtils.mkdir_p(DEFAULT_LIB_PATH) #unless Dir.exist? DEFAULT_LIB_PATH # Dir.mkdir DEFAULT_LIB_PATH #end !Dir.empty? DEFAULT_LIB_PATH end |
#list_dicts ⇒ Object
-
List Default library’s dicts
-
List Extra library’s dicts
-
List official dicts
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 |
# File 'lib/cr.rb', line 502 def list_dicts def _do_the_same_thing count = 0 Dir.children(".").each do |dict| next if File.file? dict count += 1 prefix = (count.to_s + '.').ljust 4 puts " #{prefix}#{dict}" end end Dir.chdir DEFAULT_LIB_PATH do puts "Default library: #{DEFAULT_LIB_PATH}".bold.green _do_the_same_thing end if @extra_lib_path puts Dir.chdir @extra_lib_path do puts "Extra library: #{@extra_lib_path}".bold.green _do_the_same_thing end end puts ; puts "Official dictionaries: (Add it by 'cr -a xxx')".bold.green puts RECOMMENDED_DICTS end |
#load_sheet(library, dict, sheet_name) ⇒ Object
117 118 119 120 121 122 123 |
# File 'lib/cr.rb', line 117 def load_sheet(library, dict, sheet_name) file = library + "/#{dict}/#{sheet_name}.toml" if File.exist? file return Tomlrb.load_file file # gem 'tomlrb' # return TOML.load_file file # gem 'toml' else nil end end |
#lookup(library, dict, file, word) ⇒ Object
Lookup the given word in a sheet (a toml file) and also print. The core idea is that:
-
if the word is ‘same` with another synonym, it will directly jump to
a word in this dictionary, but maybe a different sheet.
-
load the toml file and check the given word
2.1 with no category specifier
[abcd]
2.2 with category specifier
[abcd.tYPe]
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 |
# File 'lib/cr.rb', line 292 def lookup(library, dict, file, word) sheet_content = load_sheet(library, dict, file) return false if sheet_content.nil? info = sheet_content[word] return false if info.nil? # Warn if the info is empty. For example: # emacs = { } if info.size == 0 abort "WARN: Lack of everything of the given word Please consider fixing this in the dict `#{dict}`".red end # Word with no category specifier # We call this meaning as type 1 type_1_exist_flag = false # if already jump, don't check the word itself is_jump = false # synonym info print if same_key = info['same'] own_name = info['name'] pp_dict(dict) pp_same_info(library, dict, word, sheet_content, same_key, own_name) # It's also a type 1 type_1_exist_flag = true is_jump = true end # normal info print # To developer: # The word should at least has one of `desc` and `more` # But when none exists, this may not be considered wrong, # Because the type2 make the case too. # # So, just ignore it, even if it's really a mistake(insignificant) # by dictionary maintainers. # if !is_jump && (info.has_key?('desc') || info.has_key?('more')) pp_dict(dict) pp_info(info) type_1_exist_flag = true end # Meanings with category specifier # We call this meaning as type 2 categories = info.keys - ["name", "desc", "more", "same", "see"] if !categories.empty? if type_1_exist_flag print "OR".bold.blue, "\n" else pp_dict(dict) end categories.each do |meaning| info0 = sheet_content[word][meaning] if same_key = info0['same'] own_name = info0['name'] pp_same_info(library, dict, word, sheet_content, same_key, own_name) else pp_info(info0) end # last meaning doesn't show this separate line print "OR".bold.blue, "\n" unless categories.last == meaning end return true elsif type_1_exist_flag then return true else return false end end |
#pp_dict(dict) ⇒ Object
Print default cryptic_ dicts
174 175 176 |
# File 'lib/cr.rb', line 174 def pp_dict(dict) puts "From: #{dict}".green end |
#pp_info(info) ⇒ Object
Pretty print the info of the given word
A info looks like this
emacs = {
name = "Emacs"
desc = "edit macros"
more = "a feature-rich editor"
see = ["Vim"]
}
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/cr.rb', line 139 def pp_info(info) name = info['name'] || "No name!".red # keyword `or` is invalid here in Ruby desc = info['desc'] more = info['more'] if desc puts "\n #{name}: #{desc}" print "\n ",more,"\n" if more else puts "\n #{name}" print "\n ",more,"\n" if more end if see_also = info['see'] print "\n", "SEE ALSO ".magenta if see_also.is_a?(Array) last_ndx = see_also.size - 1 see_also.each_with_index do |x,i| if last_ndx == i print x.underline # Last word doesn't show space else print x.underline, ' ' end end else print see_also.underline end puts end puts end |
#pp_same_info(library, dict, word, cache_content, same_key, own_name) ⇒ Object
Used for synonym jump
Because we absolutely jump to a must-have word
So we can directly lookup to it
Notice that, we must jump to a specific word definition
So in the toml file, you must specify the precise word.
If it has multiple meanings, for example
[blah]
same = "xdg" # this is wrong, because xdg has multiple
# definitions, and all of them specify a
# category
[blah]
same = "XDG downloader =>xdg.Download" # this is correct
[blah]
name = "BlaH" # You may want to display a better name first
same = "XDG downloader =>xdg.Download" # this is correct
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 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 270 271 272 273 274 275 276 277 |
# File 'lib/cr.rb', line 200 def pp_same_info(library, dict, word, cache_content, same_key, own_name) # If it's a synonym for anther word, # we should lookup into this dict again, but maybe with a different file # file name x = word.chr.downcase =begin dictionary maintainer must obey the rule for xxx.yyy word: xxx should be lower case yyy can be any case Because yyy should clearly explain the category info, IBM is better than ibm Implementation should not be too simple if we want to stress the function we expect. 'jump to' will output to user, so this is important not only inside our sheet. same = "XDM downloader=>xdm.Download" We split 'same' key into two parts via spaceship symbol `=>`, first part will output to user, the second part is for internal jump. =end jump_to, same = same_key.split("=>") same = jump_to if same.nil? unless own_name own_name = word end puts own_name.bold.blue + ' redirects to ' + jump_to.bold.blue =begin As '.' is used to delimit a word and a category, what if we jump to a dotted word? [eg] same = "e.g." # this must lead to a wrong resolution to # word 'e', category 'g' All you need is to be like this: [eg] same = "'e.g.'" # cr will notice the single quote =end if same =~ /^'(.*)'$/ same, category = $1, nil else same, category = same.split('.') end if same.chr == x # No need to load another dictionary if match sheet_content = cache_content else sheet_content = load_sheet(library, dict, same.chr.downcase) end if category.nil? info = sheet_content[same] else info = sheet_content[same][category] end if info.nil? puts "WARN: Synonym jumps to the wrong place `#{same}`, Please consider fixing this in `#{x}.toml` of the dictionary `#{dict}`".red # exit return false # double or more jumps elsif same_key = info['same'] own_name = info['name'] return pp_same_info(library, dict, same, cache_content, same_key, own_name) else pp_info(info) return true end end |
#resolve_word(word) ⇒ Object
The main procedure of ‘cr`
1. Search the default library first
2. Search the extra library if it does exist
The ‘search` is done via the `lookup` function. It will print the info while finding. If `lookup` always return false then means lacking of this word in our dicts. So a welcomed contribution is printed on the screen.
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 |
# File 'lib/cr.rb', line 378 def resolve_word(word) word = word.downcase # downcase! would lead to frozen error in Ruby 2.7.2 # The index is the toml file we'll look into index = word.chr case index when '0'..'9' index = '0-9' end # cache lookup's results results = [] # First consider the default library default = Dir.children(DEFAULT_LIB_PATH) default.each do |dict| results << lookup(DEFAULT_LIB_PATH,dict,index,word) end # Then is the extra library if @extra_lib_path extra = Dir.children(@extra_lib_path) extra.each do |dict| results << lookup(@extra_lib_path,dict,index,word) end end unless results.include? true puts <<~NotFound cr: Not found anything about '#{word}'. You could try #{"case 1: Update all dictionaries".blue} #{"$ cr -u".yellow} #{"case 2: List available official and feature dictionaries".blue} #{"$ cr -l".yellow} #{"$ cr -a repo".yellow} (Add a specific dict to default lib) #{"case 3: Contribute to theses dictionaries".blue} Visit: https://github.com/CrypticResolver NotFound else return end end |
#search_related_words(pattern) ⇒ Object
Delegate to ‘search_word_internal`
424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/cr.rb', line 424 def (pattern) found_or_not1 = false found_or_not2 = false found_or_not1 = (pattern, DEFAULT_LIB_PATH) if @extra_lib_path found_or_not2 = (pattern, @extra_lib_path) end if (found_or_not1 == false) && (found_or_not2 == false) puts "cr: No words match with #{pattern.inspect}".red ; puts end end |
#search_related_words_internal(pattern, library) ⇒ Object
This routine is quite like ‘resolve_word`
Notice:
We handle two cases
1. the 'pattern' is the regexp itself
2. the 'pattern' is like '/blah/'
The second is what Ruby and Perl users like to do, handle it!
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 |
# File 'lib/cr.rb', line 449 def (pattern, library) if pattern.nil? abort "cr: Need an argument!".bold.red end if pattern =~ /^\/(.*)\/$/ regexp = %r[#$1] else regexp = %r[#{pattern}] end found_or_not = false # Try to match every word in all dicts Dir.children(library).each do |dict| path = File.join(library, dict) next if File.file? path sheets = Dir.children(path).select do _1.end_with?('.toml') end similar_words_in_a_dict = [] sheets.each do |sheet| sheet_content = load_sheet(library, dict, File.basename(sheet,'.toml')) sheet_content.keys.each do if _1 =~ regexp found_or_not = true similar_words_in_a_dict << _1 end end # end of each sheet in a dict end unless similar_words_in_a_dict.empty? pp_dict(dict) require 'ls_table' LsTable.ls(similar_words_in_a_dict) do |e| puts e.blue end puts end end return found_or_not end |
#update_dicts ⇒ Object
Notice that we only update the Default library, not Extra library
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/cr.rb', line 22 def update_dicts @counter.count_def_lib(display: false) old_wc = @counter.word_count_of_def_lib puts "cr: Updating all dicts in Default library..." begin Dir.chdir DEFAULT_LIB_PATH do if Gem.win_platform? # Windows doesn't have fork Dir.children(DEFAULT_LIB_PATH).each do |dict| next if File.file? dict puts "cr: Wait to update #{dict}..." `git -C ./#{dict} pull -q` end else # *nix Dir.children(DEFAULT_LIB_PATH).each do |dict| next if File.file? dict fork do puts "cr: Wait to update #{dict}..." `git -C ./#{dict} pull -q` end end Process.waitall end # end if/else end rescue Interrupt abort "cr: Cancel update" end puts "cr: Update done" # clear @counter.word_count_of_def_lib = 0 # recount @counter.count_def_lib(display: false) new_wc = @counter.word_count_of_def_lib puts ; puts "#{new_wc - old_wc} words added in Default library" end |