Skip to content

Instantly share code, notes, and snippets.

@aereal
Created November 8, 2014 15:30
Show Gist options
  • Save aereal/f490ee9a7632dae53582 to your computer and use it in GitHub Desktop.
Save aereal/f490ee9a7632dae53582 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'optparse'
require 'logger'
HISTORY_PATTERN = /\A: (?<started>\d+):(?<finished>\d+);(?<text>.+)\z/o.freeze
IGNORE_ENTRY_PATTERN = /\A(?:l[sla]?|man|git st\z)/o.freeze
REQUIRED_OPTION_NAMES = [:output_file, :input_file, :lines_to_read]
Config = Struct.new(:output_file, :input_file, :lines_to_read).new
def logger
@logger ||= Logger.new($stderr)
end
# maybe_matched:Maybe[MatchData] => entry:Hash
def ensure_history_entry(maybe_matched)
maybe_matched ? extract_matched_data(maybe_matched) : {}
end
# matched:MatchData => data:Hash
def extract_matched_data(matched)
matched.names.each_with_object({}) {|name, hash| hash[name] = matched[name] }
end
# line:String => entry:Hash
def extract_entry(line)
matched = line.match(HISTORY_PATTERN) rescue nil # see also Src/zsh.h
entry = ensure_history_entry(matched)
entry.merge('line' => line)
end
# entry:Hash => line:String
def serialize_entry(entry)
': %d:%d;%s' % entry.values_at('started', 'finished', 'text')
rescue => e
logger.error("fail: #{entry['line']}; because: #{e}")
''
end
def fetch_lines(enum, n)
n < 0 ? enum : enum.take(n)
end
option_parser = OptionParser.new do |o|
o.on('-o', '--output-file OUTPUT_FILE') {|v| Config.output_file = v }
o.on('-i', '--input-file INPUT_FILE') {|v| Config.input_file = v }
o.on('-n', '--lines-to-read N') {|v| Config.lines_to_read = v.to_i }
end
option_parser.parse!(ARGV)
REQUIRED_OPTION_NAMES.each do |name|
option_parser.abort("Missing argument: #{name}") unless Config.send(name)
end
lines = fetch_lines(IO.foreach(Config.input_file), Config.lines_to_read)
out = lines.lazy.
map {|line| extract_entry(line.strip) }.
reject {|entry| IGNORE_ENTRY_PATTERN === entry['text'] }.
force.
uniq {|entry| entry['text'] }.
sort_by {|entry| entry['started'].to_i }.
map {|entry| serialize_entry(entry) }
open(Config.output_file, 'w') do |f|
f.puts(*out)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment