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.
@@ -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