vimamsa 0.1.0 → 0.1.5
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 +4 -4
- data/.vma_project +0 -0
- data/README.md +110 -16
- data/exe/vimamsa +40 -0
- data/ext/vmaext/vmaext.c +0 -3
- data/lang/hyperplaintext.lang +129 -0
- data/lib/vimamsa.rb +3 -1
- data/lib/vimamsa/ack.rb +35 -0
- data/lib/vimamsa/actions.rb +125 -0
- data/lib/vimamsa/buffer.rb +1760 -0
- data/lib/vimamsa/buffer_list.rb +207 -0
- data/lib/vimamsa/constants.rb +44 -0
- data/lib/vimamsa/debug.rb +142 -0
- data/lib/vimamsa/default_key_bindings.rb +448 -0
- data/lib/vimamsa/easy_jump.rb +161 -0
- data/lib/vimamsa/editor.rb +667 -0
- data/lib/vimamsa/encrypt.rb +47 -0
- data/lib/vimamsa/file_finder.rb +103 -0
- data/lib/vimamsa/file_history.rb +100 -0
- data/lib/vimamsa/file_manager.rb +144 -0
- data/lib/vimamsa/hook.rb +46 -0
- data/lib/vimamsa/hyper_plain_text.rb +61 -0
- data/lib/vimamsa/key_binding_tree.rb +603 -0
- data/lib/vimamsa/macro.rb +177 -0
- data/lib/vimamsa/main.rb +71 -0
- data/lib/vimamsa/rbvma.rb +1072 -0
- data/lib/vimamsa/search.rb +100 -0
- data/lib/vimamsa/search_replace.rb +333 -0
- data/lib/vimamsa/text_transforms.rb +32 -0
- data/lib/vimamsa/util.rb +101 -0
- data/lib/vimamsa/version.rb +1 -1
- data/styles/134272-molokai.xml +33 -0
- data/styles/dark.xml +152 -0
- data/styles/molokai_edit.xml +49 -0
- data/vimamsa.gemspec +4 -2
- metadata +66 -10
- data/ext/vimamsa/extconf.rb +0 -11
- data/ext/vimamsa/vimamsa.c +0 -174
@@ -0,0 +1,603 @@
|
|
1
|
+
# This file has everyting related to binding key (and other) events
|
2
|
+
# into actions.
|
3
|
+
|
4
|
+
# First letter is mode (C=Command, I=Insert, V=Visual)
|
5
|
+
#
|
6
|
+
# Examples:
|
7
|
+
#
|
8
|
+
# Change mode from INSERT into COMMAND when ctrl key is released immediately
|
9
|
+
# after it has been pressed (there are no other key events between key press and key release).
|
10
|
+
# 'I ctrl!'=> '$kbd.set_mode(:command)',
|
11
|
+
# 'C ctrl!'=> '$kbd.set_mode(:insert)',
|
12
|
+
|
13
|
+
#
|
14
|
+
# In command mode: press keys "," "r" "v" and "b" sequentially.
|
15
|
+
# 'C , r v b'=> 'revert_buffer',
|
16
|
+
#
|
17
|
+
# In insert mode: press and hold ctrl, press "a"
|
18
|
+
# 'I ctrl-a'=> '$buffer.jump(BEGINNING_OF_LINE)',
|
19
|
+
#
|
20
|
+
|
21
|
+
$cnf = {} # TODO
|
22
|
+
|
23
|
+
def conf(id)
|
24
|
+
return $cnf[id]
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_conf(id, val)
|
28
|
+
$cnf[id] = val
|
29
|
+
end
|
30
|
+
|
31
|
+
def setcnf(id, val)
|
32
|
+
set_conf(id, val)
|
33
|
+
end
|
34
|
+
|
35
|
+
setcnf :indent_based_on_last_line, true
|
36
|
+
setcnf :extensions_to_open, [".txt", ".h", ".c", ".cpp", ".hpp", ".rb", ".inc", ".php", ".sh", ".m", ".gd", ".js"]
|
37
|
+
|
38
|
+
class State
|
39
|
+
attr_accessor :key_name, :eval_rule, :children, :action, :label, :major_modes
|
40
|
+
|
41
|
+
def initialize(key_name, eval_rule = "")
|
42
|
+
@key_name = key_name
|
43
|
+
@eval_rule = eval_rule
|
44
|
+
@children = []
|
45
|
+
@major_modes = []
|
46
|
+
@action = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s()
|
50
|
+
return @key_name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class KeyBindingTree
|
55
|
+
attr_accessor :C, :I, :cur_state, :root, :match_state, :last_action, :cur_action
|
56
|
+
attr_reader :mode_root_state, :state_trail
|
57
|
+
|
58
|
+
def initialize()
|
59
|
+
@modes = {}
|
60
|
+
@root = State.new("ROOT")
|
61
|
+
@cur_state = @root # used for building the tree
|
62
|
+
@default_mode = nil
|
63
|
+
@mode_history = []
|
64
|
+
@state_trail = []
|
65
|
+
@last_action = nil
|
66
|
+
@cur_action = nil
|
67
|
+
|
68
|
+
@modifiers = [] # TODO: create a queue
|
69
|
+
@last_event = [nil, nil, nil, nil, nil]
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_default_mode(label)
|
73
|
+
@match_state = [@modes[label]] # used for matching input
|
74
|
+
@mode_root_state = @modes[label]
|
75
|
+
@default_mode = label
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_mode_to_default()
|
79
|
+
set_mode(@default_mode)
|
80
|
+
end
|
81
|
+
|
82
|
+
# $kbd.add_mode("I", :insert)
|
83
|
+
def add_mode(id, label)
|
84
|
+
mode = State.new(id)
|
85
|
+
@modes[label] = mode
|
86
|
+
@root.children << mode
|
87
|
+
if @default_mode == nil
|
88
|
+
set_default_mode(label)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_minor_mode(id, label, major_mode_label)
|
93
|
+
mode = State.new(id)
|
94
|
+
@modes[label] = mode
|
95
|
+
@root.children << mode
|
96
|
+
mode.major_modes << major_mode_label
|
97
|
+
end
|
98
|
+
|
99
|
+
def clear_modifiers()
|
100
|
+
@modifiers = []
|
101
|
+
end
|
102
|
+
|
103
|
+
def find_state(key_name, eval_rule)
|
104
|
+
@cur_state.children.each { |s|
|
105
|
+
if s.key_name == key_name and s.eval_rule == eval_rule
|
106
|
+
# TODO check eval
|
107
|
+
return s
|
108
|
+
end
|
109
|
+
}
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
def match(key_name)
|
114
|
+
new_state = []
|
115
|
+
@match_state.each { |parent|
|
116
|
+
parent.children.each { |c|
|
117
|
+
# printf(" KEY MATCH: ")
|
118
|
+
# puts [c.key_name, key_name].inspect
|
119
|
+
if c.key_name == key_name and c.eval_rule == ""
|
120
|
+
new_state << c
|
121
|
+
elsif c.key_name == key_name and c.eval_rule != ""
|
122
|
+
puts "CHECK EVAL: #{c.eval_rule}"
|
123
|
+
if eval(c.eval_rule)
|
124
|
+
new_state << c
|
125
|
+
puts "EVAL TRUE"
|
126
|
+
else
|
127
|
+
puts "EVAL FALSE"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
}
|
131
|
+
}
|
132
|
+
if key_name == "o"
|
133
|
+
# Ripl.start :binding => binding
|
134
|
+
end
|
135
|
+
|
136
|
+
if new_state.any? # Match found
|
137
|
+
@match_state = new_state
|
138
|
+
return new_state
|
139
|
+
# return true
|
140
|
+
else # No match found
|
141
|
+
# @match_state = [@C] #TODO
|
142
|
+
return nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def set_mode(label)
|
147
|
+
@mode_history << @mode_root_state
|
148
|
+
|
149
|
+
# Check if label in form :label
|
150
|
+
if @modes.has_key?(label)
|
151
|
+
@mode_root_state = @modes[label]
|
152
|
+
set_state_to_root
|
153
|
+
else
|
154
|
+
# Check if label matches mode name in string format
|
155
|
+
for mode in @root.children
|
156
|
+
if mode.key_name == label
|
157
|
+
@mode_root_state = mode
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
$view.draw_cursor()
|
163
|
+
end
|
164
|
+
|
165
|
+
def cur_mode_str()
|
166
|
+
return @mode_root_state.key_name
|
167
|
+
end
|
168
|
+
|
169
|
+
def set_state(key_name, eval_rule = "")
|
170
|
+
new_state = find_state(key_name, eval_rule)
|
171
|
+
if new_state != nil
|
172
|
+
@cur_state = new_state
|
173
|
+
else
|
174
|
+
@cur_state = @mode_root_state # TODO
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def set_state_to_root
|
179
|
+
if @mode_root_state.major_modes.size == 1
|
180
|
+
modelabel = @mode_root_state.major_modes[0]
|
181
|
+
mmode = @modes[modelabel]
|
182
|
+
@match_state = [@mode_root_state, mmode]
|
183
|
+
else
|
184
|
+
@match_state = [@mode_root_state]
|
185
|
+
end
|
186
|
+
|
187
|
+
@state_trail = [@mode_root_state]
|
188
|
+
# puts get_state_trail_str()
|
189
|
+
# $next_command_count = nil # TODO: set somewhere else?
|
190
|
+
end
|
191
|
+
|
192
|
+
# Print key bindings to show as documentation or for debugging
|
193
|
+
def to_s()
|
194
|
+
s = ""
|
195
|
+
# @cur_state = @root
|
196
|
+
stack = [[@root, ""]]
|
197
|
+
lines = []
|
198
|
+
while stack.any?
|
199
|
+
t, p = *stack.pop # t = current state, p = current path
|
200
|
+
if t.children.any?
|
201
|
+
t.children.reverse.each { |c|
|
202
|
+
if c.eval_rule.size > 0
|
203
|
+
new_p = "#{p} #{c.key_name}(#{c.eval_rule})"
|
204
|
+
else
|
205
|
+
new_p = "#{p} #{c.key_name}"
|
206
|
+
end
|
207
|
+
stack << [c, new_p]
|
208
|
+
}
|
209
|
+
# stack.concat[t.children]
|
210
|
+
else
|
211
|
+
# s += p + " : #{t.action}\n"
|
212
|
+
lines << p + " : #{t.action}"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
s = lines.sort.join("\n")
|
216
|
+
return s
|
217
|
+
end
|
218
|
+
|
219
|
+
def get_state_trail_str
|
220
|
+
s = ""
|
221
|
+
s_trail = ""
|
222
|
+
last_state = @state_trail.last
|
223
|
+
last_state = last_state[0] if last_state.class == Array
|
224
|
+
for st in @state_trail
|
225
|
+
st = st[0] if st.class == Array
|
226
|
+
s_trail << " #{st.to_s}"
|
227
|
+
end
|
228
|
+
s << "CUR STATE: #{s_trail}\n"
|
229
|
+
for cstate in last_state.children
|
230
|
+
act_s = "..."
|
231
|
+
act_s = cstate.action.to_s if cstate.action != nil
|
232
|
+
s << " #{cstate.to_s} #{act_s}\n"
|
233
|
+
end
|
234
|
+
return s
|
235
|
+
end
|
236
|
+
|
237
|
+
# Modifies state of key binding tree (move to new state) based on received event
|
238
|
+
# Checks child nodes of current state if they match received event
|
239
|
+
# if yes, change state to child
|
240
|
+
# if no, go back to root
|
241
|
+
def match_key_conf(c, translated_c, event_type)
|
242
|
+
# $cur_key_dict = $key_bind_dict[$context[:mode]]
|
243
|
+
print "MATCH KEY CONF: #{[c, translated_c]}" if $debug
|
244
|
+
|
245
|
+
# Sometimes we get ASCII-8BIT encoding although content actually UTF-8
|
246
|
+
c = c.force_encoding("UTF-8"); # TODO:correct?
|
247
|
+
|
248
|
+
eval_s = nil
|
249
|
+
|
250
|
+
new_state = match(c)
|
251
|
+
# #TODO:
|
252
|
+
# new_state = match(translated_c)
|
253
|
+
# if new_state == nil and translated_c.index("shift") == 0
|
254
|
+
# new_state = match(c)
|
255
|
+
# end
|
256
|
+
|
257
|
+
if new_state == nil
|
258
|
+
s1 = match_state[0].children.select { |s| s.key_name.include?("<char>") } # TODO: [0]
|
259
|
+
if s1.any? and (c.size == 1) and event_type == :key_press
|
260
|
+
eval_s = s1.first.action.clone
|
261
|
+
# eval_s.gsub!("<char>","'#{c}'") #TODO: remove
|
262
|
+
new_state = [s1.first]
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
if new_state == nil
|
267
|
+
# Child is regexp like /[1-9]/ in:
|
268
|
+
# 'C /[1-9]/'=> 'set_next_command_count(<char>)',
|
269
|
+
# Execute child regexps one until matches
|
270
|
+
s1 = match_state[0].children.select { |s|
|
271
|
+
s.key_name =~ /^\/.*\/$/
|
272
|
+
} # TODO: [0]
|
273
|
+
|
274
|
+
if s1.any? and c.size == 1
|
275
|
+
s1.each { |x|
|
276
|
+
m = /^\/(.*)\/$/.match(x.key_name)
|
277
|
+
if m != nil
|
278
|
+
m2 = Regexp.new(m[1]).match(c)
|
279
|
+
if m2 != nil
|
280
|
+
eval_s = x.action.clone
|
281
|
+
new_state = [x]
|
282
|
+
break
|
283
|
+
end
|
284
|
+
|
285
|
+
return true
|
286
|
+
end
|
287
|
+
}
|
288
|
+
# eval_s = s1.first.action.clone
|
289
|
+
# eval_s.gsub!("<char>","'#{c}'")
|
290
|
+
# new_state = [s1.first]
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
if new_state != nil
|
295
|
+
@state_trail << new_state
|
296
|
+
puts get_state_trail_str()
|
297
|
+
# # puts "CUR STATE: #{@state_trail.collect{|x| x.to_s}.join}"
|
298
|
+
# s_trail = ""
|
299
|
+
# for st in @state_trail
|
300
|
+
# st = st[0] if st.class == Array
|
301
|
+
# s_trail << " #{st.to_s}"
|
302
|
+
# end
|
303
|
+
# puts "CUR STATE: #{s_trail}"
|
304
|
+
# for cstate in new_state[0].children
|
305
|
+
# act_s = "..."
|
306
|
+
# act_s = cstate.action.to_s if cstate.action != nil
|
307
|
+
# puts " #{cstate.to_s} #{act_s}"
|
308
|
+
# end
|
309
|
+
# Ripl.start :binding => binding
|
310
|
+
# new_state[0].children.collect{|x|x.to_s}
|
311
|
+
end
|
312
|
+
|
313
|
+
if new_state == nil
|
314
|
+
printf("NO MATCH") if $debug
|
315
|
+
if event_type == :key_press and c != "shift"
|
316
|
+
# TODO:include other modifiers in addition to shift?
|
317
|
+
set_state_to_root
|
318
|
+
printf(", BACK TO ROOT") if $debug
|
319
|
+
end
|
320
|
+
|
321
|
+
if event_type == :key_release and c == "shift!"
|
322
|
+
# Pressing a modifier key (shift) puts state back to root
|
323
|
+
# only on key release when no other key has been pressed
|
324
|
+
# after said modifier key (shift).
|
325
|
+
set_state_to_root
|
326
|
+
printf(", BACK TO ROOT") if $debug
|
327
|
+
end
|
328
|
+
|
329
|
+
printf("\n") if $debug
|
330
|
+
else
|
331
|
+
|
332
|
+
# Don't execute action if one of the states has children
|
333
|
+
state_with_children = new_state.select { |s| s.children.any? }
|
334
|
+
s_act = new_state.select { |s| s.action != nil }
|
335
|
+
|
336
|
+
if s_act.any? and !state_with_children.any?
|
337
|
+
eval_s = s_act.first.action if eval_s == nil
|
338
|
+
puts "FOUND MATCH:#{eval_s}"
|
339
|
+
puts "CHAR: #{c}"
|
340
|
+
c.gsub!("\\", %q{\\\\} * 4) # Escape \ -chars
|
341
|
+
c.gsub!("'", "#{'\\' * 4}'") # Escape ' -chars
|
342
|
+
|
343
|
+
eval_s.gsub!("<char>", "'#{c}'") if eval_s.class == String
|
344
|
+
puts eval_s
|
345
|
+
puts c
|
346
|
+
handle_key_bindigs_action(eval_s, c)
|
347
|
+
set_state_to_root
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
return true
|
352
|
+
end
|
353
|
+
|
354
|
+
# Receive keyboard event from Qt
|
355
|
+
def handle_key_event(event)
|
356
|
+
start_profiler
|
357
|
+
# puts "GOT KEY EVENT: #{key.inspect}"
|
358
|
+
debug "GOT KEY EVENT:: #{event} #{event[2]}"
|
359
|
+
debug "|#{event.inspect}|"
|
360
|
+
$debuginfo["cur_event"] = event
|
361
|
+
|
362
|
+
t1 = Time.now
|
363
|
+
|
364
|
+
keycode = event[0]
|
365
|
+
event_type = event[1]
|
366
|
+
modifierinfo = event[4]
|
367
|
+
|
368
|
+
event[3] = event[2]
|
369
|
+
# String representation of received key
|
370
|
+
key_str = event[2]
|
371
|
+
|
372
|
+
@modifiers.delete(Qt::Key_Alt) if event[4] & ALTMODIFIER == 0
|
373
|
+
@modifiers.delete(Qt::Key_Control) if event[4] & CONTROLMODIFIER == 0
|
374
|
+
@modifiers.delete(Qt::Key_Shift) if event[4] & SHIFTMODIFIER == 0
|
375
|
+
|
376
|
+
# Add as modifier if ctrl, alt or shift
|
377
|
+
if modifierinfo & ALTMODIFIER != 0 or modifierinfo & CONTROLMODIFIER != 0 or modifierinfo & SHIFTMODIFIER != 0
|
378
|
+
# And keypress and not already a modifier
|
379
|
+
if event_type == KEY_PRESS and !@modifiers.include?(keycode)
|
380
|
+
@modifiers << keycode
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# puts "----D------------"
|
385
|
+
# puts @modifiers.inspect
|
386
|
+
# puts event.inspect
|
387
|
+
# puts event[4] & ALTMODIFIER
|
388
|
+
# puts "-----------------"
|
389
|
+
|
390
|
+
@modifiers.delete(keycode) if event_type == KEY_RELEASE
|
391
|
+
|
392
|
+
# uval = keyval_to_unicode(event[0])
|
393
|
+
# event[3] = [uval].pack('c*').force_encoding('UTF-8') #TODO: 32bit?
|
394
|
+
# debug("key_code_to_uval: uval: #{uval} uchar:#{event[3]}")
|
395
|
+
|
396
|
+
if $event_keysym_translate_table.include?(keycode)
|
397
|
+
key_str = $event_keysym_translate_table[keycode]
|
398
|
+
end
|
399
|
+
|
400
|
+
# Prefix string representation with modifiers, e.g. ctrl-shift-a
|
401
|
+
key_prefix = ""
|
402
|
+
@modifiers.each { |pressed_key|
|
403
|
+
if $event_keysym_translate_table[pressed_key]
|
404
|
+
key_prefix += $event_keysym_translate_table[pressed_key] + "-"
|
405
|
+
end
|
406
|
+
}
|
407
|
+
|
408
|
+
# Get char based on keycode
|
409
|
+
# to produce prefixed_key_str "shift-ctrl-a" instead of "shift-ctrl-\x01"
|
410
|
+
key_str2 = key_str
|
411
|
+
if $translate_table.include?(keycode)
|
412
|
+
key_str2 = $translate_table[keycode].downcase
|
413
|
+
end
|
414
|
+
# puts "key_str=|#{key_str}| key_str=|#{key_str.inspect}| key_str2=|#{key_str2}|"
|
415
|
+
prefixed_key_str = key_prefix + key_str2
|
416
|
+
|
417
|
+
# Space is only key in $event_keysym_translate_table
|
418
|
+
# which is representable by single char
|
419
|
+
key_str = " " if key_str == "space" # HACK
|
420
|
+
|
421
|
+
# if keycode == @last_event[0] and event_type == KEY_RELEASE
|
422
|
+
# puts "KEY! key_str=|#{key_str}| prefixed_key_str=|#{prefixed_key_str}|"
|
423
|
+
# end
|
424
|
+
|
425
|
+
if key_str != "" or prefixed_key_str != ""
|
426
|
+
if keycode == @last_event[0] and event_type == KEY_RELEASE
|
427
|
+
# If key is released immediately after pressed with no other events between
|
428
|
+
match_key_conf(key_str + "!", prefixed_key_str + "!", event_type)
|
429
|
+
elsif event_type == KEY_PRESS
|
430
|
+
match_key_conf(key_str, prefixed_key_str, event_type)
|
431
|
+
end
|
432
|
+
@last_event = event #TODO: outside if?
|
433
|
+
end
|
434
|
+
|
435
|
+
# qt_refresh_cursor
|
436
|
+
|
437
|
+
event_handle_time = Time.now - t1
|
438
|
+
debug "RB key event handle time: #{event_handle_time}" if event_handle_time > 1 / 40.0
|
439
|
+
render_buffer($buffer)
|
440
|
+
end_profiler
|
441
|
+
end
|
442
|
+
|
443
|
+
def bindkey(key, action)
|
444
|
+
if key.class != Array
|
445
|
+
key = key.split("||")
|
446
|
+
end
|
447
|
+
|
448
|
+
a = action
|
449
|
+
if action.class == Array
|
450
|
+
label = a[0]
|
451
|
+
a = label
|
452
|
+
proc = action[1]
|
453
|
+
msg = action[2]
|
454
|
+
reg_act(label, proc, msg)
|
455
|
+
end
|
456
|
+
key.each { |k| _bindkey(k, a) }
|
457
|
+
end
|
458
|
+
|
459
|
+
def _bindkey(key, action)
|
460
|
+
key.strip!
|
461
|
+
key.gsub!(/\s+/, " ")
|
462
|
+
|
463
|
+
# if key.class == Array
|
464
|
+
# key.each { |k| bindkey(k, action) }
|
465
|
+
# return
|
466
|
+
# end
|
467
|
+
# $action_list << { :action => action, :key => key }
|
468
|
+
if !$actions.has_key?(action)
|
469
|
+
if action.class == String
|
470
|
+
reg_act(action, proc { eval(action) }, action)
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
m = key.match(/^(\S+)\s(\S.*)$/)
|
475
|
+
if m
|
476
|
+
modetmp = m[1]
|
477
|
+
puts [key, modetmp, m].inspect
|
478
|
+
modes = modetmp.split("") if modetmp.match(/^\p{Lu}+$/) # Uppercase
|
479
|
+
modes = [modetmp] if modetmp.match(/^\p{Ll}+$/) # Lowercase
|
480
|
+
keydef = m[2]
|
481
|
+
else
|
482
|
+
fatal_error("Error in keydef #{key.inspect}")
|
483
|
+
end
|
484
|
+
|
485
|
+
modes.each { |mode_id|
|
486
|
+
mode_bind_key(mode_id, keydef, action)
|
487
|
+
}
|
488
|
+
end
|
489
|
+
|
490
|
+
def mode_bind_key(mode_id, keydef, action)
|
491
|
+
set_state(mode_id, "") # TODO: check is ok?
|
492
|
+
start_state = @cur_state
|
493
|
+
|
494
|
+
k_arr = keydef.split
|
495
|
+
|
496
|
+
prev_state = nil
|
497
|
+
s1 = start_state
|
498
|
+
k_arr.each { |i|
|
499
|
+
# check if key has rules for context like q has in
|
500
|
+
# "C q(cntx.recording_macro==true)"
|
501
|
+
match = /(.+)\((.*)\)/.match(i)
|
502
|
+
eval_rule = ""
|
503
|
+
if match
|
504
|
+
key_name = match[1]
|
505
|
+
eval_rule = match[2]
|
506
|
+
else
|
507
|
+
key_name = i
|
508
|
+
end
|
509
|
+
|
510
|
+
prev_state = s1
|
511
|
+
# Create a new state for key if it doesn't exist
|
512
|
+
s1 = find_state(key_name, eval_rule)
|
513
|
+
if s1 == nil
|
514
|
+
new_state = State.new(key_name, eval_rule)
|
515
|
+
s1 = new_state
|
516
|
+
@cur_state.children << new_state
|
517
|
+
end
|
518
|
+
|
519
|
+
set_state(key_name, eval_rule) # TODO: check is ok?
|
520
|
+
}
|
521
|
+
if action == :delete_state
|
522
|
+
prev_state.children.delete(cur_state)
|
523
|
+
else
|
524
|
+
@cur_state.action = action
|
525
|
+
end
|
526
|
+
@cur_state = @root
|
527
|
+
end
|
528
|
+
|
529
|
+
def handle_key_bindigs_action(action, c)
|
530
|
+
$method_handles_repeat = false #TODO:??
|
531
|
+
n = 1
|
532
|
+
if $next_command_count and !(action.class == String and action.include?("set_next_command_count"))
|
533
|
+
n = $next_command_count
|
534
|
+
# $next_command_count = nil
|
535
|
+
debug("COUNT command #{n} times")
|
536
|
+
end
|
537
|
+
|
538
|
+
begin
|
539
|
+
n.times do
|
540
|
+
ret = exec_action(action)
|
541
|
+
|
542
|
+
if $macro.is_recording and ret != false
|
543
|
+
$macro.record_action(action)
|
544
|
+
end
|
545
|
+
break if $method_handles_repeat
|
546
|
+
# Some methods have specific implementation for repeat,
|
547
|
+
# like '5yy' => copy next five lines. (copy_line())
|
548
|
+
# By default the same command is just repeated n times
|
549
|
+
# like '20j' => go to next line 20 times.
|
550
|
+
end
|
551
|
+
rescue SyntaxError
|
552
|
+
debug("SYNTAX ERROR with eval cmd #{action}: " + $!.to_s)
|
553
|
+
# rescue NoMethodError
|
554
|
+
# debug("NoMethodError with eval cmd #{action}: " + $!.to_s)
|
555
|
+
# rescue NameError
|
556
|
+
# debug("NameError with eval cmd #{action}: " + $!.to_s)
|
557
|
+
# raise
|
558
|
+
rescue Exception => e
|
559
|
+
puts "BACKTRACE"
|
560
|
+
puts e.backtrace
|
561
|
+
puts e.inspect
|
562
|
+
puts "BACKTRACE END"
|
563
|
+
if $!.class == SystemExit
|
564
|
+
exit
|
565
|
+
else
|
566
|
+
crash("Error with action: #{action}: ", e)
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
if action.class == String and !action.include?("set_next_command_count")
|
571
|
+
$next_command_count = nil
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
def bindkey(key, action)
|
577
|
+
$kbd.bindkey(key, action)
|
578
|
+
end
|
579
|
+
|
580
|
+
$action_list = []
|
581
|
+
|
582
|
+
def exec_action(action)
|
583
|
+
$kbd.last_action = $kbd.cur_action
|
584
|
+
$kbd.cur_action = action
|
585
|
+
if action.class == Symbol
|
586
|
+
return call(action)
|
587
|
+
elsif action.class == Proc
|
588
|
+
return action.call
|
589
|
+
else
|
590
|
+
return eval(action)
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
# Try to clear modifiers when program loses focus
|
595
|
+
# e.g. after alt-tab
|
596
|
+
def focus_out
|
597
|
+
debug "RB Clear modifiers"
|
598
|
+
$kbd.clear_modifiers()
|
599
|
+
end
|
600
|
+
|
601
|
+
def handle_key_event(event)
|
602
|
+
$kbd.handle_key_event(event)
|
603
|
+
end
|