diff --git a/app/views/shared/_entry.erb b/app/views/shared/_entry.erb
index 79e494f..07a6320 100644
--- a/app/views/shared/_entry.erb
+++ b/app/views/shared/_entry.erb
@@ -8,7 +8,7 @@
-<% VimGolf::Keylog.new(entry.script).each do |key| %><% if key.size > 1 %><%= key %><% else %><%= key %><% end %><% end %>
+<% VimGolf::Keylog.new(entry.script, entry.created_at).each do |key| %><% if key.size > 1 %><%= key %><% else %><%= key %><% end %><% end %>
<% entry.comments.each do |c| %>
diff --git a/lib/vimgolf/lib/vimgolf/challenge.rb b/lib/vimgolf/lib/vimgolf/challenge.rb
index 25996f4..1f3c368 100644
--- a/lib/vimgolf/lib/vimgolf/challenge.rb
+++ b/lib/vimgolf/lib/vimgolf/challenge.rb
@@ -91,7 +91,7 @@ def upload
proxy.start(url.host, url.port) do |http|
request = Net::HTTP::Post.new(url.request_uri)
- request.set_form_data({"challenge_id" => @id, "apikey" => Config.load['key'], "entry" => IO.read(log_path)})
+ request.set_form_data({"challenge_id" => @id, "apikey" => Config.load['key'], "entry" => IO.binread(log_path)})
request["Accept"] = "application/json"
res = http.request(request)
diff --git a/lib/vimgolf/lib/vimgolf/cli.rb b/lib/vimgolf/lib/vimgolf/cli.rb
index 44fcff8..96fb859 100644
--- a/lib/vimgolf/lib/vimgolf/cli.rb
+++ b/lib/vimgolf/lib/vimgolf/cli.rb
@@ -114,7 +114,7 @@ def play(challenge)
system(*vimcmd) # assembled as an array, bypasses the shell
if $?.exitstatus.zero?
- log = Keylog.new(IO.read(challenge.log_path))
+ log = Keylog.new(IO.binread(challenge.log_path))
VimGolf.ui.info "\nHere are your keystrokes:"
VimGolf.ui.print_log log
diff --git a/lib/vimgolf/lib/vimgolf/keylog.rb b/lib/vimgolf/lib/vimgolf/keylog.rb
index ee79b40..3a94767 100644
--- a/lib/vimgolf/lib/vimgolf/keylog.rb
+++ b/lib/vimgolf/lib/vimgolf/keylog.rb
@@ -1,258 +1,268 @@
-# encoding: UTF-8
+# encoding: ASCII-8BIT
+# Force encoding of string literals. Must match solution text.
module VimGolf
class Keylog
include Enumerable
- alias_method :convert , :to_s
- alias_method :score , :count
-
- def initialize(input)
- @input = input
+ def initialize(input, time=Time.now.utc)
+ # Force encoding of solution text. Must match string literals.
+ # .force_encoding CHANGES THE ORIGINAL STRING!
+ @input = input.force_encoding(Encoding::ASCII_8BIT)
+ @time = time
end
def to_s(sep = '')
to_a.join(sep)
end
+ alias_method :convert , :to_s
+ alias_method :score , :count
+
def each
scanner = StringScanner.new(@input)
- output = ""
-
- until scanner.eos?
- c = scanner.get_byte
- n = c.unpack('C').first
-
- out_char = \
- case n
-
- # Special platform-independent encoding stuff
- when 0x80
- code = scanner.get_byte + scanner.get_byte
- # This list has been populated by looking at
- # :h terminal-options and vim source files:
- # keymap.h and misc2.c
- case code
- when "k1"; ""
- when "k2"; ""
- when "k3"; ""
- when "k4"; ""
- when "k5"; ""
- when "k6"; ""
- when "k7"; ""
- when "k8"; ""
- when "k9"; ""
- when "k;"; ""
- when "F1"; ""
- when "F2"; ""
- when "F3"; ""
- when "F4"; ""
- when "F5"; ""
- when "F6"; ""
- when "F7"; ""
- when "F8"; ""
- when "F9"; ""
-
- when "%1"; ""
- when "&8"; ""
- when "#2"; ""
- when "*7"; ""
- when "K1"; ""
- when "K4"; ""
- when "K3"; ""
- when "K5"; ""
- when "K6"; ""
- when "K7"; ""
- when "K8"; ""
- when "K9"; ""
- when "KA"; ""
- when "KB"; ""
- when "KC"; ""
- when "KD"; ""
- when "KE"; ""
- when "KF"; ""
- when "KG"; ""
- when "KH"; ""
- when "KI"; ""
- when "KJ"; ""
- when "KK"; ""
- when "KL"; ""
-
- when "kP"; ""
- when "kN"; ""
- when "kh"; ""
- when "@7"; ""
- when "kI"; ""
- when "kD"; ""
- when "kb"; ""
+ # A Vim keycode is either a single byte, or a 3-byte sequence starting
+ # with 0x80.
+ while (c = scanner.get_byte)
+ n = c.ord
+ if n == 0x80
+ b2, b3 = scanner.get_byte, scanner.get_byte
+ if b2 == "\xfd" && b3 >= "\x38" && @time > SNIFF_DATE
+ # Should we account for KE_SNIFF removal?
+ b3 = (b3.ord + 1).chr
+ end
+ code = KC_MBYTE[b2+b3]
+ yield code if code # ignore "nil" keystrokes (like window focus)
+ else
+ yield KC_1BYTE[n]
+ end
+ end
+ end
- when "ku"; ""
- when "kd"; ""
- when "kl"; ""
- when "kr"; ""
- when "#4"; ""
- when "%i"; ""
+ # Quick lookup array for single-byte keycodes
+ KC_1BYTE = []
+ (0..255).each {|n| KC_1BYTE.push("<%#04x>" % n)} # Fallback for non-ASCII
+ (1..127).each {|n| KC_1BYTE[n] = ""}
+ (32..126).each {|c| KC_1BYTE[c] = c.chr } # Printing chars
+ KC_1BYTE[0x1b] = "" # Special names for a few control chars
+ KC_1BYTE[0x0d] = ""
+ KC_1BYTE[0x0a] = ""
+ KC_1BYTE[0x09] = ""
- when "kB"; ""
- when "\xffX"; ""
+ # After this date, assume KE_SNIFF is removed
+ SNIFF_DATE = Time.utc(2016, 4)
- # This is how you escape literal 0x80
- when "\xfeX"; "<0x80>"
+ KC_MBYTE = Hash.new do |_h,k|
+ '<' + k.bytes.map {|b| "%02x" % b}.join('-') + '>' # For missing keycodes
+ end.update({
+ # This list has been populated by looking at
+ # :h terminal-options and vim source files:
+ # keymap.h and misc2.c
+ "k1" => "",
+ "k2" => "",
+ "k3" => "",
+ "k4" => "",
+ "k5" => "",
+ "k6" => "",
+ "k7" => "",
+ "k8" => "",
+ "k9" => "",
+ "k;" => "",
+ "F1" => "",
+ "F2" => "",
+ "F3" => "",
+ "F4" => "",
+ "F5" => "",
+ "F6" => "",
+ "F7" => "",
+ "F8" => "",
+ "F9" => "",
- # These rarely-used modifiers should be combined with the next
- # stroke (like ), but let's put them here for now
- when "\xfc\x02"; ""
- when "\xfc\x04"; ""
- when "\xfc\x06"; ""
- when "\xfc\x08"; ""
- when "\xfc\x0a"; ""
- when "\xfc\x0c"; ""
- when "\xfc\x0e"; ""
- when "\xfc\x10"; ""
- when "\xfc\x12"; ""
- when "\xfc\x14"; ""
- when "\xfc\x16"; ""
- when "\xfc\x18"; ""
- when "\xfc\x1a"; ""
- when "\xfc\x1c"; ""
- when "\xfc\x1e"; ""
+ "%1" => "",
+ "&8" => "",
+ "#2" => "",
+ "*7" => "",
+ "K1" => "",
+ "K4" => "",
+ "K3" => "",
+ "K5" => "",
+ "K6" => "",
+ "K7" => "",
+ "K8" => "",
+ "K9" => "",
+ "KA" => "",
+ "KB" => "",
+ "KC" => "",
+ "KD" => "",
+ "KE" => "",
+ "KF" => "",
+ "KG" => "",
+ "KH" => "",
+ "KI" => "",
+ "KJ" => "",
+ "KK" => "",
+ "KL" => "",
- when "\xfd\x4"; ""
- when "\xfd\x5"; ""
- when "\xfd\x6"; ""
- when "\xfd\x7"; ""
- when "\xfd\x8"; ""
- when "\xfd\x9"; ""
- when "\xfd\xa"; ""
- when "\xfd\xb"; ""
- when "\xfd\xc"; ""
- when "\xfd\xd"; ""
- when "\xfd\xe"; ""
- when "\xfd\xf"; ""
- when "\xfd\x10"; ""
- when "\xfd\x11"; ""
- when "\xfd\x12"; ""
- when "\xfd\x13"; ""
- when "\xfd\x14"; ""
- when "\xfd\x15"; ""
- when "\xfd\x16"; ""
- when "\xfd\x17"; ""
- when "\xfd\x18"; ""
- when "\xfd\x19"; ""
- when "\xfd\x1a"; ""
- when "\xfd\x1b"; ""
- when "\xfd\x1c"; ""
- when "\xfd\x1d"; ""
- when "\xfd\x1e"; ""
- when "\xfd\x1f"; ""
- when "\xfd\x20"; ""
- when "\xfd\x21"; ""
- when "\xfd\x22"; ""
- when "\xfd\x23"; ""
- when "\xfd\x24"; ""
- when "\xfd\x25"; ""
- when "\xfd\x26"; ""
- when "\xfd\x27"; ""
- when "\xfd\x28"; ""
- when "\xfd\x29"; ""
- when "\xfd\x2a"; ""
- when "\xfd\x2b"; ""
- when "\xfd\x2c"; ""
- when "\xfd\x2d"; ""
- when "\xfd\x2e"; ""
- when "\xfd\x2f"; ""
- when "\xfd\x30"; ""
- when "\xfd\x31"; ""
- when "\xfd\x32"; ""
- when "\xfd\x33"; ""
- when "\xfd\x34"; ""
- when "\xfd\x35"; nil # KE_IGNORE
- #when "\xfd\x36"; "KE_TAB"
- #when "\xfd\x37"; "KE_S_TAB_OLD"
- #when "\xfd\x38"; "KE_SNIFF"
- #when "\xfd\x39"; "KE_XF1"
- #when "\xfd\x3a"; "KE_XF2"
- #when "\xfd\x3b"; "KE_XF3"
- #when "\xfd\x3c"; "KE_XF4"
- #when "\xfd\x3d"; "KE_XEND"
- #when "\xfd\x3e"; "KE_ZEND"
- #when "\xfd\x3f"; "KE_XHOME"
- #when "\xfd\x40"; "KE_ZHOME"
- #when "\xfd\x41"; "KE_XUP"
- #when "\xfd\x42"; "KE_XDOWN"
- #when "\xfd\x43"; "KE_XLEFT"
- #when "\xfd\x44"; "KE_XRIGHT"
- #when "\xfd\x45"; "KE_LEFTMOUSE_NM"
- #when "\xfd\x46"; "KE_LEFTRELEASE_NM"
- #when "\xfd\x47"; "KE_S_XF1"
- #when "\xfd\x48"; "KE_S_XF2"
- #when "\xfd\x49"; "KE_S_XF3"
- #when "\xfd\x4a"; "KE_S_XF4"
- when "\xfd\x4b"; ""
- when "\xfd\x4c"; ""
+ "kP" => "",
+ "kN" => "",
+ "kh" => "",
+ "@7" => "",
+ "kI" => "",
+ "kD" => "",
+ "kb" => "",
- # Horizontal scroll wheel support was added in Vim 7.3c. These
- # 2 entries shifted the rest of the KS_EXTRA mappings down 2.
- # Though Vim 7.2 is rare today, it was common soon after
- # vimgolf.com was launched. In cases where the 7.3 code is
- # never used but the 7.2 code was common, it makes sense to use
- # the 7.2 code. There are conflicts though, so some legacy
- # keycodes have to stay wrong.
- when "\xfd\x4d"; ""
- when "\xfd\x4e"; ""
- when "\xfd\x4f"; ""
- when "\xfd\x50"; ""
- when "\xfd\x51"; "<0x9b>" # :help
- #when "\xfd\x52"; "KE_SNR"
- #when "\xfd\x53"; "KE_PLUG" # never used
- when "\xfd\x53"; "" # 7.2 compat
- #when "\xfd\x54"; "KE_CMDWIN" # never used
- when "\xfd\x54"; "" # 7.2 compat
- when "\xfd\x55"; "" # 7.2 conflict
- when "\xfd\x56"; "" # 7.2 conflict
- when "\xfd\x57"; ""
- when "\xfd\x58"; ""
- #when "\xfd\x59"; "KE_X1MOUSE"
- #when "\xfd\x5a"; "KE_X1DRAG"
- #when "\xfd\x5b"; "KE_X1RELEASE"
- #when "\xfd\x5c"; "KE_X2MOUSE"
- #when "\xfd\x5d"; "KE_X2DRAG"
- #when "\xfd\x5e"; "KE_X2RELEASE"
- when "\xfd\x5e"; nil # 7.2 compat (I think?)
- #when "\xfd\x5f"; "KE_DROP"
- #when "\xfd\x60"; "KE_CURSORHOLD"
- when "\xfd\x60"; nil # 7.2 Focus Gained compat
- #when "\xfd\x61"; "KE_NOP"
- when "\xfd\x62"; nil # Focus Gained (GVIM)
- when "\xfd\x63"; nil # Focus Lost (GVIM)
+ "ku" => "",
+ "kd" => "",
+ "kl" => "",
+ "kr" => "",
+ "#4" => "",
+ "%i" => "",
- else
- #puts "Unknown Vim code: #{code.inspect}"
- '<%02x-%02x>' % code.unpack('CC')
- end
+ "kB" => "",
+ "\xffX" => "",
- # Printable ASCII
- when 32..126; c
+ # This is how you escape literal 0x80
+ "\xfeX" => "<0x80>",
- # Control characters with special names
- when 0; ""
- when 9; ""
- when 10; ""
- when 13; ""
- when 27; ""
+ # These rarely-used modifiers should be combined with the next
+ # stroke (like ), but let's put them here for now
+ "\xfc\x02" => "",
+ "\xfc\x04" => "",
+ "\xfc\x06" => "",
+ "\xfc\x08" => "",
+ "\xfc\x0a" => "",
+ "\xfc\x0c" => "",
+ "\xfc\x0e" => "",
+ "\xfc\x10" => "",
+ "\xfc\x12" => "",
+ "\xfc\x14" => "",
+ "\xfc\x16" => "",
+ "\xfc\x18" => "",
+ "\xfc\x1a" => "",
+ "\xfc\x1c" => "",
+ "\xfc\x1e" => "",
- # Otherwise, use format. Flip bit 7
- when 0..127; ""
+ # KS_EXTRA keycodes (starting with 0x80 0xfd) are defined by an enum in
+ # Vim's keymap.h. Sometimes, a new Vim adds or removes a keycode, which
+ # changes the binary representation of every keycode after it. Very
+ # annoying.
+ "\xfd\x4" => "",
+ "\xfd\x5" => "",
+ "\xfd\x6" => "",
+ "\xfd\x7" => "",
+ "\xfd\x8" => "",
+ "\xfd\x9" => "",
+ "\xfd\xa" => "",
+ "\xfd\xb" => "",
+ "\xfd\xc" => "",
+ "\xfd\xd" => "",
+ "\xfd\xe" => "",
+ "\xfd\xf" => "",
+ "\xfd\x10" => "",
+ "\xfd\x11" => "",
+ "\xfd\x12" => "",
+ "\xfd\x13" => "",
+ "\xfd\x14" => "",
+ "\xfd\x15" => "",
+ "\xfd\x16" => "",
+ "\xfd\x17" => "",
+ "\xfd\x18" => "",
+ "\xfd\x19" => "",
+ "\xfd\x1a" => "",
+ "\xfd\x1b" => "",
+ "\xfd\x1c" => "",
+ "\xfd\x1d" => "",
+ "\xfd\x1e" => "",
+ "\xfd\x1f" => "",
+ "\xfd\x20" => "",
+ "\xfd\x21" => "