cercasogno/cercasogno.rb
2017-01-06 12:41:43 +00:00

294 lines
8.8 KiB
Ruby

#!/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)