vimamsa 0.1.21 → 0.1.22

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b44f279b048a7cd7baba9e21e70dc9666a251f9608466f9e4bd38094c486738
4
- data.tar.gz: ef57b1ad00c480d30720ec8d272df38ec289640c7951b8a04a070837e43a261a
3
+ metadata.gz: 3eba5b3c9a7821e14802295825b32369ff9440046b91b065ca2b1e82e44cc444
4
+ data.tar.gz: 16bd4a94226c3b07cb5d40cb6e395a09f8fbbb4e96e164af19b05415048a9812
5
5
  SHA512:
6
- metadata.gz: 756f7c861bff64427fac5e397f8e0b4d539f46dd9d72edf39ddbc190eefff09cb86c0107ec38664ee9a1c450c2de9912f6960aee7c990c8b99c4c914cf0f5a56
7
- data.tar.gz: 15a6b06c01f13435a154a2b64c544e8d5b14f253bed3e4b65e845dc510f591fcd77c1e9c278d4cf7b67699205c97952ad6ab8042fffe222260d972b88db42bee
6
+ metadata.gz: f74f902af54d82002d4b0d814e80cafc5326bfb712c0cd124c39be0bb2bf3f70d1eaabe6c2f05de619ee9e9ac4cf12a10a4350c568ca1b815129904b40797c48
7
+ data.tar.gz: a01c59f8358904e1ad511c4608de70bc31ac8bc8ccbbfa240eb897240a68f7673b7ae1bc124dcf312b94a6c7f12834d33dba692e6795713f083a5b2736ac6291
data/README.md CHANGED
@@ -18,15 +18,21 @@ Vi/Vim -inspired experimental GUI-oriented text editor written with Ruby and GTK
18
18
  - Ruby 3.0+
19
19
  - GTK 4
20
20
 
21
+
21
22
  ## Installation
22
23
 
23
24
 
24
- On Ubuntu:
25
+ On Ubuntu (22.04):
25
26
  ```
26
- sudo apt install ruby-dev
27
+ sudo apt install ruby-dev build-essential
27
28
  sudo gem install vimamsa
28
29
  ```
29
30
 
31
+ Run:
32
+ ```
33
+ vimamsa
34
+ ```
35
+
30
36
  ### Other install options
31
37
 
32
38
  Install from sources:
@@ -51,7 +57,6 @@ vimamsa
51
57
  Install packages for optional features:
52
58
  ```
53
59
  sudo apt install ack-grep clang-format
54
- gem install ripl ripl-multi_line differ parallel listen rufo language_server-protocol
55
60
  ```
56
61
 
57
62
  For customization, edit ~/.vimamsa/custom.rb
@@ -63,7 +68,7 @@ For customization, edit ~/.vimamsa/custom.rb
63
68
 
64
69
  ## Key bindings
65
70
 
66
- Key bindings are very much like in VIm. For details, see file lib/vimamsa/key_bindings.rb and lib/vimamsa/key_bindings_vimlike.rb
71
+ Key bindings are very much like in VIm. For details, see menu item "Help -> Show key bindings" and file lib/vimamsa/key_bindings_vimlike.rb
67
72
 
68
73
  Keys that work somewhat similarly as in Vim:
69
74
 
@@ -81,8 +86,9 @@ d y gU gu
81
86
  Keys that work differently to Vim are documented in the tables below
82
87
 
83
88
  Syntax:
84
- ctrl! means press and immediate release of ctrl key. Triggered by key up event.
85
- ctrl-x means press and hold ctrl key, press x
89
+
90
+ - ctrl! means press and immediate release of ctrl key. Triggered by key up event when no other keys were pressed between key down and key up events.
91
+ - ctrl-x means press and hold ctrl key, press x
86
92
 
87
93
  <table>
88
94
  <colgroup>
data/install.sh ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+ gem build vimamsa.gemspec
3
+ sudo gem uninstall vimamsa
4
+ sudo gem install --local $(ls -1tr *gem |head -n 1)
@@ -141,7 +141,7 @@ class Buffer < String
141
141
  end
142
142
 
143
143
  def jump_to_next_instance_of_word()
144
- if $kbd.last_action == $kbd.cur_action and @current_word != nil
144
+ if vma.kbd.last_action == vma.kbd.cur_action and @current_word != nil
145
145
  # debug "REPEATING *"
146
146
  else
147
147
  start_search = [@pos - 150, 0].max
@@ -180,7 +180,7 @@ class BufferList
180
180
  end
181
181
 
182
182
  def get_last_dir
183
- return @last_dir
183
+ return File.expand_path(@last_dir)
184
184
  end
185
185
 
186
186
  def reset_navigation
@@ -74,7 +74,7 @@ class Editor
74
74
 
75
75
  # build_key_bindings_tree
76
76
  @kbd = KeyBindingTree.new()
77
- $kbd = @kbd
77
+ $kbd = @kbd #TODO: remove global
78
78
  require "vimamsa/key_bindings_vimlike"
79
79
 
80
80
  $buffers = BufferList.new
@@ -261,6 +261,9 @@ class Editor
261
261
  if p and !@file_content_search_paths.include?(p)
262
262
  r.insert(0, p)
263
263
  end
264
+
265
+ # Ensure that paths are in correct format
266
+ r = r.map{|x|File.expand_path(x)}
264
267
 
265
268
  return r
266
269
  end
@@ -365,21 +368,6 @@ def start_minibuffer_cmd(bufname, bufstr, cmd)
365
368
  $minibuffer.call_func = method(cmd)
366
369
  end
367
370
 
368
- def show_key_bindings()
369
- kbd_s = "❙Key bindings❙\n"
370
- kbd_s << "\n⦁[Mode] keys : action⦁\n"
371
-
372
- kbd_s << "[B]=Browse, [C]=Command, [I]=Insert, [V]=Visual\n"
373
- kbd_s << "key!: Press key once, release before pressing any other keys\n"
374
-
375
- kbd_s << "===============================================\n"
376
- kbd_s << vma.kbd.to_s
377
- kbd_s << "\n"
378
- kbd_s << "===============================================\n"
379
- b = create_new_buffer(kbd_s, "key-bindings")
380
- gui_set_file_lang(b.id, "hyperplaintext")
381
- #
382
- end
383
371
 
384
372
  def diff_buffer()
385
373
  bufstr = ""
@@ -412,14 +400,14 @@ end
412
400
 
413
401
  def minibuffer_end()
414
402
  debug "minibuffer_end"
415
- $kbd.set_mode(:command)
403
+ vma.kbd.set_mode(:command)
416
404
  minibuffer_input = $minibuffer.to_s[0..-2]
417
405
  return $minibuffer.call_func.call(minibuffer_input)
418
406
  end
419
407
 
420
408
  def minibuffer_cancel()
421
409
  debug "minibuffer_cancel"
422
- $kbd.set_mode(:command)
410
+ vma.kbd.set_mode(:command)
423
411
  minibuffer_input = $minibuffer.to_s[0..-2]
424
412
  # $minibuffer.call_func.call('')
425
413
  end
@@ -18,7 +18,7 @@ class FileManager
18
18
  end
19
19
 
20
20
  def self.init()
21
- reg_act(:start_file_selector, proc { FileManager.new.run; vma.kbd.set_mode(:file_exp); }, "File selector")
21
+ reg_act(:start_file_selector, proc { FileManager.new.run; vma.kbd.set_mode(:file_exp) }, "File selector")
22
22
 
23
23
  reg_act(:fexp_chdir_parent, proc { FileManager.chdir_parent }, "File selector")
24
24
  reg_act(:fexp_select, proc { buf.module.select_line }, "")
@@ -162,13 +162,14 @@ class FileManager
162
162
  #TODO:
163
163
  end
