vimamsa 0.1.6 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/custom_example.rb +47 -0
- data/demo.txt +25 -0
- data/lang/hyperplaintext.lang +9 -25
- data/lib/vimamsa/ack.rb +90 -7
- data/lib/vimamsa/actions.rb +27 -8
- data/lib/vimamsa/buffer.rb +130 -76
- data/lib/vimamsa/buffer_list.rb +24 -0
- data/lib/vimamsa/buffer_manager.rb +83 -0
- data/lib/vimamsa/conf.rb +21 -0
- data/lib/vimamsa/debug.rb +3 -2
- data/lib/vimamsa/easy_jump.rb +15 -20
- data/lib/vimamsa/editor.rb +93 -73
- data/lib/vimamsa/encrypt.rb +1 -1
- data/lib/vimamsa/file_finder.rb +6 -9
- data/lib/vimamsa/file_history.rb +3 -3
- data/lib/vimamsa/file_manager.rb +9 -8
- data/lib/vimamsa/gui.rb +89 -87
- data/lib/vimamsa/gui_image.rb +43 -0
- data/lib/vimamsa/gui_menu.rb +11 -2
- data/lib/vimamsa/gui_select_window.rb +16 -13
- data/lib/vimamsa/gui_sourceview.rb +64 -37
- data/lib/vimamsa/hyper_plain_text.rb +38 -19
- data/lib/vimamsa/key_actions.rb +40 -13
- data/lib/vimamsa/key_binding_tree.rb +52 -128
- data/lib/vimamsa/key_bindings_vimlike.rb +28 -25
- data/lib/vimamsa/macro.rb +5 -5
- data/lib/vimamsa/rbvma.rb +22 -18
- data/lib/vimamsa/search.rb +1 -1
- data/lib/vimamsa/search_replace.rb +11 -8
- data/lib/vimamsa/text_transforms.rb +2 -0
- data/lib/vimamsa/util.rb +34 -0
- data/lib/vimamsa/version.rb +1 -1
- data/lib/vimamsa.rb +5 -0
- data/sheep.jpg +0 -0
- data/styles/dark.xml +1 -0
- data/styles/molokai_edit.xml +1 -1
- data/vimamsa.gemspec +1 -1
- metadata +13 -8
- data/lib/vimamsa/gui_gtk_sourceview.rb +0 -294
@@ -1,24 +1,57 @@
|
|
1
1
|
|
2
|
+
|
3
|
+
# class VSourceView < Gtk::TextView
|
2
4
|
class VSourceView < GtkSource::View
|
3
|
-
|
5
|
+
attr_accessor :bufo
|
6
|
+
# :highlight_matching_brackets
|
7
|
+
|
8
|
+
# def set_highlight_current_line(vbool)
|
9
|
+
# end
|
10
|
+
|
11
|
+
# def set_show_line_numbers(vbool)
|
12
|
+
# end
|
13
|
+
|
14
|
+
# def highlight_matching_brackets=(vbool)
|
15
|
+
# end
|
16
|
+
|
17
|
+
|
18
|
+
def initialize(title = nil,bufo=nil)
|
4
19
|
# super(:toplevel)
|
20
|
+
@highlight_matching_brackets = true
|
5
21
|
super()
|
6
|
-
|
22
|
+
@bufo = bufo #object of Buffer class buffer.rb
|
23
|
+
debug "vsource init"
|
7
24
|
@last_keyval = nil
|
8
25
|
@last_event = [nil, nil]
|
26
|
+
self.drag_dest_add_image_targets
|
27
|
+
self.drag_dest_add_uri_targets
|
9
28
|
|
10
29
|
signal_connect "button-press-event" do |_widget, event|
|
11
30
|
if event.button == Gdk::BUTTON_PRIMARY
|
12
|
-
#
|
31
|
+
# debug "Gdk::BUTTON_PRIMARY"
|
13
32
|
false
|
14
33
|
elsif event.button == Gdk::BUTTON_SECONDARY
|
15
|
-
#
|
34
|
+
# debug "Gdk::BUTTON_SECONDARY"
|
16
35
|
true
|
17
36
|
else
|
18
37
|
true
|
19
38
|
end
|
20
39
|
end
|
21
40
|
|
41
|
+
signal_connect("drag-data-received") do |widget, event, x, y, data, info, time|
|
42
|
+
puts "drag-data-received"
|
43
|
+
puts
|
44
|
+
if data.uris.size >= 1
|
45
|
+
imgpath = CGI.unescape(data.uris[0])
|
46
|
+
m = imgpath.match(/^file:\/\/(.*)/)
|
47
|
+
if m
|
48
|
+
fp = m[1]
|
49
|
+
handle_drag_and_drop(fp)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
22
55
|
signal_connect("key_press_event") do |widget, event|
|
23
56
|
handle_key_event(event, :key_press_event)
|
24
57
|
true
|
@@ -44,14 +77,14 @@ class VSourceView < GtkSource::View
|
|
44
77
|
def handle_key_event(event, sig)
|
45
78
|
if $update_cursor
|
46
79
|
curpos = buffer.cursor_position
|
47
|
-
|
80
|
+
debug "MOVE CURSOR: #{curpos}"
|
48
81
|
buf.set_pos(curpos)
|
49
82
|
$update_cursor = false
|
50
83
|
end
|
51
|
-
|
84
|
+
debug $view.visible_rect.inspect
|
52
85
|
|
53
|
-
|
54
|
-
|
86
|
+
debug "key event"
|
87
|
+
debug event
|
55
88
|
|
56
89
|
key_name = event.string
|
57
90
|
if event.state.control_mask?
|
@@ -98,9 +131,9 @@ class VSourceView < GtkSource::View
|
|
98
131
|
key_str_parts << key_name
|
99
132
|
key_str = key_str_parts.join("-")
|
100
133
|
keynfo = { :key_str => key_str, :key_name => key_name, :keyval => event.keyval }
|
101
|
-
|
134
|
+
debug keynfo.inspect
|
102
135
|
# $kbd.match_key_conf(key_str, nil, :key_press)
|
103
|
-
#
|
136
|
+
# debug "key_str=#{key_str} key_"
|
104
137
|
|
105
138
|
if key_str != "" # or prefixed_key_str != ""
|
106
139
|
if sig == :key_release_event and event.keyval == @last_keyval
|
@@ -132,7 +165,7 @@ class VSourceView < GtkSource::View
|
|
132
165
|
y = iterxy.y
|
133
166
|
|
134
167
|
# buffer_to_window_coords(Gtk::TextWindowType::TEXT, iterxy.x, iterxy.y).inspect
|
135
|
-
#
|
168
|
+
# debug buffer_to_window_coords(Gtk::TextWindowType::TEXT, x, y).inspect
|
136
169
|
(x, y) = buffer_to_window_coords(Gtk::TextWindowType::TEXT, x, y)
|
137
170
|
# Ripl.start :binding => binding
|
138
171
|
|
@@ -141,7 +174,7 @@ class VSourceView < GtkSource::View
|
|
141
174
|
|
142
175
|
def handle_deltas()
|
143
176
|
any_change = false
|
144
|
-
while d =
|
177
|
+
while d = @bufo.deltas.shift
|
145
178
|
any_change = true
|
146
179
|
pos = d[0]
|
147
180
|
op = d[1]
|
@@ -157,7 +190,7 @@ class VSourceView < GtkSource::View
|
|
157
190
|
end
|
158
191
|
end
|
159
192
|
if any_change
|
160
|
-
gui_set_cursor_pos(
|
193
|
+
gui_set_cursor_pos(@bufo.id, @bufo.pos) #TODO: only when necessary
|
161
194
|
end
|
162
195
|
|
163
196
|
# sanity_check #TODO
|
@@ -166,15 +199,15 @@ class VSourceView < GtkSource::View
|
|
166
199
|
def sanity_check()
|
167
200
|
a = buffer.text
|
168
201
|
b = buf.to_s
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
202
|
+
# debug "===================="
|
203
|
+
# debug a.lines[0..10].join()
|
204
|
+
# debug "===================="
|
205
|
+
# debug b.lines[0..10].join()
|
206
|
+
# debug "===================="
|
174
207
|
if a == b
|
175
|
-
|
208
|
+
debug "Buffers match"
|
176
209
|
else
|
177
|
-
|
210
|
+
debug "ERROR: Buffer's don't match."
|
178
211
|
end
|
179
212
|
end
|
180
213
|
|
@@ -210,7 +243,7 @@ class VSourceView < GtkSource::View
|
|
210
243
|
end
|
211
244
|
|
212
245
|
def cursor_visible_idle_func
|
213
|
-
|
246
|
+
debug "cursor_visible_idle_func"
|
214
247
|
# From https://picheta.me/articles/2013/08/gtk-plus--a-method-to-guarantee-scrolling.html
|
215
248
|
# vr = visible_rect
|
216
249
|
|
@@ -247,8 +280,8 @@ class VSourceView < GtkSource::View
|
|
247
280
|
|
248
281
|
intr = iterxy.intersect(vr)
|
249
282
|
if intr.nil?
|
250
|
-
|
251
|
-
|
283
|
+
debug iterxy.inspect
|
284
|
+
debug vr.inspect
|
252
285
|
# Ripl.start :binding => binding
|
253
286
|
|
254
287
|
# exit!
|
@@ -269,26 +302,20 @@ class VSourceView < GtkSource::View
|
|
269
302
|
|
270
303
|
def draw_cursor
|
271
304
|
if is_command_mode
|
272
|
-
itr = buffer.get_iter_at(:offset =>
|
273
|
-
itr2 = buffer.get_iter_at(:offset =>
|
305
|
+
itr = buffer.get_iter_at(:offset => @bufo.pos)
|
306
|
+
itr2 = buffer.get_iter_at(:offset => @bufo.pos + 1)
|
274
307
|
$view.buffer.select_range(itr, itr2)
|
275
|
-
elsif
|
276
|
-
|
277
|
-
(_start, _end) =
|
278
|
-
|
308
|
+
elsif @bufo.visual_mode?
|
309
|
+
debug "VISUAL MODE"
|
310
|
+
(_start, _end) = @bufo.get_visual_mode_range2
|
311
|
+
debug "#{_start}, #{_end}"
|
279
312
|
itr = buffer.get_iter_at(:offset => _start)
|
280
313
|
itr2 = buffer.get_iter_at(:offset => _end + 1)
|
281
314
|
$view.buffer.select_range(itr, itr2)
|
282
315
|
else # Insert mode
|
283
|
-
itr = buffer.get_iter_at(:offset =>
|
316
|
+
itr = buffer.get_iter_at(:offset => @bufo.pos)
|
284
317
|
$view.buffer.select_range(itr, itr)
|
285
|
-
|
318
|
+
debug "INSERT MODE"
|
286
319
|
end
|
287
320
|
end
|
288
|
-
|
289
|
-
# def quit
|
290
|
-
# destroy
|
291
|
-
# true
|
292
|
-
# end
|
293
321
|
end
|
294
|
-
|
@@ -1,10 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
def hpt_open_link()
|
4
|
-
end
|
5
|
-
|
6
1
|
def hpt_check_cur_word(w)
|
7
|
-
|
2
|
+
debug "check_cur_word(w)"
|
8
3
|
m = w.match(/⟦(.*)⟧/)
|
9
4
|
if m
|
10
5
|
fpfx = m[1]
|
@@ -12,8 +7,10 @@ def hpt_check_cur_word(w)
|
|
12
7
|
dn = File.dirname($buffer.fname)
|
13
8
|
|
14
9
|
fcands = []
|
15
|
-
|
16
|
-
|
10
|
+
if fpfx[0] != "/"
|
11
|
+
fcands << "#{dn}/#{fpfx}"
|
12
|
+
fcands << "#{dn}/#{fpfx}.txt"
|
13
|
+
end
|
17
14
|
fcands << File.expand_path("#{fpfx}")
|
18
15
|
fcands << File.expand_path("#{fpfx}.txt")
|
19
16
|
|
@@ -38,24 +35,46 @@ def hpt_check_cur_word(w)
|
|
38
35
|
return nil
|
39
36
|
end
|
40
37
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
38
|
+
def translate_path(fn, bf)
|
39
|
+
if File.exist?(fn)
|
40
|
+
outfn = fn
|
41
|
+
elsif fn[0] == "$"
|
42
|
+
outfn = ppath(fn[1..-1]) # Path to source location
|
43
|
+
elsif fn[0] == "~"
|
44
|
+
outfn = File.expand_path(fn)
|
45
|
+
elsif !bf.fname.nil?
|
46
|
+
pd = File.dirname(bf.fname)
|
47
|
+
outfn = "#{pd}/#{fn}"
|
48
|
+
else
|
49
|
+
outfn = File.expand_path(fn)
|
50
|
+
end
|
51
|
+
return outfn
|
52
|
+
end
|
53
|
+
|
54
|
+
# Scan images inserted with ⟦img:filepath⟧ syntax
|
55
|
+
def hpt_scan_images(bf = nil)
|
56
|
+
bf = buf() if bf.nil?
|
57
|
+
return if bf.nil?
|
58
|
+
return if !bf.fname
|
59
|
+
return if !bf.fname.match(/.*txt$/)
|
60
|
+
imgpos = scan_indexes(bf, /⟦img:.+?⟧/)
|
61
|
+
imgtags = bf.scan(/(⟦img:(.+?)⟧)/)
|
47
62
|
c = 0
|
48
63
|
imgpos.each.with_index { |x, i|
|
49
64
|
a = imgpos[i]
|
50
65
|
t = imgtags[i]
|
51
66
|
insert_pos = a + t[0].size + c
|
52
|
-
|
53
|
-
|
67
|
+
fn = t[1]
|
68
|
+
imgfn = translate_path(fn, bf)
|
54
69
|
next if !File.exist?(imgfn)
|
55
|
-
|
56
|
-
|
70
|
+
# Show as image in gui, handle as empty space in txt file
|
71
|
+
|
72
|
+
if bf[insert_pos..(insert_pos + 2)] != "\n \n"
|
73
|
+
bf.insert_txt_at("\n \n", insert_pos)
|
74
|
+
bf.view.handle_deltas
|
57
75
|
c += 3
|
58
76
|
end
|
59
|
-
|
77
|
+
bf.add_image(imgfn, insert_pos + 1)
|
60
78
|
}
|
79
|
+
vma.gui.delex.run
|
61
80
|
end
|
data/lib/vimamsa/key_actions.rb
CHANGED
@@ -28,13 +28,16 @@ def is_visual_mode()
|
|
28
28
|
return 0
|
29
29
|
end
|
30
30
|
|
31
|
+
reg_act(:enable_debug, proc { $debug = true }, "Enable debug")
|
32
|
+
reg_act(:disable_debug, proc { $debug = false }, "Disable debug")
|
33
|
+
|
31
34
|
reg_act(:easy_jump, proc { EasyJump.start }, "Easy jump")
|
32
35
|
reg_act(:savedebug, "savedebug", "Save debug info", { :group => :debug })
|
33
36
|
reg_act(:open_file_dialog, "open_file_dialog", "Open file", { :group => :file })
|
34
37
|
reg_act(:create_new_file, "create_new_file", "Create new file", { :group => :file })
|
35
|
-
reg_act(:backup_all_buffers, proc{backup_all_buffers}, "Backup all buffers", { :group => :file })
|
36
|
-
reg_act(:e_move_forward_char, "e_move_forward_char", "", { :group => :move })
|
37
|
-
reg_act(:e_move_backward_char, "e_move_backward_char", "", { :group => :move })
|
38
|
+
reg_act(:backup_all_buffers, proc { backup_all_buffers }, "Backup all buffers", { :group => :file })
|
39
|
+
reg_act(:e_move_forward_char, "e_move_forward_char", "", { :group => [:move, :basic] })
|
40
|
+
reg_act(:e_move_backward_char, "e_move_backward_char", "", { :group => [:move, :basic] })
|
38
41
|
reg_act(:history_switch_backwards, "history_switch_backwards", "", { :group => :file })
|
39
42
|
reg_act(:history_switch_forwards, "history_switch_forwards", "", { :group => :file })
|
40
43
|
reg_act(:center_on_current_line, "center_on_current_line", "", { :group => :view })
|
@@ -48,10 +51,11 @@ reg_act(:put_file_path_to_clipboard, proc { buf.put_file_path_to_clipboard }, "P
|
|
48
51
|
# reg_act(:encrypt_file, proc{buf.set_encrypted},"Set current file to encrypt on save")
|
49
52
|
reg_act(:encrypt_file, proc { encrypt_cur_buffer }, "Set current file to encrypt on save")
|
50
53
|
reg_act(:set_unencrypted, proc { buf.set_unencrypted }, "Set current file to save unencrypted")
|
54
|
+
reg_act(:set_executable, proc { buf.set_executable }, "Set current file permissions to executable")
|
51
55
|
reg_act(:close_all_buffers, proc { bufs.close_all_buffers() }, "Close all buffers")
|
52
56
|
reg_act(:close_current_buffer, proc { bufs.close_current_buffer(true) }, "Close current buffer")
|
53
57
|
reg_act(:comment_selection, proc { buf.comment_selection }, "")
|
54
|
-
reg_act(:delete_char_forward, proc { buf.delete(CURRENT_CHAR_FORWARD) }, "Delete char forward")
|
58
|
+
reg_act(:delete_char_forward, proc { buf.delete(CURRENT_CHAR_FORWARD) }, "Delete char forward", { :group => [:edit, :basic] })
|
55
59
|
reg_act(:load_theme, proc { load_theme }, "Load theme")
|
56
60
|
reg_act(:gui_file_finder, proc { vma.FileFinder.start_gui }, "Fuzzy file finder")
|
57
61
|
reg_act(:gui_file_history_finder, proc { vma.FileHistory.start_gui }, "Fuzzy file history finder")
|
@@ -77,19 +81,20 @@ reg_act(:diff_buffer, "diff_buffer", "")
|
|
77
81
|
reg_act(:invoke_grep_search, proc { gui_grep }, "Grep current buffer")
|
78
82
|
reg_act(:ack_search, proc { gui_ack }, "") #invoke_ack_search
|
79
83
|
reg_act :update_file_index, proc { update_file_index }, "Update file index"
|
80
|
-
reg_act :delete_to_word_end, proc { buf.delete2(:to_word_end) }, "Delete to file end"
|
81
|
-
reg_act :delete_to_line_start, proc { buf.delete2(:to_line_start) }, "Delete to line start"
|
84
|
+
reg_act :delete_to_word_end, proc { buf.delete2(:to_word_end) }, "Delete to file end", { :group => [:edit, :basic] }
|
85
|
+
reg_act :delete_to_line_start, proc { buf.delete2(:to_line_start) }, "Delete to line start", { :group => [:edit, :basic] }
|
82
86
|
reg_act :start_browse_mode, proc { $kbd.set_mode(:browse); $kbd.set_default_mode(:browse) }, "Start browse mode"
|
83
87
|
reg_act :exit_browse_mode, proc {
|
84
88
|
bufs.add_current_buf_to_history(); $kbd.set_mode(:command); $kbd.set_default_mode(:command)
|
85
89
|
}, "Exit browse mode"
|
86
90
|
|
87
|
-
reg_act :page_down, proc { page_down }
|
88
|
-
reg_act :page_up, proc { page_up }
|
91
|
+
reg_act :page_down, proc { page_down }, "Page down", :group => [:move, :basic]
|
92
|
+
reg_act :page_up, proc { page_up }, "Page up", :group => [:move, :basic]
|
89
93
|
reg_act :jump_to_start_of_buffer, proc { buf.jump(START_OF_BUFFER) }, "Jump to start of buffer"
|
90
94
|
reg_act :jump_to_end_of_buffer, proc { buf.jump(END_OF_BUFFER) }, "Jump to end of buffer"
|
91
95
|
reg_act(:auto_indent_buffer, proc { buf.indent }, "Auto format buffer")
|
92
96
|
reg_act(:execute_current_line_in_terminal, proc { buf.execute_current_line_in_terminal }, "Execute current line in terminal")
|
97
|
+
reg_act(:execute_current_line_in_terminal_autoclose, proc { buf.execute_current_line_in_terminal(true) }, "Execute current line in terminal. Close after execution.")
|
93
98
|
reg_act(:show_images, proc { hpt_scan_images() }, "Show images inserted with ⟦img:file.png⟧ syntax")
|
94
99
|
reg_act(:delete_current_file, proc { bufs.delete_current_buffer() }, "Delete current file")
|
95
100
|
|
@@ -112,8 +117,28 @@ act_list = {
|
|
112
117
|
:desc => "Undo edit", :group => :edit },
|
113
118
|
|
114
119
|
:find_in_buffer => { :proc => proc { invoke_search },
|
115
|
-
|
120
|
+
:desc => "Find", :group => :edit },
|
121
|
+
|
122
|
+
:selection_upcase => { :proc => proc { buf.transform_selection(:upcase) },
|
123
|
+
:desc => "Transform text: upcase", :group => :edit },
|
124
|
+
|
125
|
+
:selection_downcase => { :proc => proc { buf.transform_selection(:downcase) },
|
126
|
+
:desc => "Transform text: downcase", :group => :edit },
|
127
|
+
|
128
|
+
:selection_capitalize => { :proc => proc { buf.transform_selection(:capitalize) },
|
129
|
+
:desc => "Transform text: capitalize", :group => :edit },
|
130
|
+
|
131
|
+
:selection_swapcase => { :proc => proc { buf.transform_selection(:swapcase) },
|
132
|
+
:desc => "Transform text: swapcase", :group => :edit },
|
116
133
|
|
134
|
+
:selection_reverse => { :proc => proc { buf.transform_selection(:reverse) },
|
135
|
+
:desc => "Transform text: reverse", :group => :edit },
|
136
|
+
|
137
|
+
:forward_line => { :proc => proc { buf.move(FORWARD_LINE) },
|
138
|
+
:desc => "Move one line forward", :group => [:move, :basic] },
|
139
|
+
|
140
|
+
:backward_line => { :proc => proc { buf.move(BACKWARD_LINE) },
|
141
|
+
:desc => "Move one line backward", :group => [:move, :basic] },
|
117
142
|
|
118
143
|
# { :proc => proc { },
|
119
144
|
# :desc => "", :group => : },
|
@@ -121,6 +146,9 @@ act_list = {
|
|
121
146
|
:search_actions => { :proc => proc { search_actions },
|
122
147
|
:desc => "Search actions", :group => :search },
|
123
148
|
|
149
|
+
:content_search => { :proc => proc { FileContentSearch.start_gui },
|
150
|
+
:desc => "Search content of files", :group => :search },
|
151
|
+
|
124
152
|
:quit => { :proc => proc { _quit },
|
125
153
|
:desc => "Quit", :group => :app },
|
126
154
|
|
@@ -143,14 +171,13 @@ act_list_todo = {
|
|
143
171
|
:desc => "Close current file", :group => :file },
|
144
172
|
#"C , b" => '$kbd.set_mode("S");gui_select_buffer',
|
145
173
|
|
146
|
-
|
147
174
|
# MOVING
|
148
175
|
# 'VC h' => 'buf.move(BACKWARD_CHAR)',
|
149
176
|
:m_forward_char => { :proc => proc { buf.move(FORWARD_CHAR) },
|
150
177
|
:desc => "Move cursor one char forward",
|
151
178
|
:group => :move },
|
152
|
-
"VC j" => "buf.move(FORWARD_LINE)",
|
153
|
-
"VC k" => "buf.move(BACKWARD_LINE)",
|
179
|
+
# "VC j" => "buf.move(FORWARD_LINE)",
|
180
|
+
# "VC k" => "buf.move(BACKWARD_LINE)",
|
154
181
|
|
155
182
|
"VC pagedown" => "page_down",
|
156
183
|
"VC pageup" => "page_up",
|
@@ -256,7 +283,7 @@ act_list_todo = {
|
|
256
283
|
# Visual mode only:
|
257
284
|
"V esc" => "buf.end_visual_mode",
|
258
285
|
"V ctrl!" => "buf.end_visual_mode",
|
259
|
-
"V y" => "buf.copy_active_selection",
|
286
|
+
"V y" => "buf.copy_active_selection(:foo)",
|
260
287
|
"V g U" => "buf.transform_selection(:upcase)",
|
261
288
|
"V g u" => "buf.transform_selection(:downcase)",
|
262
289
|
"V g c" => "buf.transform_selection(:capitalize)",
|