vimamsa 0.1.21 → 0.1.23

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.
@@ -7,8 +7,8 @@
7
7
  #
8
8
  # Change mode from INSERT into COMMAND when ctrl key is released immediately
9
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)',
10
+ # 'I ctrl!'=> 'vma.kbd.set_mode(:command)',
11
+ # 'C ctrl!'=> 'vma.kbd.set_mode(:insert)',
12
12
 
13
13
  #
14
14
  # In command mode: press keys "," "r" "v" and "b" sequentially.
@@ -18,9 +18,8 @@
18
18
  # 'I ctrl-a'=> 'vma.buf.jump(BEGINNING_OF_LINE)',
19
19
  #
20
20
 
21
-
22
21
  class State
23
- attr_accessor :key_name, :eval_rule, :children, :action, :label, :major_modes, :level, :cursor_type
22
+ attr_accessor :key_name, :eval_rule, :children, :action, :label, :major_modes, :level, :cursor_type, :keywords, :root, :parent
24
23
  attr_reader :cur_mode, :scope
25
24
 
26
25
  def initialize(key_name, eval_rule = "", ctype = :command, scope: :buffer)
@@ -29,8 +28,11 @@ class State
29
28
  @children = []
30
29
  @scope = scope
31
30
  @major_modes = []
31
+ @keywords = []
32
32
  @action = nil
33
33
  @level = 0
34
+ @root = self # parent of a parent ... until mode root
35
+ @parent = nil
34
36
  @cursor_type = ctype
35
37
  end
36
38
 
@@ -55,6 +57,7 @@ class KeyBindingTree
55
57
  @last_action = nil
56
58
  @cur_action = nil
57
59
  @method_handles_repeat = false
60
+ @overwriting_state = nil # A branch which has priority over other branches
58
61
 
59
62
  @modifiers = { :ctrl => false, :shift => false, :alt => false } # TODO: create a queue
60
63
  @last_event = [nil, nil, nil, nil, nil]
@@ -68,8 +71,9 @@ class KeyBindingTree
68
71
  return if get_mode == :label
69
72
  @match_state = [@modes[label]] # used for matching input
70
73
  @mode_root_state = @modes[label]
71
- # @default_mode = label
72
- @default_mode_stack << label
74
+
75
+ #TODO: should not happen? @default_mode_stack[-1] should be always the same as get_mode ?
76
+ @default_mode_stack << label if label != @default_mode_stack[-1]
73
77
 
74
78
  __set_mode(label)
75
79
  if !vma.buf.nil?
@@ -86,7 +90,7 @@ class KeyBindingTree
86
90
 
87
91
  def set_mode_stack(ms)
88
92
  debug "set_mode_stack(#{ms})", 2
89
- show_caller if cnf.debug? # TODO: remove
93
+ show_caller if cnf.debug? # TODO: remove
90
94
  @default_mode_stack = ms
91
95
  label = @default_mode_stack[-1]
92
96
  @match_state = [@modes[label]]
@@ -110,7 +114,7 @@ class KeyBindingTree
110
114
  end
111
115
 
112
116
  def to_previous_mode()
113
- debug "to_previous_mode",2
117
+ debug "to_previous_mode", 2
114
118
  debug @default_mode_stack
115
119
  if @default_mode_stack.size > 1
116
120
  @default_mode_stack.pop
@@ -135,7 +139,6 @@ class KeyBindingTree
135
139
  @modes[label] = mode
136
140
  if @root.nil?
137
141
  show_caller
138
- Ripl.start :binding => binding
139
142
  end
140
143
  @root.children << mode
141
144
  mode.major_modes << major_mode_label
@@ -165,6 +168,7 @@ class KeyBindingTree
165
168
 
166
169
  def match(key_name)
167
170
  new_state = []