164
164
 
165
+ # Main inteface, show contents of current dir
165
166
  def dir_to_buf(dirpath, b = nil)
166
167
  # File.stat("testfile").mtime
167
168
 
168
169
  debug "last file: #{vma.buffers.last_file}", 2
169
170
  lastf = vma.buffers.last_file
170
171
  jumpto = nil
171
- if File.dirname(lastf) == dirpath
172
+ if !lastf.nil? and File.dirname(lastf) == dirpath
172
173
  jumpto = File.basename(lastf)
173
174
  end
174
175
  vma.buffers.last_dir = dirpath
@@ -231,6 +232,13 @@ class FileManager
231
232
  else
232
233
  @buf.set_line_and_column_pos(@header.size, 0)
233
234
  end
235
+
236
+ if @cdirs.size > 0
237
+ r = vma.buf.line_range(2, @cdirs.size+1)
238
+
239
+ # Hilight works only if done after buffer is drawn
240
+ run_as_idle proc { Gui.hilight_range(vma.buf, r, color: "#4488ffff") }
241
+ end
234
242
  end
235
243
 
236
244
  def fullp(fn)
@@ -267,7 +275,6 @@ class FileManager
267
275
  jump_to_file(fn)
268
276
  # vma.buffers.set_current_buffer(idx)
269
277
  vma.buffers.close_other_buffer(@buf.id)
270
-
271
278
  else
272
279
  open_with_default_program(fn)
273
280
  end
data/lib/vimamsa/gui.rb CHANGED
@@ -348,7 +348,7 @@ class VMAgui
348
348
  sw.set_child(view)
349
349
  end
350
350
 
351
- #TODO: implement in gtk4
351
+ #TODO: implement in gtk4
352
352
  def init_header_bar()
353
353
  header = Gtk::HeaderBar.new
354
354
  @header = header
@@ -577,17 +577,36 @@ class VMAgui
577
577
 
578
578
  reset_controllers
579
579
 
580
+ focus_controller = Gtk::EventControllerFocus.new
581
+
582
+ focus_controller.signal_connect("enter") do
583
+ debug "Gained focus"
584
+ draw_cursor_bug_workaround
585
+ end
586
+ @window.add_controller(focus_controller)
587
+
588
+ motion_controller = Gtk::EventControllerMotion.new
589
+ motion_controller.signal_connect("motion") do |controller, x, y|
590
+ # label.set_text("Mouse at: (%.1f, %.1f)" % [x, y])
591
+ # puts "MOVE #{x} #{y}"
592
+
593
+ # Cursor vanishes when hovering over menubar
594
+ draw_cursor_bug_workaround if y < 30
595
+ @last_cursor = [x, y]
596
+ @cursor_move_time = Time.now
597
+ end
598
+ @window.add_controller(motion_controller)
599
+
580
600
  @windows[1] = new_window(1)
581
601
 
582
602
  @last_adj_time = Time.now
583
603
 
584
-
585
604
  # To show keyboard key binding state
586
605
  @statnfo = Gtk::Label.new
587
-
606
+
588
607
  # To show e.g. current folder
589
608
  @subtitle = Gtk::Label.new("")
590
-
609
+
591
610
  @statbox = Gtk::Box.new(:horizontal, 2)
592
611
  @statnfo.set_size_request(150, 10)
593
612
  @statbox.append(@subtitle)
@@ -618,6 +637,12 @@ class VMAgui
618
637
 
619
638
  @window.show
620
639
 
640
+ surface = @window.native.surface
641
+ tt = Time.now
642
+ surface.signal_connect("layout") do
643
+ # puts "Window resized or moved, other redraw."
644
+ end
645
+
621
646
  run_as_idle proc { idle_set_size }
622
647
 
623
648
  prov = Gtk::CssProvider.new
@@ -666,9 +691,9 @@ class VMAgui
666
691
  return true if Time.now - @monitor_time < 0.2
667
692
  # Detect element resize
668
693
  if swa.width != @sw_width
669
- # puts "@sw.width=#{@sw.width}"
694
+ debug "Width change sw_width #{@sw_width}"
670
695
  @sw_width = swa.width
671
- DelayExecutioner.exec(id: :scale_images, wait: 0.7, callable: proc { vma.gui.scale_all_images })
696
+ DelayExecutioner.exec(id: :scale_images, wait: 0.7, callable: proc { debug ":scale_images"; vma.gui.scale_all_images })
672
697
  end
673
698
  @monitor_time = Time.now
674
699
  return true
@@ -1,16 +1,7 @@
1
1
  # class VSourceView < Gtk::TextView
2
2
  class VSourceView < GtkSource::View
3
3
  attr_accessor :bufo, :autocp_active, :cpl_list
4
- # :highlight_matching_brackets
5
4
 
6
- # def set_highlight_current_line(vbool)
7
- # end
8
-
9
- # def set_show_line_numbers(vbool)
10
- # end
11
-
12
- # def highlight_matching_brackets=(vbool)
13
- # end
14
5
 
15
6
  # def initialize(title = nil,bufo=nil)
16
7
  def initialize(title, bufo)
@@ -27,29 +18,6 @@ class VSourceView < GtkSource::View
27
18
 
28
19
  @tt = nil
29
20
 
30
- # self.drag_dest_add_image_targets #TODO:gtk4
31
- # self.drag_dest_add_uri_targets #TODO:gtk4
32
-
33
- # signal_connect("drag-data-received") do |widget, event, x, y, data, info, time| #TODO:gtk4
34
- # puts "drag-data-received"
35
- # puts
36
- # if data.uris.size >= 1
37
-
38
- # imgpath = CGI.unescape(data.uris[0])
39
- # m = imgpath.match(/^file:\/\/(.*)/)
40
- # if m
41
- # fp = m[1]
42
- # handle_drag_and_drop(fp)
43
- # end
44
- # end
45
- # true
46
- # end
47
-
48
- # signal_connect("show-completion") do |x, y, z|
49
- # debug "SHOW-COMPLETION", 2
50
- # false
51
- # end
52
-
53
21
  # Mainly after page-up or page-down
54
22
  signal_connect("move-cursor") do |widget, event|
55
23
  # if event.name == "GTK_MOVEMENT_PAGES" and (vma.actions.last_action == "page_up" or vma.actions.last_action == "page_down")
@@ -70,7 +38,6 @@ class VSourceView < GtkSource::View
70
38
 
71
39
  return
72
40
 
73
- #TODO:gtk4
74
41
  signal_connect "button-release-event" do |widget, event|
75
42
  vma.buf.set_pos(buffer.cursor_position)
76
43
  false
@@ -115,7 +82,7 @@ class VSourceView < GtkSource::View
115
82
  # Gtk::GestureDrag
116
83
  # Gtk::ShortcutController
117
84
 
118
- if ctr != @click and [Gtk::DropControllerMotion, Gtk::DropTarget, Gtk::GestureDrag, Gtk::GestureClick, Gtk::EventControllerKey].include?(ctr.class)
85
+ if ![@click, @dt].include?(ctr) and [Gtk::DropControllerMotion, Gtk::DropTarget, Gtk::GestureDrag, Gtk::GestureClick, Gtk::EventControllerKey].include?(ctr.class)
119
86
  to_remove << ctr
120
87
  end
121
88
  }
@@ -157,11 +124,8 @@ class VSourceView < GtkSource::View
157
124
  uri = v.value.gsub(/\r\n$/, "")
158
125
  end
159
126
  debug "dt,drop #{v.value},#{x},#{y}", 2
160
- begin
161
- fp = URI(uri).path
162
- buf.handle_drag_and_drop(fp)
163
- rescue URI::InvalidURIError
164
- end
127
+ fp = uri_to_path(uri)
128
+ buf.handle_drag_and_drop(fp) if !fp.nil?
165
129
  true
