kward 0.73.0 → 0.74.0
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/CHANGELOG.md +22 -0
- data/Gemfile.lock +2 -2
- data/doc/configuration.md +14 -1
- data/doc/mcp.md +72 -0
- data/doc/rpc.md +1 -0
- data/doc/usage.md +7 -0
- data/lib/kward/cli/commands.rb +3 -0
- data/lib/kward/cli/doctor.rb +18 -10
- data/lib/kward/cli/interactive_turn.rb +9 -3
- data/lib/kward/cli/tabs.rb +6 -9
- data/lib/kward/cli.rb +38 -1
- data/lib/kward/config_files.rb +35 -2
- data/lib/kward/mcp/client.rb +56 -0
- data/lib/kward/mcp/server_config.rb +55 -0
- data/lib/kward/mcp/stdio_transport.rb +106 -0
- data/lib/kward/prompt_interface/editor/controller.rb +64 -3
- data/lib/kward/prompt_interface/editor/modes/emacs.rb +4 -0
- data/lib/kward/prompt_interface/editor/modes/modern.rb +10 -4
- data/lib/kward/prompt_interface/editor/modes/vibe.rb +448 -20
- data/lib/kward/prompt_interface/editor/renderer.rb +16 -0
- data/lib/kward/prompt_interface/editor/search.rb +70 -13
- data/lib/kward/prompt_interface/editor/state.rb +67 -12
- data/lib/kward/prompt_interface/editor/vibe_state.rb +12 -1
- data/lib/kward/prompt_interface/selection_prompt.rb +9 -0
- data/lib/kward/rpc/server.rb +1 -0
- data/lib/kward/tools/mcp_tool.rb +118 -0
- data/lib/kward/tools/registry.rb +32 -2
- data/lib/kward/version.rb +1 -1
- metadata +6 -1
|
@@ -5,7 +5,7 @@ module Kward
|
|
|
5
5
|
# Vibe-style keymap for the built-in composer file editor.
|
|
6
6
|
module VibeEditorMode
|
|
7
7
|
VIBE_SIMPLE_MOTION_KEYS = [
|
|
8
|
-
"w", "e", "b", "$", "0", "^", "+", "\n", "\r", "-", "_",
|
|
8
|
+
"w", "e", "b", "W", "E", "B", "$", "0", "^", "|", "+", "\n", "\r", "-", "_",
|
|
9
9
|
"h", "\b", "\x7F", "j", "k", "l", " ", "{", "}"
|
|
10
10
|
].freeze
|
|
11
11
|
VIBE_PAIR_TEXT_OBJECTS = {
|
|
@@ -38,6 +38,8 @@ module Kward
|
|
|
38
38
|
csi_result = handle_vibe_csi_u_key(key)
|
|
39
39
|
return csi_result unless csi_result == false
|
|
40
40
|
|
|
41
|
+
return handle_vibe_command_key(key) if @editor_state.vibe_mode == "command"
|
|
42
|
+
|
|
41
43
|
tab_result = handle_tab_key_binding(key)
|
|
42
44
|
return tab_result unless tab_result == false
|
|
43
45
|
|
|
@@ -46,7 +48,6 @@ module Kward
|
|
|
46
48
|
return vibe_begin_visual_mode("visual_block") if key == TerminalKeys::CTRL_V && @editor_state.vibe_mode == "normal"
|
|
47
49
|
return handle_vibe_repeat_change if key == "." && @editor_state.vibe_mode == "normal"
|
|
48
50
|
return handle_vibe_search_key(key) if editor_search_active?
|
|
49
|
-
return handle_vibe_command_key(key) if @editor_state.vibe_mode == "command"
|
|
50
51
|
return handle_vibe_insert_key(key) if @editor_state.vibe_mode == "insert"
|
|
51
52
|
return handle_vibe_replace_key(key) if @editor_state.vibe_mode == "replace"
|
|
52
53
|
return handle_vibe_visual_key(key) if vibe_visual_mode?
|
|
@@ -57,6 +58,10 @@ module Kward
|
|
|
57
58
|
def handle_vibe_csi_u_key(key)
|
|
58
59
|
sequence = parse_csi_u_key(key)
|
|
59
60
|
return false unless sequence
|
|
61
|
+
if editor_search_active?
|
|
62
|
+
search_result = handle_editor_search_csi_u_key(sequence)
|
|
63
|
+
return search_result unless search_result == false
|
|
64
|
+
end
|
|
60
65
|
|
|
61
66
|
code = sequence[:code]
|
|
62
67
|
modifier = sequence[:modifier]
|
|
@@ -91,8 +96,12 @@ module Kward
|
|
|
91
96
|
@editor_state.move_indentation_down
|
|
92
97
|
when 107
|
|
93
98
|
@editor_state.move_indentation_up
|
|
99
|
+
when 105
|
|
100
|
+
vibe_jump_forward
|
|
94
101
|
when 108
|
|
95
102
|
@editor_state.move_line_end
|
|
103
|
+
when 111
|
|
104
|
+
vibe_jump_backward
|
|
96
105
|
when 118
|
|
97
106
|
vibe_begin_visual_mode("visual_block")
|
|
98
107
|
else
|
|
@@ -104,6 +113,7 @@ module Kward
|
|
|
104
113
|
code = sequence[:code]
|
|
105
114
|
text = csi_u_text(sequence)
|
|
106
115
|
normalized_code = code.to_i.chr.downcase.ord rescue code
|
|
116
|
+
return "\t" if code == 9
|
|
107
117
|
return "\n" if code == 13
|
|
108
118
|
return "\x7F" if [8, 127].include?(code)
|
|
109
119
|
return (normalized_code - 96).chr if ctrl_modifier?(sequence[:modifier]) && normalized_code.between?(97, 122)
|
|
@@ -189,6 +199,8 @@ module Kward
|
|
|
189
199
|
when "\b", "\x7F"
|
|
190
200
|
@editor_state.vibe_command = @editor_state.vibe_command[0...-1].to_s
|
|
191
201
|
@editor_state.status = ":#{@editor_state.vibe_command}"
|
|
202
|
+
when "\t"
|
|
203
|
+
vibe_complete_command_path
|
|
192
204
|
when "\n", "\r"
|
|
193
205
|
execute_vibe_command(@editor_state.vibe_command)
|
|
194
206
|
else
|
|
@@ -209,6 +221,8 @@ module Kward
|
|
|
209
221
|
save_editor
|
|
210
222
|
when /\Aw\s+(.+)\z/
|
|
211
223
|
save_editor(Regexp.last_match(1))
|
|
224
|
+
when /\Ae(!?)\s+(.+)\z/
|
|
225
|
+
vibe_edit_file(Regexp.last_match(2), force: Regexp.last_match(1) == "!")
|
|
212
226
|
when "run"
|
|
213
227
|
vibe_record_undo { run_editor_buffer }
|
|
214
228
|
when "q"
|
|
@@ -233,6 +247,81 @@ module Kward
|
|
|
233
247
|
true
|
|
234
248
|
end
|
|
235
249
|
|
|
250
|
+
def vibe_edit_file(path, force: false)
|
|
251
|
+
if @editor_state.dirty? && !force
|
|
252
|
+
@editor_state.status = "No write since last change (:e! overrides)"
|
|
253
|
+
return true
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
open_editor(path, allow_new: true)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def vibe_complete_command_path
|
|
260
|
+
command = @editor_state.vibe_command.to_s
|
|
261
|
+
match = command.match(/\A(e!?)\s+(.*)\z/)
|
|
262
|
+
return false unless match
|
|
263
|
+
|
|
264
|
+
prefix = match[2]
|
|
265
|
+
candidates = vibe_path_completion_candidates(prefix)
|
|
266
|
+
if candidates.empty?
|
|
267
|
+
@editor_state.status = "No matches"
|
|
268
|
+
return true
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
replacement = candidates.length == 1 ? candidates.first : vibe_common_prefix(candidates)
|
|
272
|
+
if replacement.length > prefix.length
|
|
273
|
+
@editor_state.vibe_command = "#{match[1]} #{replacement}"
|
|
274
|
+
@editor_state.status = ":#{@editor_state.vibe_command}"
|
|
275
|
+
elsif candidates.length > 1
|
|
276
|
+
@editor_state.status = vibe_path_completion_status(candidates)
|
|
277
|
+
end
|
|
278
|
+
true
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def vibe_path_completion_candidates(prefix)
|
|
282
|
+
directory_prefix, basename_prefix = vibe_split_path_completion_prefix(prefix)
|
|
283
|
+
search_directory = File.expand_path(directory_prefix.empty? ? "." : directory_prefix, Dir.pwd)
|
|
284
|
+
root = File.expand_path(Dir.pwd)
|
|
285
|
+
return [] unless search_directory == root || search_directory.start_with?("#{root}/")
|
|
286
|
+
return [] unless File.directory?(search_directory)
|
|
287
|
+
|
|
288
|
+
Dir.children(search_directory).sort.filter_map do |entry|
|
|
289
|
+
next if entry.start_with?(".") && !basename_prefix.start_with?(".")
|
|
290
|
+
next unless entry.start_with?(basename_prefix)
|
|
291
|
+
|
|
292
|
+
path = File.join(search_directory, entry)
|
|
293
|
+
candidate = "#{directory_prefix}#{entry}"
|
|
294
|
+
File.directory?(path) ? "#{candidate}/" : candidate
|
|
295
|
+
end
|
|
296
|
+
rescue StandardError
|
|
297
|
+
[]
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def vibe_split_path_completion_prefix(prefix)
|
|
301
|
+
if prefix.include?(File::SEPARATOR)
|
|
302
|
+
directory = prefix[0..prefix.rindex(File::SEPARATOR)].to_s
|
|
303
|
+
basename = prefix[(prefix.rindex(File::SEPARATOR) + 1)..].to_s
|
|
304
|
+
[directory, basename]
|
|
305
|
+
else
|
|
306
|
+
["", prefix]
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def vibe_common_prefix(values)
|
|
311
|
+
return "" if values.empty?
|
|
312
|
+
|
|
313
|
+
values.reduce(values.first.dup) do |prefix, value|
|
|
314
|
+
prefix = prefix[0...-1] until value.start_with?(prefix) || prefix.empty?
|
|
315
|
+
prefix
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def vibe_path_completion_status(candidates)
|
|
320
|
+
visible = candidates.first(6)
|
|
321
|
+
suffix = candidates.length > visible.length ? " …" : ""
|
|
322
|
+
"#{candidates.length} matches: #{visible.join(" ")}#{suffix}"
|
|
323
|
+
end
|
|
324
|
+
|
|
236
325
|
def vibe_substitute_command(range, pattern, replacement, global: false)
|
|
237
326
|
if pattern.empty?
|
|
238
327
|
@editor_state.status = "Substitute pattern required"
|
|
@@ -327,7 +416,7 @@ module Kward
|
|
|
327
416
|
end
|
|
328
417
|
|
|
329
418
|
def vibe_normal_control_key?(key)
|
|
330
|
-
["\n", "\r", "\b", "\x7F", TerminalKeys::CTRL_B, TerminalKeys::CTRL_D, TerminalKeys::CTRL_E, TerminalKeys::CTRL_F, TerminalKeys::CTRL_R, TerminalKeys::CTRL_U, TerminalKeys::CTRL_Y].include?(key)
|
|
419
|
+
["\n", "\r", "\b", "\x7F", TerminalKeys::CTRL_B, TerminalKeys::CTRL_D, TerminalKeys::CTRL_E, TerminalKeys::CTRL_F, "\x0F", TerminalKeys::CTRL_R, TerminalKeys::CTRL_U, TerminalKeys::CTRL_Y].include?(key)
|
|
331
420
|
end
|
|
332
421
|
|
|
333
422
|
def vibe_visual_mode?
|
|
@@ -361,6 +450,7 @@ module Kward
|
|
|
361
450
|
def vibe_waiting_for_more?(command)
|
|
362
451
|
return true if command.match?(/\A\d+\z/) && command != "0"
|
|
363
452
|
return true if command.match?(/\A\d*g\z/)
|
|
453
|
+
return true if command.match?(/\A\d*g[uU~]\z/)
|
|
364
454
|
return true if command.match?(/\A\d*z\z/)
|
|
365
455
|
return true if command.match?(/\A\d*[cdy]\d*\z/)
|
|
366
456
|
return true if command.match?(/\A\d*[cdy]\d*[ai]\z/)
|
|
@@ -374,6 +464,7 @@ module Kward
|
|
|
374
464
|
return true if command.match?(/\Aq\z/)
|
|
375
465
|
return true if command.match?(/\A@\z/)
|
|
376
466
|
return true if command.match?(/\A[\[\]]\z/)
|
|
467
|
+
return true if command.match?(/\A\d*[<>]\z/)
|
|
377
468
|
return true if command.match?(/\A['`]\z/)
|
|
378
469
|
|
|
379
470
|
false
|
|
@@ -393,9 +484,30 @@ module Kward
|
|
|
393
484
|
when *VIBE_SIMPLE_MOTION_KEYS
|
|
394
485
|
vibe_apply_cursor_motion(body, count)
|
|
395
486
|
when "gg"
|
|
487
|
+
vibe_record_jump
|
|
396
488
|
@editor_state.move_file_start
|
|
489
|
+
when "g_"
|
|
490
|
+
vibe_move_line_last_non_blank
|
|
491
|
+
when "gJ"
|
|
492
|
+
vibe_join_lines(count, command, separator: "")
|
|
493
|
+
when "ge"
|
|
494
|
+
count.times { vibe_move_to_previous_word_end }
|
|
495
|
+
when "gE"
|
|
496
|
+
count.times { vibe_move_to_previous_big_word_end }
|
|
497
|
+
when "guu"
|
|
498
|
+
vibe_transform_lines(count, :downcase, command)
|
|
499
|
+
when "gUU"
|
|
500
|
+
vibe_transform_lines(count, :upcase, command)
|
|
501
|
+
when "g~~"
|
|
502
|
+
vibe_transform_lines(count, :swapcase, command)
|
|
503
|
+
when /\Ag([uU~])(.+)\z/
|
|
504
|
+
vibe_transform_operator(Regexp.last_match(1), Regexp.last_match(2), count, command)
|
|
397
505
|
when "gv"
|
|
398
506
|
vibe_restore_visual_selection
|
|
507
|
+
when "`."
|
|
508
|
+
vibe_jump_to_previous_change(linewise: false)
|
|
509
|
+
when "'."
|
|
510
|
+
vibe_jump_to_previous_change(linewise: true)
|
|
399
511
|
when "]m"
|
|
400
512
|
vibe_jump_ruby_method(:forward)
|
|
401
513
|
when "[m"
|
|
@@ -413,6 +525,7 @@ module Kward
|
|
|
413
525
|
when /\A`(.+)\z/
|
|
414
526
|
vibe_jump_to_mark(Regexp.last_match(1), linewise: false)
|
|
415
527
|
when "G"
|
|
528
|
+
vibe_record_jump
|
|
416
529
|
line = command.match?(/\A\d+G\z/) ? count - 1 : @editor_state.lines.length - 1
|
|
417
530
|
@editor_state.set_cursor_line_and_column(line, 0)
|
|
418
531
|
when "zz"
|
|
@@ -439,6 +552,8 @@ module Kward
|
|
|
439
552
|
vibe_scroll_down
|
|
440
553
|
when TerminalKeys::CTRL_Y
|
|
441
554
|
vibe_scroll_up
|
|
555
|
+
when "\x0F"
|
|
556
|
+
vibe_jump_backward
|
|
442
557
|
when TerminalKeys::CTRL_R
|
|
443
558
|
@editor_state.redo
|
|
444
559
|
when "i"
|
|
@@ -466,6 +581,8 @@ module Kward
|
|
|
466
581
|
vibe_change_lines(count, command)
|
|
467
582
|
when "J"
|
|
468
583
|
vibe_join_lines(count, command)
|
|
584
|
+
when "~"
|
|
585
|
+
vibe_swapcase_characters(count, command)
|
|
469
586
|
when "n"
|
|
470
587
|
editor_search_repeat
|
|
471
588
|
when "N"
|
|
@@ -477,7 +594,8 @@ module Kward
|
|
|
477
594
|
when "U"
|
|
478
595
|
vibe_restore_current_line
|
|
479
596
|
when "%"
|
|
480
|
-
|
|
597
|
+
vibe_record_jump
|
|
598
|
+
command.match?(/\A\d+%\z/) ? vibe_jump_to_file_percentage(count) : vibe_jump_to_matching_pair
|
|
481
599
|
when /^([fFtT])(.?)$/
|
|
482
600
|
vibe_find_character(Regexp.last_match(1), Regexp.last_match(2), count)
|
|
483
601
|
when ";"
|
|
@@ -495,11 +613,9 @@ module Kward
|
|
|
495
613
|
when "O"
|
|
496
614
|
vibe_open_line_above
|
|
497
615
|
when "x"
|
|
498
|
-
|
|
499
|
-
vibe_remember_change(command)
|
|
616
|
+
vibe_delete_characters(count, command)
|
|
500
617
|
when "X"
|
|
501
|
-
|
|
502
|
-
vibe_remember_change(command)
|
|
618
|
+
vibe_delete_characters_before_cursor(count, command)
|
|
503
619
|
when "dd"
|
|
504
620
|
vibe_delete_lines(count)
|
|
505
621
|
vibe_store_active_register
|
|
@@ -510,9 +626,17 @@ module Kward
|
|
|
510
626
|
when "yy"
|
|
511
627
|
vibe_yank_lines(count)
|
|
512
628
|
vibe_store_active_register
|
|
629
|
+
when "Y"
|
|
630
|
+
vibe_yank_lines(count)
|
|
631
|
+
vibe_store_active_register
|
|
632
|
+
when ">>"
|
|
633
|
+
vibe_indent_lines(count, :right)
|
|
634
|
+
vibe_remember_change(command)
|
|
635
|
+
when "<<"
|
|
636
|
+
vibe_indent_lines(count, :left)
|
|
637
|
+
vibe_remember_change(command)
|
|
513
638
|
when "p"
|
|
514
|
-
|
|
515
|
-
vibe_remember_change(original_command)
|
|
639
|
+
vibe_active_register_linewise? ? vibe_paste_line(:below, original_command) : vibe_paste_after(original_command)
|
|
516
640
|
when "P"
|
|
517
641
|
vibe_paste_before(original_command)
|
|
518
642
|
when "u"
|
|
@@ -709,12 +833,56 @@ module Kward
|
|
|
709
833
|
@vibe_replaying_macro = false
|
|
710
834
|
end
|
|
711
835
|
|
|
836
|
+
def vibe_record_jump
|
|
837
|
+
current = @editor_state.cursor
|
|
838
|
+
return if @editor_state.vibe_jump_back_list.last == current
|
|
839
|
+
|
|
840
|
+
@editor_state.vibe_jump_back_list << current
|
|
841
|
+
@editor_state.vibe_jump_forward_list.clear
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
def vibe_jump_backward
|
|
845
|
+
target = @editor_state.vibe_jump_back_list.pop
|
|
846
|
+
unless target
|
|
847
|
+
@editor_state.status = "Already at oldest jump"
|
|
848
|
+
return false
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
@editor_state.vibe_jump_forward_list << @editor_state.cursor
|
|
852
|
+
@editor_state.cursor = [[target, 0].max, @editor_state.buffer.length].min
|
|
853
|
+
true
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
def vibe_jump_forward
|
|
857
|
+
target = @editor_state.vibe_jump_forward_list.pop
|
|
858
|
+
unless target
|
|
859
|
+
@editor_state.status = "Already at newest jump"
|
|
860
|
+
return false
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
@editor_state.vibe_jump_back_list << @editor_state.cursor
|
|
864
|
+
@editor_state.cursor = [[target, 0].max, @editor_state.buffer.length].min
|
|
865
|
+
true
|
|
866
|
+
end
|
|
867
|
+
|
|
712
868
|
def vibe_set_mark(name)
|
|
713
869
|
@editor_state.vibe_marks[name] = { cursor: @editor_state.cursor }
|
|
714
870
|
@editor_state.status = "Set mark #{name}"
|
|
715
871
|
true
|
|
716
872
|
end
|
|
717
873
|
|
|
874
|
+
def vibe_jump_to_previous_change(linewise:)
|
|
875
|
+
cursor = @editor_state.vibe_previous_change_cursor
|
|
876
|
+
unless cursor
|
|
877
|
+
@editor_state.status = "Previous change not set"
|
|
878
|
+
return false
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
@editor_state.cursor = [[cursor, 0].max, @editor_state.buffer.length].min
|
|
882
|
+
@editor_state.move_line_first_non_blank if linewise
|
|
883
|
+
true
|
|
884
|
+
end
|
|
885
|
+
|
|
718
886
|
def vibe_jump_to_mark(name, linewise:)
|
|
719
887
|
mark = @editor_state.vibe_marks[name]
|
|
720
888
|
unless mark
|
|
@@ -722,6 +890,7 @@ module Kward
|
|
|
722
890
|
return false
|
|
723
891
|
end
|
|
724
892
|
|
|
893
|
+
vibe_record_jump
|
|
725
894
|
@editor_state.cursor = [[mark[:cursor], 0].max, @editor_state.buffer.length].min
|
|
726
895
|
@editor_state.move_line_first_non_blank if linewise
|
|
727
896
|
true
|
|
@@ -920,6 +1089,14 @@ module Kward
|
|
|
920
1089
|
@editor_state.move_to_line_first_non_blank(line + offset)
|
|
921
1090
|
end
|
|
922
1091
|
|
|
1092
|
+
def vibe_move_line_last_non_blank
|
|
1093
|
+
line, = @editor_state.cursor_line_and_column
|
|
1094
|
+
text = @editor_state.lines[line].to_s
|
|
1095
|
+
column = text.rindex(/\S/) || 0
|
|
1096
|
+
@editor_state.set_cursor_line_and_column(line, column)
|
|
1097
|
+
true
|
|
1098
|
+
end
|
|
1099
|
+
|
|
923
1100
|
def vibe_move_to_screen_line(offset)
|
|
924
1101
|
target_row = @editor_state.viewport_row + offset
|
|
925
1102
|
if current_editor_soft_wrap?
|
|
@@ -996,14 +1173,72 @@ module Kward
|
|
|
996
1173
|
@editor_state.status = "INSERT · Esc normal"
|
|
997
1174
|
end
|
|
998
1175
|
|
|
1176
|
+
def vibe_delete_characters(count, command = nil)
|
|
1177
|
+
start_index = @editor_state.cursor
|
|
1178
|
+
end_index = [start_index + count, @editor_state.buffer.length].min
|
|
1179
|
+
return @editor_state.status = "Empty range" if start_index == end_index
|
|
1180
|
+
|
|
1181
|
+
@editor_state.copy_range(start_index, end_index)
|
|
1182
|
+
@editor_state.vibe_kill_linewise = false
|
|
1183
|
+
vibe_record_undo { @editor_state.replace_range(start_index, end_index, "") }
|
|
1184
|
+
@vibe_character_delete_for_paste = @vibe_active_register.nil?
|
|
1185
|
+
vibe_store_active_register
|
|
1186
|
+
vibe_remember_change(command)
|
|
1187
|
+
end
|
|
1188
|
+
|
|
1189
|
+
def vibe_delete_characters_before_cursor(count, command = nil)
|
|
1190
|
+
end_index = @editor_state.cursor
|
|
1191
|
+
start_index = [end_index - count, 0].max
|
|
1192
|
+
return @editor_state.status = "Empty range" if start_index == end_index
|
|
1193
|
+
|
|
1194
|
+
@editor_state.copy_range(start_index, end_index)
|
|
1195
|
+
@editor_state.vibe_kill_linewise = false
|
|
1196
|
+
vibe_record_undo { @editor_state.replace_range(start_index, end_index, "") }
|
|
1197
|
+
vibe_store_active_register
|
|
1198
|
+
vibe_remember_change(command)
|
|
1199
|
+
end
|
|
1200
|
+
|
|
1201
|
+
def vibe_paste_after(command = nil)
|
|
1202
|
+
return vibe_paste_line(:below, command) if vibe_active_register_linewise?
|
|
1203
|
+
|
|
1204
|
+
text = vibe_active_register_text
|
|
1205
|
+
return false if text.empty?
|
|
1206
|
+
|
|
1207
|
+
vibe_record_undo do
|
|
1208
|
+
@editor_state.cursor = [@editor_state.cursor + 1, @editor_state.buffer.length].min if @vibe_character_delete_for_paste
|
|
1209
|
+
@editor_state.insert(text)
|
|
1210
|
+
end
|
|
1211
|
+
@vibe_character_delete_for_paste = false
|
|
1212
|
+
vibe_remember_change(command)
|
|
1213
|
+
end
|
|
1214
|
+
|
|
999
1215
|
def vibe_paste_before(command = nil)
|
|
1216
|
+
return vibe_paste_line(:above, command) if vibe_active_register_linewise?
|
|
1217
|
+
|
|
1000
1218
|
text = vibe_active_register_text
|
|
1001
1219
|
return false if text.empty?
|
|
1002
1220
|
|
|
1221
|
+
vibe_record_undo { @editor_state.insert(text) }
|
|
1222
|
+
vibe_remember_change(command)
|
|
1223
|
+
end
|
|
1224
|
+
|
|
1225
|
+
def vibe_paste_line(position, command = nil)
|
|
1226
|
+
text = vibe_active_register_text
|
|
1227
|
+
return false if text.empty?
|
|
1228
|
+
|
|
1229
|
+
text += "\n" unless text.end_with?("\n")
|
|
1230
|
+
line, = @editor_state.cursor_line_and_column
|
|
1231
|
+
target_line = position == :below ? line + 1 : line
|
|
1232
|
+
insert_index = if target_line >= @editor_state.lines.length
|
|
1233
|
+
@editor_state.buffer.end_with?("\n") ? @editor_state.buffer.length : @editor_state.line_range(line)[0]
|
|
1234
|
+
else
|
|
1235
|
+
@editor_state.line_range(target_line)[0]
|
|
1236
|
+
end
|
|
1003
1237
|
vibe_record_undo do
|
|
1004
|
-
@editor_state.cursor =
|
|
1238
|
+
@editor_state.cursor = insert_index
|
|
1005
1239
|
@editor_state.insert(text)
|
|
1006
1240
|
end
|
|
1241
|
+
@editor_state.set_cursor_line_and_column(target_line, 0)
|
|
1007
1242
|
vibe_remember_change(command)
|
|
1008
1243
|
end
|
|
1009
1244
|
|
|
@@ -1030,12 +1265,29 @@ module Kward
|
|
|
1030
1265
|
start_index, = @editor_state.line_range(line)
|
|
1031
1266
|
end_line = [line + count - 1, @editor_state.lines.length - 1].min
|
|
1032
1267
|
_, end_index = @editor_state.line_range(end_line)
|
|
1033
|
-
vibe_copy_range(start_index, end_index, "Yanked #{count} line#{count == 1 ? "" : "s"}")
|
|
1268
|
+
vibe_copy_range(start_index, end_index, "Yanked #{count} line#{count == 1 ? "" : "s"}", linewise: true)
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
def vibe_indent_lines(count, direction)
|
|
1272
|
+
line, = @editor_state.cursor_line_and_column
|
|
1273
|
+
end_line = [line + count - 1, @editor_state.lines.length - 1].min
|
|
1274
|
+
start_index = @editor_state.line_range(line)[0]
|
|
1275
|
+
end_index = @editor_state.line_range(end_line)[1]
|
|
1276
|
+
original_text = @editor_state.buffer[start_index...end_index].to_s
|
|
1277
|
+
lines = @editor_state.lines[line..end_line].map do |source|
|
|
1278
|
+
direction == :right ? " #{source}" : source.sub(/\A(?: |\t| )/, "")
|
|
1279
|
+
end
|
|
1280
|
+
replacement = lines.join("\n")
|
|
1281
|
+
replacement += "\n" if original_text.end_with?("\n")
|
|
1282
|
+
vibe_record_undo { @editor_state.replace_range(start_index, end_index, replacement) }
|
|
1283
|
+
@editor_state.set_cursor_line_and_column(line, 0)
|
|
1284
|
+
true
|
|
1034
1285
|
end
|
|
1035
1286
|
|
|
1036
1287
|
def vibe_change_lines(count, command = nil)
|
|
1037
1288
|
start_index, end_index = vibe_linewise_change_range(count)
|
|
1038
1289
|
@editor_state.copy_range(start_index, end_index)
|
|
1290
|
+
@editor_state.vibe_kill_linewise = true
|
|
1039
1291
|
vibe_record_undo { @editor_state.replace_range(start_index, end_index, "") }
|
|
1040
1292
|
@editor_state.cursor = start_index
|
|
1041
1293
|
vibe_enter_insert_mode(command)
|
|
@@ -1080,6 +1332,17 @@ module Kward
|
|
|
1080
1332
|
vibe_enter_insert_mode(command)
|
|
1081
1333
|
end
|
|
1082
1334
|
|
|
1335
|
+
def vibe_swapcase_characters(count, command = nil)
|
|
1336
|
+
start_index = @editor_state.cursor
|
|
1337
|
+
return @editor_state.status = "Empty range" if start_index >= @editor_state.buffer.length
|
|
1338
|
+
|
|
1339
|
+
end_index = [start_index + count, @editor_state.buffer.length].min
|
|
1340
|
+
text = @editor_state.buffer[start_index...end_index].to_s.swapcase
|
|
1341
|
+
vibe_record_undo { @editor_state.replace_range(start_index, end_index, text) }
|
|
1342
|
+
@editor_state.cursor = [end_index, @editor_state.buffer.length].min
|
|
1343
|
+
vibe_remember_change(command)
|
|
1344
|
+
end
|
|
1345
|
+
|
|
1083
1346
|
def vibe_replace_single_character(character, count, command = nil)
|
|
1084
1347
|
return @editor_state.status = "Replacement character required" if character.to_s.empty?
|
|
1085
1348
|
|
|
@@ -1093,7 +1356,7 @@ module Kward
|
|
|
1093
1356
|
vibe_remember_change(command)
|
|
1094
1357
|
end
|
|
1095
1358
|
|
|
1096
|
-
def vibe_join_lines(count, command = nil)
|
|
1359
|
+
def vibe_join_lines(count, command = nil, separator: nil)
|
|
1097
1360
|
line, = @editor_state.cursor_line_and_column
|
|
1098
1361
|
join_count = [count, 2].max
|
|
1099
1362
|
end_line = [line + join_count - 1, @editor_state.lines.length - 1].min
|
|
@@ -1105,8 +1368,8 @@ module Kward
|
|
|
1105
1368
|
next_line_start = line_end + 1
|
|
1106
1369
|
next_line_end = next_line_start + @editor_state.lines[line + 1].to_s.length
|
|
1107
1370
|
next_line = @editor_state.buffer[next_line_start...next_line_end].to_s.sub(/\A\s+/, "")
|
|
1108
|
-
|
|
1109
|
-
@editor_state.replace_range(line_end, next_line_end,
|
|
1371
|
+
join_separator = separator.nil? ? (next_line.empty? ? "" : " ") : separator
|
|
1372
|
+
@editor_state.replace_range(line_end, next_line_end, join_separator + next_line)
|
|
1110
1373
|
@editor_state.cursor = line_end
|
|
1111
1374
|
end
|
|
1112
1375
|
end
|
|
@@ -1119,8 +1382,42 @@ module Kward
|
|
|
1119
1382
|
vibe_begin_change_recording(command) if command
|
|
1120
1383
|
end
|
|
1121
1384
|
|
|
1385
|
+
def vibe_transform_operator(transform_key, motion, count, command = nil)
|
|
1386
|
+
motion_count, motion = vibe_count_and_body(motion)
|
|
1387
|
+
count *= motion_count if motion_count.positive?
|
|
1388
|
+
target = vibe_operator_target(motion, count)
|
|
1389
|
+
return false unless target
|
|
1390
|
+
return @editor_state.status = "Empty range" if target.start_index == target.end_index
|
|
1391
|
+
|
|
1392
|
+
transform = { "u" => :downcase, "U" => :upcase, "~" => :swapcase }.fetch(transform_key)
|
|
1393
|
+
vibe_transform_range(target.start_index, target.end_index, transform)
|
|
1394
|
+
vibe_remember_change(command)
|
|
1395
|
+
end
|
|
1396
|
+
|
|
1397
|
+
def vibe_transform_lines(count, transform, command = nil)
|
|
1398
|
+
line, = @editor_state.cursor_line_and_column
|
|
1399
|
+
end_line = [line + count - 1, @editor_state.lines.length - 1].min
|
|
1400
|
+
start_index = @editor_state.line_range(line)[0]
|
|
1401
|
+
end_index = @editor_state.line_range(end_line)[1]
|
|
1402
|
+
vibe_transform_range(start_index, end_index, transform)
|
|
1403
|
+
@editor_state.set_cursor_line_and_column(line, 0)
|
|
1404
|
+
vibe_remember_change(command)
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
def vibe_transform_range(start_index, end_index, transform)
|
|
1408
|
+
text = @editor_state.buffer[start_index...end_index].to_s
|
|
1409
|
+
replacement = case transform
|
|
1410
|
+
when :swapcase then text.swapcase
|
|
1411
|
+
when :downcase then text.downcase
|
|
1412
|
+
else text.upcase
|
|
1413
|
+
end
|
|
1414
|
+
vibe_record_undo { @editor_state.replace_range(start_index, end_index, replacement) }
|
|
1415
|
+
end
|
|
1416
|
+
|
|
1122
1417
|
def vibe_operator_motion(operator, motion, count, command = nil)
|
|
1123
1418
|
motion_count, motion = vibe_count_and_body(motion)
|
|
1419
|
+
return vibe_operator_linewise_motion(operator, motion_count, command) if motion == "G"
|
|
1420
|
+
|
|
1124
1421
|
count *= motion_count if motion_count.positive?
|
|
1125
1422
|
return vibe_operator_linewise(operator, count, command) if motion == operator
|
|
1126
1423
|
|
|
@@ -1131,28 +1428,49 @@ module Kward
|
|
|
1131
1428
|
vibe_apply_operator_to_target(operator, target, command, motion, count, motion_count)
|
|
1132
1429
|
end
|
|
1133
1430
|
|
|
1431
|
+
def vibe_operator_linewise_motion(operator, line_count, command = nil)
|
|
1432
|
+
line, = @editor_state.cursor_line_and_column
|
|
1433
|
+
target_line = line_count.positive? ? line_count - 1 : @editor_state.lines.length - 1
|
|
1434
|
+
start_line, end_line = [line, target_line].minmax
|
|
1435
|
+
start_index, = @editor_state.line_range(start_line)
|
|
1436
|
+
_, end_index = @editor_state.line_range(end_line)
|
|
1437
|
+
start_index -= 1 if end_index == @editor_state.buffer.length && start_index.positive?
|
|
1438
|
+
|
|
1439
|
+
target = VibeOperatorTarget.new(type: :linewise, start_index: start_index, end_index: end_index)
|
|
1440
|
+
vibe_apply_operator_to_target(operator, target, command, "G", 1, line_count)
|
|
1441
|
+
end
|
|
1442
|
+
|
|
1134
1443
|
def vibe_active_register_text
|
|
1135
1444
|
return @editor_state.vibe_registers[@vibe_active_register].to_s if @vibe_active_register
|
|
1136
1445
|
|
|
1137
1446
|
@editor_state.kill_buffer.to_s
|
|
1138
1447
|
end
|
|
1139
1448
|
|
|
1140
|
-
def
|
|
1449
|
+
def vibe_active_register_linewise?
|
|
1450
|
+
return @editor_state.vibe_register_types[@vibe_active_register] == :linewise if @vibe_active_register
|
|
1451
|
+
|
|
1452
|
+
@editor_state.vibe_kill_linewise || @editor_state.kill_buffer.to_s.end_with?("\n")
|
|
1453
|
+
end
|
|
1454
|
+
|
|
1455
|
+
def vibe_store_active_register(linewise: @editor_state.vibe_kill_linewise)
|
|
1141
1456
|
return unless @vibe_active_register
|
|
1142
1457
|
|
|
1143
1458
|
@editor_state.vibe_registers[@vibe_active_register] = @editor_state.kill_buffer.to_s
|
|
1459
|
+
@editor_state.vibe_register_types[@vibe_active_register] = linewise ? :linewise : :characterwise
|
|
1144
1460
|
end
|
|
1145
1461
|
|
|
1146
1462
|
def vibe_apply_operator_to_target(operator, target, command, motion, count, motion_count)
|
|
1147
1463
|
case operator
|
|
1148
1464
|
when "d"
|
|
1149
1465
|
@editor_state.copy_range(target.start_index, target.end_index)
|
|
1466
|
+
@editor_state.vibe_kill_linewise = target.type == :linewise
|
|
1150
1467
|
vibe_record_undo { @editor_state.replace_range(target.start_index, target.end_index, "") }
|
|
1151
1468
|
@editor_state.status = "Deleted"
|
|
1152
1469
|
vibe_store_active_register
|
|
1153
1470
|
vibe_remember_change(command)
|
|
1154
1471
|
when "c"
|
|
1155
1472
|
@editor_state.copy_range(target.start_index, target.end_index)
|
|
1473
|
+
@editor_state.vibe_kill_linewise = target.type == :linewise
|
|
1156
1474
|
vibe_record_undo do
|
|
1157
1475
|
@editor_state.replace_range(target.start_index, target.end_index, target.change_replacement_text)
|
|
1158
1476
|
@editor_state.cursor = target.change_cursor_index
|
|
@@ -1168,7 +1486,7 @@ module Kward
|
|
|
1168
1486
|
|
|
1169
1487
|
def vibe_operator_target(motion, count)
|
|
1170
1488
|
return vibe_text_object_target(motion) if motion.match?(/\A[ai].\z/)
|
|
1171
|
-
return vibe_word_motion_target(motion, count) if %w[w e b].include?(motion)
|
|
1489
|
+
return vibe_word_motion_target(motion, count) if %w[w e b W E B ge gE].include?(motion)
|
|
1172
1490
|
return vibe_find_motion_target(motion, count) if motion.match?(/\A[fFtT].\z/)
|
|
1173
1491
|
return vibe_percent_motion_target if motion == "%"
|
|
1174
1492
|
|
|
@@ -1222,9 +1540,11 @@ module Kward
|
|
|
1222
1540
|
end_index = start_index
|
|
1223
1541
|
if motion == "w"
|
|
1224
1542
|
end_index = vibe_word_operator_forward_index(end_index, count)
|
|
1543
|
+
elsif motion == "W"
|
|
1544
|
+
end_index = vibe_big_word_operator_forward_index(end_index, count)
|
|
1225
1545
|
else
|
|
1226
1546
|
count.times { end_index = vibe_word_motion_index(motion, end_index) }
|
|
1227
|
-
end_index = [end_index + 1, @editor_state.buffer.length].min if motion
|
|
1547
|
+
end_index = [end_index + 1, @editor_state.buffer.length].min if %w[e E].include?(motion)
|
|
1228
1548
|
end
|
|
1229
1549
|
@editor_state.cursor = end_index
|
|
1230
1550
|
VibeOperatorTarget.new(type: :characterwise, start_index: start_index, end_index: end_index)
|
|
@@ -1251,8 +1571,18 @@ module Kward
|
|
|
1251
1571
|
vibe_move_to_next_word_start
|
|
1252
1572
|
when "e"
|
|
1253
1573
|
vibe_move_to_word_end
|
|
1254
|
-
|
|
1574
|
+
when "b"
|
|
1255
1575
|
vibe_move_to_previous_word_start
|
|
1576
|
+
when "W"
|
|
1577
|
+
vibe_move_to_next_big_word_start
|
|
1578
|
+
when "E"
|
|
1579
|
+
vibe_move_to_big_word_end
|
|
1580
|
+
when "B"
|
|
1581
|
+
vibe_move_to_previous_big_word_start
|
|
1582
|
+
when "ge"
|
|
1583
|
+
vibe_move_to_previous_word_end
|
|
1584
|
+
else
|
|
1585
|
+
vibe_move_to_previous_big_word_end
|
|
1256
1586
|
end
|
|
1257
1587
|
@editor_state.cursor
|
|
1258
1588
|
ensure
|
|
@@ -1464,6 +1794,14 @@ module Kward
|
|
|
1464
1794
|
cursor
|
|
1465
1795
|
end
|
|
1466
1796
|
|
|
1797
|
+
def vibe_jump_to_file_percentage(percent)
|
|
1798
|
+
percent = [[percent, 1].max, 100].min
|
|
1799
|
+
line_count = @editor_state.lines.length
|
|
1800
|
+
target_line = ((line_count - 1) * percent / 100.0).floor
|
|
1801
|
+
@editor_state.move_to_line_first_non_blank(target_line)
|
|
1802
|
+
true
|
|
1803
|
+
end
|
|
1804
|
+
|
|
1467
1805
|
def vibe_jump_to_matching_pair
|
|
1468
1806
|
index = vibe_matching_pair_index(@editor_state.cursor)
|
|
1469
1807
|
unless index
|
|
@@ -1632,6 +1970,12 @@ module Kward
|
|
|
1632
1970
|
count.times { vibe_move_to_word_end }
|
|
1633
1971
|
when "b"
|
|
1634
1972
|
count.times { vibe_move_to_previous_word_start }
|
|
1973
|
+
when "W"
|
|
1974
|
+
count.times { vibe_move_to_next_big_word_start }
|
|
1975
|
+
when "E"
|
|
1976
|
+
count.times { vibe_move_to_big_word_end }
|
|
1977
|
+
when "B"
|
|
1978
|
+
count.times { vibe_move_to_previous_big_word_start }
|
|
1635
1979
|
else
|
|
1636
1980
|
return vibe_apply_motion(motion, count)
|
|
1637
1981
|
end
|
|
@@ -1652,6 +1996,8 @@ module Kward
|
|
|
1652
1996
|
@editor_state.move_line_start
|
|
1653
1997
|
when "^"
|
|
1654
1998
|
@editor_state.move_line_first_non_blank
|
|
1999
|
+
when "|"
|
|
2000
|
+
@editor_state.set_cursor_line_and_column(@editor_state.cursor_line_and_column.first, [count - 1, 0].max)
|
|
1655
2001
|
when "+", "\n", "\r"
|
|
1656
2002
|
vibe_move_to_relative_line_first_non_blank(count)
|
|
1657
2003
|
when "-"
|
|
@@ -1705,6 +2051,18 @@ module Kward
|
|
|
1705
2051
|
@editor_state.cursor = cursor
|
|
1706
2052
|
end
|
|
1707
2053
|
|
|
2054
|
+
def vibe_big_word_operator_forward_index(index, count)
|
|
2055
|
+
cursor = index
|
|
2056
|
+
buffer = @editor_state.buffer
|
|
2057
|
+
count.times do |step|
|
|
2058
|
+
cursor += 1 while cursor < buffer.length && vibe_word_kind(buffer[cursor]) != :space
|
|
2059
|
+
if step < count - 1
|
|
2060
|
+
cursor += 1 while cursor < buffer.length && vibe_word_kind(buffer[cursor]) == :space
|
|
2061
|
+
end
|
|
2062
|
+
end
|
|
2063
|
+
cursor
|
|
2064
|
+
end
|
|
2065
|
+
|
|
1708
2066
|
def vibe_move_to_word_end
|
|
1709
2067
|
cursor = @editor_state.cursor
|
|
1710
2068
|
buffer = @editor_state.buffer
|
|
@@ -1721,6 +2079,22 @@ module Kward
|
|
|
1721
2079
|
@editor_state.cursor = cursor
|
|
1722
2080
|
end
|
|
1723
2081
|
|
|
2082
|
+
def vibe_move_to_previous_word_end
|
|
2083
|
+
cursor = @editor_state.cursor
|
|
2084
|
+
buffer = @editor_state.buffer
|
|
2085
|
+
return if cursor.zero? || buffer.empty?
|
|
2086
|
+
|
|
2087
|
+
cursor = [cursor - 1, buffer.length - 1].min
|
|
2088
|
+
cursor -= 1 while cursor.positive? && vibe_word_kind(buffer[cursor]) == :space
|
|
2089
|
+
if cursor.positive? && vibe_word_kind(buffer[cursor]) != :space
|
|
2090
|
+
current_kind = vibe_word_kind(buffer[cursor])
|
|
2091
|
+
cursor -= 1 while cursor.positive? && vibe_word_kind(buffer[cursor - 1]) == current_kind
|
|
2092
|
+
cursor -= 1 if cursor.positive?
|
|
2093
|
+
cursor -= 1 while cursor.positive? && vibe_word_kind(buffer[cursor]) == :space
|
|
2094
|
+
end
|
|
2095
|
+
@editor_state.cursor = cursor
|
|
2096
|
+
end
|
|
2097
|
+
|
|
1724
2098
|
def vibe_move_to_previous_word_start
|
|
1725
2099
|
cursor = @editor_state.cursor
|
|
1726
2100
|
buffer = @editor_state.buffer
|
|
@@ -1733,6 +2107,55 @@ module Kward
|
|
|
1733
2107
|
@editor_state.cursor = cursor
|
|
1734
2108
|
end
|
|
1735
2109
|
|
|
2110
|
+
def vibe_move_to_next_big_word_start
|
|
2111
|
+
cursor = @editor_state.cursor
|
|
2112
|
+
buffer = @editor_state.buffer
|
|
2113
|
+
return if cursor >= buffer.length
|
|
2114
|
+
|
|
2115
|
+
cursor += 1 while cursor < buffer.length && vibe_word_kind(buffer[cursor]) != :space
|
|
2116
|
+
cursor += 1 while cursor < buffer.length && vibe_word_kind(buffer[cursor]) == :space
|
|
2117
|
+
@editor_state.cursor = cursor
|
|
2118
|
+
end
|
|
2119
|
+
|
|
2120
|
+
def vibe_move_to_big_word_end
|
|
2121
|
+
cursor = @editor_state.cursor
|
|
2122
|
+
buffer = @editor_state.buffer
|
|
2123
|
+
return if buffer.empty? || cursor >= buffer.length
|
|
2124
|
+
|
|
2125
|
+
cursor += 1 if vibe_word_kind(buffer[cursor]) != :space && cursor < buffer.length - 1 && vibe_word_kind(buffer[cursor + 1]) == :space
|
|
2126
|
+
cursor += 1 while cursor < buffer.length && vibe_word_kind(buffer[cursor]) == :space
|
|
2127
|
+
return @editor_state.cursor = cursor if cursor >= buffer.length
|
|
2128
|
+
|
|
2129
|
+
cursor += 1 while cursor < buffer.length - 1 && vibe_word_kind(buffer[cursor + 1]) != :space
|
|
2130
|
+
@editor_state.cursor = cursor
|
|
2131
|
+
end
|
|
2132
|
+
|
|
2133
|
+
def vibe_move_to_previous_big_word_start
|
|
2134
|
+
cursor = @editor_state.cursor
|
|
2135
|
+
buffer = @editor_state.buffer
|
|
2136
|
+
return if cursor.zero? || buffer.empty?
|
|
2137
|
+
|
|
2138
|
+
cursor -= 1
|
|
2139
|
+
cursor -= 1 while cursor.positive? && vibe_word_kind(buffer[cursor]) == :space
|
|
2140
|
+
cursor -= 1 while cursor.positive? && vibe_word_kind(buffer[cursor - 1]) != :space
|
|
2141
|
+
@editor_state.cursor = cursor
|
|
2142
|
+
end
|
|
2143
|
+
|
|
2144
|
+
def vibe_move_to_previous_big_word_end
|
|
2145
|
+
cursor = @editor_state.cursor
|
|
2146
|
+
buffer = @editor_state.buffer
|
|
2147
|
+
return if cursor.zero? || buffer.empty?
|
|
2148
|
+
|
|
2149
|
+
cursor = [cursor - 1, buffer.length - 1].min
|
|
2150
|
+
cursor -= 1 while cursor.positive? && vibe_word_kind(buffer[cursor]) == :space
|
|
2151
|
+
if cursor.positive? && vibe_word_kind(buffer[cursor]) != :space
|
|
2152
|
+
cursor -= 1 while cursor.positive? && vibe_word_kind(buffer[cursor - 1]) != :space
|
|
2153
|
+
cursor -= 1 if cursor.positive?
|
|
2154
|
+
cursor -= 1 while cursor.positive? && vibe_word_kind(buffer[cursor]) == :space
|
|
2155
|
+
end
|
|
2156
|
+
@editor_state.cursor = cursor
|
|
2157
|
+
end
|
|
2158
|
+
|
|
1736
2159
|
def vibe_word_kind(char)
|
|
1737
2160
|
case char.to_s
|
|
1738
2161
|
when /\s/
|
|
@@ -1744,8 +2167,9 @@ module Kward
|
|
|
1744
2167
|
end
|
|
1745
2168
|
end
|
|
1746
2169
|
|
|
1747
|
-
def vibe_copy_range(start_index, end_index, status)
|
|
2170
|
+
def vibe_copy_range(start_index, end_index, status, linewise: false)
|
|
1748
2171
|
@editor_state.copy_range(start_index, end_index)
|
|
2172
|
+
@editor_state.vibe_kill_linewise = linewise
|
|
1749
2173
|
@output_io.print(TerminalSequences.osc52(@editor_state.kill_buffer))
|
|
1750
2174
|
@output_io.flush if @output_io.respond_to?(:flush)
|
|
1751
2175
|
@editor_state.status = status
|
|
@@ -1765,6 +2189,8 @@ module Kward
|
|
|
1765
2189
|
end
|
|
1766
2190
|
|
|
1767
2191
|
def handle_vibe_repeat_change
|
|
2192
|
+
return execute_vibe_normal_command(@editor_state.vibe_pending.to_s + ".") unless @editor_state.vibe_pending.to_s.empty?
|
|
2193
|
+
|
|
1768
2194
|
change = @editor_state.vibe_last_change
|
|
1769
2195
|
return @editor_state.status = "No change to repeat" unless change
|
|
1770
2196
|
|
|
@@ -1774,6 +2200,7 @@ module Kward
|
|
|
1774
2200
|
|
|
1775
2201
|
def vibe_begin_change_recording(command)
|
|
1776
2202
|
@editor_state.vibe_last_change = vibe_change_keys(command)
|
|
2203
|
+
@editor_state.vibe_previous_change_cursor = @editor_state.cursor
|
|
1777
2204
|
end
|
|
1778
2205
|
|
|
1779
2206
|
def vibe_record_insert_change_key(key)
|
|
@@ -1785,6 +2212,7 @@ module Kward
|
|
|
1785
2212
|
|
|
1786
2213
|
def vibe_remember_change(command)
|
|
1787
2214
|
@editor_state.vibe_last_change = vibe_change_keys(command) if command
|
|
2215
|
+
@editor_state.vibe_previous_change_cursor = @editor_state.cursor
|
|
1788
2216
|
end
|
|
1789
2217
|
|
|
1790
2218
|
def vibe_build_change_command(operator, motion, count, motion_count)
|