171
+ debug = false
168
172
  @match_state.each { |parent|
169
173
  parent.children.each { |c|
170
174
  # printf(" KEY MATCH: ")
@@ -174,6 +178,9 @@ class KeyBindingTree
174
178
  elsif c.key_name == key_name and c.eval_rule != ""
175
179
  debug "CHECK EVAL: #{c.eval_rule}"
176
180
  if eval(c.eval_rule)
181
+ # if eval_rule.match(/macro/)
182
+ debug = true
183
+ # end
177
184
  new_state << c
178
185
  debug "EVAL TRUE"
179
186
  else
@@ -183,6 +190,10 @@ class KeyBindingTree
183
190
  }
184
191
  }
185
192
 
193
+ if debug
194
+ # require "pry"; binding.pry
195
+ end
196
+
186
197
  if new_state.any? # Match found
187
198
  @match_state = new_state
188
199
  return new_state
@@ -251,25 +262,83 @@ class KeyBindingTree
251
262
  end
252
263
 
253
264
  def set_state_to_root
265
+ # if root state is a minor mode
254
266
  if @mode_root_state.major_modes.size == 1
255
- modelabel = @mode_root_state.major_modes[0]
256
- mmode = @modes[modelabel]
257
- @match_state = [@mode_root_state, mmode]
267
+ modelabel = @mode_root_state.major_modes[0] #TODO: support multiple inheritance?
268
+ parent_mode = @modes[modelabel]
269
+ # Have two branches for the matching (both major and minor modes)
270
+ # @mode_root_state = minor, parent_mode = major
271
+ @match_state = [@mode_root_state, parent_mode]
272
+ @overwriting_state = @mode_root_state # States from this branch have priority over others.
258
273
  else
274
+ # if root state is a major mode
275
+ @overwriting_state = nil
259
276
  @match_state = [@mode_root_state]
260
277
  end
261
278
 
262
279
  @state_trail = [@mode_root_state]
263
280
  end
264
281
 
282
+ def get_by_keywords(modes: [], keywords: [])
283
+ s = ""
284
+ stack = [[@root, ""]]
285
+ lines = []
286
+
287
+ # Traverse the tree (class State objects) using a stack
288
+ while stack.any?
289
+ t, p = *stack.pop # t = current state, p = current path
290
+ if t.children.any?
291
+ t.children.reverse.each { |c|
292
+
293
+ # Restrict by modes if specified
294
+ if c.level == 1 and !modes.empty?
295
+ next if !modes.include?(c.key_name)
296
+ end
297
+
298
+ if c.eval_rule.size > 0
299
+ new_p = "#{p} #{c.key_name}(#{c.eval_rule})"
300
+ else
301
+ if c.level == 1
302
+ new_p = "#{p} [#{c.key_name}]"
303
+ else
304
+ new_p = "#{p} #{c.key_name}"
305
+ end
306
+ end
307
+ stack << [c, new_p]
308
+ }
309
+ # stack.concat[t.children]
310
+
311
+ else
312
+ method_desc = t.action
313
+ if t.action.class == Symbol
314
+ if vma.actions.include?(t.action)
315
+ a = vma.actions[t.action].method_name
316
+ if !a.nil? and !a.empty?
317
+ method_desc = a
318
+ end
319
+ end
320
+ end
321
+
322
+ if (keywords - t.keywords).empty? # All keywords are present
323
+ kw = ""
324
+ # kw = ", [#{t.keywords.join(",")}]" if !t.keywords.empty?
325
+ lines << p + " : #{method_desc}#{kw}"
326
+ end
327
+ end
328
+ end
329
+ s = lines.sort.join("\n")
330
+ return s
331
+ end
332
+
265
333
  # Print key bindings to show as documentation or for debugging
266
334
  def to_s()
267
- # return self.class.to_s
335
+ return self.class.to_s
268
336
  s = ""
269
337
  # @cur_state = @root
270
338
  stack = [[@root, ""]]
271
339
  lines = []
272
340
 
341
+ # Traverse the tree (class State objects) using a stack
273
342
  while stack.any?
274
343
  t, p = *stack.pop # t = current state, p = current path
275
344
  if t.children.any?
@@ -297,7 +366,9 @@ class KeyBindingTree
297
366
  end
298
367
  end