166
130
  end
167
131
 
@@ -422,15 +386,15 @@ class VSourceView < GtkSource::View
422
386
 
423
387
  keynfo = { :key_str => key_str, :key_name => keyname, :keyval => keyval }
424
388
  debug keynfo.inspect
425
- # $kbd.match_key_conf(key_str, nil, :key_press)
389
+ # vma.kbd.match_key_conf(key_str, nil, :key_press)
426
390
  # debug "key_str=#{key_str} key_"
427
391
 
428
392
  if key_str != "" # or prefixed_key_str != ""
429
393
  if sig == :key_release and keyval == @last_keyval
430
- $kbd.match_key_conf(key_str + "!", nil, :key_release)
394
+ vma.kbd.match_key_conf(key_str + "!", nil, :key_release)
431
395
  @last_event = [keynfo, :key_release]
432
396
  elsif sig == :key_press
433
- $kbd.match_key_conf(key_str, nil, :key_press)
397
+ vma.kbd.match_key_conf(key_str, nil, :key_press)
434
398
  @last_event = [keynfo, key_str, :key_press]
435
399
  end
436
400
  end
@@ -669,3 +633,4 @@ class VSourceView < GtkSource::View
669
633
  end
670
634
  end #end draw_cursor
671
635
  end
636
+
@@ -19,12 +19,12 @@ def jump_to_next_edit
19
19
  end
20
20
 
21
21
  def is_command_mode()
22
- return true if $kbd.mode_root_state.to_s() == "C"
22
+ return true if vma.kbd.mode_root_state.to_s() == "C"
23
23
  return false
24
24
  end
25
25
 
26
26
  def is_visual_mode()
27
- return 1 if $kbd.mode_root_state.to_s() == "V"
27
+ return 1 if vma.kbd.mode_root_state.to_s() == "V"
28
28
  return 0
29
29
  end
30
30
 
