ruvim 0.4.0 → 0.6.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/AGENTS.md +53 -4
- data/README.md +15 -6
- data/Rakefile +7 -0
- data/benchmark/cext_compare.rb +165 -0
- data/benchmark/chunked_load.rb +256 -0
- data/benchmark/file_load.rb +140 -0
- data/benchmark/hotspots.rb +178 -0
- data/docs/binding.md +3 -2
- data/docs/command.md +81 -9
- data/docs/done.md +23 -0
- data/docs/spec.md +105 -19
- data/docs/todo.md +9 -0
- data/docs/tutorial.md +9 -1
- data/docs/vim_diff.md +13 -0
- data/ext/ruvim/extconf.rb +5 -0
- data/ext/ruvim/ruvim_ext.c +519 -0
- data/lib/ruvim/app.rb +217 -2778
- data/lib/ruvim/browser.rb +104 -0
- data/lib/ruvim/buffer.rb +39 -28
- data/lib/ruvim/command_invocation.rb +2 -2
- data/lib/ruvim/completion_manager.rb +708 -0
- data/lib/ruvim/dispatcher.rb +14 -8
- data/lib/ruvim/display_width.rb +91 -45
- data/lib/ruvim/editor.rb +64 -81
- data/lib/ruvim/ex_command_registry.rb +3 -1
- data/lib/ruvim/gh/link.rb +207 -0
- data/lib/ruvim/git/blame.rb +16 -6
- data/lib/ruvim/git/branch.rb +20 -5
- data/lib/ruvim/git/grep.rb +107 -0
- data/lib/ruvim/git/handler.rb +42 -1
- data/lib/ruvim/global_commands.rb +175 -35
- data/lib/ruvim/highlighter.rb +4 -13
- data/lib/ruvim/key_handler.rb +1510 -0
- data/lib/ruvim/keymap_manager.rb +7 -7
- data/lib/ruvim/lang/base.rb +5 -0
- data/lib/ruvim/lang/c.rb +116 -0
- data/lib/ruvim/lang/cpp.rb +107 -0
- data/lib/ruvim/lang/csv.rb +4 -1
- data/lib/ruvim/lang/diff.rb +2 -0
- data/lib/ruvim/lang/dockerfile.rb +36 -0
- data/lib/ruvim/lang/elixir.rb +85 -0
- data/lib/ruvim/lang/erb.rb +30 -0
- data/lib/ruvim/lang/go.rb +83 -0
- data/lib/ruvim/lang/html.rb +34 -0
- data/lib/ruvim/lang/javascript.rb +83 -0
- data/lib/ruvim/lang/json.rb +6 -0
- data/lib/ruvim/lang/lua.rb +76 -0
- data/lib/ruvim/lang/makefile.rb +36 -0
- data/lib/ruvim/lang/markdown.rb +3 -4
- data/lib/ruvim/lang/ocaml.rb +77 -0
- data/lib/ruvim/lang/perl.rb +91 -0
- data/lib/ruvim/lang/python.rb +85 -0
- data/lib/ruvim/lang/registry.rb +102 -0
- data/lib/ruvim/lang/ruby.rb +7 -0
- data/lib/ruvim/lang/rust.rb +95 -0
- data/lib/ruvim/lang/scheme.rb +5 -0
- data/lib/ruvim/lang/sh.rb +76 -0
- data/lib/ruvim/lang/sql.rb +52 -0
- data/lib/ruvim/lang/toml.rb +36 -0
- data/lib/ruvim/lang/tsv.rb +4 -1
- data/lib/ruvim/lang/typescript.rb +53 -0
- data/lib/ruvim/lang/yaml.rb +62 -0
- data/lib/ruvim/rich_view/table_renderer.rb +3 -3
- data/lib/ruvim/rich_view.rb +14 -7
- data/lib/ruvim/screen.rb +126 -72
- data/lib/ruvim/stream/file_load.rb +85 -0
- data/lib/ruvim/stream/follow.rb +40 -0
- data/lib/ruvim/stream/git.rb +43 -0
- data/lib/ruvim/stream/run.rb +74 -0
- data/lib/ruvim/stream/stdin.rb +55 -0
- data/lib/ruvim/stream.rb +35 -0
- data/lib/ruvim/stream_mixer.rb +394 -0
- data/lib/ruvim/terminal.rb +18 -4
- data/lib/ruvim/text_metrics.rb +84 -65
- data/lib/ruvim/version.rb +1 -1
- data/lib/ruvim/window.rb +5 -5
- data/lib/ruvim.rb +23 -6
- data/test/app_command_test.rb +382 -0
- data/test/app_completion_test.rb +43 -19
- data/test/app_dot_repeat_test.rb +27 -3
- data/test/app_ex_command_test.rb +154 -0
- data/test/app_motion_test.rb +13 -12
- data/test/app_register_test.rb +2 -1
- data/test/app_scenario_test.rb +15 -10
- data/test/app_startup_test.rb +70 -27
- data/test/app_text_object_test.rb +2 -1
- data/test/app_unicode_behavior_test.rb +3 -2
- data/test/browser_test.rb +88 -0
- data/test/buffer_test.rb +24 -0
- data/test/cli_test.rb +63 -0
- data/test/command_invocation_test.rb +33 -0
- data/test/config_dsl_test.rb +47 -0
- data/test/dispatcher_test.rb +74 -4
- data/test/ex_command_registry_test.rb +106 -0
- data/test/follow_test.rb +20 -21
- data/test/gh_link_test.rb +141 -0
- data/test/git_blame_test.rb +96 -17
- data/test/git_grep_test.rb +64 -0
- data/test/highlighter_test.rb +125 -0
- data/test/indent_test.rb +137 -0
- data/test/input_screen_integration_test.rb +1 -1
- data/test/keyword_chars_test.rb +85 -0
- data/test/lang_test.rb +634 -0
- data/test/markdown_renderer_test.rb +5 -5
- data/test/on_save_hook_test.rb +12 -8
- data/test/render_snapshot_test.rb +78 -0
- data/test/rich_view_test.rb +42 -42
- data/test/run_command_test.rb +307 -0
- data/test/screen_test.rb +68 -5
- data/test/stream_test.rb +165 -0
- data/test/window_test.rb +59 -0
- metadata +52 -2
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class AppExCommandTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@app = RuVim::App.new(clean: true)
|
|
8
|
+
@editor = @app.instance_variable_get(:@editor)
|
|
9
|
+
@key_handler = @app.instance_variable_get(:@key_handler)
|
|
10
|
+
@editor.materialize_intro_buffer!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def feed(*keys)
|
|
14
|
+
keys.each { |k| @key_handler.handle(k) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def buf
|
|
18
|
+
@editor.current_buffer
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def win
|
|
22
|
+
@editor.current_window
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# --- :set ---
|
|
26
|
+
|
|
27
|
+
def test_set_number_on_off
|
|
28
|
+
feed(":", "s", "e", "t", " ", "n", "u", "m", "b", "e", "r", :enter)
|
|
29
|
+
assert @editor.get_option("number")
|
|
30
|
+
|
|
31
|
+
feed(":", "s", "e", "t", " ", "n", "o", "n", "u", "m", "b", "e", "r", :enter)
|
|
32
|
+
refute @editor.get_option("number")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_set_tabstop_value
|
|
36
|
+
feed(":", "s", "e", "t", " ", "t", "a", "b", "s", "t", "o", "p", "=", "8", :enter)
|
|
37
|
+
assert_equal 8, @editor.get_option("tabstop")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_set_option_query
|
|
41
|
+
feed(":", "s", "e", "t", " ", "n", "u", "m", "b", "e", "r", "?", :enter)
|
|
42
|
+
assert_equal :normal, @editor.mode
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# --- :bindings ---
|
|
46
|
+
|
|
47
|
+
def test_bindings_command
|
|
48
|
+
feed(":", "b", "i", "n", "d", "i", "n", "g", "s", :enter)
|
|
49
|
+
assert_equal :normal, @editor.mode
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_bindings_with_mode_filter
|
|
53
|
+
feed(":", "b", "i", "n", "d", "i", "n", "g", "s", " ", "n", :enter)
|
|
54
|
+
assert_equal :normal, @editor.mode
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# --- :tabnew / :tabnext / :tabprev ---
|
|
58
|
+
|
|
59
|
+
def test_tabnext_tabprev_via_ex
|
|
60
|
+
feed(":", "t", "a", "b", "n", "e", "w", :enter)
|
|
61
|
+
feed(":", "t", "a", "b", "p", "r", "e", "v", :enter)
|
|
62
|
+
feed(":", "t", "a", "b", "n", "e", "x", "t", :enter)
|
|
63
|
+
assert_equal :normal, @editor.mode
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# --- :bnext / :bprev ---
|
|
67
|
+
|
|
68
|
+
def test_bnext_bprev_via_ex
|
|
69
|
+
feed(":", "b", "n", "e", "x", "t", :enter)
|
|
70
|
+
feed(":", "b", "p", "r", "e", "v", :enter)
|
|
71
|
+
assert_equal :normal, @editor.mode
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# --- :copen / :cclose / :cprev / :lclose / :lprev ---
|
|
75
|
+
|
|
76
|
+
def test_copen_and_cclose
|
|
77
|
+
feed(":", "c", "o", "p", "e", "n", :enter)
|
|
78
|
+
feed(":", "c", "c", "l", "o", "s", "e", :enter)
|
|
79
|
+
assert_equal :normal, @editor.mode
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_cprev_with_empty_list
|
|
83
|
+
feed(":", "c", "p", "r", "e", "v", :enter)
|
|
84
|
+
assert_equal :normal, @editor.mode
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_lclose_without_open
|
|
88
|
+
feed(":", "l", "c", "l", "o", "s", "e", :enter)
|
|
89
|
+
assert_equal :normal, @editor.mode
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_lprev_with_empty_list
|
|
93
|
+
feed(":", "l", "p", "r", "e", "v", :enter)
|
|
94
|
+
assert_equal :normal, @editor.mode
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# --- :args ---
|
|
98
|
+
|
|
99
|
+
def test_arglist_operations
|
|
100
|
+
feed(":", "a", "r", "g", "s", :enter)
|
|
101
|
+
assert_equal :normal, @editor.mode
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# --- :wq ---
|
|
105
|
+
|
|
106
|
+
def test_wq_with_no_filename_shows_error
|
|
107
|
+
feed(":", "w", "q", :enter)
|
|
108
|
+
assert_equal :normal, @editor.mode
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# --- :edit ---
|
|
112
|
+
|
|
113
|
+
def test_edit_no_file_reloads_or_errors
|
|
114
|
+
feed(":", "e", "d", "i", "t", :enter)
|
|
115
|
+
assert_equal :normal, @editor.mode
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# --- :split / :vsplit + window focus ---
|
|
119
|
+
|
|
120
|
+
def test_window_focus_after_split
|
|
121
|
+
feed(":", "s", "p", "l", "i", "t", :enter)
|
|
122
|
+
original_win_id = @editor.current_window.id
|
|
123
|
+
|
|
124
|
+
feed(:ctrl_w, "w")
|
|
125
|
+
refute_equal original_win_id, @editor.current_window.id
|
|
126
|
+
|
|
127
|
+
feed(:ctrl_w, "j")
|
|
128
|
+
feed(:ctrl_w, "k")
|
|
129
|
+
assert_equal :normal, @editor.mode
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def test_window_focus_left_right
|
|
133
|
+
feed(":", "v", "s", "p", "l", "i", "t", :enter)
|
|
134
|
+
feed(:ctrl_w, "h")
|
|
135
|
+
feed(:ctrl_w, "l")
|
|
136
|
+
assert_equal :normal, @editor.mode
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# --- :b# (alternate buffer) ---
|
|
140
|
+
|
|
141
|
+
def test_buffer_alternate_hash
|
|
142
|
+
feed(":", "s", "p", "l", "i", "t", :enter)
|
|
143
|
+
feed(":", "b", "#", :enter)
|
|
144
|
+
assert_equal :normal, @editor.mode
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# --- rich view toggle ---
|
|
148
|
+
|
|
149
|
+
def test_rich_toggle
|
|
150
|
+
buf.replace_all_lines!(["hello world"])
|
|
151
|
+
feed("g", "r")
|
|
152
|
+
assert_equal :normal, @editor.mode
|
|
153
|
+
end
|
|
154
|
+
end
|
data/test/app_motion_test.rb
CHANGED
|
@@ -4,11 +4,12 @@ class AppMotionTest < Minitest::Test
|
|
|
4
4
|
def setup
|
|
5
5
|
@app = RuVim::App.new(clean: true)
|
|
6
6
|
@editor = @app.instance_variable_get(:@editor)
|
|
7
|
+
@key_handler = @app.instance_variable_get(:@key_handler)
|
|
7
8
|
@editor.materialize_intro_buffer!
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def press(*keys)
|
|
11
|
-
keys.each { |k| @
|
|
12
|
+
keys.each { |k| @key_handler.send(:handle_normal_key, k) }
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def test_find_char_and_repeat
|
|
@@ -59,14 +60,14 @@ class AppMotionTest < Minitest::Test
|
|
|
59
60
|
|
|
60
61
|
@editor.current_window_view_height_hint = 5
|
|
61
62
|
|
|
62
|
-
@
|
|
63
|
+
@key_handler.send(:handle_normal_key, :pagedown)
|
|
63
64
|
assert_equal 4, @editor.current_window.cursor_y
|
|
64
65
|
|
|
65
66
|
@editor.pending_count = 2
|
|
66
|
-
@
|
|
67
|
+
@key_handler.send(:handle_normal_key, :pagedown)
|
|
67
68
|
assert_equal 12, @editor.current_window.cursor_y
|
|
68
69
|
|
|
69
|
-
@
|
|
70
|
+
@key_handler.send(:handle_normal_key, :pageup)
|
|
70
71
|
assert_equal 8, @editor.current_window.cursor_y
|
|
71
72
|
end
|
|
72
73
|
|
|
@@ -78,16 +79,16 @@ class AppMotionTest < Minitest::Test
|
|
|
78
79
|
|
|
79
80
|
@editor.current_window_view_height_hint = 10
|
|
80
81
|
|
|
81
|
-
@
|
|
82
|
+
@key_handler.send(:handle_normal_key, :ctrl_d)
|
|
82
83
|
assert_equal 5, @editor.current_window.cursor_y
|
|
83
84
|
|
|
84
|
-
@
|
|
85
|
+
@key_handler.send(:handle_normal_key, :ctrl_u)
|
|
85
86
|
assert_equal 0, @editor.current_window.cursor_y
|
|
86
87
|
|
|
87
|
-
@
|
|
88
|
+
@key_handler.send(:handle_normal_key, :ctrl_f)
|
|
88
89
|
assert_equal 9, @editor.current_window.cursor_y
|
|
89
90
|
|
|
90
|
-
@
|
|
91
|
+
@key_handler.send(:handle_normal_key, :ctrl_b)
|
|
91
92
|
assert_equal 0, @editor.current_window.cursor_y
|
|
92
93
|
end
|
|
93
94
|
|
|
@@ -100,11 +101,11 @@ class AppMotionTest < Minitest::Test
|
|
|
100
101
|
|
|
101
102
|
@editor.current_window_view_height_hint = 10
|
|
102
103
|
|
|
103
|
-
@
|
|
104
|
+
@key_handler.send(:handle_normal_key, :ctrl_e)
|
|
104
105
|
assert_equal 6, @editor.current_window.row_offset
|
|
105
106
|
assert_equal 6, @editor.current_window.cursor_y
|
|
106
107
|
|
|
107
|
-
@
|
|
108
|
+
@key_handler.send(:handle_normal_key, :ctrl_y)
|
|
108
109
|
assert_equal 5, @editor.current_window.row_offset
|
|
109
110
|
assert_equal 6, @editor.current_window.cursor_y
|
|
110
111
|
end
|
|
@@ -118,7 +119,7 @@ class AppMotionTest < Minitest::Test
|
|
|
118
119
|
keymaps = @app.instance_variable_get(:@keymaps)
|
|
119
120
|
keymaps.bind(:normal, ["<C-d>"], "cursor.down")
|
|
120
121
|
|
|
121
|
-
@
|
|
122
|
+
@key_handler.send(:handle_normal_key, :ctrl_d)
|
|
122
123
|
assert_equal 1, @editor.current_window.cursor_y
|
|
123
124
|
end
|
|
124
125
|
|
|
@@ -131,7 +132,7 @@ class AppMotionTest < Minitest::Test
|
|
|
131
132
|
keymaps = @app.instance_variable_get(:@keymaps)
|
|
132
133
|
keymaps.bind(:normal, ["<PageDown>"], "cursor.down")
|
|
133
134
|
|
|
134
|
-
@
|
|
135
|
+
@key_handler.send(:handle_normal_key, :pagedown)
|
|
135
136
|
assert_equal 1, @editor.current_window.cursor_y
|
|
136
137
|
end
|
|
137
138
|
|
data/test/app_register_test.rb
CHANGED
|
@@ -4,12 +4,13 @@ class AppRegisterTest < Minitest::Test
|
|
|
4
4
|
def setup
|
|
5
5
|
@app = RuVim::App.new(clean: true)
|
|
6
6
|
@editor = @app.instance_variable_get(:@editor)
|
|
7
|
+
@key_handler = @app.instance_variable_get(:@key_handler)
|
|
7
8
|
@editor.materialize_intro_buffer!
|
|
8
9
|
@buffer = @editor.current_buffer
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def press(*keys)
|
|
12
|
-
keys.each { |k| @
|
|
13
|
+
keys.each { |k| @key_handler.send(:handle_normal_key, k) }
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def test_yy_updates_register_zero
|
data/test/app_scenario_test.rb
CHANGED
|
@@ -8,11 +8,12 @@ class AppScenarioTest < Minitest::Test
|
|
|
8
8
|
@app = RuVim::App.new(clean: true)
|
|
9
9
|
@editor = @app.instance_variable_get(:@editor)
|
|
10
10
|
@dispatcher = @app.instance_variable_get(:@dispatcher)
|
|
11
|
+
@key_handler = @app.instance_variable_get(:@key_handler)
|
|
11
12
|
@editor.materialize_intro_buffer!
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def feed(*keys)
|
|
15
|
-
keys.each { |k| @
|
|
16
|
+
keys.each { |k| @key_handler.handle(k) }
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def test_insert_edit_search_and_delete_scenario
|
|
@@ -353,7 +354,7 @@ class AppScenarioTest < Minitest::Test
|
|
|
353
354
|
@editor.current_buffer.modifiable = false
|
|
354
355
|
@editor.current_buffer.readonly = true
|
|
355
356
|
|
|
356
|
-
@
|
|
357
|
+
@key_handler.handle("x")
|
|
357
358
|
|
|
358
359
|
assert_equal ["hello"], @editor.current_buffer.lines
|
|
359
360
|
assert_match(/not modifiable/i, @editor.message)
|
|
@@ -364,7 +365,7 @@ class AppScenarioTest < Minitest::Test
|
|
|
364
365
|
@editor.current_buffer.modifiable = false
|
|
365
366
|
@editor.current_buffer.readonly = true
|
|
366
367
|
|
|
367
|
-
@
|
|
368
|
+
@key_handler.handle("i")
|
|
368
369
|
|
|
369
370
|
assert_equal :normal, @editor.mode
|
|
370
371
|
assert_equal ["hello"], @editor.current_buffer.lines
|
|
@@ -373,15 +374,16 @@ class AppScenarioTest < Minitest::Test
|
|
|
373
374
|
|
|
374
375
|
def test_normal_ctrl_c_stops_stdin_stream_via_default_binding
|
|
375
376
|
stream = StringIO.new("hello\n")
|
|
376
|
-
@app.
|
|
377
|
-
|
|
377
|
+
sh = @app.instance_variable_get(:@stream_mixer)
|
|
378
|
+
sh.prepare_stdin_stream_buffer!(stream)
|
|
379
|
+
sh.start_pending_stdin!
|
|
378
380
|
|
|
379
|
-
@
|
|
381
|
+
@key_handler.handle(:ctrl_c)
|
|
380
382
|
|
|
381
|
-
assert_equal :closed, @editor.current_buffer.
|
|
383
|
+
assert_equal :closed, @editor.current_buffer.stream.state
|
|
382
384
|
assert_equal :normal, @editor.mode
|
|
383
385
|
assert_equal true, stream.closed?
|
|
384
|
-
assert_match(
|
|
386
|
+
assert_match(/stopped/, @editor.message)
|
|
385
387
|
end
|
|
386
388
|
|
|
387
389
|
def test_ctrl_z_calls_terminal_suspend
|
|
@@ -392,6 +394,7 @@ class AppScenarioTest < Minitest::Test
|
|
|
392
394
|
end
|
|
393
395
|
terminal_stub.define_singleton_method(:suspend_calls) { @suspend_calls }
|
|
394
396
|
@app.instance_variable_set(:@terminal, terminal_stub)
|
|
397
|
+
@app.instance_variable_get(:@key_handler).instance_variable_set(:@terminal, terminal_stub)
|
|
395
398
|
|
|
396
399
|
feed("i", "a", :ctrl_z)
|
|
397
400
|
|
|
@@ -404,6 +407,7 @@ class AppScenarioTest < Minitest::Test
|
|
|
404
407
|
terminal_stub = Object.new
|
|
405
408
|
terminal_stub.define_singleton_method(:suspend_for_tstp) {}
|
|
406
409
|
@app.instance_variable_set(:@terminal, terminal_stub)
|
|
410
|
+
@app.instance_variable_get(:@key_handler).instance_variable_set(:@terminal, terminal_stub)
|
|
407
411
|
|
|
408
412
|
screen_stub = Object.new
|
|
409
413
|
screen_stub.instance_variable_set(:@invalidated, false)
|
|
@@ -412,6 +416,7 @@ class AppScenarioTest < Minitest::Test
|
|
|
412
416
|
end
|
|
413
417
|
screen_stub.define_singleton_method(:invalidated?) { @invalidated }
|
|
414
418
|
@app.instance_variable_set(:@screen, screen_stub)
|
|
419
|
+
@app.instance_variable_get(:@key_handler).instance_variable_set(:@screen, screen_stub)
|
|
415
420
|
|
|
416
421
|
feed(:ctrl_z)
|
|
417
422
|
|
|
@@ -709,9 +714,9 @@ class AppScenarioTest < Minitest::Test
|
|
|
709
714
|
@editor.current_window.cursor_y = 0
|
|
710
715
|
@editor.current_window.cursor_x = 0
|
|
711
716
|
feed("A")
|
|
712
|
-
@app.
|
|
717
|
+
@app.instance_variable_get(:@key_handler).paste_batch = true
|
|
713
718
|
feed(:enter, *"world".chars)
|
|
714
|
-
@app.
|
|
719
|
+
@app.instance_variable_get(:@key_handler).paste_batch = false
|
|
715
720
|
feed(:escape)
|
|
716
721
|
|
|
717
722
|
assert_equal [" hello", "world"], @editor.current_buffer.lines
|
data/test/app_startup_test.rb
CHANGED
|
@@ -185,6 +185,46 @@ class AppStartupTest < Minitest::Test
|
|
|
185
185
|
assert_match(/Restricted mode/, editor.message)
|
|
186
186
|
end
|
|
187
187
|
|
|
188
|
+
def test_restricted_mode_disables_ex_grep
|
|
189
|
+
app = RuVim::App.new(clean: true, restricted: true)
|
|
190
|
+
editor = app.instance_variable_get(:@editor)
|
|
191
|
+
dispatcher = app.instance_variable_get(:@dispatcher)
|
|
192
|
+
|
|
193
|
+
dispatcher.dispatch_ex(editor, "grep pattern file.txt")
|
|
194
|
+
|
|
195
|
+
assert_match(/Restricted mode/, editor.message)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def test_restricted_mode_disables_ex_lgrep
|
|
199
|
+
app = RuVim::App.new(clean: true, restricted: true)
|
|
200
|
+
editor = app.instance_variable_get(:@editor)
|
|
201
|
+
dispatcher = app.instance_variable_get(:@dispatcher)
|
|
202
|
+
|
|
203
|
+
dispatcher.dispatch_ex(editor, "lgrep pattern file.txt")
|
|
204
|
+
|
|
205
|
+
assert_match(/Restricted mode/, editor.message)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def test_restricted_mode_disables_ex_git
|
|
209
|
+
app = RuVim::App.new(clean: true, restricted: true)
|
|
210
|
+
editor = app.instance_variable_get(:@editor)
|
|
211
|
+
dispatcher = app.instance_variable_get(:@dispatcher)
|
|
212
|
+
|
|
213
|
+
dispatcher.dispatch_ex(editor, "git status")
|
|
214
|
+
|
|
215
|
+
assert_match(/Restricted mode/, editor.message)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def test_restricted_mode_disables_ex_gh
|
|
219
|
+
app = RuVim::App.new(clean: true, restricted: true)
|
|
220
|
+
editor = app.instance_variable_get(:@editor)
|
|
221
|
+
dispatcher = app.instance_variable_get(:@dispatcher)
|
|
222
|
+
|
|
223
|
+
dispatcher.dispatch_ex(editor, "gh link")
|
|
224
|
+
|
|
225
|
+
assert_match(/Restricted mode/, editor.message)
|
|
226
|
+
end
|
|
227
|
+
|
|
188
228
|
def test_verbose_logs_startup_and_startup_ex_actions
|
|
189
229
|
log = StringIO.new
|
|
190
230
|
app = RuVim::App.new(
|
|
@@ -260,10 +300,11 @@ class AppStartupTest < Minitest::Test
|
|
|
260
300
|
)
|
|
261
301
|
editor = app.instance_variable_get(:@editor)
|
|
262
302
|
|
|
303
|
+
sh = app.instance_variable_get(:@stream_mixer)
|
|
304
|
+
buf = editor.current_buffer
|
|
263
305
|
20.times do
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
break unless thread&.alive?
|
|
306
|
+
sh.drain_events!
|
|
307
|
+
break unless buf.stream.thread&.alive?
|
|
267
308
|
sleep 0.005
|
|
268
309
|
end
|
|
269
310
|
|
|
@@ -272,11 +313,11 @@ class AppStartupTest < Minitest::Test
|
|
|
272
313
|
assert_equal "[stdin]", buf.display_name
|
|
273
314
|
assert_equal true, buf.readonly?
|
|
274
315
|
assert_equal false, buf.modifiable?
|
|
275
|
-
assert_includes [:live, :closed], buf.
|
|
276
|
-
assert_equal ["line1", "line2"
|
|
316
|
+
assert_includes [:live, :closed], buf.stream.state
|
|
317
|
+
assert_equal ["line1", "line2"], buf.lines
|
|
277
318
|
assert_match(/\[stdin\] (follow|EOF)/, editor.message)
|
|
278
319
|
ensure
|
|
279
|
-
app&.
|
|
320
|
+
app&.instance_variable_get(:@stream_mixer)&.shutdown!
|
|
280
321
|
end
|
|
281
322
|
|
|
282
323
|
def test_large_file_threshold_uses_async_loader
|
|
@@ -290,28 +331,28 @@ class AppStartupTest < Minitest::Test
|
|
|
290
331
|
ENV["RUVIM_ASYNC_FILE_THRESHOLD_BYTES"] = "1"
|
|
291
332
|
app = RuVim::App.new(clean: true)
|
|
292
333
|
editor = app.instance_variable_get(:@editor)
|
|
334
|
+
sh = app.instance_variable_get(:@stream_mixer)
|
|
293
335
|
|
|
294
336
|
buf = editor.open_path(f.path)
|
|
295
337
|
assert_match(/loading/i, editor.message)
|
|
296
|
-
assert_includes [:live, :closed], buf.
|
|
338
|
+
assert_includes [:live, :closed], buf.stream&.state
|
|
297
339
|
assert_equal false, buf.modifiable?
|
|
298
340
|
|
|
299
341
|
100.times do
|
|
300
|
-
|
|
301
|
-
break if buf.
|
|
302
|
-
|
|
303
|
-
break unless state && state[:thread]&.alive?
|
|
342
|
+
sh.drain_events!
|
|
343
|
+
break if buf.stream&.state != :live
|
|
344
|
+
break unless buf.stream.is_a?(RuVim::Stream::FileLoad) && buf.stream.thread&.alive?
|
|
304
345
|
sleep 0.005
|
|
305
346
|
end
|
|
306
|
-
|
|
347
|
+
sh.drain_events!
|
|
307
348
|
|
|
308
|
-
assert_equal :closed, buf.
|
|
349
|
+
assert_equal :closed, buf.stream&.state
|
|
309
350
|
assert_equal true, buf.modifiable?
|
|
310
351
|
assert_equal %w[a b c], buf.lines
|
|
311
352
|
assert_match(/#{Regexp.escape(f.path)}/, editor.message)
|
|
312
353
|
ensure
|
|
313
354
|
ENV["RUVIM_ASYNC_FILE_THRESHOLD_BYTES"] = prev
|
|
314
|
-
app&.
|
|
355
|
+
app&.instance_variable_get(:@stream_mixer)&.shutdown!
|
|
315
356
|
end
|
|
316
357
|
end
|
|
317
358
|
end
|
|
@@ -329,25 +370,26 @@ class AppStartupTest < Minitest::Test
|
|
|
329
370
|
ENV["RUVIM_ASYNC_FILE_PREFIX_BYTES"] = "4"
|
|
330
371
|
app = RuVim::App.new(clean: true)
|
|
331
372
|
editor = app.instance_variable_get(:@editor)
|
|
373
|
+
sh = app.instance_variable_get(:@stream_mixer)
|
|
332
374
|
|
|
333
375
|
buf = editor.open_path(f.path)
|
|
334
|
-
assert_equal :live, buf.
|
|
335
|
-
assert_equal ["ab", "
|
|
376
|
+
assert_equal :live, buf.stream&.state
|
|
377
|
+
assert_equal ["ab", ""], buf.lines
|
|
336
378
|
assert_match(/showing first/i, editor.message)
|
|
337
379
|
|
|
338
380
|
100.times do
|
|
339
|
-
|
|
340
|
-
break if buf.
|
|
381
|
+
sh.drain_events!
|
|
382
|
+
break if buf.stream&.state != :live
|
|
341
383
|
sleep 0.005
|
|
342
384
|
end
|
|
343
|
-
|
|
385
|
+
sh.drain_events!
|
|
344
386
|
|
|
345
|
-
assert_equal :closed, buf.
|
|
387
|
+
assert_equal :closed, buf.stream&.state
|
|
346
388
|
assert_equal %w[ab cd ef], buf.lines
|
|
347
389
|
ensure
|
|
348
390
|
ENV["RUVIM_ASYNC_FILE_THRESHOLD_BYTES"] = prev_async
|
|
349
391
|
ENV["RUVIM_ASYNC_FILE_PREFIX_BYTES"] = prev_prefix
|
|
350
|
-
app&.
|
|
392
|
+
app&.instance_variable_get(:@stream_mixer)&.shutdown!
|
|
351
393
|
end
|
|
352
394
|
end
|
|
353
395
|
end
|
|
@@ -360,16 +402,17 @@ class AppStartupTest < Minitest::Test
|
|
|
360
402
|
ENV["HOME"] = dir
|
|
361
403
|
|
|
362
404
|
app1 = RuVim::App.new(clean: true)
|
|
363
|
-
app1.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
405
|
+
completion1 = app1.instance_variable_get(:@completion)
|
|
406
|
+
completion1.push_history(":", "set number")
|
|
407
|
+
completion1.push_history("/", "foo")
|
|
408
|
+
completion1.push_history("?", "bar")
|
|
409
|
+
completion1.save_history!
|
|
367
410
|
|
|
368
411
|
path = File.join(dir, "ruvim", "history.json")
|
|
369
412
|
assert_equal true, File.file?(path)
|
|
370
413
|
|
|
371
414
|
app2 = RuVim::App.new(clean: true)
|
|
372
|
-
hist = app2.instance_variable_get(:@cmdline_history)
|
|
415
|
+
hist = app2.instance_variable_get(:@completion).instance_variable_get(:@cmdline_history)
|
|
373
416
|
assert_equal ["set number"], hist[":"]
|
|
374
417
|
assert_equal ["foo"], hist["/"]
|
|
375
418
|
assert_equal ["bar"], hist["?"]
|
|
@@ -387,7 +430,7 @@ class AppStartupTest < Minitest::Test
|
|
|
387
430
|
ENV["HOME"] = dir
|
|
388
431
|
|
|
389
432
|
app = RuVim::App.new(clean: true)
|
|
390
|
-
assert_equal File.join(dir, ".ruvim", "history.json"), app.send(:
|
|
433
|
+
assert_equal File.join(dir, ".ruvim", "history.json"), app.instance_variable_get(:@completion).send(:history_file_path)
|
|
391
434
|
ensure
|
|
392
435
|
ENV["XDG_STATE_HOME"] = prev_xdg
|
|
393
436
|
ENV["HOME"] = prev_home
|
|
@@ -4,13 +4,14 @@ class AppTextObjectTest < Minitest::Test
|
|
|
4
4
|
def setup
|
|
5
5
|
@app = RuVim::App.new(clean: true)
|
|
6
6
|
@editor = @app.instance_variable_get(:@editor)
|
|
7
|
+
@key_handler = @app.instance_variable_get(:@key_handler)
|
|
7
8
|
@editor.materialize_intro_buffer!
|
|
8
9
|
@buffer = @editor.current_buffer
|
|
9
10
|
@win = @editor.current_window
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def press(*keys)
|
|
13
|
-
keys.each { |k| @
|
|
14
|
+
keys.each { |k| @key_handler.send(:handle_normal_key, k) }
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def test_delete_inside_square_brackets
|
|
@@ -4,17 +4,18 @@ class AppUnicodeBehaviorTest < Minitest::Test
|
|
|
4
4
|
def setup
|
|
5
5
|
@app = RuVim::App.new(clean: true)
|
|
6
6
|
@editor = @app.instance_variable_get(:@editor)
|
|
7
|
+
@key_handler = @app.instance_variable_get(:@key_handler)
|
|
7
8
|
@editor.materialize_intro_buffer!
|
|
8
9
|
@buffer = @editor.current_buffer
|
|
9
10
|
@win = @editor.current_window
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def press_normal(*keys)
|
|
13
|
-
keys.each { |k| @
|
|
14
|
+
keys.each { |k| @key_handler.send(:handle_normal_key, k) }
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def press(*keys)
|
|
17
|
-
keys.each { |k| @
|
|
18
|
+
keys.each { |k| @key_handler.handle(k) }
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def test_word_motions_on_japanese_text_do_not_break_character_boundaries
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class BrowserTest < Minitest::Test
|
|
6
|
+
def test_wsl_mount_point_default
|
|
7
|
+
assert_equal "/mnt/", RuVim::Browser.wsl_mount_point(config: nil)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_wsl_mount_point_from_config
|
|
11
|
+
config = <<~CONF
|
|
12
|
+
[automount]
|
|
13
|
+
root = /win/
|
|
14
|
+
CONF
|
|
15
|
+
assert_equal "/win/", RuVim::Browser.wsl_mount_point(config: config)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_wsl_mount_point_with_missing_trailing_slash
|
|
19
|
+
config = <<~CONF
|
|
20
|
+
[automount]
|
|
21
|
+
root = /win
|
|
22
|
+
CONF
|
|
23
|
+
assert_equal "/win/", RuVim::Browser.wsl_mount_point(config: config)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_wsl_mount_point_ignores_commented_line
|
|
27
|
+
config = <<~CONF
|
|
28
|
+
[automount]
|
|
29
|
+
# root = /old/
|
|
30
|
+
root = /new/
|
|
31
|
+
CONF
|
|
32
|
+
assert_equal "/new/", RuVim::Browser.wsl_mount_point(config: config)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_detect_backend_returns_a_hash_or_nil
|
|
36
|
+
result = RuVim::Browser.detect_backend
|
|
37
|
+
if result
|
|
38
|
+
assert_kind_of Symbol, result[:type]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_powershell_path_uses_mount_point
|
|
43
|
+
path = RuVim::Browser.powershell_path("/mnt/")
|
|
44
|
+
assert_equal "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe", path
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# --- URL validation ---
|
|
48
|
+
|
|
49
|
+
def test_valid_url_https
|
|
50
|
+
assert RuVim::Browser.valid_url?("https://github.com/user/repo")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_valid_url_http
|
|
54
|
+
assert RuVim::Browser.valid_url?("http://example.com")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_invalid_url_file
|
|
58
|
+
refute RuVim::Browser.valid_url?("file:///etc/passwd")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_invalid_url_javascript
|
|
62
|
+
refute RuVim::Browser.valid_url?("javascript:alert(1)")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_invalid_url_empty
|
|
66
|
+
refute RuVim::Browser.valid_url?("")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_invalid_url_nil
|
|
70
|
+
refute RuVim::Browser.valid_url?(nil)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# --- PowerShell encoded command ---
|
|
74
|
+
|
|
75
|
+
def test_powershell_encoded_command_uses_encoded_flag
|
|
76
|
+
cmd = RuVim::Browser.powershell_encoded_command("/ps.exe", "https://github.com/user/repo")
|
|
77
|
+
assert_equal "/ps.exe", cmd[0]
|
|
78
|
+
assert_includes cmd, "-EncodedCommand"
|
|
79
|
+
refute_includes cmd, "-Command"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_powershell_encoded_command_escapes_single_quotes
|
|
83
|
+
cmd = RuVim::Browser.powershell_encoded_command("/ps.exe", "https://example.com/it's")
|
|
84
|
+
encoded = cmd.last
|
|
85
|
+
decoded = encoded.unpack1("m0").force_encoding("UTF-16LE").encode("UTF-8")
|
|
86
|
+
assert_includes decoded, "it''s"
|
|
87
|
+
end
|
|
88
|
+
end
|
data/test/buffer_test.rb
CHANGED
|
@@ -41,6 +41,30 @@ class BufferTest < Minitest::Test
|
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
def test_from_file_rejects_non_regular_file
|
|
45
|
+
Dir.mktmpdir do |dir|
|
|
46
|
+
fifo_path = File.join(dir, "test_fifo")
|
|
47
|
+
system("mkfifo", fifo_path)
|
|
48
|
+
assert File.exist?(fifo_path), "FIFO should exist"
|
|
49
|
+
|
|
50
|
+
err = assert_raises(RuVim::CommandError) do
|
|
51
|
+
RuVim::Buffer.from_file(id: 1, path: fifo_path)
|
|
52
|
+
end
|
|
53
|
+
assert_match(/Not a regular file/, err.message)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_reload_rejects_non_regular_file
|
|
58
|
+
b = RuVim::Buffer.new(id: 1, path: "/dev/null")
|
|
59
|
+
# /dev/null exists but is not a regular file on Linux
|
|
60
|
+
if !File.file?("/dev/null") && File.exist?("/dev/null")
|
|
61
|
+
err = assert_raises(RuVim::CommandError) do
|
|
62
|
+
b.reload_from_file!("/dev/null")
|
|
63
|
+
end
|
|
64
|
+
assert_match(/Not a regular file/, err.message)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
44
68
|
def test_utf8_file_is_loaded_as_utf8_text_not_binary_bytes
|
|
45
69
|
Dir.mktmpdir do |dir|
|
|
46
70
|
path = File.join(dir, "ruvim_utf8_test.txt")
|