299
368
 
300
- lines << p + " : #{method_desc}"
369
+ kw = ""
370
+ # kw = ", " + t.keywords.join(",") if !t.keywords.empty?
371
+ lines << p + " : #{method_desc}#{kw}"
301
372
  end
302
373
  end
303
374
  s = lines.sort.join("\n")
@@ -317,7 +388,13 @@ class KeyBindingTree
317
388
  mmid = st.major_modes.first
318
389
  trailpfx = "#{@modes[mmid].to_s}>"
319
390
  end
320
- s_trail << "[#{trailpfx}#{st.to_s}]"
391
+ mode_str = st.to_s
392
+ mode_str = "COMMAND" if mode_str == "C"
393
+ mode_str = "INSERT" if mode_str == "I"
394
+ mode_str = "VISUAL" if mode_str == "V"
395
+ mode_str = "BROWSE" if mode_str == "B"
396
+
397
+ s_trail << "[#{trailpfx}#{mode_str}]"
321
398
  else
322
399
  s_trail << " #{st.to_s}"
323
400
  end
@@ -426,11 +503,39 @@ class KeyBindingTree
426
503
  else
427
504
 
428
505
  # Don't execute action if one of the states has children
506
+ # TODO: why?
429
507
  state_with_children = new_state.select { |s| s.children.any? }
430
508
  s_act = new_state.select { |s| s.action != nil }
431
509
 
510
+ # Multiple matching states/modes (search has forked)
511
+ if new_state.size > 1
512
+ # puts "AAA"
513
+ # Conflict: One of them has actions (matching should stop),
514
+ # another has children (matching should continue)
515
+ if s_act.any? and state_with_children.any?
516
+ # puts "AAA1"
517
+ a = s_act[0]
518
+ b = state_with_children[0]
519
+ # Running major+minor mode. Minor mode overwriting the major mode
520
+ if a.root == @overwriting_state or b.root == @overwriting_state
521
+ # puts "AAA3:del"
522
+ # Remove those states not belonging to the overwriting branch (minor mode)
523
+ [s_act, state_with_children, new_state].each { |z| z.delete_if { |x| x.root != @overwriting_state } }
524
+ end
525
+ end
526
+ end
527
+ # new_state[0].root.key_name
528
+
529
+ if s_act.any? and state_with_children.any?
530
+ # debug "Conflict: s_act.any? and state_with_children.any?"
531
+ # require "pry"; binding.pry
532
+ end
533
+
432
534
  if s_act.any? and !state_with_children.any?
433
535
  eval_s = s_act.first.action if eval_s == nil
536
+ # if eval_s.to_s.match(/end_recording/)
537
+ # require "pry"; binding.pry
538
+ # end
434
539
  debug "FOUND MATCH:#{eval_s}"
435
540
  debug "CHAR: #{c}"
436
541
  c.gsub!("\\", %q{\\\\} * 4) # Escape \ -chars
@@ -449,7 +554,12 @@ class KeyBindingTree
449
554
  return true
450
555
  end
451
556
 
452
- def bindkey(key, action)
557
+ def bindkey(key, action, keywords: "")
558
+ # puts "keywords #{keywords}" if !keywords.empty?
559
+ if keywords.class == String
560
+ keywords = keywords.split(" ")
561
+ end
562
+ # puts "keywords #{keywords}" if !keywords.empty?
453
563
  if key.class != Array
454
564
  # Handle syntax like :
455
565
  # "X esc || X ctrl!" => "vma.kbd.to_previous_mode",
@@ -464,10 +574,10 @@ class KeyBindingTree
464
574
  msg = action[2]
465
575
  reg_act(label, proc, msg)
466
576
  end
467
- key.each { |k| _bindkey(k, a) }
577
+ key.each { |k| _bindkey(k, a, keywords: keywords) }
468
578
  end
469
579
 
470
- def _bindkey(key, action)
580
+ def _bindkey(key, action, keywords: [])
471
581
  key.strip!
472
582
  key.gsub!(/\s+/, " ")
473
583
 