@@ -69,7 +69,6 @@ reg_act(:set_executable, proc { buf.set_executable }, "Set current file permissi
69
69
  reg_act(:close_current_buffer, proc { bufs.close_current_buffer(true) }, "Close current buffer")
70
70
  reg_act(:comment_selection, proc { buf.comment_selection }, "")
71
71
  reg_act(:delete_char_forward, proc { buf.delete(CURRENT_CHAR_FORWARD) }, "Delete char forward", { :group => [:edit, :basic] })
72
- reg_act(:load_theme, proc { load_theme }, "Load theme")
73
72
  reg_act(:gui_file_finder, proc { vma.FileFinder.start_gui }, "Fuzzy file finder")
74
73
  reg_act(:gui_file_history_finder, proc { vma.FileHistory.start_gui }, "Fuzzy file history finder")
75
74
  reg_act(:gui_search_replace, proc { gui_search_replace }, "Search and replace")
@@ -85,7 +84,7 @@ reg_act(:set_line_style_h4, proc { buf.set_line_style(:h4) }, "Set cur line as H
85
84
  reg_act(:set_line_style_bold, proc { buf.set_line_style(:bold) }, "Set style of current line as bold")
86
85
  reg_act(:set_line_style_title, proc { buf.set_line_style(:title) }, "Set style of current line as title")
87
86
  reg_act(:clear_line_styles, proc { buf.set_line_style(:clear) }, "Clear styles of current line")
88
- reg_act(:gui_select_buffer, proc { $kbd.set_mode("S"); gui_select_buffer }, "Select buffer")
87
+ reg_act(:gui_select_buffer, proc { vma.kbd.set_mode("S"); gui_select_buffer }, "Select buffer")
89
88
  reg_act :open_file_dialog, "open_file_dialog", "Open file"
90
89
  reg_act :minibuffer_end, proc { minibuffer_end }
91
90
  reg_act(:invoke_replace, "invoke_replace", "")
@@ -98,6 +97,30 @@ reg_act :delete_to_word_end, proc { buf.delete2(:to_word_end) }, "Delete to file
98
97
  reg_act :delete_to_next_word_start, proc { buf.delete2(:to_next_word) }, "Delete to start of next word", { :group => [:edit, :basic] }
99
98
  reg_act :delete_to_line_start, proc { buf.delete2(:to_line_start) }, "Delete to line start", { :group => [:edit, :basic] }
100
99
 
100
+
101
+ reg_act(:ack_search, proc { gui_ack }, "")
102
+
103
+ reg_act(:copy_cur_line, proc {buf.copy_line}, "Copy the current line")
104
+ reg_act(:paste_before_cursor, proc {buf.paste(BEFORE)}, "Paste text before the cursor")
105
+ reg_act(:paste_after_cursor, proc {buf.paste(AFTER)}, "Paste text after the cursor")
106
+ reg_act(:redo, proc {buf.redo()}, "Redo the last undone action")
107
+ reg_act(:undo, proc {buf.undo()}, "Undo the last action")
108
+ reg_act(:jump_end_of_line, proc { buf.jump(END_OF_LINE) }, "Move to the end of the current line")
109
+ reg_act(:jump_end_of_buffer, proc {buf.jump(END_OF_BUFFER)}, "Move to the end of the buffer")
110
+ reg_act(:jump_start_of_buffer, proc { buf.jump(START_OF_BUFFER) }, "Move to the start of the buffer")
111
+ reg_act(:jump_beginning_of_line, proc { buf.jump(BEGINNING_OF_LINE) }, "Move to the beginning of the current line")
112
+ reg_act(:jump_next_word_end, proc { buf.jump_word(FORWARD,WORD_END) }, "Jump to the end of the next word")
113
+ reg_act(:jump_prev_word_start, proc { buf.jump_word(BACKWARD,WORD_START) }, "Jump to the start of the previous word")
114
+ reg_act(:jump_next_word_start, proc { buf.jump_word(FORWARD,WORD_START) }, "Jump to the start of the next word")
115
+ reg_act(:insert_mode, proc { vma.kbd.set_mode(:insert) }, "Switch to INSERT mode")
116
+ reg_act(:prev_mode, proc { vma.kbd.to_previous_mode }, "Return to the previous mode")
117
+ reg_act(:move_prev_line, proc { buf.move(BACKWARD_LINE) }, "Move the cursor to the previous line")
118
+ reg_act(:move_next_line, proc { buf.move(FORWARD_LINE) }, "Move the cursor to the next line")
119
+ reg_act(:move_backward_char, proc { buf.move(BACKWARD_CHAR) }, "Move one character backward")
120
+ reg_act(:start_visual_mode, proc { buf.start_selection;vma.kbd.set_mode(:visual) }, "Enter VISUAL mode (for selections)")
121
+ reg_act(:jump_last_edit, proc { buf.jump_to_last_edit }, "Jump to the last edit location")
122
+
123
+
101
124
  reg_act :start_browse_mode, proc {
102
125
  vma.kbd.set_mode(:browse)
103
126
  bufs.reset_navigation
@@ -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
24
23
  attr_reader :cur_mode, :scope
25
24
 
26
25
  def initialize(key_name, eval_rule = "", ctype = :command, scope: :buffer)
@@ -29,6 +28,7 @@ class State
29
28
  @children = []
30
29
  @scope = scope
31
30
  @major_modes = []
31
+ @keywords = []
32
32
  @action = nil
33
33
  @level = 0
34
34
  @cursor_type = ctype
@@ -86,7 +86,7 @@ class KeyBindingTree
86
86
 
87
87
  def set_mode_stack(ms)
88
88
  debug "set_mode_stack(#{ms})", 2
89
- show_caller if cnf.debug? # TODO: remove
89
+ show_caller if cnf.debug? # TODO: remove
90
90
  @default_mode_stack = ms
91
91
  label = @default_mode_stack[-1]
92
92
  @match_state = [@modes[label]]
@@ -110,7 +110,7 @@ class KeyBindingTree
110
110
  end
111
111
 
112
112
  def to_previous_mode()
113
- debug "to_previous_mode",2
113
+ debug "to_previous_mode", 2
114
114
  debug @default_mode_stack
115
115
  if @default_mode_stack.size > 1
116
116
  @default_mode_stack.pop
@@ -135,7 +135,6 @@ class KeyBindingTree
135
135
  @modes[label] = mode
136
136
  if @root.nil?
137
137
  show_caller
138
- Ripl.start :binding => binding
139
138
  end
140
139
  @root.children << mode
141
140
  mode.major_modes << major_mode_label
@@ -165,6 +164,7 @@ class KeyBindingTree
165
164
 
166
165
  def match(key_name)
167
166
  new_state = []
167
+ debug = false
168
168
  @match_state.each { |parent|
169
169
  parent.children.each { |c|
170
170
  # printf(" KEY MATCH: ")
@@ -174,6 +174,9 @@ class KeyBindingTree
174
174
  elsif c.key_name == key_name and c.eval_rule != ""
175
175
  debug "CHECK EVAL: #{c.eval_rule}"
176
176
  if eval(c.eval_rule)
177
+ # if eval_rule.match(/macro/)
178
+ debug = true
179
+ # end
177
180
  new_state << c
178
181
  debug "EVAL TRUE"
179
182
  else
@@ -183,6 +186,10 @@ class KeyBindingTree
183
186
  }
184
187
  }
185
188
 
189
+ if debug
190
+ # require "pry"; binding.pry
191
+ end
192
+
186
193
  if new_state.any? # Match found
187
194
  @match_state = new_state
188
195
  return new_state
@@ -262,14 +269,66 @@ class KeyBindingTree
262
269
  @state_trail = [@mode_root_state]
263
270
  end
264
271
 
272
+ def get_by_keywords(modes: [], keywords: [])
273
+ s = ""
274
+ stack = [[@root, ""]]
275
+ lines = []
276
+
277
+ # Traverse the tree (class State objects) using a stack
278
+ while stack.any?
279
+ t, p = *stack.pop # t = current state, p = current path
280
+ if t.children.any?
281
+ t.children.reverse.each { |c|
282
+
283
+ # Restrict by modes if specified
284
+ if c.level == 1 and !modes.empty?
285
+ next if !modes.include?(c.key_name)
286
+ end
287
+
288
+ if c.eval_rule.size > 0
289
+ new_p = "#{p} #{c.key_name}(#{c.eval_rule})"
290
+ else
291
+ if c.level == 1
292
+ new_p = "#{p} [#{c.key_name}]"
293
+ else
294
+ new_p = "#{p} #{c.key_name}"
295
+ end
296
+ end
297
+ stack << [c, new_p]
298
+ }
299
+ # stack.concat[t.children]
300
+
301
+ else
302
+ method_desc = t.action
303
+ if t.action.class == Symbol
304
+ if vma.actions.include?(t.action)
305
+ a = vma.actions[t.action].method_name
306
+ if !a.nil? and !a.empty?
307
+ method_desc = a
308
+ end
309
+ end
310
+ end
311
+
312
+ if (keywords - t.keywords).empty? # All keywords are present
313
+ kw = ""
314
+ # kw = ", [#{t.keywords.join(",")}]" if !t.keywords.empty?
315
+ lines << p + " : #{method_desc}#{kw}"
316
+ end
317
+ end
318
+ end
319
+ s = lines.sort.join("\n")
320
+ return s
321
+ end
322
+
265
323
  # Print key bindings to show as documentation or for debugging
266
324
  def to_s()
267
- # return self.class.to_s
325
+ return self.class.to_s
268
326
  s = ""
269
327
  # @cur_state = @root
270
328
  stack = [[@root, ""]]
271
329
  lines = []
272
330
 
331
+ # Traverse the tree (class State objects) using a stack
273
332
  while stack.any?
274
333
  t, p = *stack.pop # t = current state, p = current path
275
334
  if t.children.any?
@@ -297,7 +356,9 @@ class KeyBindingTree
297
356
  end
298
357
  end
299
358
 
300
- lines << p + " : #{method_desc}"
359
+ kw = ""
360
+ # kw = ", " + t.keywords.join(",") if !t.keywords.empty?
361
+ lines << p + " : #{method_desc}#{kw}"
301
362
  end
302
363
  end
303
364
  s = lines.sort.join("\n")
@@ -426,11 +487,15 @@ class KeyBindingTree
426
487
  else
427
488
 
428
489
  # Don't execute action if one of the states has children
490
+ # TODO: why?
429
491
  state_with_children = new_state.select { |s| s.children.any? }
430
492
  s_act = new_state.select { |s| s.action != nil }
431
493
 
432
- if s_act.any? and !state_with_children.any?
494
+ if s_act.any? #and !state_with_children.any?
433
495
  eval_s = s_act.first.action if eval_s == nil
496
+ # if eval_s.to_s.match(/end_recording/)
497
+ # require "pry"; binding.pry
498
+ # end
434
499
  debug "FOUND MATCH:#{eval_s}"
435
500
  debug "CHAR: #{c}"
436
501
  c.gsub!("\\", %q{\\\\} * 4) # Escape \ -chars
@@ -449,7 +514,12 @@ class KeyBindingTree
449
514
  return true
450
515
  end
451
516
 
452
- def bindkey(key, action)
517
+ def bindkey(key, action, keywords: "")
518
+ # puts "keywords #{keywords}" if !keywords.empty?
519
+ if keywords.class == String
520
+ keywords = keywords.split(" ")
521
+ end
522
+ # puts "keywords #{keywords}" if !keywords.empty?
453
523
  if key.class != Array
454
524
  # Handle syntax like :
455
525
  # "X esc || X ctrl!" => "vma.kbd.to_previous_mode",
@@ -464,10 +534,10 @@ class KeyBindingTree
464
534
  msg = action[2]
465
535
  reg_act(label, proc, msg)
466
536
  end
467
- key.each { |k| _bindkey(k, a) }
537
+ key.each { |k| _bindkey(k, a, keywords: keywords) }
468
538
  end
469
539
 
470
- def _bindkey(key, action)
540
+ def _bindkey(key, action, keywords: [])
471
541
  key.strip!
472
542
  key.gsub!(/\s+/, " ")
473
543
 
@@ -502,15 +572,18 @@ class KeyBindingTree
502
572
  fatal_error("Error in keydef #{key.inspect}")
503
573
  end
504
574
 
575
+ # puts "keywords #{keywords}"
505
576
  modes.each { |mode_id|
506
- mode_bind_key(mode_id, keydef, action)
577
+ mode_bind_key(mode_id, keydef, action, keywords: keywords)
578
+
579
+ # Map froma actions to keybindings (e.g. to show bindings in menu)
507
580
  @act_bindings[mode_id][action] = keydef
508
581
  }
509
582
  end
510
583
 
511
584
  # Binds a keyboard key combination to an action,
512
585
  # for a given keyboard mode like insert ("I") or command ("C")
513
- def mode_bind_key(mode_id, keydef, action)
586
+ def mode_bind_key(mode_id, keydef, action, keywords: [])
514
587
  # debug "mode_bind_key #{mode_id.inspect}, #{keydef.inspect}, #{action.inspect}", 2
515
588
  # Example:
516
589
  # bindkey "C , f", :gui_file_finder
@@ -520,7 +593,7 @@ class KeyBindingTree
520
593
  set_state(mode_id, "") # TODO: check is ok?
521
594
  start_state = @cur_state
522
595
 
523
- k_arr = keydef.split
596
+ k_arr = keydef.split #e.g. definition: "C y e" = > ["C", "y", "e"]
524
597
 
525
598
  prev_state = nil
526
599
  s1 = start_state
@@ -561,6 +634,7 @@ class KeyBindingTree
561
634
  else
562
635
  @cur_state.action = action
563
636
  end
637
+ @cur_state.keywords = keywords
564
638
  @cur_state = @root
565
639
  end
566
640
 
@@ -614,13 +688,19 @@ class KeyBindingTree
614
688
  end
615
689
  end
616
690
 
617
- def bindkey(key, action)
618
- $kbd.bindkey(key, action)
691
+ def bindkey(key, action, keywords: "")
692
+ vma.kbd.bindkey(key, action, keywords: keywords)
693
+ end
694
+
695
+ def add_keys(keywords, to_add)
696
+ to_add.each { |key, value|
697
+ bindkey(key, value, keywords: keywords)
698
+ }
619
699
  end
620
700
 
621
701
  def exec_action(action)
622
- $kbd.last_action = $kbd.cur_action
623
- $kbd.cur_action = action
702
+ vma.kbd.last_action = vma.kbd.cur_action
703
+ vma.kbd.cur_action = action
624
704
  if action.class == Symbol
625
705
  return call_action(action)
626
706
  elsif action.class == Proc
@@ -630,14 +710,70 @@ def exec_action(action)
630
710
  end
631
711
  end
632
712
 
713
+ def show_key_bindings()
714
+ kbd_s = "❙Key bindings❙\n"
715
+ kbd_s << "\n⦁[Mode] <keys> : <action>⦁\n"
716
+ done = []
717
+
718
+ kbd_s << "[B]=Browse, [C]=Command, [I]=Insert, [V]=Visual\n"
719
+ kbd_s << "<key>!: Press <key> once, release before pressing any other keys\n"
720
+ kbd_s << "===============================================\n"
721
+ kbd_s << "◼ Basic\n"
722
+ kbd_s << "◼◼ Command mode\n"
723
+
724
+ x = vma.kbd.get_by_keywords(modes: ["C"], keywords: ["intro"])
725
+ done.concat(x.lines); kbd_s << x
726
+
727
+ kbd_s << "\n"
728
+ kbd_s << "◼◼ Insert mode\n"
729
+ x = vma.kbd.get_by_keywords(modes: ["I"], keywords: ["intro"])
730
+ done.concat(x.lines); kbd_s << x
731
+ kbd_s << "\n"
732
+ kbd_s << "◼◼ Visual mode\n"
733
+ x = vma.kbd.get_by_keywords(modes: ["V"], keywords: ["intro"])
734
+ done.concat(x.lines); kbd_s << x
735
+ kbd_s << "\n"
736
+
737
+ kbd_s << "◼ Core\n"
738
+ x = vma.kbd.get_by_keywords(modes: [], keywords: ["core"])
739
+ x << vma.kbd.get_by_keywords(modes: ["X"], keywords: ["intro"])
740
+
741
+ done.concat(x.lines); kbd_s << x
742
+ kbd_s << "\n"
743
+
744
+ kbd_s << "◼ Debug / Experimental\n"
745
+ x = vma.kbd.get_by_keywords(modes: [], keywords: ["experimental"])
746
+ done.concat(x.lines); kbd_s << x
747
+ kbd_s << "\n"
748
+
749
+ kbd_s << "◼ Others\n"
750
+ # x = vma.kbd.get_by_keywords(modes: [], keywords:["experimental"])
751
+ # x = vma.kbd.to_s
752
+ x = vma.kbd.get_by_keywords(modes: [], keywords: [])
753
+ done << x.lines - done
754
+ kbd_s << (x.lines - done).join
755
+ kbd_s << "\n"
756
+
757
+ kbd_s << "===============================================\n"
758
+ # x = vma.kbd.to_s
759
+ # # require "pry"; binding.pry
760
+ # done << x.lines - done
761
+ # kbd_s << (x.lines - done).join
762
+ # kbd_s << "\n"
763
+ # kbd_s << "===============================================\n"
764
+ b = create_new_buffer(kbd_s, "key-bindings")
765
+ gui_set_file_lang(b.id, "hyperplaintext")
766
+ #
767
+ end
768
+
633
769
  # Try to clear modifiers when program loses focus
634
770
  # e.g. after alt-tab
635
771
  # TODO
636
772
  # def focus_out
637
- # debug "RB Clear modifiers"
638
- # $kbd.clear_modifiers()
773
+ # debug "RB Clear modifiers"
774
+ # vma.kbd.clear_modifiers()
639
775
  # end
640
776
 
641
777
  # def handle_key_event(event)
642
- # $kbd.handle_key_event(event)
778
+ # vma.kbd.handle_key_event(event)
643
779
  # end
@@ -4,6 +4,7 @@ vma.kbd.add_mode("V", :visual, :visual)
4
4
  vma.kbd.add_mode("M", :minibuffer) #TODO: needed?
5
5
  vma.kbd.add_mode("R", :readchar)
6
6
  vma.kbd.add_minor_mode("audio", :audio, :command)
7
+ vma.kbd.add_minor_mode("macro", :macro, :command)
7
8
  vma.kbd.add_mode("B", :browse, :browse, scope: :editor)
8
9
  vma.kbd.add_mode("X", :replace, :replace, name: "Replace")
9
10
  vma.kbd.set_default_mode(:command)
@@ -40,95 +41,44 @@ def insert_move(op)
40
41
  _insert_move(op)
41
42
  end
42
43
 
43
- bindkey ["VCB M", "B m"], :run_last_macro
44
-
45
- bindkey "VC s", :easy_jump
46
- bindkey "I alt-s", :easy_jump
47
- bindkey "VC , m f", [:find_macro_gui, proc { vma.macro.find_macro_gui }, "Find named macro"]
48
- bindkey "C , m n", [:gui_name_macro, proc { vma.macro.gui_name_macro }, "Name last macro"]
49
- bindkey "C , j r", :jump_to_random
50
- bindkey "C , ; s k", :show_key_bindings #TODO: better binding
51
- bindkey "C , , c b", :put_file_path_to_clipboard #TODO: better binding or remove?
52
- bindkey "C , , e", :encrypt_file #TODO: better binding
53
- bindkey "C , ; u", :set_unencrypted #TODO: better binding
54
- bindkey "C , c b", :close_current_buffer
55
- bindkey "V ctrl-c", :comment_selection
56
- bindkey "C x", :delete_char_forward
57
- bindkey "C , , l t", :load_theme
58
- bindkey "C , f", :gui_file_finder
59
- bindkey "C , h", :gui_file_history_finder
60
- bindkey "C , z", :gui_file_finder
61
-
62
- bindkey "C ` k", :lsp_debug
63
- bindkey "C ` j", :lsp_jump_to_definition
64
-
65
- bindkey "C , r r", :gui_search_replace
66
- bindkey "V , r r", :gui_search_replace
67
- bindkey "V , t b", :set_style_bold
68
- bindkey "V , t l", :set_style_link
69
- bindkey "V J", :V_join_lines
70
- bindkey "V , t c", :clear_formats
71
- bindkey "C , t h", :set_line_style_heading
72
- bindkey "C , t 1", :set_line_style_h1
73
- bindkey "C , t 2", :set_line_style_h2
74
- bindkey "C , t 3", :set_line_style_h3
75
- bindkey "C , t 4", :set_line_style_h4
76
- bindkey "C , t b", :set_line_style_bold
77
- bindkey "C , t t", :set_line_style_title
78
- bindkey "C , t c", :clear_line_styles
79
- bindkey "C , b", :start_buf_manager
80
- bindkey "C , w", :toggle_active_window
81
- bindkey "C , , w", :toggle_two_column
82
-
83
- bindkey "C , u s", :audio_stop
84
- bindkey "C , m a", "vma.kbd.set_mode(:audio)"
85
- bindkey "audio s", :audio_stop
86
- bindkey "audio f || audio right", [:audio_forward, proc { Audio.seek_forward }, "Seek forward in audio stream"]
87
- bindkey "audio left", [:audio_backward, proc { Audio.seek_forward(-5.0) }, "Seek backward in audio stream"]
88
-
89
- bindkey "audio space", :audio_stop
90
- bindkey "audio q || audio esc", "vma.kbd.to_previous_mode"
44
+ add_keys "intro", {
45
+ "C y y" => :copy_cur_line,
46
+ "C P" => :paste_before_cursor,
47
+ "C p" => :paste_after_cursor,
48
+ "C v" => :start_visual_mode,
49
+ "C ctrl-r" => :redo,
50
+ "C u" => :undo,
51
+ "VC O" => :jump_end_of_line,
52
+ # NOTE: "G(condition)" need to be defined before "G"
53
+ "VC G(vma.kbd.next_command_count!=nil)" => "buf.jump_to_line()",
54
+ "VC G" => :jump_end_of_buffer,
55
+ "VC g ;" => :jump_last_edit,
56
+ "VC g g" => :jump_start_of_buffer,
57
+ "VC e" => :jump_next_word_end,
58
+ "VC b" => :jump_prev_word_start,
59
+ "VC w" => :jump_next_word_start,
60
+ "V esc" => "buf.end_visual_mode",
61
+ "V ctrl!" => "buf.end_visual_mode",
62
+
63
+ "C ctrl!" => :insert_mode,
64
+ "C i" => :insert_mode,
65
+ "I esc || I ctrl!" => :prev_mode,
66
+ "IX alt-b" => :jump_prev_word_start,
67
+ "IX alt-f" => :jump_next_word_start,
68
+ "IX ctrl-e" => :jump_end_of_line,
69
+ "IX ctrl-p" => :move_prev_line,
70
+ "IX ctrl-n" => :move_next_line,
71
+ "IX ctrl-b" => :move_backward_char,
72
+ "IX ctrl-a" => :jump_beginning_of_line,
73
+ }
91
74
 
92
- # bindkey "C , f o", :open_file_dialog
93
- bindkey "CI ctrl-o", :open_file_dialog
94
- # bindkey "M enter", :minibuffer_end
95
- bindkey "C , a", :ack_search
96
- bindkey "C d w", :delete_to_next_word_start
97
-
98
- bindkey "C d 0", :delete_to_line_start
99
- bindkey "C , , f", :file_finder
100
- bindkey "VC h", :e_move_backward_char
101
- bindkey "C , , .", :backup_all_buffers
102
- bindkey "C z ", :start_browse_mode
103
- bindkey "B h", :history_switch_backwards
104
- bindkey "B l", :history_switch_forwards
105
- bindkey "B z", "center_on_current_line();call_action(:exit_browse_mode)"
106
- bindkey "B enter || B return || B esc || B j || B ctrl!", :exit_browse_mode
107
- bindkey "B s", :page_up
108
- bindkey "B d", :page_down
109
- bindkey "B r", proc { vma.gui.page_down(multip: 0.25) }
110
- bindkey "B e", proc { vma.gui.page_up(multip: 0.25) }
111
-
112
- bindkey "B i", :jump_to_start_of_buffer
113
- bindkey "B o", :jump_to_end_of_buffer
114
- bindkey "B c", :close_current_buffer
115
- bindkey "B ;", "buf.jump_to_last_edit"
116
- bindkey "B q", :jump_to_last_edit
117
- bindkey "B w", :jump_to_next_edit
118
- # bindkey "C , d", :diff_buffer
119
- #bindkey 'C , g', proc{invoke_grep_search}
120
- bindkey "C , v", :auto_indent_buffer
121
- bindkey "C , , d", :savedebug
122
- bindkey "C , , u", :update_file_index
123
- bindkey "C , s a", :buf_save_as
124
- bindkey "C d d", [:delete_line, proc { buf.delete_line }, "Delete current line"]
125
- bindkey "C enter || C return", [:line_action, proc { buf.handle_line_action() }, "Line action"]
126
- bindkey "C p", [:paste_after, proc { buf.paste(AFTER) }, ""] # TODO: implement as replace for visual mode
127
- bindkey "V d", [:delete_selection, proc { buf.delete(SELECTION) }, ""]
128
- bindkey "V a d", [:delete_append_selection, proc { buf.delete(SELECTION, :append) }, "Delete and append selection"]
75
+ add_keys "intro delete", {
76
+ "C x" => :delete_char_forward,
77
+ "C d d" => [:delete_line, proc { buf.delete_line }, "Delete current line"]
78
+ }
129
79
 
130
80
 
131
- default_keys = {
81
+ add_keys "core", {
132
82
 
133
83
  # File handling
134
84
  "C ctrl-s" => :buf_save,
@@ -185,9 +135,6 @@ default_keys = {
185
135
  "VCX down" => "buf.move(FORWARD_LINE)",
186
136
  "VCX up" => "buf.move(BACKWARD_LINE)",
187
137
 
188
- "VC w" => "buf.jump_word(FORWARD,WORD_START)",
189
- "VC b" => "buf.jump_word(BACKWARD,WORD_START)",
190
- "VC e" => "buf.jump_word(FORWARD,WORD_END)",
191
138
  # 'C '=> 'buf.jump_word(BACKWARD,END)',#TODO
192
139
  "VC f <char>" => "buf.jump_to_next_instance_of_char(<char>)",
193
140
  "VC F <char>" => "buf.jump_to_next_instance_of_char(<char>,BACKWARD)",
@@ -197,14 +144,10 @@ default_keys = {
197
144
  "VC /[1-9]/" => "vma.kbd.set_next_command_count(<char>)",
198
145
  # 'VC number=/[0-9]/+ g'=> 'jump_to_line(<number>)',
199
146
  # 'VC X=/[0-9]/+ * Y=/[0-9]/+ '=> 'x_times_y(<X>,<Y>)',
200
- "VC G(vma.kbd.next_command_count!=nil)" => "buf.jump_to_line()",
201
147
  "VC 0(vma.kbd.next_command_count!=nil)" => "set_next_command_count(<char>)",
202
148
  "VC 0(vma.kbd.next_command_count==nil)" => "buf.jump(BEGINNING_OF_LINE)",
203
149
  # 'C 0'=> 'buf.jump(BEGINNING_OF_LINE)',
204
- "VC g g" => "buf.jump(START_OF_BUFFER)",
205
- "VC g ;" => "buf.jump_to_last_edit",
206
150
  "VC ^" => "buf.jump(BEGINNING_OF_LINE)",
207
- "VC G" => "buf.jump(END_OF_BUFFER)",
208
151
  # 'VC z z' => 'center_on_current_line',
209
152
  "VC *" => "buf.jump_to_next_instance_of_word",
210
153
 
@@ -221,17 +164,8 @@ default_keys = {
221
164
 
222
165
  "C C" => :content_search,
223
166
 
224
- # Debug
225
- "C , d r p" => "start_ripl",
226
- "C , d o" => "vma.gui.clear_overlay",
227
- "C , D" => "debug_print_buffer",
228
167
  "C , c s" => "bufs.close_scrap_buffers",
229
- "C , d b" => "debug_print_buffer",
230
- "C , d c" => "debug_dump_clipboard",
231
- "C , d d" => "debug_dump_deltas",
232
- "C , d m" => :kbd_dump_state,
233
168
 
234
- "VC O" => "buf.jump(END_OF_LINE)",
235
169
  "VC $" => "buf.jump(END_OF_LINE)",
236
170
 
237
171
  "C o" => 'buf.jump(END_OF_LINE);buf.insert_txt("\n");vma.kbd.set_mode(:insert)',
@@ -240,23 +174,18 @@ default_keys = {
240
174
  "C I" => "buf.jump(FIRST_NON_WHITESPACE);vma.kbd.set_mode(:insert)",
241
175
  "C a" => "buf.move(FORWARD_CHAR);vma.kbd.set_mode(:insert)",
242
176
  "C J" => "buf.join_lines()",
243
- "C u" => "buf.undo()",
244
177
 
245
178
  "C ^" => "buf.jump(BEGINNING_OF_LINE)",
246
179
  # "C /[1-9]/" => "vma.kbd.set_next_command_count(<char>)",
247
180
 
248
181
  # Command mode only:
249
- "C ctrl-r" => "buf.redo()", # TODO:???
250
- "C v" => "buf.start_selection;vma.kbd.set_mode(:visual)",
251
- "C P" => "buf.paste(BEFORE)", # TODO: implement as replace for visual mode
252
182
  "C space <char>" => "buf.insert_txt(<char>)",
253
183
  "C space space" => "buf.insert_txt(' ')",
254
- "C y y" => "buf.copy_line",
255
184
  "C y O" => "buf.copy(:to_line_end)",
256
185
  "C y 0" => "buf.copy(:to_line_start)",
257
186
  "C y e" => "buf.copy(:to_word_end)", # TODO
258
187
  #### Deleting
259
- "C x" => "buf.delete(CURRENT_CHAR_FORWARD)",
188
+ # "C x" => "buf.delete(CURRENT_CHAR_FORWARD)",
260
189
  # 'C d k'=> 'delete_line(BACKWARD)', #TODO
261
190
  # 'C d j'=> 'delete_line(FORWARD)', #TODO
262
191
  # 'C d d'=> 'buf.delete_cur_line',
@@ -279,8 +208,7 @@ default_keys = {
279
208
  # 'C 0($next_command_count==nil)'=> 'jump_to_beginning_of_line',
280
209
 
281
210
  # Visual mode only:
282
- "V esc" => "buf.end_visual_mode",
283
- "V ctrl!" => "buf.end_visual_mode",
211
+
284
212
  "V y" => "buf.copy_active_selection()",
285
213
  "V a y" => "buf.copy_active_selection(:append)",
286
214
  "V g U" => :selection_upcase,
@@ -305,9 +233,7 @@ default_keys = {
305
233
  # "CV ''" =>'jump_to_mark(NEXT_MARK)', #TODO
306
234
 
307
235
  # Switch to another mode
308
- "C i" => "vma.kbd.set_mode(:insert)",
309
236
  "C R" => "vma.kbd.set_mode(:replace)",
310
- "C ctrl!" => "vma.kbd.set_mode(:insert)",
311
237
 
312
238
  # Replace mode
313
239
  "X esc || X ctrl!" => "vma.kbd.to_previous_mode",
@@ -316,49 +242,140 @@ default_keys = {
316
242
  # Macros
317
243
  # (experimental, may not work correctly)
318
244
  # "C q a" => 'vma.macro.start_recording("a")',
319
- "VC q <char>" => "vma.macro.start_recording(<char>)",
320
- "VC q(vma.macro.is_recording==true) " => "$macro.end_recording", # TODO
245
+
246
+ "macro q" => "vma.kbd.to_previous_mode; vma.macro.end_recording",
247
+ "macro q z" => "vma.kbd.to_previous_mode; vma.macro.end_recording",
248
+
249
+ # "VC q(vma.macro.is_recording==true)" => "vma.macro.end_recording", # TODO: does not work
250
+ # "VC o(vma.macro.is_recording==true)" => "vma.macro.end_recording", # TODO: does not work
251
+ # "VC q q(vma.macro.is_recording==true)" => "vma.macro.end_recording",
252
+ "VC q <char>" => "vma.kbd.set_mode(:macro);vma.macro.start_recording(<char>)",
321
253
  # 'C q'=> 'vma.macro.end_recording', #TODO
322
- "C q v" => "vma.macro.end_recording",
254
+ "C q v" => "vma.kbd.to_previous_mode; vma.macro.end_recording",
323
255
  # 'C v'=> 'vma.macro.end_recording',
324
256
  # "C M" => 'vma.macro.run_last_macro',
325
257
  "C @ <char>" => "vma.macro.run_macro(<char>)",
326
258
  "C , m S" => 'vma.macro.save_macro("a")',
327
259
  "C , m s" => "vma.macro.save",
328
- "C , t r" => "run_tests()",
329
260
 
330
261
  # "C ." => "repeat_last_action", # TODO
331
262
  "VC ;" => "repeat_last_find",
332
263
  # "CV Q" => :quit,
333
264
  "CV ctrl-q" => :quit,
334
- "CV , R" => "restart_application",
335
265
  # "I ctrl!" => "vma.kbd.to_previous_mode",
336
266
  "C shift! s" => "buf.save",
337
267
  "I ctrl-s" => "buf.save",
338
268
  "I <char>" => "buf.insert_txt(<char>)",
339
- "I esc || I ctrl!" => "vma.kbd.to_previous_mode",
340
269
 
341
270
  "I ctrl-d" => "buf.delete2(:to_word_end)",
342
271
 
343
272
  # Insert and Replace modes: Moving
344
- "IX ctrl-a" => "buf.jump(BEGINNING_OF_LINE)",
345
- "IX ctrl-b" => "buf.move(BACKWARD_CHAR)",
346
273
  "IX ctrl-f" => "buf.move(FORWARD_CHAR)",
347
- "IX ctrl-n" => "buf.move(FORWARD_LINE)",
348
- "IX ctrl-p" => "buf.move(BACKWARD_LINE)",
349
- "IX ctrl-e" => "buf.jump(END_OF_LINE)", # context: mode:I, buttons down: {C}
350
- "IX alt-f" => "buf.jump_word(FORWARD,WORD_START)",
351
- "IX alt-b" => "buf.jump_word(BACKWARD,WORD_START)",
352
274
 
353
- "I ctrl-h" => :show_autocomplete,
354
275
  "I ctrl-j" => "vma.buf.view.hide_completions",
355
276
 
356
277
  "I space" => 'buf.insert_txt(" ")',
357
278
  # "I return" => 'buf.insert_new_line()',
279
+
280
+ "CI ctrl-o" => :open_file_dialog,
281
+ "C , a" => :ack_search,
282
+ "C d w" => :delete_to_next_word_start,
283
+
284
+ "C d 0" => :delete_to_line_start,
285
+ "C , , f" => :file_finder,
286
+ "VC h" => :e_move_backward_char,
287
+ "C , , ." => :backup_all_buffers,
288
+ "C z " => :start_browse_mode,
289
+ "B h" => :history_switch_backwards,
290
+ "B l" => :history_switch_forwards,
291
+ "B z" => "center_on_current_line();call_action(:exit_browse_mode)",
292
+ "B enter || B return || B esc || B j || B ctrl!" => :exit_browse_mode,
293
+ "B s" => :page_up,
294
+ "B d" => :page_down,
295
+ "B r" => proc { vma.gui.page_down(multip: 0.25) },
296
+ "B e" => proc { vma.gui.page_up(multip: 0.25) },
297
+
298
+ "B i" => :jump_to_start_of_buffer,
299
+ "B o" => :jump_to_end_of_buffer,
300
+ "B c" => :close_current_buffer,
301
+ "B ;" => :jump_last_edit,
302
+ "B q" => :jump_to_last_edit,
303
+ "B w" => :jump_to_next_edit,
304
+ "C , v" => :auto_indent_buffer,
305
+ "C , , u" => :update_file_index,
306
+ "C , s a" => :buf_save_as,
307
+ "VC , r r" => :gui_search_replace,
308
+ "V , t b" => :set_style_bold,
309
+ "V , t l" => :set_style_link,
310
+ "V J" => :V_join_lines,
311
+ "V , t c" => :clear_formats,
312
+ "C , t h" => :set_line_style_heading,
313
+ "C , t 1" => :set_line_style_h1,
314
+ "C , t 2" => :set_line_style_h2,
315
+ "C , t 3" => :set_line_style_h3,
316
+ "C , t 4" => :set_line_style_h4,
317
+ "C , t b" => :set_line_style_bold,
318
+ "C , t t" => :set_line_style_title,
319
+ "C , t c" => :clear_line_styles,
320
+ "C , b" => :start_buf_manager,
321
+ "C , w" => :toggle_active_window,
322
+ "C , , w" => :toggle_two_column,
323
+
324
+ "VC s" => :easy_jump,
325
+ "I alt-s" => :easy_jump,
326
+ "VC , m f" => [:find_macro_gui, proc { vma.macro.find_macro_gui }, "Find named macro"],
327
+ "C , m n" => [:gui_name_macro, proc { vma.macro.gui_name_macro }, "Name last macro"],
328
+ "C , j r" => :jump_to_random,
329
+ "C , ; s k" => :show_key_bindings, #TODO: better binding,
330
+ "C , , c b" => :put_file_path_to_clipboard, #TODO: better binding or remove?,
331
+ "C , , e" => :encrypt_file, #TODO: better binding,
332
+ "C , ; u" => :set_unencrypted, #TODO: better binding,
333
+ "C , c b" => :close_current_buffer,
334
+ "V ctrl-c" => :comment_selection,
335
+ "C , f" => :gui_file_finder,
336
+ "C , h" => :gui_file_history_finder,
337
+ "C , z" => :gui_file_finder,
338
+
339
+ "C enter || C return" => [:line_action, proc { buf.handle_line_action() }, "Line action"],
340
+ "V d" => [:delete_selection, proc { buf.delete(SELECTION) }, ""],
341
+ "V a d" => [:delete_append_selection, proc { buf.delete(SELECTION, :append) }, "Delete and append selection"]
358
342
  }
359
343
 
360
- bindkey "C , i p", "generate_password_to_buf(15)"
344
+ bindkey ["VCB M", "B m"], :run_last_macro
345
+
346
+ add_keys "experimental", {
347
+ "C ` k" => :lsp_debug,
348
+ "C ` j" => :lsp_jump_to_definition,
349
+ "C , u s" => :audio_stop,
361
350
 
362
- default_keys.each { |key, value|
363
- bindkey(key, value)
351
+ "C , t r" => "run_tests()",
352
+ # "CV , R" => "restart_application", #TODO: does not work
353
+ "I ctrl-h" => :show_autocomplete, #TODO: does not work
354
+ "C , d m" => :kbd_dump_state,
355
+ "C , d d" => "debug_dump_deltas",
356
+ "C , d c" => "debug_dump_clipboard",
357
+ "C , d b" => "debug_print_buffer",
358
+ "C , D" => "debug_print_buffer",
359
+ "C , d o" => "vma.gui.clear_overlay",
360
+ # Debug
361
+ "C , d r p" => 'require "pry"; binding.pry', #TODO
362
+ #bindkey 'C , g', proc{invoke_grep_search}
363
+ # bindkey "C , d", :diff_buffer
364
+ "C , , d" => :savedebug,
365
+ "C , m a" => "vma.kbd.set_mode(:audio)",
366
+ "audio s" => :audio_stop,
364
367
  }
368
+
369
+
370
+ bindkey "audio f || audio right", [:audio_forward, proc { Audio.seek_forward }, "Seek forward in audio stream"]
371
+ bindkey "audio left", [:audio_backward, proc { Audio.seek_forward(-5.0) }, "Seek backward in audio stream"]
372
+
373
+ bindkey "audio space", :audio_stop
374
+ bindkey "audio q || audio esc", "vma.kbd.to_previous_mode"
375
+
376
+
377
+ bindkey "C , i p", "generate_password_to_buf(15)"
378
+
379
+ # default_keys.each { |key, value|
380
+ # bindkey(key, value)
381
+ # }
data/lib/vimamsa/util.rb CHANGED
@@ -3,6 +3,10 @@ require "open3"
3
3
  VOWELS = %w(a e i o u)
4
4
  CONSONANTS = %w(b c d f g h j k l m n p q r s t v w x y z)
5
5
 
6
+ def draw_cursor_bug_workaround()
7
+ DelayExecutioner.exec(id: :bug_workaround_draw_cursor, wait: 1.0, callable: proc { vma.gui.view.draw_cursor(); debug ":bug_workaround_draw_cursor"; false })
8
+ end
9
+
6
10
  def running_wayland?
7
11
  sess = ENV["DESKTOP_SESSION"]
8
12
  sess ||= ENV["XDG_SESSION_DESKTOP"]
@@ -218,7 +222,7 @@ end
218
222
  # Used for image scaling after window resize
219
223
 
220
224
  class DelayExecutioner
221
-
225
+ attr_accessor :last_run, :wait_before_next
222
226
  # Run 'callable.call' if 'wait' time elapsed from last exec call for this id
223
227
  def self.exec(id:, wait:, callable:)
224
228
  @@h ||= {}
@@ -226,6 +230,12 @@ class DelayExecutioner
226
230
  if h[id].nil?
227
231
  h[id] = DelayExecutioner.new(wait, callable)
228
232
  end
233
+ o = h[id]
234
+ if !o.last_run.nil?
235
+ # if Time.now - o.last_run < o.wait_before_next
236
+ # return
237
+ # end
238
+ end
229
239
  h[id].run
230
240
  end
231
241
 
@@ -234,6 +244,8 @@ class DelayExecutioner
234
244
  @proc = _proc
235
245
  @lastt = Time.now
236
246
  @thread_running = false
247
+ @last_run = nil
248
+ @wait_before_next = 0
237
249
  end
238
250
 
239
251
  def start_thread
@@ -242,6 +254,7 @@ class DelayExecutioner
242
254
  sleep 0.1
243
255
  if Time.now - @lastt > @wait_time
244
256
  @proc.call
257
+ @last_run = Time.now
245
258
  @thread_running = false
246
259
  break
247
260
  end
@@ -344,6 +357,17 @@ def is_existing_file(s)
344
357
  return false
345
358
  end
346
359
 
360
+ def uri_to_path(uri)
361
+ fp = nil
362
+ begin
363
+ x = URI::DEFAULT_PARSER.unescape(URI(uri).path)
364
+ rescue URI::InvalidURIError
365
+ else
366
+ fp = x # only if no exception
367
+ end
368
+ return fp
369
+ end
370
+
347
371
  def is_image_file(fpath)
348
372
  return false if !File.exist?(fpath)
349
373
  # return false if !fpath.match(/.(jpg|jpeg|png)$/i)
@@ -1,3 +1,3 @@
1
1
  module Vimamsa
2
- VERSION = "0.1.21"
2
+ VERSION = "0.1.22"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vimamsa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.21
4
+ version: 0.1.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sami Sieranoja
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-07-21 00:00:00.000000000 Z
11
+ date: 2025-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -241,6 +241,7 @@ files:
241
241
  - exe/vimamsa
242
242
  - ext/vmaext/extconf.rb
243
243
  - ext/vmaext/vmaext.c
244
+ - install.sh
244
245
  - lang/hyperplaintext.lang
245
246
  - lib/vimamsa.rb
246
247
  - lib/vimamsa/ack.rb