vimamsa 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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