@@ -502,15 +612,18 @@ class KeyBindingTree
502
612
  fatal_error("Error in keydef #{key.inspect}")
503
613
  end
504
614
 
615
+ # puts "keywords #{keywords}"
505
616
  modes.each { |mode_id|
506
- mode_bind_key(mode_id, keydef, action)
617
+ mode_bind_key(mode_id, keydef, action, keywords: keywords)
618
+
619
+ # Map froma actions to keybindings (e.g. to show bindings in menu)
507
620
  @act_bindings[mode_id][action] = keydef
508
621
  }
509
622
  end
510
623
 
511
624
  # Binds a keyboard key combination to an action,
512
625
  # for a given keyboard mode like insert ("I") or command ("C")
513
- def mode_bind_key(mode_id, keydef, action)
626
+ def mode_bind_key(mode_id, keydef, action, keywords: [])
514
627
  # debug "mode_bind_key #{mode_id.inspect}, #{keydef.inspect}, #{action.inspect}", 2
515
628
  # Example:
516
629
  # bindkey "C , f", :gui_file_finder
@@ -520,7 +633,7 @@ class KeyBindingTree
520
633
  set_state(mode_id, "") # TODO: check is ok?
521
634
  start_state = @cur_state
522
635
 
523
- k_arr = keydef.split
636
+ k_arr = keydef.split #e.g. definition: "C y e" = > ["C", "y", "e"]
524
637
 
525
638
  prev_state = nil
526
639
  s1 = start_state
@@ -552,6 +665,8 @@ class KeyBindingTree
552
665
  end
553
666
  s1 = new_state
554
667
  @cur_state.children << new_state
668
+ new_state.root = @cur_state.root
669
+ new_state.parent = @cur_state
555
670
  end
556
671
 
557
672
  set_state(key_name, eval_rule) # TODO: check is ok?
@@ -561,10 +676,12 @@ class KeyBindingTree
561
676
  else
562
677
  @cur_state.action = action
563
678
  end
679
+ @cur_state.keywords = keywords
564
680
  @cur_state = @root
565
681
  end
566
682
 
567
683
  def handle_key_bindigs_action(action, c)
684
+ trail_str = get_state_trail_str[0]
568
685
  # $acth << action #TODO:needed here?
569
686
  @method_handles_repeat = false #TODO:??
570
687
  n = 1
@@ -611,16 +728,37 @@ class KeyBindingTree
611
728
  if !(action.class == String and action.include?("set_next_command_count"))
612
729
  @next_command_count = nil
613
730
  end
731
+
732
+ if cnf.kbd.show_prev_action? and trail_str.class==String
733
+ len_limit = 35
734
+ action_desc = "UNK"
735
+ if action.class == String && (m = action.match(/\Abuf\.insert_txt\((.+)\)\z/))
736
+ char_part = m[1].gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
737
+ action_desc = "insert #{char_part}"
738
+ puts action_desc.inspect
739
+ else
740
+ action_desc = vma.actions[action]&.method_name || action.to_s
741
+ action_desc = action_desc[0..len_limit] if action_desc.size > len_limit
742
+ end
743
+ trail_str = trail_str.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
744
+ vma.gui.action_trail_label.markup = "<span weight='bold'>#{action_desc}|#{trail_str}</span>"
745
+ end
614
746
  end
615
747
  end
616
748
 
617
- def bindkey(key, action)
618
- $kbd.bindkey(key, action)
749
+ def bindkey(key, action, keywords: "")
750
+ vma.kbd.bindkey(key, action, keywords: keywords)
751
+ end
752
+
753
+ def add_keys(keywords, to_add)
754
+ to_add.each { |key, value|
755
+ bindkey(key, value, keywords: keywords)
756
+ }
619
757
  end
620
758
 
621
759
  def exec_action(action)
622
- $kbd.last_action = $kbd.cur_action
623
- $kbd.cur_action = action
760
+ vma.kbd.last_action = vma.kbd.cur_action
761
+ vma.kbd.cur_action = action
624
762
  if action.class == Symbol
