vimgolf 0.4.4 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +2 -10
- data/lib/vimgolf.rb +3 -0
- data/lib/vimgolf/challenge.rb +44 -11
- data/lib/vimgolf/cli.rb +85 -32
- data/lib/vimgolf/keylog.rb +253 -207
- data/lib/vimgolf/ui.rb +13 -1
- data/lib/vimgolf/version.rb +1 -1
- data/lib/vimgolf/vimgolf.vimrc +28 -0
- metadata +67 -89
- data/.gitignore +0 -4
- data/.gitmodules +0 -3
- data/.rspec +0 -0
- data/Gemfile +0 -4
- data/Gemfile.lock +0 -30
- data/Rakefile +0 -10
- data/emacs/README.md +0 -69
- data/emacs/ROADMAP.md +0 -69
- data/emacs/features/emacs.feature +0 -24
- data/emacs/features/step-definitions/emacs-steps.el +0 -53
- data/emacs/features/support/env.el +0 -37
- data/emacs/todo.org +0 -45
- data/emacs/vimgolf.el +0 -484
- data/spec/challenge_spec.rb +0 -31
- data/spec/cli_spec.rb +0 -25
- data/spec/fixtures/4d19832d8ae121365c00000b.log +0 -1
- data/spec/fixtures/4d1a1c36567bac34a9000002.log +0 -1
- data/spec/fixtures/4d1a1c69567bac34a9000004.log +0 -1
- data/spec/fixtures/4d1a21e88ae121365c00000e.log +0 -1
- data/spec/fixtures/4d1a34ccfa85f32065000004.log +0 -1
- data/spec/helper.rb +0 -25
- data/spec/keylog_spec.rb +0 -16
- data/vimgolf.gemspec +0 -42
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 54dea0f839cd9e4b251b1b3d9f1b8d1be31fb0dc
|
4
|
+
data.tar.gz: 0e96fa1acc4894b104702e6492748742ea42204c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2d413648da95e0c81b4f0668841605732e8265d373f56c5a98afd771d304ed547b19d8f2551e4cd007bbed2ab77d0607c2b81eedb90d22735c6159eb74b46e12
|
7
|
+
data.tar.gz: bf03e208e358a3522d4c0fb6e3fcbb301a44b6202f3f9b9900c1d3a8e78820558ea7783b5ee208d180f5b22c0cfd79a687ed462ff372846158ab87cf84f20714
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# [VimGolf.com](http://www.vimgolf.com) Client
|
1
|
+
# [VimGolf.com](http://www.vimgolf.com) CLI Client
|
2
2
|
|
3
3
|
Real Vim ninjas count every keystroke - do you? Head on over to vimgolf.com, pick a challenge, and show us what you've got!
|
4
4
|
|
@@ -14,16 +14,8 @@ When you launch a challenge from the command line, it will be downloaded from th
|
|
14
14
|
$> gem install vimgolf
|
15
15
|
|
16
16
|
(Go to vimgolf.com, sign in, and grab your API key)
|
17
|
-
$> vimgolf setup
|
17
|
+
$> vimgolf setup
|
18
18
|
|
19
19
|
(Pick a challenge on vimgolf.com)
|
20
20
|
$> vimgolf put [challenge ID]
|
21
21
|
```
|
22
|
-
|
23
|
-
**Emacs**: yes, it is true, you can [play vimgolf in emacs too](https://github.com/igrigorik/vimgolf/tree/master/emacs)!
|
24
|
-
|
25
|
-
Patches, tips and ideas are welcome!
|
26
|
-
|
27
|
-
## License
|
28
|
-
|
29
|
-
(MIT License) - Copyright (c) 2010 Ilya Grigorik
|
data/lib/vimgolf.rb
CHANGED
data/lib/vimgolf/challenge.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module VimGolf
|
2
2
|
class Challenge
|
3
|
-
attr_reader :id, :type
|
3
|
+
attr_reader :id, :type, :otype, :remote
|
4
4
|
|
5
5
|
def self.path(path)
|
6
6
|
@@path = path if path
|
@@ -11,7 +11,32 @@ module VimGolf
|
|
11
11
|
@id = id
|
12
12
|
end
|
13
13
|
|
14
|
+
def local(infile, outfile)
|
15
|
+
@remote = false
|
16
|
+
@input_path = File.expand_path(infile)
|
17
|
+
@output_path = File.expand_path(outfile)
|
18
|
+
@type = File.basename(@input_path) # extension? use the whole thing
|
19
|
+
@otype = File.basename(@output_path)
|
20
|
+
|
21
|
+
work_files
|
22
|
+
end
|
23
|
+
|
24
|
+
def work_files
|
25
|
+
@vimrc_path = File.expand_path('../vimgolf.vimrc', __FILE__)
|
26
|
+
|
27
|
+
# keep these Tempfile's around so they don't unlink
|
28
|
+
@work = Tempfile.new(['vimgolf', ".#{@type}"])
|
29
|
+
@log = Tempfile.new('golflog')
|
30
|
+
# close tmp files, but don't unlink
|
31
|
+
@work.close
|
32
|
+
@log.close
|
33
|
+
|
34
|
+
@work_path = @work.path()
|
35
|
+
@log_path = @log.path()
|
36
|
+
end
|
37
|
+
|
14
38
|
def download
|
39
|
+
@remote = true
|
15
40
|
begin
|
16
41
|
url = URI.parse("#{GOLFHOST}/challenges/#{@id}.json")
|
17
42
|
req = Net::HTTP::Get.new(url.path)
|
@@ -34,9 +59,14 @@ module VimGolf
|
|
34
59
|
@data['in']['data'].gsub!(/\r\n/, "\n")
|
35
60
|
@data['out']['data'].gsub!(/\r\n/, "\n")
|
36
61
|
|
37
|
-
|
62
|
+
# be sure to sanitize the types
|
63
|
+
@type = @data['in']['type'].gsub(/[^\w-]/, '.')
|
64
|
+
@otype = @data['out']['type'].gsub(/[^\w-]/, '.')
|
65
|
+
@input_path = path + ".input.#{@type}"
|
66
|
+
@output_path = path + ".output.#{@otype}"
|
67
|
+
|
38
68
|
save
|
39
|
-
|
69
|
+
work_files
|
40
70
|
rescue Exception => e
|
41
71
|
debug(e)
|
42
72
|
raise "Uh oh, couldn't download or parse challenge, please verify your challenge id & client version."
|
@@ -44,13 +74,12 @@ module VimGolf
|
|
44
74
|
end
|
45
75
|
|
46
76
|
def start
|
47
|
-
|
77
|
+
FileUtils.cp(@input_path, @work_path)
|
48
78
|
end
|
49
79
|
|
50
80
|
def save
|
51
81
|
File.open(input_path, "w") {|f| f.puts @data['in']['data']}
|
52
82
|
File.open(output_path, "w") {|f| f.puts @data['out']['data']}
|
53
|
-
File.open(vimrc_path, "w") {|f| f.puts @data['vimrc']}
|
54
83
|
end
|
55
84
|
|
56
85
|
def upload
|
@@ -62,7 +91,7 @@ module VimGolf
|
|
62
91
|
|
63
92
|
proxy.start(url.host, url.port) do |http|
|
64
93
|
request = Net::HTTP::Post.new(url.request_uri)
|
65
|
-
request.set_form_data({"challenge_id" => @id, "apikey" => Config.load['key'], "entry" => IO.
|
94
|
+
request.set_form_data({"challenge_id" => @id, "apikey" => Config.load['key'], "entry" => IO.binread(log_path)})
|
66
95
|
request["Accept"] = "application/json"
|
67
96
|
|
68
97
|
res = http.request(request)
|
@@ -78,11 +107,15 @@ module VimGolf
|
|
78
107
|
end
|
79
108
|
end
|
80
109
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
110
|
+
attr_reader :input_path
|
111
|
+
attr_reader :work_path
|
112
|
+
attr_reader :output_path
|
113
|
+
attr_reader :log_path
|
114
|
+
attr_reader :vimrc_path
|
115
|
+
|
116
|
+
def correct?
|
117
|
+
FileUtils.compare_file(@work_path, @output_path)
|
118
|
+
end
|
86
119
|
|
87
120
|
def path
|
88
121
|
@@path + "/#{@id}"
|
data/lib/vimgolf/cli.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
module VimGolf
|
2
2
|
|
3
3
|
GOLFDEBUG = ENV['GOLFDEBUG'].to_sym rescue false
|
4
|
-
GOLFHOST = ENV['GOLFHOST'] || "http://vimgolf.com"
|
5
|
-
GOLFDIFF = ENV['GOLFDIFF'] || 'diff'
|
4
|
+
GOLFHOST = ENV['GOLFHOST'] || "http://www.vimgolf.com"
|
6
5
|
GOLFSHOWDIFF = ENV['GOLFSHOWDIFF'] || 'vim -d -n'
|
7
6
|
GOLFVIM = ENV['GOLFVIM'] || 'vim'
|
8
7
|
PROXY = ENV['http_proxy'] || ''
|
@@ -33,6 +32,15 @@ module VimGolf
|
|
33
32
|
super
|
34
33
|
end
|
35
34
|
|
35
|
+
desc "version", "print version of Vimgolf client"
|
36
|
+
long_desc <<-DESC
|
37
|
+
Print version of the Vimgolf client.
|
38
|
+
DESC
|
39
|
+
|
40
|
+
def version
|
41
|
+
VimGolf.ui.info "Client #{Vimgolf::VERSION}"
|
42
|
+
end
|
43
|
+
|
36
44
|
desc "setup", "configure your VimGolf credentials"
|
37
45
|
long_desc <<-DESC
|
38
46
|
To participate in the challenge please go to vimgolf.com and register an
|
@@ -43,7 +51,12 @@ module VimGolf
|
|
43
51
|
DESC
|
44
52
|
|
45
53
|
def setup
|
46
|
-
|
54
|
+
VimGolf.ui.info "\nLet's setup your VimGolf key..."
|
55
|
+
VimGolf.ui.warn "1) Open vimgolf.com in your browser."
|
56
|
+
VimGolf.ui.warn "2) Click \"Sign in with Twitter\"."
|
57
|
+
VimGolf.ui.warn "3) Once signed in, copy your key (black box, top right)."
|
58
|
+
|
59
|
+
key = VimGolf.ui.ask "\nPaste your VimGolf key:"
|
47
60
|
|
48
61
|
if key =~ /[\w\d]{32}/
|
49
62
|
FileUtils.mkdir_p Config.path
|
@@ -56,41 +69,74 @@ module VimGolf
|
|
56
69
|
end
|
57
70
|
end
|
58
71
|
|
59
|
-
desc "put
|
72
|
+
desc "put CHALLENGE_ID", "launch vimgolf.com challenge"
|
60
73
|
long_desc <<-DESC
|
61
74
|
Launch a VimGolf session for the specified challenge ID. To find a currently
|
62
75
|
active challenge ID, please visit vimgolf.com!
|
63
76
|
DESC
|
64
77
|
|
65
|
-
def put(id
|
78
|
+
def put(id)
|
79
|
+
FileUtils.mkdir_p Config.put_path
|
66
80
|
VimGolf.ui.warn "Downloading Vimgolf challenge: #{id}"
|
67
81
|
VimGolf::Challenge.path(Config.put_path)
|
68
82
|
challenge = Challenge.new(id)
|
69
83
|
challenge.download
|
70
84
|
|
85
|
+
play(challenge)
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "local INFILE OUTFILE", "launch local challenge"
|
89
|
+
long_desc <<-DESC
|
90
|
+
Launch a local VimGolf challenge. A temporary copy of INFILE is made; the original files will not be touched.
|
91
|
+
DESC
|
92
|
+
|
93
|
+
def local(infile, outfile)
|
94
|
+
# make sure our files are sane
|
95
|
+
if !(File.file?(infile) and File.file?(outfile))
|
96
|
+
VimGolf.ui.error "INFILE and OUTFILE must exist and be regular files."
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
|
100
|
+
challenge = Challenge.new(infile) # use the filename as id
|
101
|
+
challenge.local(infile, outfile)
|
102
|
+
|
103
|
+
play(challenge)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def play(challenge)
|
71
109
|
begin
|
72
|
-
|
73
|
-
|
74
|
-
# -
|
75
|
-
# -
|
76
|
-
#
|
77
|
-
# -
|
78
|
-
|
110
|
+
challenge.start
|
111
|
+
VimGolf.ui.warn "Launching VimGolf session for challenge: #{challenge.id}"
|
112
|
+
# -Z - restricted mode, utilities not allowed
|
113
|
+
# -n - no swap file, memory only editing
|
114
|
+
# --noplugin - don't load any plugins, lets be fair!
|
115
|
+
# --nofork - otherwise GOLFVIM=gvim forks and returns immediately
|
116
|
+
# -i NONE - don't load .viminfo (for saved macros and the like)
|
117
|
+
# +0 - always start on line 0
|
118
|
+
# -u vimrc - load vimgolf .vimrc to level the playing field
|
119
|
+
# -U NONE - don't load .gvimrc
|
120
|
+
# -W logfile - keylog file (overwrites if already exists)
|
121
|
+
vimcmd = GOLFVIM.shellsplit + %W{-Z -n --noplugin -i NONE +0 -u #{challenge.vimrc_path} -U NONE -W #{challenge.log_path} #{challenge.work_path}}
|
122
|
+
if GOLFVIM == "gvim"
|
123
|
+
vimcmd += %W{ --nofork}
|
124
|
+
end
|
79
125
|
debug(vimcmd)
|
80
|
-
system(vimcmd)
|
126
|
+
system(*vimcmd) # assembled as an array, bypasses the shell
|
81
127
|
|
82
128
|
if $?.exitstatus.zero?
|
83
|
-
|
84
|
-
|
85
|
-
|
129
|
+
log = Keylog.new(IO.binread(challenge.log_path))
|
130
|
+
|
131
|
+
VimGolf.ui.info "\nHere are your keystrokes:"
|
132
|
+
VimGolf.ui.print_log log
|
86
133
|
|
87
134
|
# Handle incorrect solutions
|
88
|
-
if
|
135
|
+
if !challenge.correct?()
|
89
136
|
VimGolf.ui.error "\nUh oh, looks like your entry does not match the desired output."
|
90
137
|
VimGolf.ui.error "Your score for this failed attempt was: #{log.score}"
|
91
|
-
|
92
138
|
loop do
|
93
|
-
VimGolf.ui.warn "[d] Show
|
139
|
+
VimGolf.ui.warn "[d] Show diff"
|
94
140
|
VimGolf.ui.warn "[r] Retry the current challenge"
|
95
141
|
VimGolf.ui.warn "[q] Quit vimgolf"
|
96
142
|
|
@@ -99,10 +145,9 @@ module VimGolf
|
|
99
145
|
:choices => [:diff, :retry, :quit]
|
100
146
|
when :diff
|
101
147
|
VimGolf.ui.warn "Showing vimdiff of your attempt (left) and correct output (right)"
|
102
|
-
system(
|
148
|
+
system(*GOLFSHOWDIFF.shellsplit + [challenge.work_path, challenge.output_path])
|
103
149
|
when :retry
|
104
150
|
VimGolf.ui.warn "Retrying current challenge..."
|
105
|
-
challenge.start
|
106
151
|
raise RetryException
|
107
152
|
when :quit
|
108
153
|
raise Interrupt
|
@@ -114,23 +159,32 @@ module VimGolf
|
|
114
159
|
VimGolf.ui.info "\nSuccess! Your output matches. Your score: #{log.score}"
|
115
160
|
|
116
161
|
loop do
|
117
|
-
|
118
|
-
|
162
|
+
choices = []
|
163
|
+
begin
|
164
|
+
Config.load # raises error if user hasn't finished setup
|
165
|
+
choices = [:w, :x]
|
166
|
+
VimGolf.ui.warn "[w] Upload result and retry the challenge"
|
167
|
+
VimGolf.ui.warn "[x] Upload result and quit"
|
168
|
+
rescue
|
169
|
+
choices = [:setup]
|
170
|
+
VimGolf.ui.warn "[s] Set up vimgolf.com key to submit result"
|
171
|
+
end if challenge.remote
|
119
172
|
VimGolf.ui.warn "[r] Do not upload result and retry the challenge"
|
120
173
|
VimGolf.ui.warn "[q] Do not upload result and quit"
|
121
174
|
|
122
175
|
case VimGolf.ui.ask_question "Choice> ",
|
123
176
|
:type => :warn,
|
124
|
-
:choices =>
|
177
|
+
:choices => choices + [:retry, :quit]
|
125
178
|
when :w
|
126
179
|
next unless upload?(challenge)
|
127
|
-
challenge.start
|
128
180
|
raise RetryException
|
129
181
|
when :x
|
130
182
|
next unless upload?(challenge)
|
131
183
|
raise Interrupt
|
184
|
+
when :setup
|
185
|
+
setup
|
186
|
+
next # we can hopefully submit this time
|
132
187
|
when :retry
|
133
|
-
challenge.start
|
134
188
|
raise RetryException
|
135
189
|
when :quit
|
136
190
|
raise Interrupt
|
@@ -139,8 +193,9 @@ module VimGolf
|
|
139
193
|
|
140
194
|
else
|
141
195
|
error = <<-MSG
|
142
|
-
|
143
|
-
|
196
|
+
Uh oh, Vim did not exit properly.
|
197
|
+
Please ensure you can execute 'Vim' from the commandline.
|
198
|
+
If the problem persists, please report the error on github.com/igrigorik/vimgolf
|
144
199
|
MSG
|
145
200
|
|
146
201
|
VimGolf.ui.error error
|
@@ -150,15 +205,13 @@ module VimGolf
|
|
150
205
|
retry
|
151
206
|
end
|
152
207
|
|
153
|
-
rescue Interrupt
|
208
|
+
rescue Interrupt
|
154
209
|
VimGolf.ui.info "\nThanks for playing!"
|
155
|
-
rescue Exception => e
|
210
|
+
rescue RuntimeError, Exception => e
|
156
211
|
VimGolf.ui.error "Uh oh, something went wrong! Error: #{e}"
|
157
212
|
VimGolf.ui.error "If the error persists, please report it to github.com/igrigorik/vimgolf"
|
158
213
|
end
|
159
214
|
|
160
|
-
private
|
161
|
-
|
162
215
|
def upload?(challenge)
|
163
216
|
VimGolf.ui.warn "Uploading to VimGolf..."
|
164
217
|
|
data/lib/vimgolf/keylog.rb
CHANGED
@@ -1,225 +1,271 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# Force encoding of string literals. Must match solution text.
|
3
|
+
|
1
4
|
module VimGolf
|
2
5
|
class Keylog
|
3
6
|
include Enumerable
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@
|
8
|
+
def initialize(input, time=Time.now.utc)
|
9
|
+
# Force encoding of solution text. Must match string literals.
|
10
|
+
# .force_encoding CHANGES THE ORIGINAL STRING!
|
11
|
+
@input = input.force_encoding(Encoding::ASCII_8BIT)
|
12
|
+
@time = time
|
10
13
|
end
|
11
14
|
|
12
15
|
def to_s(sep = '')
|
13
16
|
to_a.join(sep)
|
14
17
|
end
|
15
18
|
|
19
|
+
alias_method :convert , :to_s
|
20
|
+
alias_method :score , :count
|
21
|
+
|
16
22
|
def each
|
17
23
|
scanner = StringScanner.new(@input)
|
18
|
-
output = ""
|
19
|
-
|
20
|
-
until scanner.eos?
|
21
|
-
c = scanner.get_byte
|
22
|
-
n = c.unpack('C').first
|
23
|
-
|
24
|
-
out_char = \
|
25
|
-
case n
|
26
|
-
|
27
|
-
# Special platform-independent encoding stuff
|
28
|
-
when 0x80
|
29
|
-
code = scanner.get_byte + scanner.get_byte
|
30
|
-
|
31
|
-
# This list has been populated by looking at
|
32
|
-
# :h terminal-options and vim source files:
|
33
|
-
# keymap.h and misc2.c
|
34
|
-
case code
|
35
|
-
when "k1"; "<F1>"
|
36
|
-
when "k2"; "<F2>"
|
37
|
-
when "k3"; "<F3>"
|
38
|
-
when "k4"; "<F4>"
|
39
|
-
when "k5"; "<F5>"
|
40
|
-
when "k6"; "<F6>"
|
41
|
-
when "k7"; "<F7>"
|
42
|
-
when "k8"; "<F8>"
|
43
|
-
when "k9"; "<F9>"
|
44
|
-
when "k;"; "<F10>"
|
45
|
-
when "F1"; "<F11>"
|
46
|
-
when "F2"; "<F12>"
|
47
|
-
when "F3"; "<F13>"
|
48
|
-
when "F4"; "<F14>"
|
49
|
-
when "F5"; "<F15>"
|
50
|
-
when "F6"; "<F16>"
|
51
|
-
when "F7"; "<F17>"
|
52
|
-
when "F8"; "<F18>"
|
53
|
-
when "F9"; "<F19>"
|
54
|
-
|
55
|
-
when "%1"; "<Help>"
|
56
|
-
when "&8"; "<Undo>"
|
57
|
-
when "#2"; "<S-Home>"
|
58
|
-
when "*7"; "<S-End>"
|
59
|
-
when "K1"; "<kHome>"
|
60
|
-
when "K4"; "<kEnd>"
|
61
|
-
when "K3"; "<kPageUp>"
|
62
|
-
when "K5"; "<kPageDown>"
|
63
|
-
when "K6"; "<kPlus>"
|
64
|
-
when "K7"; "<kMinus>"
|
65
|
-
when "K8"; "<kDivide>"
|
66
|
-
when "K9"; "<kMultiply>"
|
67
|
-
when "KA"; "<kEnter>"
|
68
|
-
when "KB"; "<kPoint>"
|
69
|
-
when "KC"; "<k0>"
|
70
|
-
when "KD"; "<k1>"
|
71
|
-
when "KE"; "<k2>"
|
72
|
-
when "KF"; "<k3>"
|
73
|
-
when "KG"; "<k4>"
|
74
|
-
when "KH"; "<k5>"
|
75
|
-
when "KI"; "<k6>"
|
76
|
-
when "KJ"; "<k7>"
|
77
|
-
when "KK"; "<k8>"
|
78
|
-
when "KL"; "<k9>"
|
79
|
-
|
80
|
-
when "kP"; "<PageUp>"
|
81
|
-
when "kN"; "<PageDown>"
|
82
|
-
when "kh"; "<Home>"
|
83
|
-
when "@7"; "<End>"
|
84
|
-
when "kI"; "<Insert>"
|
85
|
-
when "kD"; "<Del>"
|
86
|
-
when "kb"; "<BS>"
|
87
|
-
|
88
|
-
when "ku"; "<Up>"
|
89
|
-
when "kd"; "<Down>"
|
90
|
-
when "kl"; "<Left>"
|
91
|
-
when "kr"; "<Right>"
|
92
|
-
when "#4"; "<S-Left>"
|
93
|
-
when "%i"; "<S-Right>"
|
94
|
-
|
95
|
-
when "kB"; "<S-Tab>"
|
96
|
-
when "\xffX"; "<C-@>"
|
97
|
-
|
98
|
-
when "\xfd\x4"; "<S-Up>"
|
99
|
-
when "\xfd\x5"; "<S-Down>"
|
100
|
-
when "\xfd\x6"; "<S-F1>"
|
101
|
-
when "\xfd\x7"; "<S-F2>"
|
102
|
-
when "\xfd\x8"; "<S-F3>"
|
103
|
-
when "\xfd\x9"; "<S-F4>"
|
104
|
-
when "\xfd\xa"; "<S-F5>"
|
105
|
-
when "\xfd\xb"; "<S-F6>"
|
106
|
-
when "\xfd\xc"; "<S-F7>"
|
107
|
-
when "\xfd\xd"; "<S-F9>"
|
108
|
-
when "\xfd\xe"; "<S-F10>"
|
109
|
-
when "\xfd\xf"; "<S-F10>"
|
110
|
-
when "\xfd\x10"; "<S-F11>"
|
111
|
-
when "\xfd\x11"; "<S-F12>"
|
112
|
-
when "\xfd\x12"; "<S-F13>"
|
113
|
-
when "\xfd\x13"; "<S-F14>"
|
114
|
-
when "\xfd\x14"; "<S-F15>"
|
115
|
-
when "\xfd\x15"; "<S-F16>"
|
116
|
-
when "\xfd\x16"; "<S-F17>"
|
117
|
-
when "\xfd\x17"; "<S-F18>"
|
118
|
-
when "\xfd\x18"; "<S-F19>"
|
119
|
-
when "\xfd\x19"; "<S-F20>"
|
120
|
-
when "\xfd\x1a"; "<S-F21>"
|
121
|
-
when "\xfd\x1b"; "<S-F22>"
|
122
|
-
when "\xfd\x1c"; "<S-F23>"
|
123
|
-
when "\xfd\x1d"; "<S-F24>"
|
124
|
-
when "\xfd\x1e"; "<S-F25>"
|
125
|
-
when "\xfd\x1f"; "<S-F26>"
|
126
|
-
when "\xfd\x20"; "<S-F27>"
|
127
|
-
when "\xfd\x21"; "<S-F28>"
|
128
|
-
when "\xfd\x22"; "<S-F29>"
|
129
|
-
when "\xfd\x23"; "<S-F30>"
|
130
|
-
when "\xfd\x24"; "<S-F31>"
|
131
|
-
when "\xfd\x25"; "<S-F32>"
|
132
|
-
when "\xfd\x26"; "<S-F33>"
|
133
|
-
when "\xfd\x27"; "<S-F34>"
|
134
|
-
when "\xfd\x28"; "<S-F35>"
|
135
|
-
when "\xfd\x29"; "<S-F36>"
|
136
|
-
when "\xfd\x2a"; "<S-F37>"
|
137
|
-
when "\xfd\x2b"; "<Mouse>"
|
138
|
-
when "\xfd\x2c"; "<LeftMouse>"
|
139
|
-
when "\xfd\x2d"; "<LeftDrag>"
|
140
|
-
when "\xfd\x2e"; "<LeftRelease>"
|
141
|
-
when "\xfd\x2f"; "<MiddleMouse>"
|
142
|
-
when "\xfd\x30"; "<MiddleDrag>"
|
143
|
-
when "\xfd\x31"; "<MiddleRelease>"
|
144
|
-
when "\xfd\x32"; "<RightMouse>"
|
145
|
-
when "\xfd\x33"; "<RightDrag>"
|
146
|
-
when "\xfd\x34"; "<RightRelease>"
|
147
|
-
#when "\xfd\x35"; "KE_IGNORE"
|
148
|
-
#when "\xfd\x36"; "KE_TAB"
|
149
|
-
#when "\xfd\x37"; "KE_S_TAB_OLD"
|
150
|
-
#when "\xfd\x38"; "KE_SNIFF"
|
151
|
-
#when "\xfd\x39"; "KE_XF1"
|
152
|
-
#when "\xfd\x3a"; "KE_XF2"
|
153
|
-
#when "\xfd\x3b"; "KE_XF3"
|
154
|
-
#when "\xfd\x3c"; "KE_XF4"
|
155
|
-
#when "\xfd\x3d"; "KE_XEND"
|
156
|
-
#when "\xfd\x3e"; "KE_ZEND"
|
157
|
-
#when "\xfd\x3f"; "KE_XHOME"
|
158
|
-
#when "\xfd\x40"; "KE_ZHOME"
|
159
|
-
#when "\xfd\x41"; "KE_XUP"
|
160
|
-
#when "\xfd\x42"; "KE_XDOWN"
|
161
|
-
#when "\xfd\x43"; "KE_XLEFT"
|
162
|
-
#when "\xfd\x44"; "KE_XRIGHT"
|
163
|
-
#when "\xfd\x45"; "KE_LEFTMOUSE_NM"
|
164
|
-
#when "\xfd\x46"; "KE_LEFTRELEASE_NM"
|
165
|
-
#when "\xfd\x47"; "KE_S_XF1"
|
166
|
-
#when "\xfd\x48"; "KE_S_XF2"
|
167
|
-
#when "\xfd\x49"; "KE_S_XF3"
|
168
|
-
#when "\xfd\x4a"; "KE_S_XF4"
|
169
|
-
when "\xfd\x4b"; "<MouseDown>"
|
170
|
-
when "\xfd\x4c"; "<MouseUp>"
|
171
|
-
when "\xfd\x4d"; "<MouseLeft>"
|
172
|
-
when "\xfd\x4e"; "<MouseRight>"
|
173
|
-
#when "\xfd\x4f"; "KE_KINS"
|
174
|
-
#when "\xfd\x50"; "KE_KDEL"
|
175
|
-
#when "\xfd\x51"; "KE_CSI"
|
176
|
-
#when "\xfd\x52"; "KE_SNR"
|
177
|
-
#when "\xfd\x53"; "KE_PLUG"
|
178
|
-
#when "\xfd\x54"; "KE_CMDWIN"
|
179
|
-
when "\xfd\x55"; "<C-Left>"
|
180
|
-
when "\xfd\x56"; "<C-Right>"
|
181
|
-
when "\xfd\x57"; "<C-Home>"
|
182
|
-
when "\xfd\x58"; "<C-End>"
|
183
|
-
#when "\xfd\x59"; "KE_X1MOUSE"
|
184
|
-
#when "\xfd\x5a"; "KE_X1DRAG"
|
185
|
-
#when "\xfd\x5b"; "KE_X1RELEASE"
|
186
|
-
#when "\xfd\x5c"; "KE_X2MOUSE"
|
187
|
-
#when "\xfd\x5d"; "KE_X2DRAG"
|
188
|
-
#when "\xfd\x5e"; "KE_X2RELEASE"
|
189
|
-
#when "\xfd\x5f"; "KE_DROP"
|
190
|
-
#when "\xfd\x5e"; "KE_CURSORHOLD"
|
191
|
-
#when "\xfd\x61"; "KE_NOP"
|
192
|
-
when "\xfd\x62"; nil # Focus Gained (GVIM)
|
193
|
-
when "\xfd\x63"; nil # Focus Lost (GVIM)
|
194
|
-
|
195
|
-
else
|
196
|
-
#puts "Unknown Vim code: #{code.inspect}"
|
197
|
-
'<%02x-%02x>' % code.unpack('CC')
|
198
|
-
end
|
199
|
-
|
200
|
-
# Control characters with special names
|
201
|
-
when 0; "<Nul>"
|
202
|
-
when 9; "<Tab>"
|
203
|
-
when 10; "<NL>"
|
204
|
-
when 13; "<CR>"
|
205
|
-
when 27; "<Esc>"
|
206
|
-
|
207
|
-
when 127; "<Del>"
|
208
|
-
|
209
|
-
# Otherwise, use <C-x> format
|
210
|
-
when 0..31; "<C-#{(n + 64).chr}>"
|
211
|
-
|
212
|
-
# The rest of ANSI is printable
|
213
|
-
when 32..126; c
|
214
|
-
|
215
|
-
else
|
216
|
-
#puts "Unexpected extended ASCII: #{'%#04x' % n}"
|
217
|
-
'<%#04x>' % n
|
218
24
|
|
25
|
+
# A Vim keycode is either a single byte, or a 3-byte sequence starting
|
26
|
+
# with 0x80.
|
27
|
+
while (c = scanner.get_byte)
|
28
|
+
n = c.ord
|
29
|
+
if n == 0x80
|
30
|
+
b2, b3 = scanner.get_byte, scanner.get_byte
|
31
|
+
if b2 == "\xfd" && b3 >= "\x38" && @time.between?(*NO_SNIFF_DATE_RANGE)
|
32
|
+
# Should we account for KE_SNIFF removal?
|
33
|
+
b3 = (b3.ord + 1).chr
|
34
|
+
end
|
35
|
+
code = KC_MBYTE[b2+b3]
|
36
|
+
yield code if code # ignore "nil" keystrokes (like window focus)
|
37
|
+
else
|
38
|
+
yield KC_1BYTE[n]
|
219
39
|
end
|
220
|
-
|
221
|
-
yield out_char if out_char
|
222
40
|
end
|
223
41
|
end
|
42
|
+
|
43
|
+
# Quick lookup array for single-byte keycodes
|
44
|
+
KC_1BYTE = []
|
45
|
+
(0..255).each {|n| KC_1BYTE.push("<%#04x>" % n)} # Fallback for non-ASCII
|
46
|
+
(1..127).each {|n| KC_1BYTE[n] = "<C-#{(n ^ 0x40).chr}>"}
|
47
|
+
(32..126).each {|c| KC_1BYTE[c] = c.chr } # Printing chars
|
48
|
+
KC_1BYTE[0x1b] = "<Esc>" # Special names for a few control chars
|
49
|
+
KC_1BYTE[0x0d] = "<CR>"
|
50
|
+
KC_1BYTE[0x0a] = "<NL>"
|
51
|
+
KC_1BYTE[0x09] = "<Tab>"
|
52
|
+
|
53
|
+
# Between these dates, assume KE_SNIFF is removed.
|
54
|
+
NO_SNIFF_DATE_RANGE = [Time.utc(2016, 4), Time.utc(2017, 7)]
|
55
|
+
|
56
|
+
KC_MBYTE = Hash.new do |_h,k|
|
57
|
+
'<' + k.bytes.map {|b| "%02x" % b}.join('-') + '>' # For missing keycodes
|
58
|
+
end.update({
|
59
|
+
# This list has been populated by looking at
|
60
|
+
# :h terminal-options and vim source files:
|
61
|
+
# keymap.h and misc2.c
|
62
|
+
"k1" => "<F1>",
|
63
|
+
"k2" => "<F2>",
|
64
|
+
"k3" => "<F3>",
|
65
|
+
"k4" => "<F4>",
|
66
|
+
"k5" => "<F5>",
|
67
|
+
"k6" => "<F6>",
|
68
|
+
"k7" => "<F7>",
|
69
|
+
"k8" => "<F8>",
|
70
|
+
"k9" => "<F9>",
|
71
|
+
"k;" => "<F10>",
|
72
|
+
"F1" => "<F11>",
|
73
|
+
"F2" => "<F12>",
|
74
|
+
"F3" => "<F13>",
|
75
|
+
"F4" => "<F14>",
|
76
|
+
"F5" => "<F15>",
|
77
|
+
"F6" => "<F16>",
|
78
|
+
"F7" => "<F17>",
|
79
|
+
"F8" => "<F18>",
|
80
|
+
"F9" => "<F19>",
|
81
|
+
|
82
|
+
"%1" => "<Help>",
|
83
|
+
"&8" => "<Undo>",
|
84
|
+
"#2" => "<S-Home>",
|
85
|
+
"*7" => "<S-End>",
|
86
|
+
"K1" => "<kHome>",
|
87
|
+
"K4" => "<kEnd>",
|
88
|
+
"K3" => "<kPageUp>",
|
89
|
+
"K5" => "<kPageDown>",
|
90
|
+
"K6" => "<kPlus>",
|
91
|
+
"K7" => "<kMinus>",
|
92
|
+
"K8" => "<kDivide>",
|
93
|
+
"K9" => "<kMultiply>",
|
94
|
+
"KA" => "<kEnter>",
|
95
|
+
"KB" => "<kPoint>",
|
96
|
+
"KC" => "<k0>",
|
97
|
+
"KD" => "<k1>",
|
98
|
+
"KE" => "<k2>",
|
99
|
+
"KF" => "<k3>",
|
100
|
+
"KG" => "<k4>",
|
101
|
+
"KH" => "<k5>",
|
102
|
+
"KI" => "<k6>",
|
103
|
+
"KJ" => "<k7>",
|
104
|
+
"KK" => "<k8>",
|
105
|
+
"KL" => "<k9>",
|
106
|
+
|
107
|
+
"kP" => "<PageUp>",
|
108
|
+
"kN" => "<PageDown>",
|
109
|
+
"kh" => "<Home>",
|
110
|
+
"@7" => "<End>",
|
111
|
+
"kI" => "<Insert>",
|
112
|
+
"kD" => "<Del>",
|
113
|
+
"kb" => "<BS>",
|
114
|
+
|
115
|
+
"ku" => "<Up>",
|
116
|
+
"kd" => "<Down>",
|
117
|
+
"kl" => "<Left>",
|
118
|
+
"kr" => "<Right>",
|
119
|
+
"#4" => "<S-Left>",
|
120
|
+
"%i" => "<S-Right>",
|
121
|
+
|
122
|
+
"kB" => "<S-Tab>",
|
123
|
+
"\xffX" => "<C-@>",
|
124
|
+
|
125
|
+
# This is how you escape literal 0x80
|
126
|
+
"\xfeX" => "<0x80>",
|
127
|
+
|
128
|
+
# These rarely-used modifiers should be combined with the next
|
129
|
+
# stroke (like <S-Space>), but let's put them here for now
|
130
|
+
"\xfc\x02" => "<S->",
|
131
|
+
"\xfc\x04" => "<C->",
|
132
|
+
"\xfc\x06" => "<C-S->",
|
133
|
+
"\xfc\x08" => "<A->",
|
134
|
+
"\xfc\x0a" => "<A-S->",
|
135
|
+
"\xfc\x0c" => "<C-A>",
|
136
|
+
"\xfc\x0e" => "<C-A-S->",
|
137
|
+
"\xfc\x10" => "<M->",
|
138
|
+
"\xfc\x12" => "<M-S->",
|
139
|
+
"\xfc\x14" => "<M-C->",
|
140
|
+
"\xfc\x16" => "<M-C-S->",
|
141
|
+
"\xfc\x18" => "<M-A->",
|
142
|
+
"\xfc\x1a" => "<M-A-S->",
|
143
|
+
"\xfc\x1c" => "<M-C-A>",
|
144
|
+
"\xfc\x1e" => "<M-C-A-S->",
|
145
|
+
|
146
|
+
# KS_EXTRA keycodes (starting with 0x80 0xfd) are defined by an enum in
|
147
|
+
# Vim's keymap.h. Sometimes, a new Vim adds or removes a keycode, which
|
148
|
+
# changes the binary representation of every keycode after it. Very
|
149
|
+
# annoying.
|
150
|
+
"\xfd\x4" => "<S-Up>",
|
151
|
+
"\xfd\x5" => "<S-Down>",
|
152
|
+
"\xfd\x6" => "<S-F1>",
|
153
|
+
"\xfd\x7" => "<S-F2>",
|
154
|
+
"\xfd\x8" => "<S-F3>",
|
155
|
+
"\xfd\x9" => "<S-F4>",
|
156
|
+
"\xfd\xa" => "<S-F5>",
|
157
|
+
"\xfd\xb" => "<S-F6>",
|
158
|
+
"\xfd\xc" => "<S-F7>",
|
159
|
+
"\xfd\xd" => "<S-F9>",
|
160
|
+
"\xfd\xe" => "<S-F10>",
|
161
|
+
"\xfd\xf" => "<S-F10>",
|
162
|
+
"\xfd\x10" => "<S-F11>",
|
163
|
+
"\xfd\x11" => "<S-F12>",
|
164
|
+
"\xfd\x12" => "<S-F13>",
|
165
|
+
"\xfd\x13" => "<S-F14>",
|
166
|
+
"\xfd\x14" => "<S-F15>",
|
167
|
+
"\xfd\x15" => "<S-F16>",
|
168
|
+
"\xfd\x16" => "<S-F17>",
|
169
|
+
"\xfd\x17" => "<S-F18>",
|
170
|
+
"\xfd\x18" => "<S-F19>",
|
171
|
+
"\xfd\x19" => "<S-F20>",
|
172
|
+
"\xfd\x1a" => "<S-F21>",
|
173
|
+
"\xfd\x1b" => "<S-F22>",
|
174
|
+
"\xfd\x1c" => "<S-F23>",
|
175
|
+
"\xfd\x1d" => "<S-F24>",
|
176
|
+
"\xfd\x1e" => "<S-F25>",
|
177
|
+
"\xfd\x1f" => "<S-F26>",
|
178
|
+
"\xfd\x20" => "<S-F27>",
|
179
|
+
"\xfd\x21" => "<S-F28>",
|
180
|
+
"\xfd\x22" => "<S-F29>",
|
181
|
+
"\xfd\x23" => "<S-F30>",
|
182
|
+
"\xfd\x24" => "<S-F31>",
|
183
|
+
"\xfd\x25" => "<S-F32>",
|
184
|
+
"\xfd\x26" => "<S-F33>",
|
185
|
+
"\xfd\x27" => "<S-F34>",
|
186
|
+
"\xfd\x28" => "<S-F35>",
|
187
|
+
"\xfd\x29" => "<S-F36>",
|
188
|
+
"\xfd\x2a" => "<S-F37>",
|
189
|
+
"\xfd\x2b" => "<Mouse>",
|
190
|
+
"\xfd\x2c" => "<LeftMouse>",
|
191
|
+
"\xfd\x2d" => "<LeftDrag>",
|
192
|
+
"\xfd\x2e" => "<LeftRelease>",
|
193
|
+
"\xfd\x2f" => "<MiddleMouse>",
|
194
|
+
"\xfd\x30" => "<MiddleDrag>",
|
195
|
+
"\xfd\x31" => "<MiddleRelease>",
|
196
|
+
"\xfd\x32" => "<RightMouse>",
|
197
|
+
"\xfd\x33" => "<RightDrag>",
|
198
|
+
"\xfd\x34" => "<RightRelease>",
|
199
|
+
"\xfd\x35" => nil, # KE_IGNORE
|
200
|
+
#"\xfd\x36" => "KE_TAB",
|
201
|
+
#"\xfd\x37" => "KE_S_TAB_OLD",
|
202
|
+
|
203
|
+
# Vim 7.4.1433 removed KE_SNIFF. Unfortunately, this changed the
|
204
|
+
# offset of every keycode after it.
|
205
|
+
# Vim 8.0.0697 added back a KE_SNIFF_UNUSED to fill in for the
|
206
|
+
# removed KE_SNIFF.
|
207
|
+
# Keycodes after this point should be accurate for vim < 7.4.1433
|
208
|
+
# and vim > 8.0.0697.
|
209
|
+
#"\xfd\x38" => "KE_SNIFF",
|
210
|
+
#"\xfd\x39" => "KE_XF1",
|
211
|
+
#"\xfd\x3a" => "KE_XF2",
|
212
|
+
#"\xfd\x3b" => "KE_XF3",
|
213
|
+
#"\xfd\x3c" => "KE_XF4",
|
214
|
+
#"\xfd\x3d" => "KE_XEND",
|
215
|
+
#"\xfd\x3e" => "KE_ZEND",
|
216
|
+
#"\xfd\x3f" => "KE_XHOME",
|
217
|
+
#"\xfd\x40" => "KE_ZHOME",
|
218
|
+
#"\xfd\x41" => "KE_XUP",
|
219
|
+
#"\xfd\x42" => "KE_XDOWN",
|
220
|
+
#"\xfd\x43" => "KE_XLEFT",
|
221
|
+
#"\xfd\x44" => "KE_XRIGHT",
|
222
|
+
#"\xfd\x45" => "KE_LEFTMOUSE_NM",
|
223
|
+
#"\xfd\x46" => "KE_LEFTRELEASE_NM",
|
224
|
+
#"\xfd\x47" => "KE_S_XF1",
|
225
|
+
#"\xfd\x48" => "KE_S_XF2",
|
226
|
+
#"\xfd\x49" => "KE_S_XF3",
|
227
|
+
#"\xfd\x4a" => "KE_S_XF4",
|
228
|
+
"\xfd\x4b" => "<ScrollWheelUp>",
|
229
|
+
"\xfd\x4c" => "<ScrollWheelDown>",
|
230
|
+
|
231
|
+
# Horizontal scroll wheel support was added in Vim 7.3c. These
|
232
|
+
# 2 entries shifted the rest of the KS_EXTRA mappings down 2.
|
233
|
+
# Though Vim 7.2 is rare today, it was common soon after
|
234
|
+
# vimgolf.com was launched. In cases where the 7.3 code is
|
235
|
+
# never used but the 7.2 code was common, it makes sense to use
|
236
|
+
# the 7.2 code. There are conflicts though, so some legacy
|
237
|
+
# keycodes have to stay wrong.
|
238
|
+
"\xfd\x4d" => "<ScrollWheelRight>",
|
239
|
+
"\xfd\x4e" => "<ScrollWheelLeft>",
|
240
|
+
"\xfd\x4f" => "<kInsert>",
|
241
|
+
"\xfd\x50" => "<kDel>",
|
242
|
+
"\xfd\x51" => "<0x9b>", # :help <CSI>
|
243
|
+
#"\xfd\x52" => "KE_SNR",
|
244
|
+
#"\xfd\x53" => "KE_PLUG", # never used
|
245
|
+
"\xfd\x53" => "<C-Left>", # 7.2 compat
|
246
|
+
#"\xfd\x54" => "KE_CMDWIN", # never used
|
247
|
+
"\xfd\x54" => "<C-Right>", # 7.2 compat
|
248
|
+
"\xfd\x55" => "<C-Left>", # 7.2 <C-Home> conflict
|
249
|
+
"\xfd\x56" => "<C-Right>", # 7.2 <C-End> conflict
|
250
|
+
"\xfd\x57" => "<C-Home>",
|
251
|
+
"\xfd\x58" => "<C-End>",
|
252
|
+
#"\xfd\x59" => "KE_X1MOUSE",
|
253
|
+
#"\xfd\x5a" => "KE_X1DRAG",
|
254
|
+
#"\xfd\x5b" => "KE_X1RELEASE",
|
255
|
+
#"\xfd\x5c" => "KE_X2MOUSE",
|
256
|
+
#"\xfd\x5d" => "KE_X2DRAG",
|
257
|
+
#"\xfd\x5e" => "KE_X2RELEASE",
|
258
|
+
"\xfd\x5e" => nil, # 7.2 compat (I think?)
|
259
|
+
#"\xfd\x5f" => "KE_DROP",
|
260
|
+
#"\xfd\x60" => "KE_CURSORHOLD",
|
261
|
+
|
262
|
+
# If you use gvim, you'll get an entry in your keylog every time the
|
263
|
+
# window gains or loses focus. These "keystrokes" should not show and
|
264
|
+
# should not be counted.
|
265
|
+
"\xfd\x60" => nil, # 7.2 Focus Gained compat
|
266
|
+
"\xfd\x61" => nil, # Focus Gained (GVIM) (>7.4.1433)
|
267
|
+
"\xfd\x62" => nil, # Focus Gained (GVIM)
|
268
|
+
"\xfd\x63" => nil, # Focus Lost (GVIM)
|
269
|
+
})
|
224
270
|
end
|
225
271
|
end
|