#!/usr/bin/env ruby #coding: utf-8 require 'pathname' $APP_PATH = File.join(File.dirname(Pathname.new(__FILE__).realpath), "/") require 'yaml' #require 'set' begin require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /win32/ $COLORIZING = true rescue LoadError $COLORIZING = false end =begin start of code copied from cercasogno_dizmaker.rb note that the following code should never change!! =end DREAM_INFO = Struct.new("DREAM_INFO", :strDesc, :strMeaning, :nNumber, :aSubDescs, :strReferenceURL) =begin end of code copied from cercasogno_dizmaker.rb =end CConsoleInfo = Struct.new(:nRows, :nColumns, :bAutoReturn) class CIndexer def initialize(hData) @hExpanded = Hash.new hData.each do |strCompactIndex, value| raise "Tutti gli indici per questo dizionario devono essere di tipo String ma è stato trovato un oggetto di classe #{strCompactIndex.class}: \"#{strCompactIndex.inspect}\"" unless strCompactIndex.is_a? String raise "Tutti gli indici per questo dizionario devono essere stringhe con lunghezza almeno 1" if strCompactIndex.length < 1 getExpandedIndex(strCompactIndex).each do |strExp| raise "Un elemento precedentemente incontrato è già stato espanso in \"#{strExp}\"" if @hExpanded.include? strExp @hExpanded[strExp] = value end end #puts @hExpanded.inspect end def [](index) @hExpanded[index] end def getExactKeysByGuessing(strKey) if @hExpanded.include? strKey return [@hExpanded[strKey]] else #if the easy part didn't work, we need to put some extra effort in the search return getListOfCandidates(strKey) end end private def getExpandedIndex(strContracted) aSplit = strContracted.split(/[\\\/-]/).collect {|strItem| strItem.strip}.select {|strItem| strItem.length > 0} return [strContracted] if aSplit.length <= 1 aSuffixes = aSplit[1..-1] nSuffixLen = aSuffixes.inject(aSuffixes.first.length) do |memo, strSuffix| nLen = strSuffix.length(); strSuffix.length().between?(1, memo) ? strSuffix.length() : memo end strBase = aSplit.first nSuffixLen = strBase.length if strBase.length < nSuffixLen aSplit[0] = strBase[strBase.length - nSuffixLen..-1] strBase = (strBase.length > nSuffixLen ? strBase[0..(strBase.length - nSuffixLen - 1)] : "") aSplit.collect {|strSuffix| strBase + strSuffix} end def getListOfCandidates(strSearch) regSearch = /(?i)#{CIndexer::duplicateAccentedForRegex(strSearch)}/ getListOfCandidatesByRegex(regSearch) end def getListOfCandidatesByRegex(regSearch) aRet = Array.new @hExpanded.each_key do |strKey| aRet << strKey if regSearch =~ strKey end aRet end def self.duplicateAccentedForRegex(str) aAccents = [ ["a", "á", "à", "â", "ä", "ã"], ["A", "Ã", "Ä", "Â", "À", "Á"], ["e", "é", "è", "ê", "ë"], ["E", "Ë", "É", "È", "Ê"], ["i", "í", "ì", "î", "ï"], ["I", "Í", "Î", "Ì", "Ï"], ["o", "ó", "ò", "ô", "ö", "õ"], ["O", "Õ", "Ö", "Ô", "Ò", "Ó"], ["u", "ú", "ù", "û", "ü"], ["U", "Ú", "Û", "Ù", "Ü"], ["c", "ç"], ["C", "Ç"], ["n", "ñ"], ["N", "Ñ"] ] #sAccentedMix = Set.new(aAccents.flatten) hAccentedMix = Hash.new aAccents.each_index do |z| aAccents[z].each {|strLetter| hAccentedMix[strLetter] = z} end strRet = "" str.each_char do |strLetter| if hAccentedMix.include? strLetter then strRet += "[" + aAccents[hAccentedMix[strLetter]].join("|") + "]" else strRet += strLetter end end return strRet end end class CAnsiColorizer @@regStartsByDigit = /^\d/ def initialize(bEnabled=nil) @bEnabled = (bEnabled.nil? ? $COLORIZING : bEnabled) @hTemplates = Hash.new @hRegexToColor = Hash.new end def addTemplate(strName, nForeground, nBackground=nil, nMain=0) raise "Template names cannot start by a number" if strName.is_a?(String) && (@@regStartsByDigit =~ strName) @hTemplates[strName] = [nMain, nForeground, nBackground].compact.join(";") return true end def getColorized(strText, color) if color.is_a?(Numeric) || color.is_a?(String) && (@@regStartsByDigit =~ color) then return colorize(strText, color.to_s) else if @hTemplates.include?(color) then return colorize(strText, @hTemplates[color]) else return strText end end end def addAutoColorRegex(regRegex, color) raise "Invalid regex" unless regRegex.is_a? Regexp @hRegexToColor[regRegex] = color true end def getAutoColorized(strText) strRet = strText @hRegexToColor.each do |regMatch, color| m = regMatch.match(strText) nFrom = 0 while m do nOldLen = strRet.length strRet[(nFrom+m.begin(0))..(nFrom+m.end(0)-1)] = self.getColorized(m[0], color) nFrom += m.end(0) + strRet.length - nOldLen m = regMatch.match(strRet[nFrom..-1]) end end strRet end private def colorize(text, color_code) #source: http://kpumuk.info/ruby-on-rails/colorizing-console-ruby-script-output/ @bEnabled ? "\033[#{color_code}m#{text}\033[0m" : text end end def GetConsoleInfo() CConsoleInfo.new(40, 80, false) end def PutsMulticolumnFixedLength(aList, colorizer, nWidth=nil, bVerticalOrder=true, nForceMinSpacing=1) return 0 if aList.empty? nLen = aList.first.length strEOL = "\n" unless nWidth then ciInfo = GetConsoleInfo() strEOL = "" if ciInfo.bAutoReturn nWidth = ciInfo.nColumns end raise "Gli elementi devono avere lunghezza maggiore di 0" if nLen == 0 raise "Gli elementi non devono essere più lunghi dello spazio disponibile (#{nLen} > #{nWidth})" if nLen > nWidth nColumns = (nWidth < nLen + nForceMinSpacing ? 1 : nWidth / (nLen + nForceMinSpacing) ) strSpacing = " " * ((nWidth - nLen * nColumns) / nColumns) #raise "assert" if strSpacing.length < nForceMinSpacing nBlockLen = strSpacing.length + nLen #assert strSpacing >= 1 if !bVerticalOrder then nCount = nColumns (aList.length / nColumns).times do |z| nFrom = z * nColumns nCount = aList.length - nFrom if aList.length - nFrom < nCount print colorizer.getAutoColorized(aList[nFrom..(nFrom + nCount - 1)].collect {|s| s + strSpacing}.join("")) + strEOL end else nCount = nColumns nRows = aList.length / nColumns nLongerColumns = aList.length % nColumns ((aList.length + nColumns - 1) / nColumns).times do |z| nFrom = z * nColumns nCount = aList.length - nFrom if aList.length - nFrom < nCount aIndices = Array.new(nCount) {|i| i * nRows + [nLongerColumns, i].min + z} print colorizer.getAutoColorized(aList.values_at(*aIndices).collect {|s| s.to_s + strSpacing}.join("")) + strEOL end end end def Disambiguate(aList, colorizer) raise "Ricevuta una lista vuota" if aList.empty? return 0 if aList.length == 1 puts "Specifica meglio il termine della ricerca:" nLongestEntry = aList.inject(aList.first.length) do |memo, strItem| raise "Tutti gli elementi ricevuti devono essere di tipo String, ma è stato trovato un #{strItem.class}" unless strItem.is_a? String (strItem.length > memo ? strItem.length : memo) end strSeparator = " - " nLongestID = aList.length.to_s.length nRequiredCols = nLongestID + nLongestEntry + strSeparator.length aMenu = Array.new aList.each_index do |z| aMenu << (z + 1).to_s.ljust(nLongestID, " ") + strSeparator + aList[z].rjust(nLongestEntry, " ") end PutsMulticolumnFixedLength(aMenu, colorizer, nil, true, 2) bDone = false nIndex = 0 regInt = /^\d{1,3}$/ until bDone do strInp = $stdin.gets.chomp.strip if regInt =~ strInp then nIndexMaybe = strInp.to_i if nIndexMaybe.between?(0, aList.length) then nIndex = nIndexMaybe bDone = true end end end nIndex - 1 end def DrawMeaning(objDream, colorizer) raise "objDream can't be null" if objDream.nil? #:strDesc, :strMeaning, :nNumber, :aSubDescs, :strReferenceURL puts "#{colorizer.getColorized(objDream.strDesc.capitalize, :main)} (#{colorizer.getColorized(objDream.nNumber, :numeric)}):" puts objDream.strMeaning if objDream.strMeaning && objDream.strMeaning.length > 0 if objDream.aSubDescs then aMeanings = Array.new objDream.aSubDescs.each do |objSubDesc| aMeanings << "#{colorizer.getColorized(objSubDesc.strDesc, :desc)}: #{colorizer.getColorized(objSubDesc.strMeaning, :normal)}; #{colorizer.getColorized(objSubDesc.nNumber, :numeric)}" end puts aMeanings.sort.join("\n") end nil end if ARGV.length == 0 then puts "Specificare il tema della ricerca" exit end #hDreams = YAML::load(File.read(File.join($APP_PATH, "cercasogno_diz.yml"))) hDreams = nil File.open(File.join($APP_PATH, "cercasogno_diz.yml"), 'r') {|fh| hDreams = YAML.load(fh) } #puts hDreams.length indexer = CIndexer.new(hDreams) colorizer = CAnsiColorizer.new colorizer.addAutoColorRegex(/\d+/, :numeric) colorizer.addTemplate(:numeric, 36) colorizer.addTemplate(:main, 31, nil, 4) colorizer.addTemplate(:desc, 33) aGuess = indexer.getExactKeysByGuessing(ARGV.first) nSelection = 0 if aGuess.length == 0 then puts "Nessun risultato per \"#{ARGV.first}\"" exit elsif aGuess.length > 1 then nSelection = Disambiguate(aGuess, colorizer) end DrawMeaning(indexer[aGuess[nSelection]], colorizer)