625
763
  return call_action(action)
626
764
  elsif action.class == Proc
@@ -630,14 +768,76 @@ def exec_action(action)
630
768
  end
631
769
  end
632
770
 
771
+ def show_key_bindings()
772
+ kbd_s = "❙Key bindings❙\n"
773
+ kbd_s << "\n⦁[Mode] <keys> : <action>⦁\n"
774
+ done = []
775
+
776
+ kbd_s << "[B]=Browse, [C]=Command, [I]=Insert, [V]=Visual\n"
777
+ kbd_s << "<key>!: Press <key> once, release before pressing any other keys\n"
778
+ kbd_s << "===============================================\n"
779
+ kbd_s << "◼ Basic\n"
780
+ kbd_s << "◼◼ Command mode\n"
781
+
782
+ x = vma.kbd.get_by_keywords(modes: ["C"], keywords: ["intro"])
783
+ done.concat(x.lines); kbd_s << x
784
+
785
+ kbd_s << "\n"
786
+ kbd_s << "◼◼ Insert mode\n"
787
+ x = vma.kbd.get_by_keywords(modes: ["I"], keywords: ["intro"])
788
+ done.concat(x.lines); kbd_s << x
789
+ kbd_s << "\n"
790
+ kbd_s << "◼◼ Visual mode\n"
791
+ x = vma.kbd.get_by_keywords(modes: ["V"], keywords: ["intro"])
792
+ done.concat(x.lines); kbd_s << x
793
+ kbd_s << "\n"
794
+
795
+ kbd_s << "◼ Hyper Plaintext\n"
796
+ x = vma.kbd.get_by_keywords(modes: ["C"], keywords: ["hyperplaintext"])
797
+ x2 = vma.kbd.get_by_keywords(modes: ["V"], keywords: ["hyperplaintext"])
798
+ done.concat(x.lines); kbd_s << x << "\n" << x2
799
+ kbd_s << "\n"
800
+
801
+ kbd_s << "◼ Core\n"
802
+ x = vma.kbd.get_by_keywords(modes: [], keywords: ["core"])
803
+ x << vma.kbd.get_by_keywords(modes: ["X"], keywords: ["intro"])
804
+
805
+ done.concat(x.lines); kbd_s << x
806
+ kbd_s << "\n"
807
+
808
+ kbd_s << "◼ Debug / Experimental\n"
809
+ x = vma.kbd.get_by_keywords(modes: [], keywords: ["experimental"])
810
+ done.concat(x.lines); kbd_s << x
811
+ kbd_s << "\n"
812
+
813
+ kbd_s << "◼ Others\n"
814
+ # x = vma.kbd.get_by_keywords(modes: [], keywords:["experimental"])
815
+ # x = vma.kbd.to_s
816
+ x = vma.kbd.get_by_keywords(modes: [], keywords: [])
817
+ done << x.lines - done
818
+ kbd_s << (x.lines - done).join
819
+ kbd_s << "\n"
820
+
821
+ kbd_s << "===============================================\n"
822
+ # x = vma.kbd.to_s
823
+ # # require "pry"; binding.pry
824
+ # done << x.lines - done
825
+ # kbd_s << (x.lines - done).join
826
+ # kbd_s << "\n"
827
+ # kbd_s << "===============================================\n"
828
+ b = create_new_buffer(kbd_s, "key-bindings")
829
+ gui_set_file_lang(b.id, "hyperplaintext")
830
+ #
831
+ end
832
+
633
833
  # Try to clear modifiers when program loses focus
634
834
  # e.g. after alt-tab
635
835
  # TODO
636
836
  # def focus_out
637
- # debug "RB Clear modifiers"
638
- # $kbd.clear_modifiers()
837
+ # debug "RB Clear modifiers"
838
+ # vma.kbd.clear_modifiers()
639
839
  # end
640
840
 
641
841
  # def handle_key_event(event)
642
- # $kbd.handle_key_event(event)
842
+ # vma.kbd.handle_key_event(event)
643
843
  # end