ruvim 0.1.0 → 0.2.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/docs/binding.md +6 -0
- data/docs/command.md +16 -0
- data/docs/config.md +203 -84
- data/docs/lib_cleanup_report.md +79 -0
- data/docs/plugin.md +13 -15
- data/docs/spec.md +39 -22
- data/docs/todo.md +187 -10
- data/docs/tutorial.md +1 -1
- data/docs/vim_diff.md +2 -1
- data/lib/ruvim/app.rb +681 -123
- data/lib/ruvim/config_loader.rb +19 -5
- data/lib/ruvim/context.rb +0 -7
- data/lib/ruvim/dispatcher.rb +10 -0
- data/lib/ruvim/display_width.rb +25 -2
- data/lib/ruvim/editor.rb +173 -4
- data/lib/ruvim/global_commands.rb +500 -55
- data/lib/ruvim/input.rb +22 -10
- data/lib/ruvim/keyword_chars.rb +46 -0
- data/lib/ruvim/screen.rb +388 -53
- data/lib/ruvim/text_metrics.rb +26 -0
- data/lib/ruvim/version.rb +2 -2
- data/lib/ruvim/window.rb +35 -10
- data/lib/ruvim.rb +1 -0
- data/test/app_completion_test.rb +101 -0
- data/test/app_motion_test.rb +97 -2
- data/test/app_scenario_test.rb +270 -0
- data/test/app_startup_test.rb +10 -0
- data/test/config_loader_test.rb +37 -0
- data/test/dispatcher_test.rb +116 -0
- data/test/display_width_test.rb +18 -0
- data/test/fixtures/render_basic_snapshot.txt +7 -8
- data/test/fixtures/render_basic_snapshot_nonumber.txt +1 -2
- data/test/fixtures/render_unicode_scrolled_snapshot.txt +6 -7
- data/test/input_screen_integration_test.rb +26 -13
- data/test/screen_test.rb +166 -0
- data/test/window_test.rb +26 -0
- metadata +5 -1
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require "tempfile"
|
|
2
|
+
require "open3"
|
|
3
|
+
|
|
1
4
|
module RuVim
|
|
2
5
|
class GlobalCommands
|
|
3
6
|
include Singleton
|
|
@@ -12,11 +15,11 @@ module RuVim
|
|
|
12
15
|
end
|
|
13
16
|
|
|
14
17
|
def cursor_left(ctx, count:, **)
|
|
15
|
-
|
|
18
|
+
move_cursor_horizontally(ctx, direction: :left, count:)
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def cursor_right(ctx, count:, **)
|
|
19
|
-
|
|
22
|
+
move_cursor_horizontally(ctx, direction: :right, count:)
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
def cursor_up(ctx, count:, **)
|
|
@@ -37,6 +40,38 @@ module RuVim
|
|
|
37
40
|
ctx.window.move_down(ctx.buffer, page_lines * [count.to_i, 1].max)
|
|
38
41
|
end
|
|
39
42
|
|
|
43
|
+
def cursor_page_up_default(ctx, count:, bang:, **)
|
|
44
|
+
call(:cursor_page_up, ctx, count:, bang:, kwargs: { page_lines: current_page_step_lines(ctx) })
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def cursor_page_down_default(ctx, count:, bang:, **)
|
|
48
|
+
call(:cursor_page_down, ctx, count:, bang:, kwargs: { page_lines: current_page_step_lines(ctx) })
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def cursor_page_up_half(ctx, count:, bang:, **)
|
|
52
|
+
call(:cursor_page_up, ctx, count:, bang:, kwargs: { page_lines: current_half_page_step_lines(ctx) })
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def cursor_page_down_half(ctx, count:, bang:, **)
|
|
56
|
+
call(:cursor_page_down, ctx, count:, bang:, kwargs: { page_lines: current_half_page_step_lines(ctx) })
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def window_scroll_up(ctx, kwargs:, count:, **)
|
|
60
|
+
scroll_window_vertically(ctx, direction: :up, lines: kwargs[:lines] || kwargs["lines"], view_height: kwargs[:view_height] || kwargs["view_height"], count:)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def window_scroll_down(ctx, kwargs:, count:, **)
|
|
64
|
+
scroll_window_vertically(ctx, direction: :down, lines: kwargs[:lines] || kwargs["lines"], view_height: kwargs[:view_height] || kwargs["view_height"], count:)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def window_scroll_up_line(ctx, count:, bang:, **)
|
|
68
|
+
call(:window_scroll_up, ctx, count:, bang:, kwargs: { lines: 1, view_height: current_view_height(ctx) + 1 })
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def window_scroll_down_line(ctx, count:, bang:, **)
|
|
72
|
+
call(:window_scroll_down, ctx, count:, bang:, kwargs: { lines: 1, view_height: current_view_height(ctx) + 1 })
|
|
73
|
+
end
|
|
74
|
+
|
|
40
75
|
def cursor_line_start(ctx, **)
|
|
41
76
|
ctx.window.cursor_x = 0
|
|
42
77
|
ctx.window.clamp_to_buffer(ctx.buffer)
|
|
@@ -149,6 +184,7 @@ module RuVim
|
|
|
149
184
|
x = ctx.buffer.line_length(y)
|
|
150
185
|
ctx.buffer.begin_change_group
|
|
151
186
|
new_y, new_x = ctx.buffer.insert_newline(y, x)
|
|
187
|
+
new_x = apply_autoindent_to_newline(ctx, row: new_y, previous_row: y, start_col: new_x)
|
|
152
188
|
ctx.window.cursor_y = new_y
|
|
153
189
|
ctx.window.cursor_x = new_x
|
|
154
190
|
ctx.editor.enter_insert_mode
|
|
@@ -159,8 +195,10 @@ module RuVim
|
|
|
159
195
|
materialize_intro_buffer_if_needed(ctx)
|
|
160
196
|
y = ctx.window.cursor_y
|
|
161
197
|
ctx.buffer.begin_change_group
|
|
162
|
-
ctx.buffer.insert_newline(y, 0)
|
|
163
|
-
|
|
198
|
+
_new_y, new_x = ctx.buffer.insert_newline(y, 0)
|
|
199
|
+
new_x = apply_autoindent_to_newline(ctx, row: y, previous_row: y + 1, start_col: 0)
|
|
200
|
+
ctx.window.cursor_y = y
|
|
201
|
+
ctx.window.cursor_x = new_x
|
|
164
202
|
ctx.editor.enter_insert_mode
|
|
165
203
|
ctx.editor.echo("-- INSERT --")
|
|
166
204
|
end
|
|
@@ -181,12 +219,14 @@ module RuVim
|
|
|
181
219
|
end
|
|
182
220
|
|
|
183
221
|
def window_split(ctx, **)
|
|
184
|
-
ctx.editor.
|
|
222
|
+
place = ctx.editor.effective_option("splitbelow", window: ctx.window, buffer: ctx.buffer) ? :after : :before
|
|
223
|
+
ctx.editor.split_current_window(layout: :horizontal, place:)
|
|
185
224
|
ctx.editor.echo("split")
|
|
186
225
|
end
|
|
187
226
|
|
|
188
227
|
def window_vsplit(ctx, **)
|
|
189
|
-
ctx.editor.
|
|
228
|
+
place = ctx.editor.effective_option("splitright", window: ctx.window, buffer: ctx.buffer) ? :after : :before
|
|
229
|
+
ctx.editor.split_current_window(layout: :vertical, place:)
|
|
190
230
|
ctx.editor.echo("vsplit")
|
|
191
231
|
end
|
|
192
232
|
|
|
@@ -212,9 +252,11 @@ module RuVim
|
|
|
212
252
|
|
|
213
253
|
def tab_new(ctx, argv:, **)
|
|
214
254
|
path = argv[0]
|
|
215
|
-
if ctx.buffer.modified?
|
|
216
|
-
|
|
217
|
-
|
|
255
|
+
if ctx.buffer.modified? && !ctx.editor.effective_option("hidden", window: ctx.window, buffer: ctx.buffer)
|
|
256
|
+
unless maybe_autowrite_before_switch(ctx)
|
|
257
|
+
ctx.editor.echo_error("Unsaved changes (use :w or :q!)")
|
|
258
|
+
return
|
|
259
|
+
end
|
|
218
260
|
end
|
|
219
261
|
tab = ctx.editor.tabnew(path: path)
|
|
220
262
|
if path && !path.empty?
|
|
@@ -423,7 +465,7 @@ module RuVim
|
|
|
423
465
|
when "w"
|
|
424
466
|
y = ctx.window.cursor_y
|
|
425
467
|
x = ctx.window.cursor_x
|
|
426
|
-
target = advance_word_forward(ctx.buffer, y, x, count)
|
|
468
|
+
target = advance_word_forward(ctx.buffer, y, x, count, editor: ctx.editor, window: ctx.window)
|
|
427
469
|
target ||= { row: y, col: x }
|
|
428
470
|
text = ctx.buffer.span_text(y, x, target[:row], target[:col])
|
|
429
471
|
store_yank_register(ctx, text:, type: :charwise)
|
|
@@ -587,8 +629,14 @@ module RuVim
|
|
|
587
629
|
end
|
|
588
630
|
|
|
589
631
|
if ctx.buffer.modified? && !bang
|
|
590
|
-
ctx.editor.
|
|
591
|
-
|
|
632
|
+
if ctx.editor.effective_option("hidden", window: ctx.window, buffer: ctx.buffer)
|
|
633
|
+
# hidden permits abandoning a modified buffer without forcing write.
|
|
634
|
+
elsif maybe_autowrite_before_switch(ctx)
|
|
635
|
+
# autowrite handled
|
|
636
|
+
else
|
|
637
|
+
ctx.editor.echo_error("Unsaved changes (use :e! to discard and open)")
|
|
638
|
+
return
|
|
639
|
+
end
|
|
592
640
|
end
|
|
593
641
|
|
|
594
642
|
new_buffer = ctx.editor.add_buffer_from_file(path)
|
|
@@ -596,6 +644,29 @@ module RuVim
|
|
|
596
644
|
ctx.editor.echo(File.exist?(path) ? "\"#{path}\" #{new_buffer.line_count}L" : "\"#{path}\" [New File]")
|
|
597
645
|
end
|
|
598
646
|
|
|
647
|
+
def file_goto_under_cursor(ctx, **)
|
|
648
|
+
token = file_token_under_cursor(ctx.buffer, ctx.window)
|
|
649
|
+
if token.nil? || token.empty?
|
|
650
|
+
ctx.editor.echo_error("No file under cursor")
|
|
651
|
+
return
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
path = resolve_gf_path(ctx, token)
|
|
655
|
+
unless path
|
|
656
|
+
ctx.editor.echo_error("File not found: #{token}")
|
|
657
|
+
return
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
if ctx.buffer.modified? && !ctx.editor.effective_option("hidden", window: ctx.window, buffer: ctx.buffer)
|
|
661
|
+
unless maybe_autowrite_before_switch(ctx)
|
|
662
|
+
ctx.editor.echo_error("Unsaved changes (set hidden or :w)")
|
|
663
|
+
return
|
|
664
|
+
end
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
ctx.editor.open_path(path)
|
|
668
|
+
end
|
|
669
|
+
|
|
599
670
|
def buffer_list(ctx, **)
|
|
600
671
|
current_id = ctx.buffer.id
|
|
601
672
|
alt_id = ctx.editor.alternate_buffer_id
|
|
@@ -639,6 +710,28 @@ module RuVim
|
|
|
639
710
|
switch_buffer_id(ctx, target_id, bang:)
|
|
640
711
|
end
|
|
641
712
|
|
|
713
|
+
def buffer_delete(ctx, argv:, bang:, **)
|
|
714
|
+
arg = argv[0]
|
|
715
|
+
target_id =
|
|
716
|
+
if arg.nil? || arg.empty?
|
|
717
|
+
ctx.buffer.id
|
|
718
|
+
elsif arg == "#"
|
|
719
|
+
ctx.editor.alternate_buffer_id || raise(RuVim::CommandError, "No alternate buffer")
|
|
720
|
+
elsif arg.match?(/\A\d+\z/)
|
|
721
|
+
arg.to_i
|
|
722
|
+
else
|
|
723
|
+
find_buffer_by_name(ctx.editor, arg)&.id || raise(RuVim::CommandError, "No such buffer: #{arg}")
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
target = ctx.editor.buffers[target_id] || raise(RuVim::CommandError, "No such buffer: #{target_id}")
|
|
727
|
+
if target.modified? && !bang
|
|
728
|
+
raise RuVim::CommandError, "No write since last change (use :bdelete! to discard)"
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
ctx.editor.delete_buffer(target_id)
|
|
732
|
+
ctx.editor.echo("buffer #{target_id} deleted")
|
|
733
|
+
end
|
|
734
|
+
|
|
642
735
|
def ex_help(ctx, argv: [], **)
|
|
643
736
|
topic = argv.first.to_s
|
|
644
737
|
registry = RuVim::ExCommandRegistry.instance
|
|
@@ -724,13 +817,98 @@ module RuVim
|
|
|
724
817
|
raise RuVim::CommandError, "Usage: :ruby <code>" if code.strip.empty?
|
|
725
818
|
|
|
726
819
|
b = binding
|
|
820
|
+
# Use local_variable_set for eval locals to avoid "assigned but unused variable"
|
|
821
|
+
# warnings while still exposing editor/buffer/window in :ruby.
|
|
727
822
|
b.local_variable_set(:editor, ctx.editor)
|
|
728
823
|
b.local_variable_set(:buffer, ctx.buffer)
|
|
729
824
|
b.local_variable_set(:window, ctx.window)
|
|
730
|
-
|
|
731
|
-
|
|
825
|
+
saved_stdout = STDOUT.dup
|
|
826
|
+
saved_stderr = STDERR.dup
|
|
827
|
+
original_g_stdout = $stdout
|
|
828
|
+
original_g_stderr = $stderr
|
|
829
|
+
result = nil
|
|
830
|
+
stdout_text = ""
|
|
831
|
+
stderr_text = ""
|
|
832
|
+
Tempfile.create("ruvim-ruby-stdout") do |outf|
|
|
833
|
+
Tempfile.create("ruvim-ruby-stderr") do |errf|
|
|
834
|
+
STDOUT.reopen(outf)
|
|
835
|
+
STDERR.reopen(errf)
|
|
836
|
+
$stdout = STDOUT
|
|
837
|
+
$stderr = STDERR
|
|
838
|
+
result = eval(code, b) # rubocop:disable Security/Eval
|
|
839
|
+
STDOUT.flush
|
|
840
|
+
STDERR.flush
|
|
841
|
+
outf.flush
|
|
842
|
+
errf.flush
|
|
843
|
+
outf.rewind
|
|
844
|
+
errf.rewind
|
|
845
|
+
stdout_text = outf.read.to_s
|
|
846
|
+
stderr_text = errf.read.to_s
|
|
847
|
+
end
|
|
848
|
+
end
|
|
849
|
+
if !stdout_text.empty? || !stderr_text.empty?
|
|
850
|
+
lines = ["Ruby output", ""]
|
|
851
|
+
unless stdout_text.empty?
|
|
852
|
+
lines << "[stdout]"
|
|
853
|
+
lines.concat(stdout_text.lines(chomp: true))
|
|
854
|
+
lines << ""
|
|
855
|
+
end
|
|
856
|
+
unless stderr_text.empty?
|
|
857
|
+
lines << "[stderr]"
|
|
858
|
+
lines.concat(stderr_text.lines(chomp: true))
|
|
859
|
+
lines << ""
|
|
860
|
+
end
|
|
861
|
+
lines << "[result]"
|
|
862
|
+
lines << (result.nil? ? "nil" : result.inspect)
|
|
863
|
+
ctx.editor.show_help_buffer!(title: "[Ruby Output]", lines:, filetype: "ruby")
|
|
864
|
+
else
|
|
865
|
+
ctx.editor.echo(result.nil? ? "ruby: nil" : "ruby: #{result.inspect}")
|
|
866
|
+
end
|
|
732
867
|
rescue StandardError => e
|
|
733
868
|
raise RuVim::CommandError, "Ruby error: #{e.class}: #{e.message}"
|
|
869
|
+
ensure
|
|
870
|
+
if defined?(saved_stdout) && saved_stdout
|
|
871
|
+
STDOUT.reopen(saved_stdout)
|
|
872
|
+
saved_stdout.close unless saved_stdout.closed?
|
|
873
|
+
end
|
|
874
|
+
if defined?(saved_stderr) && saved_stderr
|
|
875
|
+
STDERR.reopen(saved_stderr)
|
|
876
|
+
saved_stderr.close unless saved_stderr.closed?
|
|
877
|
+
end
|
|
878
|
+
$stdout = (defined?(original_g_stdout) && original_g_stdout) ? original_g_stdout : STDOUT
|
|
879
|
+
$stderr = (defined?(original_g_stderr) && original_g_stderr) ? original_g_stderr : STDERR
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
def ex_shell(ctx, command:, **)
|
|
883
|
+
raise RuVim::CommandError, "Restricted mode: :! is disabled" if ctx.editor.respond_to?(:restricted_mode?) && ctx.editor.restricted_mode?
|
|
884
|
+
|
|
885
|
+
cmd = command.to_s
|
|
886
|
+
raise RuVim::CommandError, "Usage: :!<command>" if cmd.strip.empty?
|
|
887
|
+
|
|
888
|
+
shell = ENV["SHELL"].to_s
|
|
889
|
+
shell = "/bin/sh" if shell.empty?
|
|
890
|
+
stdout_text, stderr_text, status = Open3.capture3(shell, "-c", cmd)
|
|
891
|
+
|
|
892
|
+
if !stdout_text.to_s.empty? || !stderr_text.to_s.empty?
|
|
893
|
+
lines = ["Shell output", "", "[command]", cmd, ""]
|
|
894
|
+
unless stdout_text.to_s.empty?
|
|
895
|
+
lines << "[stdout]"
|
|
896
|
+
lines.concat(stdout_text.to_s.lines(chomp: true))
|
|
897
|
+
lines << ""
|
|
898
|
+
end
|
|
899
|
+
unless stderr_text.to_s.empty?
|
|
900
|
+
lines << "[stderr]"
|
|
901
|
+
lines.concat(stderr_text.to_s.lines(chomp: true))
|
|
902
|
+
lines << ""
|
|
903
|
+
end
|
|
904
|
+
lines << "[status]"
|
|
905
|
+
lines << "exit #{status.exitstatus}"
|
|
906
|
+
ctx.editor.show_help_buffer!(title: "[Shell Output]", lines:, filetype: "sh")
|
|
907
|
+
else
|
|
908
|
+
ctx.editor.echo("shell exit #{status.exitstatus}")
|
|
909
|
+
end
|
|
910
|
+
rescue Errno::ENOENT => e
|
|
911
|
+
raise RuVim::CommandError, "Shell error: #{e.message}"
|
|
734
912
|
end
|
|
735
913
|
|
|
736
914
|
def ex_commands(ctx, **)
|
|
@@ -783,7 +961,7 @@ module RuVim
|
|
|
783
961
|
end
|
|
784
962
|
|
|
785
963
|
def ex_copen(ctx, **)
|
|
786
|
-
open_list_window(ctx, kind: :quickfix, title: "[Quickfix]", lines: quickfix_buffer_lines(ctx.editor))
|
|
964
|
+
open_list_window(ctx, kind: :quickfix, title: "[Quickfix]", lines: quickfix_buffer_lines(ctx.editor), source_window_id: ctx.window.id)
|
|
787
965
|
end
|
|
788
966
|
|
|
789
967
|
def ex_cclose(ctx, **)
|
|
@@ -811,7 +989,8 @@ module RuVim
|
|
|
811
989
|
end
|
|
812
990
|
|
|
813
991
|
def ex_lopen(ctx, **)
|
|
814
|
-
open_list_window(ctx, kind: :location_list, title: "[Location List]", lines: location_list_buffer_lines(ctx.editor, ctx.window.id)
|
|
992
|
+
open_list_window(ctx, kind: :location_list, title: "[Location List]", lines: location_list_buffer_lines(ctx.editor, ctx.window.id),
|
|
993
|
+
source_window_id: ctx.window.id)
|
|
815
994
|
end
|
|
816
995
|
|
|
817
996
|
def ex_lclose(ctx, **)
|
|
@@ -933,10 +1112,11 @@ module RuVim
|
|
|
933
1112
|
]
|
|
934
1113
|
end
|
|
935
1114
|
|
|
936
|
-
def open_list_window(ctx, kind:, title:, lines:)
|
|
1115
|
+
def open_list_window(ctx, kind:, title:, lines:, source_window_id:)
|
|
937
1116
|
editor = ctx.editor
|
|
938
1117
|
editor.split_current_window(layout: :horizontal)
|
|
939
1118
|
buffer = editor.add_virtual_buffer(kind:, name: title, lines:, filetype: "qf", readonly: true, modifiable: false)
|
|
1119
|
+
buffer.options["ruvim_list_source_window_id"] = source_window_id
|
|
940
1120
|
editor.switch_to_buffer(buffer.id)
|
|
941
1121
|
editor.echo(title)
|
|
942
1122
|
buffer
|
|
@@ -979,9 +1159,11 @@ module RuVim
|
|
|
979
1159
|
raise RuVim::CommandError, "No such buffer: #{buffer_id}"
|
|
980
1160
|
end
|
|
981
1161
|
|
|
982
|
-
if ctx.buffer.modified? && ctx.buffer.id != buffer_id && !bang
|
|
983
|
-
|
|
984
|
-
|
|
1162
|
+
if ctx.buffer.modified? && ctx.buffer.id != buffer_id && !bang && !ctx.editor.effective_option("hidden", window: ctx.window, buffer: ctx.buffer)
|
|
1163
|
+
unless maybe_autowrite_before_switch(ctx)
|
|
1164
|
+
ctx.editor.echo_error("Unsaved changes (use :w or :buffer! / :bnext! / :bprev!)")
|
|
1165
|
+
return
|
|
1166
|
+
end
|
|
985
1167
|
end
|
|
986
1168
|
|
|
987
1169
|
record_jump(ctx)
|
|
@@ -1017,28 +1199,55 @@ module RuVim
|
|
|
1017
1199
|
end.join("\n")
|
|
1018
1200
|
end
|
|
1019
1201
|
|
|
1202
|
+
def apply_autoindent_to_newline(ctx, row:, previous_row:, start_col: 0)
|
|
1203
|
+
return start_col unless ctx.editor.effective_option("autoindent", window: ctx.window, buffer: ctx.buffer)
|
|
1204
|
+
|
|
1205
|
+
prev = ctx.buffer.line_at(previous_row)
|
|
1206
|
+
indent = prev[/\A[ \t]*/].to_s
|
|
1207
|
+
|
|
1208
|
+
if ctx.editor.effective_option("smartindent", window: ctx.window, buffer: ctx.buffer)
|
|
1209
|
+
trimmed = prev.rstrip
|
|
1210
|
+
if trimmed.end_with?("{", "[", "(")
|
|
1211
|
+
sw = ctx.editor.effective_option("shiftwidth", window: ctx.window, buffer: ctx.buffer).to_i
|
|
1212
|
+
sw = 2 if sw <= 0
|
|
1213
|
+
indent += " " * sw
|
|
1214
|
+
end
|
|
1215
|
+
end
|
|
1216
|
+
|
|
1217
|
+
return start_col if indent.empty?
|
|
1218
|
+
|
|
1219
|
+
_y, x = ctx.buffer.insert_text(row, start_col, indent)
|
|
1220
|
+
x
|
|
1221
|
+
end
|
|
1222
|
+
|
|
1020
1223
|
def search_current_word(ctx, exact:, direction:)
|
|
1021
|
-
|
|
1224
|
+
keyword_rx = keyword_char_regex(ctx.editor, ctx.buffer, ctx.window)
|
|
1225
|
+
word = current_word_under_cursor(ctx.buffer, ctx.window, keyword_rx:)
|
|
1022
1226
|
if word.nil? || word.empty?
|
|
1023
1227
|
ctx.editor.echo("No word under cursor")
|
|
1024
1228
|
return
|
|
1025
1229
|
end
|
|
1026
1230
|
|
|
1027
|
-
pattern =
|
|
1231
|
+
pattern =
|
|
1232
|
+
if exact
|
|
1233
|
+
"(?<!#{keyword_rx.source})#{Regexp.escape(word)}(?!#{keyword_rx.source})"
|
|
1234
|
+
else
|
|
1235
|
+
Regexp.escape(word)
|
|
1236
|
+
end
|
|
1028
1237
|
ctx.editor.set_last_search(pattern:, direction:)
|
|
1029
1238
|
move_to_search(ctx, pattern:, direction:, count: 1)
|
|
1030
1239
|
end
|
|
1031
1240
|
|
|
1032
|
-
def current_word_under_cursor(buffer, window)
|
|
1241
|
+
def current_word_under_cursor(buffer, window, keyword_rx: /[[:alnum:]_]/)
|
|
1033
1242
|
line = buffer.line_at(window.cursor_y)
|
|
1034
1243
|
return nil if line.empty?
|
|
1035
1244
|
|
|
1036
1245
|
x = [window.cursor_x, line.length - 1].min
|
|
1037
1246
|
return nil if x.negative?
|
|
1038
1247
|
|
|
1039
|
-
if line[x]
|
|
1248
|
+
if !keyword_char?(line[x], keyword_rx)
|
|
1040
1249
|
left = x - 1
|
|
1041
|
-
if left >= 0 && line[left]
|
|
1250
|
+
if left >= 0 && keyword_char?(line[left], keyword_rx)
|
|
1042
1251
|
x = left
|
|
1043
1252
|
else
|
|
1044
1253
|
return nil
|
|
@@ -1046,9 +1255,9 @@ module RuVim
|
|
|
1046
1255
|
end
|
|
1047
1256
|
|
|
1048
1257
|
s = x
|
|
1049
|
-
s -= 1 while s.positive? && line[s - 1]
|
|
1258
|
+
s -= 1 while s.positive? && keyword_char?(line[s - 1], keyword_rx)
|
|
1050
1259
|
e = x + 1
|
|
1051
|
-
e += 1 while e < line.length && line[e]
|
|
1260
|
+
e += 1 while e < line.length && keyword_char?(line[e], keyword_rx)
|
|
1052
1261
|
line[s...e]
|
|
1053
1262
|
end
|
|
1054
1263
|
|
|
@@ -1132,7 +1341,7 @@ module RuVim
|
|
|
1132
1341
|
def delete_word_forward(ctx, count)
|
|
1133
1342
|
y = ctx.window.cursor_y
|
|
1134
1343
|
x = ctx.window.cursor_x
|
|
1135
|
-
target = advance_word_forward(ctx.buffer, y, x, count)
|
|
1344
|
+
target = advance_word_forward(ctx.buffer, y, x, count, editor: ctx.editor, window: ctx.window)
|
|
1136
1345
|
return true unless target
|
|
1137
1346
|
|
|
1138
1347
|
deleted = ctx.buffer.span_text(y, x, target[:row], target[:col])
|
|
@@ -1194,12 +1403,13 @@ module RuVim
|
|
|
1194
1403
|
true
|
|
1195
1404
|
end
|
|
1196
1405
|
|
|
1197
|
-
def advance_word_forward(buffer, row, col, count)
|
|
1406
|
+
def advance_word_forward(buffer, row, col, count, editor: nil, window: nil)
|
|
1198
1407
|
text = buffer.lines.join("\n")
|
|
1199
1408
|
flat = cursor_to_offset(buffer, row, col)
|
|
1200
1409
|
idx = flat
|
|
1410
|
+
keyword_rx = keyword_char_regex(editor, buffer, window)
|
|
1201
1411
|
count.times do
|
|
1202
|
-
idx = next_word_start_offset(text, idx)
|
|
1412
|
+
idx = next_word_start_offset(text, idx, keyword_rx)
|
|
1203
1413
|
return nil unless idx
|
|
1204
1414
|
end
|
|
1205
1415
|
offset_to_cursor(buffer, idx)
|
|
@@ -1214,9 +1424,9 @@ module RuVim
|
|
|
1214
1424
|
count.times do
|
|
1215
1425
|
target =
|
|
1216
1426
|
case kind
|
|
1217
|
-
when :forward_start then advance_word_forward(buffer, target[:row], target[:col], 1)
|
|
1218
|
-
when :backward_start then advance_word_backward(buffer, target[:row], target[:col], 1)
|
|
1219
|
-
when :forward_end then advance_word_end(buffer, target[:row], target[:col], 1)
|
|
1427
|
+
when :forward_start then advance_word_forward(buffer, target[:row], target[:col], 1, editor: ctx.editor, window: ctx.window)
|
|
1428
|
+
when :backward_start then advance_word_backward(buffer, target[:row], target[:col], 1, editor: ctx.editor, window: ctx.window)
|
|
1429
|
+
when :forward_end then advance_word_end(buffer, target[:row], target[:col], 1, editor: ctx.editor, window: ctx.window)
|
|
1220
1430
|
end
|
|
1221
1431
|
break unless target
|
|
1222
1432
|
end
|
|
@@ -1227,60 +1437,62 @@ module RuVim
|
|
|
1227
1437
|
ctx.window.clamp_to_buffer(buffer)
|
|
1228
1438
|
end
|
|
1229
1439
|
|
|
1230
|
-
def advance_word_backward(buffer, row, col, _count)
|
|
1440
|
+
def advance_word_backward(buffer, row, col, _count, editor: nil, window: nil)
|
|
1231
1441
|
text = buffer.lines.join("\n")
|
|
1232
1442
|
idx = cursor_to_offset(buffer, row, col)
|
|
1233
1443
|
idx = [idx - 1, 0].max
|
|
1234
|
-
|
|
1444
|
+
keyword_rx = keyword_char_regex(editor, buffer, window)
|
|
1445
|
+
while idx > 0 && char_class(text[idx], keyword_rx) == :space
|
|
1235
1446
|
idx -= 1
|
|
1236
1447
|
end
|
|
1237
|
-
cls = char_class(text[idx])
|
|
1238
|
-
while idx > 0 && char_class(text[idx - 1]) == cls && cls != :space
|
|
1448
|
+
cls = char_class(text[idx], keyword_rx)
|
|
1449
|
+
while idx > 0 && char_class(text[idx - 1], keyword_rx) == cls && cls != :space
|
|
1239
1450
|
idx -= 1
|
|
1240
1451
|
end
|
|
1241
|
-
while idx > 0 && char_class(text[idx]) == :space
|
|
1452
|
+
while idx > 0 && char_class(text[idx], keyword_rx) == :space
|
|
1242
1453
|
idx += 1
|
|
1243
1454
|
end
|
|
1244
1455
|
offset_to_cursor(buffer, idx)
|
|
1245
1456
|
end
|
|
1246
1457
|
|
|
1247
|
-
def advance_word_end(buffer, row, col, _count)
|
|
1458
|
+
def advance_word_end(buffer, row, col, _count, editor: nil, window: nil)
|
|
1248
1459
|
text = buffer.lines.join("\n")
|
|
1249
1460
|
idx = cursor_to_offset(buffer, row, col)
|
|
1250
1461
|
n = text.length
|
|
1251
|
-
|
|
1462
|
+
keyword_rx = keyword_char_regex(editor, buffer, window)
|
|
1463
|
+
while idx < n && char_class(text[idx], keyword_rx) == :space
|
|
1252
1464
|
idx += 1
|
|
1253
1465
|
end
|
|
1254
1466
|
return nil if idx >= n
|
|
1255
1467
|
|
|
1256
|
-
cls = char_class(text[idx])
|
|
1257
|
-
idx += 1 while idx + 1 < n && char_class(text[idx + 1]) == cls && cls != :space
|
|
1468
|
+
cls = char_class(text[idx], keyword_rx)
|
|
1469
|
+
idx += 1 while idx + 1 < n && char_class(text[idx + 1], keyword_rx) == cls && cls != :space
|
|
1258
1470
|
offset_to_cursor(buffer, idx)
|
|
1259
1471
|
end
|
|
1260
1472
|
|
|
1261
|
-
def next_word_start_offset(text, from_offset)
|
|
1473
|
+
def next_word_start_offset(text, from_offset, keyword_rx = nil)
|
|
1262
1474
|
i = [from_offset, 0].max
|
|
1263
1475
|
n = text.length
|
|
1264
1476
|
return nil if i >= n
|
|
1265
1477
|
|
|
1266
|
-
cls = char_class(text[i])
|
|
1478
|
+
cls = char_class(text[i], keyword_rx)
|
|
1267
1479
|
if cls == :word
|
|
1268
|
-
i += 1 while i < n && char_class(text[i]) == :word
|
|
1480
|
+
i += 1 while i < n && char_class(text[i], keyword_rx) == :word
|
|
1269
1481
|
elsif cls == :space
|
|
1270
|
-
i += 1 while i < n && char_class(text[i]) == :space
|
|
1482
|
+
i += 1 while i < n && char_class(text[i], keyword_rx) == :space
|
|
1271
1483
|
else
|
|
1272
1484
|
i += 1
|
|
1273
1485
|
end
|
|
1274
|
-
i += 1 while i < n && char_class(text[i]) == :space
|
|
1486
|
+
i += 1 while i < n && char_class(text[i], keyword_rx) == :space
|
|
1275
1487
|
return n if i > n
|
|
1276
1488
|
|
|
1277
1489
|
i <= n ? i : nil
|
|
1278
1490
|
end
|
|
1279
1491
|
|
|
1280
|
-
def char_class(ch)
|
|
1492
|
+
def char_class(ch, keyword_rx = nil)
|
|
1281
1493
|
return :space if ch == "\n"
|
|
1282
1494
|
return :space if ch =~ /\s/
|
|
1283
|
-
return :word if ch
|
|
1495
|
+
return :word if keyword_char?(ch, keyword_rx)
|
|
1284
1496
|
:punct
|
|
1285
1497
|
end
|
|
1286
1498
|
|
|
@@ -1306,11 +1518,12 @@ module RuVim
|
|
|
1306
1518
|
x = nxt
|
|
1307
1519
|
end
|
|
1308
1520
|
|
|
1309
|
-
|
|
1521
|
+
keyword_rx = keyword_char_regex(nil, buffer, window)
|
|
1522
|
+
cls = keyword_char?(line[x], keyword_rx) ? :word : :punct
|
|
1310
1523
|
start_col = x
|
|
1311
|
-
start_col -= 1 while start_col.positive? && same_word_class?(line[start_col - 1], cls)
|
|
1524
|
+
start_col -= 1 while start_col.positive? && same_word_class?(line[start_col - 1], cls, keyword_rx)
|
|
1312
1525
|
end_col = x + 1
|
|
1313
|
-
end_col += 1 while end_col < line.length && same_word_class?(line[end_col], cls)
|
|
1526
|
+
end_col += 1 while end_col < line.length && same_word_class?(line[end_col], cls, keyword_rx)
|
|
1314
1527
|
|
|
1315
1528
|
if around
|
|
1316
1529
|
while end_col < line.length && line[end_col] =~ /\s/
|
|
@@ -1493,15 +1706,146 @@ module RuVim
|
|
|
1493
1706
|
nil
|
|
1494
1707
|
end
|
|
1495
1708
|
|
|
1496
|
-
def same_word_class?(ch, cls)
|
|
1709
|
+
def same_word_class?(ch, cls, keyword_rx = nil)
|
|
1497
1710
|
return false if ch.nil?
|
|
1498
1711
|
case cls
|
|
1499
|
-
when :word then ch
|
|
1500
|
-
when :punct then !(ch =~
|
|
1712
|
+
when :word then keyword_char?(ch, keyword_rx)
|
|
1713
|
+
when :punct then !(keyword_char?(ch, keyword_rx) || ch =~ /\s/)
|
|
1501
1714
|
else false
|
|
1502
1715
|
end
|
|
1503
1716
|
end
|
|
1504
1717
|
|
|
1718
|
+
def keyword_char?(ch, keyword_rx = nil)
|
|
1719
|
+
return false if ch.nil?
|
|
1720
|
+
|
|
1721
|
+
(keyword_rx || /[[:alnum:]_]/).match?(ch)
|
|
1722
|
+
end
|
|
1723
|
+
|
|
1724
|
+
def keyword_char_regex(editor, buffer, window)
|
|
1725
|
+
win = window || editor&.current_window
|
|
1726
|
+
buf = buffer || editor&.current_buffer
|
|
1727
|
+
raw =
|
|
1728
|
+
if editor
|
|
1729
|
+
editor.effective_option("iskeyword", window: win, buffer: buf).to_s
|
|
1730
|
+
else
|
|
1731
|
+
buf&.options&.fetch("iskeyword", nil).to_s
|
|
1732
|
+
end
|
|
1733
|
+
RuVim::KeywordChars.regex(raw)
|
|
1734
|
+
end
|
|
1735
|
+
|
|
1736
|
+
def move_cursor_horizontally(ctx, direction:, count:)
|
|
1737
|
+
count = [count.to_i, 1].max
|
|
1738
|
+
allow_wrap = whichwrap_allows?(ctx, direction)
|
|
1739
|
+
virtualedit_mode = virtualedit_mode(ctx)
|
|
1740
|
+
count.times do
|
|
1741
|
+
line = ctx.buffer.line_at(ctx.window.cursor_y)
|
|
1742
|
+
if direction == :left
|
|
1743
|
+
if ctx.window.cursor_x > line.length && virtualedit_mode
|
|
1744
|
+
ctx.window.cursor_x -= 1
|
|
1745
|
+
elsif ctx.window.cursor_x.positive?
|
|
1746
|
+
ctx.window.cursor_x = RuVim::TextMetrics.previous_grapheme_char_index(line, ctx.window.cursor_x)
|
|
1747
|
+
elsif allow_wrap && ctx.window.cursor_y.positive?
|
|
1748
|
+
ctx.window.cursor_y -= 1
|
|
1749
|
+
ctx.window.cursor_x = ctx.buffer.line_length(ctx.window.cursor_y)
|
|
1750
|
+
end
|
|
1751
|
+
else
|
|
1752
|
+
max_right =
|
|
1753
|
+
case virtualedit_mode
|
|
1754
|
+
when :all then line.length + count + 1024 # practical cap; clamp below uses current cursor
|
|
1755
|
+
when :onemore then line.length + 1
|
|
1756
|
+
else line.length
|
|
1757
|
+
end
|
|
1758
|
+
if ctx.window.cursor_x < max_right
|
|
1759
|
+
ctx.window.cursor_x =
|
|
1760
|
+
if virtualedit_mode == :onemore && ctx.window.cursor_x == line.length
|
|
1761
|
+
line.length + 1
|
|
1762
|
+
elsif virtualedit_mode == :all && ctx.window.cursor_x >= line.length
|
|
1763
|
+
ctx.window.cursor_x + 1
|
|
1764
|
+
else
|
|
1765
|
+
RuVim::TextMetrics.next_grapheme_char_index(line, ctx.window.cursor_x)
|
|
1766
|
+
end
|
|
1767
|
+
ctx.window.cursor_x = [ctx.window.cursor_x, max_right].min
|
|
1768
|
+
elsif allow_wrap && ctx.window.cursor_y < ctx.buffer.line_count - 1
|
|
1769
|
+
ctx.window.cursor_y += 1
|
|
1770
|
+
ctx.window.cursor_x = 0
|
|
1771
|
+
end
|
|
1772
|
+
end
|
|
1773
|
+
end
|
|
1774
|
+
extra =
|
|
1775
|
+
case virtualedit_mode
|
|
1776
|
+
when :all
|
|
1777
|
+
[ctx.window.cursor_x - ctx.buffer.line_length(ctx.window.cursor_y), 0].max
|
|
1778
|
+
when :onemore
|
|
1779
|
+
1
|
|
1780
|
+
else
|
|
1781
|
+
0
|
|
1782
|
+
end
|
|
1783
|
+
ctx.window.clamp_to_buffer(ctx.buffer, max_extra_col: extra)
|
|
1784
|
+
end
|
|
1785
|
+
|
|
1786
|
+
def whichwrap_allows?(ctx, direction)
|
|
1787
|
+
toks = ctx.editor.effective_option("whichwrap", window: ctx.window, buffer: ctx.buffer).to_s
|
|
1788
|
+
.split(",").map { |s| s.strip.downcase }.reject(&:empty?)
|
|
1789
|
+
return false if toks.empty?
|
|
1790
|
+
|
|
1791
|
+
if direction == :left
|
|
1792
|
+
toks.include?("h") || toks.include?("<") || toks.include?("left")
|
|
1793
|
+
else
|
|
1794
|
+
toks.include?("l") || toks.include?(">") || toks.include?("right")
|
|
1795
|
+
end
|
|
1796
|
+
end
|
|
1797
|
+
|
|
1798
|
+
def virtualedit_mode(ctx)
|
|
1799
|
+
spec = ctx.editor.effective_option("virtualedit", window: ctx.window, buffer: ctx.buffer).to_s
|
|
1800
|
+
toks = spec.split(",").map { |s| s.strip.downcase }
|
|
1801
|
+
return :all if toks.include?("all")
|
|
1802
|
+
return :onemore if toks.include?("onemore")
|
|
1803
|
+
|
|
1804
|
+
nil
|
|
1805
|
+
end
|
|
1806
|
+
|
|
1807
|
+
def current_view_height(ctx)
|
|
1808
|
+
hint = ctx.editor.respond_to?(:current_window_view_height_hint) ? ctx.editor.current_window_view_height_hint : nil
|
|
1809
|
+
[hint.to_i, 1].max
|
|
1810
|
+
rescue StandardError
|
|
1811
|
+
1
|
|
1812
|
+
end
|
|
1813
|
+
|
|
1814
|
+
def current_page_step_lines(ctx)
|
|
1815
|
+
[current_view_height(ctx) - 1, 1].max
|
|
1816
|
+
end
|
|
1817
|
+
|
|
1818
|
+
def current_half_page_step_lines(ctx)
|
|
1819
|
+
[current_view_height(ctx) / 2, 1].max
|
|
1820
|
+
end
|
|
1821
|
+
|
|
1822
|
+
def scroll_window_vertically(ctx, direction:, lines:, view_height:, count:)
|
|
1823
|
+
step = [[lines.to_i, 1].max * [count.to_i, 1].max, 1].max
|
|
1824
|
+
height = [view_height.to_i, 1].max
|
|
1825
|
+
max_row_offset = [ctx.buffer.line_count - height, 0].max
|
|
1826
|
+
|
|
1827
|
+
before = ctx.window.row_offset.to_i
|
|
1828
|
+
after =
|
|
1829
|
+
if direction == :up
|
|
1830
|
+
[before - step, 0].max
|
|
1831
|
+
else
|
|
1832
|
+
[before + step, max_row_offset].min
|
|
1833
|
+
end
|
|
1834
|
+
return if after == before
|
|
1835
|
+
|
|
1836
|
+
ctx.window.row_offset = after
|
|
1837
|
+
|
|
1838
|
+
# Vim-like behavior: scroll viewport first, then keep cursor inside it.
|
|
1839
|
+
top = after
|
|
1840
|
+
bottom = after + height - 1
|
|
1841
|
+
if ctx.window.cursor_y < top
|
|
1842
|
+
ctx.window.cursor_y = top
|
|
1843
|
+
elsif ctx.window.cursor_y > bottom
|
|
1844
|
+
ctx.window.cursor_y = bottom
|
|
1845
|
+
end
|
|
1846
|
+
ctx.window.clamp_to_buffer(ctx.buffer)
|
|
1847
|
+
end
|
|
1848
|
+
|
|
1505
1849
|
def cursor_to_offset(buffer, row, col)
|
|
1506
1850
|
offset = 0
|
|
1507
1851
|
row.times { |r| offset += buffer.line_length(r) + 1 }
|
|
@@ -1612,6 +1956,107 @@ module RuVim
|
|
|
1612
1956
|
nil
|
|
1613
1957
|
end
|
|
1614
1958
|
|
|
1959
|
+
def maybe_autowrite_before_switch(ctx)
|
|
1960
|
+
return false unless ctx.editor.effective_option("autowrite", window: ctx.window, buffer: ctx.buffer)
|
|
1961
|
+
return false unless ctx.buffer.file_buffer?
|
|
1962
|
+
return false unless ctx.buffer.path && !ctx.buffer.path.empty?
|
|
1963
|
+
|
|
1964
|
+
ctx.buffer.write_to(ctx.buffer.path)
|
|
1965
|
+
true
|
|
1966
|
+
rescue StandardError
|
|
1967
|
+
false
|
|
1968
|
+
end
|
|
1969
|
+
|
|
1970
|
+
def file_token_under_cursor(buffer, window)
|
|
1971
|
+
line = buffer.line_at(window.cursor_y)
|
|
1972
|
+
return nil if line.empty?
|
|
1973
|
+
|
|
1974
|
+
x = [[window.cursor_x, 0].max, [line.length - 1, 0].max].min
|
|
1975
|
+
file_char = /[[:alnum:]_\.\/~-]/
|
|
1976
|
+
if line[x] !~ file_char
|
|
1977
|
+
left = x - 1
|
|
1978
|
+
right = x + 1
|
|
1979
|
+
if left >= 0 && line[left] =~ file_char
|
|
1980
|
+
x = left
|
|
1981
|
+
elsif right < line.length && line[right] =~ file_char
|
|
1982
|
+
x = right
|
|
1983
|
+
else
|
|
1984
|
+
return nil
|
|
1985
|
+
end
|
|
1986
|
+
end
|
|
1987
|
+
|
|
1988
|
+
s = x
|
|
1989
|
+
e = x + 1
|
|
1990
|
+
s -= 1 while s.positive? && line[s - 1] =~ file_char
|
|
1991
|
+
e += 1 while e < line.length && line[e] =~ file_char
|
|
1992
|
+
line[s...e]
|
|
1993
|
+
end
|
|
1994
|
+
|
|
1995
|
+
def resolve_gf_path(ctx, token)
|
|
1996
|
+
candidates = gf_candidate_paths(ctx, token.to_s)
|
|
1997
|
+
candidates.find { |p| File.file?(p) || File.directory?(p) }
|
|
1998
|
+
end
|
|
1999
|
+
|
|
2000
|
+
def gf_candidate_paths(ctx, token)
|
|
2001
|
+
suffixes = gf_suffixes(ctx)
|
|
2002
|
+
names = [token]
|
|
2003
|
+
if File.extname(token).empty?
|
|
2004
|
+
suffixes.each { |suf| names << "#{token}#{suf}" }
|
|
2005
|
+
end
|
|
2006
|
+
names.uniq!
|
|
2007
|
+
|
|
2008
|
+
if token.start_with?("/", "~/")
|
|
2009
|
+
return names.map { |n| File.expand_path(n) }.uniq
|
|
2010
|
+
end
|
|
2011
|
+
|
|
2012
|
+
base_dirs = gf_search_dirs(ctx)
|
|
2013
|
+
base_dirs.product(names).map { |dir, name| File.expand_path(name, dir) }.uniq
|
|
2014
|
+
end
|
|
2015
|
+
|
|
2016
|
+
def gf_search_dirs(ctx)
|
|
2017
|
+
current_dir = if ctx.buffer.path && !ctx.buffer.path.empty?
|
|
2018
|
+
File.dirname(File.expand_path(ctx.buffer.path))
|
|
2019
|
+
else
|
|
2020
|
+
Dir.pwd
|
|
2021
|
+
end
|
|
2022
|
+
raw = ctx.editor.effective_option("path", window: ctx.window, buffer: ctx.buffer).to_s
|
|
2023
|
+
dirs = raw.split(",").map(&:strip).reject(&:empty?)
|
|
2024
|
+
dirs = ["."] if dirs.empty?
|
|
2025
|
+
dirs.flat_map do |dir|
|
|
2026
|
+
expand_gf_path_entry(dir, current_dir)
|
|
2027
|
+
end.uniq
|
|
2028
|
+
rescue StandardError
|
|
2029
|
+
[Dir.pwd]
|
|
2030
|
+
end
|
|
2031
|
+
|
|
2032
|
+
def gf_suffixes(ctx)
|
|
2033
|
+
raw = ctx.editor.effective_option("suffixesadd", window: ctx.window, buffer: ctx.buffer).to_s
|
|
2034
|
+
raw.split(",").map(&:strip).reject(&:empty?).map do |s|
|
|
2035
|
+
s.start_with?(".") ? s : ".#{s}"
|
|
2036
|
+
end
|
|
2037
|
+
end
|
|
2038
|
+
|
|
2039
|
+
def expand_gf_path_entry(entry, current_dir)
|
|
2040
|
+
dir = entry.to_s
|
|
2041
|
+
return [current_dir] if dir.empty? || dir == "."
|
|
2042
|
+
|
|
2043
|
+
expanded = File.expand_path(dir, current_dir)
|
|
2044
|
+
if expanded.end_with?("/**")
|
|
2045
|
+
base = expanded[0...-3]
|
|
2046
|
+
[base, *Dir.glob(File.join(base, "**", "*"))].select { |p| File.directory?(p) }
|
|
2047
|
+
elsif expanded.end_with?("**")
|
|
2048
|
+
base = expanded.sub(/\*\*\z/, "")
|
|
2049
|
+
base = base.sub(%r{/+\z}, "")
|
|
2050
|
+
[base, *Dir.glob(File.join(base, "**", "*"))].select { |p| File.directory?(p) }
|
|
2051
|
+
elsif expanded.match?(/[*?\[]/)
|
|
2052
|
+
Dir.glob(expanded).select { |p| File.directory?(p) }
|
|
2053
|
+
else
|
|
2054
|
+
[expanded]
|
|
2055
|
+
end
|
|
2056
|
+
rescue StandardError
|
|
2057
|
+
[expanded || File.expand_path(dir, current_dir)]
|
|
2058
|
+
end
|
|
2059
|
+
|
|
1615
2060
|
def ex_set_common(ctx, argv, scope:)
|
|
1616
2061
|
editor = ctx.editor
|
|
1617
2062
|
if argv.empty?
|