ruvim 0.1.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 +7 -0
- data/.github/workflows/test.yml +15 -0
- data/README.md +135 -0
- data/Rakefile +36 -0
- data/docs/binding.md +125 -0
- data/docs/command.md +306 -0
- data/docs/config.md +155 -0
- data/docs/done.md +112 -0
- data/docs/plugin.md +559 -0
- data/docs/spec.md +655 -0
- data/docs/todo.md +63 -0
- data/docs/tutorial.md +490 -0
- data/docs/vim_diff.md +179 -0
- data/exe/ruvim +6 -0
- data/lib/ruvim/app.rb +1600 -0
- data/lib/ruvim/buffer.rb +421 -0
- data/lib/ruvim/cli.rb +264 -0
- data/lib/ruvim/clipboard.rb +73 -0
- data/lib/ruvim/command_invocation.rb +14 -0
- data/lib/ruvim/command_line.rb +63 -0
- data/lib/ruvim/command_registry.rb +38 -0
- data/lib/ruvim/config_dsl.rb +134 -0
- data/lib/ruvim/config_loader.rb +68 -0
- data/lib/ruvim/context.rb +26 -0
- data/lib/ruvim/dispatcher.rb +120 -0
- data/lib/ruvim/display_width.rb +110 -0
- data/lib/ruvim/editor.rb +1025 -0
- data/lib/ruvim/ex_command_registry.rb +80 -0
- data/lib/ruvim/global_commands.rb +1889 -0
- data/lib/ruvim/highlighter.rb +52 -0
- data/lib/ruvim/input.rb +66 -0
- data/lib/ruvim/keymap_manager.rb +96 -0
- data/lib/ruvim/screen.rb +452 -0
- data/lib/ruvim/terminal.rb +30 -0
- data/lib/ruvim/text_metrics.rb +96 -0
- data/lib/ruvim/version.rb +5 -0
- data/lib/ruvim/window.rb +71 -0
- data/lib/ruvim.rb +30 -0
- data/sig/ruvim.rbs +4 -0
- data/test/app_completion_test.rb +39 -0
- data/test/app_dot_repeat_test.rb +54 -0
- data/test/app_motion_test.rb +73 -0
- data/test/app_register_test.rb +47 -0
- data/test/app_scenario_test.rb +77 -0
- data/test/app_startup_test.rb +199 -0
- data/test/app_text_object_test.rb +54 -0
- data/test/app_unicode_behavior_test.rb +66 -0
- data/test/buffer_test.rb +72 -0
- data/test/cli_test.rb +165 -0
- data/test/config_dsl_test.rb +78 -0
- data/test/dispatcher_test.rb +124 -0
- data/test/editor_mark_test.rb +69 -0
- data/test/editor_register_test.rb +64 -0
- data/test/fixtures/render_basic_snapshot.txt +8 -0
- data/test/fixtures/render_basic_snapshot_nonumber.txt +8 -0
- data/test/fixtures/render_unicode_scrolled_snapshot.txt +7 -0
- data/test/highlighter_test.rb +16 -0
- data/test/input_screen_integration_test.rb +69 -0
- data/test/keymap_manager_test.rb +48 -0
- data/test/render_snapshot_test.rb +70 -0
- data/test/screen_test.rb +123 -0
- data/test/search_option_test.rb +39 -0
- data/test/test_helper.rb +15 -0
- data/test/text_metrics_test.rb +42 -0
- data/test/window_test.rb +21 -0
- metadata +106 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
require "tempfile"
|
|
3
|
+
require "stringio"
|
|
4
|
+
|
|
5
|
+
class AppStartupTest < Minitest::Test
|
|
6
|
+
def test_startup_ex_command_runs_after_boot
|
|
7
|
+
app = RuVim::App.new(clean: true, startup_actions: [{ type: :ex, value: "set number" }])
|
|
8
|
+
editor = app.instance_variable_get(:@editor)
|
|
9
|
+
|
|
10
|
+
assert_equal true, editor.effective_option("number")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_startup_line_moves_cursor
|
|
14
|
+
Tempfile.create(["ruvim-startup", ".txt"]) do |f|
|
|
15
|
+
f.write("a\nb\nc\nd\n")
|
|
16
|
+
f.flush
|
|
17
|
+
|
|
18
|
+
app = RuVim::App.new(path: f.path, clean: true, startup_actions: [{ type: :line, value: 3 }])
|
|
19
|
+
editor = app.instance_variable_get(:@editor)
|
|
20
|
+
|
|
21
|
+
assert_equal 2, editor.current_window.cursor_y
|
|
22
|
+
assert_equal "c", editor.current_buffer.line_at(editor.current_window.cursor_y)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_startup_line_end_moves_to_last_line
|
|
27
|
+
Tempfile.create(["ruvim-startup", ".txt"]) do |f|
|
|
28
|
+
f.write("a\nb\nc\n")
|
|
29
|
+
f.flush
|
|
30
|
+
|
|
31
|
+
app = RuVim::App.new(path: f.path, clean: true, startup_actions: [{ type: :line_end }])
|
|
32
|
+
editor = app.instance_variable_get(:@editor)
|
|
33
|
+
|
|
34
|
+
assert_equal 2, editor.current_window.cursor_y
|
|
35
|
+
assert_equal "c", editor.current_buffer.line_at(editor.current_window.cursor_y)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_startup_readonly_marks_opened_buffer_readonly
|
|
40
|
+
Tempfile.create(["ruvim-startup", ".txt"]) do |f|
|
|
41
|
+
f.write("hello\n")
|
|
42
|
+
f.flush
|
|
43
|
+
|
|
44
|
+
app = RuVim::App.new(path: f.path, clean: true, readonly: true)
|
|
45
|
+
editor = app.instance_variable_get(:@editor)
|
|
46
|
+
|
|
47
|
+
assert_equal true, editor.current_buffer.readonly?
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def test_startup_diff_mode_placeholder_emits_message
|
|
52
|
+
app = RuVim::App.new(clean: true, diff_mode: true)
|
|
53
|
+
editor = app.instance_variable_get(:@editor)
|
|
54
|
+
|
|
55
|
+
assert_match(/diff mode .*not implemented/i, editor.message)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_startup_quickfix_placeholder_emits_message
|
|
59
|
+
app = RuVim::App.new(clean: true, quickfix_errorfile: "errors.log")
|
|
60
|
+
editor = app.instance_variable_get(:@editor)
|
|
61
|
+
|
|
62
|
+
assert_match(/quickfix startup .*errors\.log.*not implemented/i, editor.message)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_startup_session_placeholder_emits_message
|
|
66
|
+
app = RuVim::App.new(clean: true, session_file: "Session.ruvim")
|
|
67
|
+
editor = app.instance_variable_get(:@editor)
|
|
68
|
+
|
|
69
|
+
assert_match(/session startup .*Session\.ruvim.*not implemented/i, editor.message)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_startup_nomodifiable_marks_opened_buffer_unmodifiable_and_readonly
|
|
73
|
+
Tempfile.create(["ruvim-startup", ".txt"]) do |f|
|
|
74
|
+
f.write("hello\n")
|
|
75
|
+
f.flush
|
|
76
|
+
|
|
77
|
+
app = RuVim::App.new(path: f.path, clean: true, nomodifiable: true)
|
|
78
|
+
editor = app.instance_variable_get(:@editor)
|
|
79
|
+
|
|
80
|
+
assert_equal false, editor.current_buffer.modifiable?
|
|
81
|
+
assert_equal true, editor.current_buffer.readonly?
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_startup_restricted_mode_sets_editor_flag
|
|
86
|
+
app = RuVim::App.new(clean: true, restricted: true)
|
|
87
|
+
editor = app.instance_variable_get(:@editor)
|
|
88
|
+
|
|
89
|
+
assert_equal true, editor.restricted_mode?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_startup_horizontal_split_opens_multiple_files
|
|
93
|
+
Tempfile.create(["ruvim-a", ".txt"]) do |a|
|
|
94
|
+
Tempfile.create(["ruvim-b", ".txt"]) do |b|
|
|
95
|
+
a.write("a\n"); a.flush
|
|
96
|
+
b.write("b\n"); b.flush
|
|
97
|
+
|
|
98
|
+
app = RuVim::App.new(paths: [a.path, b.path], clean: true, startup_open_layout: :horizontal)
|
|
99
|
+
editor = app.instance_variable_get(:@editor)
|
|
100
|
+
|
|
101
|
+
assert_equal :horizontal, editor.window_layout
|
|
102
|
+
assert_equal 2, editor.window_order.length
|
|
103
|
+
names = editor.window_order.map { |id| editor.buffers[editor.windows[id].buffer_id].path }
|
|
104
|
+
assert_includes names, a.path
|
|
105
|
+
assert_includes names, b.path
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_startup_tab_layout_opens_multiple_tabs
|
|
111
|
+
Tempfile.create(["ruvim-a", ".txt"]) do |a|
|
|
112
|
+
Tempfile.create(["ruvim-b", ".txt"]) do |b|
|
|
113
|
+
a.write("a\n"); a.flush
|
|
114
|
+
b.write("b\n"); b.flush
|
|
115
|
+
|
|
116
|
+
app = RuVim::App.new(paths: [a.path, b.path], clean: true, startup_open_layout: :tab)
|
|
117
|
+
editor = app.instance_variable_get(:@editor)
|
|
118
|
+
|
|
119
|
+
assert_equal 2, editor.tabpage_count
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def test_restricted_mode_disables_ex_ruby
|
|
125
|
+
app = RuVim::App.new(clean: true, restricted: true)
|
|
126
|
+
editor = app.instance_variable_get(:@editor)
|
|
127
|
+
dispatcher = app.instance_variable_get(:@dispatcher)
|
|
128
|
+
|
|
129
|
+
dispatcher.dispatch_ex(editor, "ruby 1+1")
|
|
130
|
+
|
|
131
|
+
assert_match(/Restricted mode/, editor.message)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def test_verbose_logs_startup_and_startup_ex_actions
|
|
135
|
+
log = StringIO.new
|
|
136
|
+
app = RuVim::App.new(
|
|
137
|
+
clean: true,
|
|
138
|
+
verbose_level: 2,
|
|
139
|
+
verbose_io: log,
|
|
140
|
+
startup_actions: [{ type: :ex, value: "set number" }]
|
|
141
|
+
)
|
|
142
|
+
editor = app.instance_variable_get(:@editor)
|
|
143
|
+
assert_equal true, editor.effective_option("number")
|
|
144
|
+
|
|
145
|
+
text = log.string
|
|
146
|
+
assert_match(/startup: load_user_config/, text)
|
|
147
|
+
assert_match(/startup: run_startup_actions/, text)
|
|
148
|
+
assert_match(/startup ex: set number/, text)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def test_pre_config_actions_run_before_user_config_and_startup_actions
|
|
152
|
+
Tempfile.create(["ruvim-config", ".rb"]) do |cfg|
|
|
153
|
+
cfg.write("::ENV['RUVIM_PRECONFIG_ORDER_TEST'] = 'config_loaded'\n")
|
|
154
|
+
cfg.flush
|
|
155
|
+
|
|
156
|
+
begin
|
|
157
|
+
ENV.delete("RUVIM_PRECONFIG_ORDER_TEST")
|
|
158
|
+
log = StringIO.new
|
|
159
|
+
app = RuVim::App.new(
|
|
160
|
+
config_path: cfg.path,
|
|
161
|
+
pre_config_actions: [{ type: :ex, value: "set number" }],
|
|
162
|
+
startup_actions: [{ type: :ex, value: "set relativenumber" }],
|
|
163
|
+
verbose_level: 2,
|
|
164
|
+
verbose_io: log
|
|
165
|
+
)
|
|
166
|
+
editor = app.instance_variable_get(:@editor)
|
|
167
|
+
|
|
168
|
+
assert_equal true, editor.effective_option("number")
|
|
169
|
+
assert_equal true, editor.effective_option("relativenumber")
|
|
170
|
+
assert_equal "config_loaded", ENV["RUVIM_PRECONFIG_ORDER_TEST"]
|
|
171
|
+
|
|
172
|
+
text = log.string
|
|
173
|
+
assert_match(/startup: run_pre_config_actions count=1/, text)
|
|
174
|
+
assert_match(/pre-config ex: set number/, text)
|
|
175
|
+
assert_match(/startup: load_user_config/, text)
|
|
176
|
+
assert_match(/startup ex: set relativenumber/, text)
|
|
177
|
+
assert_operator text.index("startup: run_pre_config_actions"), :<, text.index("startup: load_user_config")
|
|
178
|
+
assert_operator text.index("startup: load_user_config"), :<, text.index("startup: run_startup_actions")
|
|
179
|
+
ensure
|
|
180
|
+
ENV.delete("RUVIM_PRECONFIG_ORDER_TEST")
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def test_startuptime_writes_startup_phase_log
|
|
186
|
+
Tempfile.create(["ruvim-startuptime", ".log"]) do |f|
|
|
187
|
+
path = f.path
|
|
188
|
+
f.close
|
|
189
|
+
|
|
190
|
+
RuVim::App.new(clean: true, startup_time_path: path)
|
|
191
|
+
|
|
192
|
+
text = File.read(path)
|
|
193
|
+
assert_match(/init\.start/, text)
|
|
194
|
+
assert_match(/pre_config_actions\.done/, text)
|
|
195
|
+
assert_match(/buffers\.opened/, text)
|
|
196
|
+
assert_match(/startup_actions\.done/, text)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class AppTextObjectTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@app = RuVim::App.new(clean: true)
|
|
6
|
+
@editor = @app.instance_variable_get(:@editor)
|
|
7
|
+
@editor.materialize_intro_buffer!
|
|
8
|
+
@buffer = @editor.current_buffer
|
|
9
|
+
@win = @editor.current_window
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def press(*keys)
|
|
13
|
+
keys.each { |k| @app.send(:handle_normal_key, k) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_delete_inside_square_brackets
|
|
17
|
+
@buffer.replace_all_lines!(["x[abc]y"])
|
|
18
|
+
@win.cursor_x = 2
|
|
19
|
+
|
|
20
|
+
press("d", "i", "]")
|
|
21
|
+
|
|
22
|
+
assert_equal "x[]y", @buffer.line_at(0)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_delete_inside_backticks
|
|
26
|
+
@buffer.replace_all_lines!(["a`bc`d"])
|
|
27
|
+
@win.cursor_x = 2
|
|
28
|
+
|
|
29
|
+
press("d", "i", "`")
|
|
30
|
+
|
|
31
|
+
assert_equal "a``d", @buffer.line_at(0)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_yank_inner_paragraph
|
|
35
|
+
@buffer.replace_all_lines!(["foo", "bar", "", "baz"])
|
|
36
|
+
@win.cursor_y = 1
|
|
37
|
+
@win.cursor_x = 1
|
|
38
|
+
|
|
39
|
+
press("y", "i", "p")
|
|
40
|
+
|
|
41
|
+
assert_equal({ text: "foo\nbar", type: :charwise }, @editor.get_register("\""))
|
|
42
|
+
assert_equal({ text: "foo\nbar", type: :charwise }, @editor.get_register("0"))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_delete_around_paragraph_includes_separator_blank_line
|
|
46
|
+
@buffer.replace_all_lines!(["foo", "bar", "", "baz"])
|
|
47
|
+
@win.cursor_y = 0
|
|
48
|
+
@win.cursor_x = 0
|
|
49
|
+
|
|
50
|
+
press("d", "a", "p")
|
|
51
|
+
|
|
52
|
+
assert_equal ["baz"], @buffer.lines
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class AppUnicodeBehaviorTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@app = RuVim::App.new(clean: true)
|
|
6
|
+
@editor = @app.instance_variable_get(:@editor)
|
|
7
|
+
@editor.materialize_intro_buffer!
|
|
8
|
+
@buffer = @editor.current_buffer
|
|
9
|
+
@win = @editor.current_window
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def press_normal(*keys)
|
|
13
|
+
keys.each { |k| @app.send(:handle_normal_key, k) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def press(*keys)
|
|
17
|
+
keys.each { |k| @app.send(:handle_key, k) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_word_motions_on_japanese_text_do_not_break_character_boundaries
|
|
21
|
+
@buffer.replace_all_lines!(["foo 日本語 編集"])
|
|
22
|
+
@win.cursor_y = 0
|
|
23
|
+
@win.cursor_x = 0
|
|
24
|
+
|
|
25
|
+
press_normal("w")
|
|
26
|
+
assert_equal "日", @buffer.line_at(0)[@win.cursor_x]
|
|
27
|
+
|
|
28
|
+
press_normal("e")
|
|
29
|
+
assert_equal "語", @buffer.line_at(0)[@win.cursor_x]
|
|
30
|
+
|
|
31
|
+
press_normal("w")
|
|
32
|
+
assert_equal "編", @buffer.line_at(0)[@win.cursor_x]
|
|
33
|
+
|
|
34
|
+
press_normal("b")
|
|
35
|
+
assert_equal "日", @buffer.line_at(0)[@win.cursor_x]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_paste_charwise_japanese_text_keeps_valid_cursor_position
|
|
39
|
+
@buffer.replace_all_lines!(["abc"])
|
|
40
|
+
@editor.set_register("\"", text: "日本", type: :charwise)
|
|
41
|
+
@win.cursor_y = 0
|
|
42
|
+
@win.cursor_x = 0
|
|
43
|
+
|
|
44
|
+
press_normal("p")
|
|
45
|
+
assert_equal "a日本bc", @buffer.line_at(0)
|
|
46
|
+
assert_equal true, @buffer.line_at(0).valid_encoding?
|
|
47
|
+
assert_equal "本", @buffer.line_at(0)[@win.cursor_x]
|
|
48
|
+
|
|
49
|
+
press_normal("P")
|
|
50
|
+
assert_equal "a日日本本bc", @buffer.line_at(0)
|
|
51
|
+
assert_equal true, @buffer.line_at(0).valid_encoding?
|
|
52
|
+
assert_equal "本", @buffer.line_at(0)[@win.cursor_x]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_visual_yank_on_japanese_text_is_inclusive_and_valid_utf8
|
|
56
|
+
@buffer.replace_all_lines!(["A日本語B"])
|
|
57
|
+
@win.cursor_x = 1 # 日
|
|
58
|
+
|
|
59
|
+
press("v", "l", "y")
|
|
60
|
+
|
|
61
|
+
reg = @editor.get_register("\"")
|
|
62
|
+
assert_equal({ text: "日本", type: :charwise }, reg)
|
|
63
|
+
assert_equal true, reg[:text].valid_encoding?
|
|
64
|
+
assert_equal :normal, @editor.mode
|
|
65
|
+
end
|
|
66
|
+
end
|
data/test/buffer_test.rb
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class BufferTest < Minitest::Test
|
|
4
|
+
def test_insert_and_undo_redo_group
|
|
5
|
+
b = RuVim::Buffer.new(id: 1)
|
|
6
|
+
b.begin_change_group
|
|
7
|
+
b.insert_char(0, 0, "a")
|
|
8
|
+
b.insert_char(0, 1, "b")
|
|
9
|
+
b.end_change_group
|
|
10
|
+
|
|
11
|
+
assert_equal ["ab"], b.lines
|
|
12
|
+
assert b.undo!
|
|
13
|
+
assert_equal [""], b.lines
|
|
14
|
+
assert b.redo!
|
|
15
|
+
assert_equal ["ab"], b.lines
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_delete_span_across_lines
|
|
19
|
+
b = RuVim::Buffer.new(id: 1, lines: ["abc", "def", "ghi"])
|
|
20
|
+
deleted = b.span_text(0, 1, 2, 1)
|
|
21
|
+
assert_equal "bc\ndef\ng", deleted
|
|
22
|
+
|
|
23
|
+
b.delete_span(0, 1, 2, 1)
|
|
24
|
+
assert_equal ["ahi"], b.lines
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_reload_from_file_clears_modified_and_history
|
|
28
|
+
path = "/tmp/ruvim_buffer_reload_test.txt"
|
|
29
|
+
File.write(path, "one\n")
|
|
30
|
+
b = RuVim::Buffer.from_file(id: 1, path: path)
|
|
31
|
+
b.insert_char(0, 0, "X")
|
|
32
|
+
assert b.modified?
|
|
33
|
+
assert b.can_undo?
|
|
34
|
+
|
|
35
|
+
b.reload_from_file!
|
|
36
|
+
assert_equal ["one"], b.lines
|
|
37
|
+
refute b.modified?
|
|
38
|
+
refute b.can_undo?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_utf8_file_is_loaded_as_utf8_text_not_binary_bytes
|
|
42
|
+
path = "/tmp/ruvim_utf8_test.txt"
|
|
43
|
+
File.binwrite(path, "bar 日本語 編集\n".encode("UTF-8"))
|
|
44
|
+
|
|
45
|
+
b = RuVim::Buffer.from_file(id: 1, path: path)
|
|
46
|
+
assert_equal Encoding::UTF_8, b.line_at(0).encoding
|
|
47
|
+
assert_equal "bar 日本語 編集", b.line_at(0)
|
|
48
|
+
assert_equal 10, b.line_length(0)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def test_invalid_bytes_are_decoded_to_valid_utf8_with_replacement
|
|
52
|
+
path = "/tmp/ruvim_invalid_bytes_test.txt"
|
|
53
|
+
File.binwrite(path, "A\xFFB\n".b)
|
|
54
|
+
|
|
55
|
+
b = RuVim::Buffer.from_file(id: 1, path: path)
|
|
56
|
+
line = b.line_at(0)
|
|
57
|
+
assert_equal Encoding::UTF_8, line.encoding
|
|
58
|
+
assert_equal true, line.valid_encoding?
|
|
59
|
+
assert_match(/A.*B/, line)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_write_to_saves_utf8_text
|
|
63
|
+
path = "/tmp/ruvim_write_utf8_test.txt"
|
|
64
|
+
b = RuVim::Buffer.new(id: 1, lines: ["日本語", "abc"])
|
|
65
|
+
b.write_to(path)
|
|
66
|
+
|
|
67
|
+
data = File.binread(path)
|
|
68
|
+
text = data.force_encoding(Encoding::UTF_8)
|
|
69
|
+
assert_equal true, text.valid_encoding?
|
|
70
|
+
assert_equal "日本語\nabc", text
|
|
71
|
+
end
|
|
72
|
+
end
|
data/test/cli_test.rb
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
require "stringio"
|
|
3
|
+
require "tempfile"
|
|
4
|
+
|
|
5
|
+
class CLITest < Minitest::Test
|
|
6
|
+
def test_parse_clean_and_u_none
|
|
7
|
+
opts = RuVim::CLI.parse(["--clean", "-u", "NONE"])
|
|
8
|
+
|
|
9
|
+
assert_equal true, opts.clean
|
|
10
|
+
assert_equal true, opts.skip_user_config
|
|
11
|
+
assert_nil opts.config_path
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_parse_startup_actions_preserves_order
|
|
15
|
+
opts = RuVim::CLI.parse(["+10", "-c", "set number", "+", "file.txt"])
|
|
16
|
+
|
|
17
|
+
assert_equal ["file.txt"], opts.files
|
|
18
|
+
assert_equal [
|
|
19
|
+
{ type: :line, value: 10 },
|
|
20
|
+
{ type: :ex, value: "set number" },
|
|
21
|
+
{ type: :line_end }
|
|
22
|
+
], opts.startup_actions
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_parse_pre_config_cmd_option
|
|
26
|
+
opts = RuVim::CLI.parse(["--cmd", "set number", "--cmd=set relativenumber", "file.txt"])
|
|
27
|
+
|
|
28
|
+
assert_equal ["file.txt"], opts.files
|
|
29
|
+
assert_equal [
|
|
30
|
+
{ type: :ex, value: "set number" },
|
|
31
|
+
{ type: :ex, value: "set relativenumber" }
|
|
32
|
+
], opts.pre_config_actions
|
|
33
|
+
assert_equal [], opts.startup_actions
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_parse_custom_config_path
|
|
37
|
+
opts = RuVim::CLI.parse(["-u", "/tmp/ruvimrc.rb"])
|
|
38
|
+
|
|
39
|
+
assert_equal false, opts.skip_user_config
|
|
40
|
+
assert_equal "/tmp/ruvimrc.rb", opts.config_path
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_parse_readonly_option
|
|
44
|
+
opts = RuVim::CLI.parse(["-R", "file.txt"])
|
|
45
|
+
|
|
46
|
+
assert_equal true, opts.readonly
|
|
47
|
+
assert_equal ["file.txt"], opts.files
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_parse_diff_mode_option
|
|
51
|
+
opts = RuVim::CLI.parse(["-d", "file.txt"])
|
|
52
|
+
|
|
53
|
+
assert_equal true, opts.diff_mode
|
|
54
|
+
assert_equal ["file.txt"], opts.files
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_parse_quickfix_option
|
|
58
|
+
opts = RuVim::CLI.parse(["-q", "errors.log", "file.txt"])
|
|
59
|
+
|
|
60
|
+
assert_equal "errors.log", opts.quickfix_errorfile
|
|
61
|
+
assert_equal ["file.txt"], opts.files
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_parse_session_option_with_and_without_argument
|
|
65
|
+
opts1 = RuVim::CLI.parse(["-S", "Session.ruvim", "file.txt"])
|
|
66
|
+
assert_equal "Session.ruvim", opts1.session_file
|
|
67
|
+
assert_equal ["file.txt"], opts1.files
|
|
68
|
+
|
|
69
|
+
opts2 = RuVim::CLI.parse(["-S"])
|
|
70
|
+
assert_equal "Session.vim", opts2.session_file
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_parse_nomodifiable_option
|
|
74
|
+
opts = RuVim::CLI.parse(["-M", "file.txt"])
|
|
75
|
+
|
|
76
|
+
assert_equal true, opts.nomodifiable
|
|
77
|
+
assert_equal ["file.txt"], opts.files
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_parse_restricted_option
|
|
81
|
+
opts = RuVim::CLI.parse(["-Z", "file.txt"])
|
|
82
|
+
|
|
83
|
+
assert_equal true, opts.restricted_mode
|
|
84
|
+
assert_equal ["file.txt"], opts.files
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_parse_verbose_options
|
|
88
|
+
v1 = RuVim::CLI.parse(["-V", "file.txt"])
|
|
89
|
+
assert_equal 1, v1.verbose_level
|
|
90
|
+
|
|
91
|
+
v2 = RuVim::CLI.parse(["-V2", "file.txt"])
|
|
92
|
+
assert_equal 2, v2.verbose_level
|
|
93
|
+
|
|
94
|
+
v3 = RuVim::CLI.parse(["--verbose=3", "file.txt"])
|
|
95
|
+
assert_equal 3, v3.verbose_level
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def test_parse_startuptime_option
|
|
99
|
+
opts = RuVim::CLI.parse(["--startuptime", "/tmp/ruvim-startup.log", "file.txt"])
|
|
100
|
+
|
|
101
|
+
assert_equal "/tmp/ruvim-startup.log", opts.startup_time_path
|
|
102
|
+
assert_equal ["file.txt"], opts.files
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_parse_n_option_as_accepted_no_op
|
|
106
|
+
opts = RuVim::CLI.parse(["-n", "file.txt"])
|
|
107
|
+
|
|
108
|
+
assert_equal true, opts.no_swap
|
|
109
|
+
assert_equal ["file.txt"], opts.files
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_parse_split_and_tab_layout_options
|
|
113
|
+
o = RuVim::CLI.parse(["-o", "a", "b"])
|
|
114
|
+
assert_equal :horizontal, o.startup_open_layout
|
|
115
|
+
assert_nil o.startup_open_count
|
|
116
|
+
|
|
117
|
+
ov = RuVim::CLI.parse(["-O3", "a", "b"])
|
|
118
|
+
assert_equal :vertical, ov.startup_open_layout
|
|
119
|
+
assert_equal 3, ov.startup_open_count
|
|
120
|
+
|
|
121
|
+
p = RuVim::CLI.parse(["-p2", "a", "b"])
|
|
122
|
+
assert_equal :tab, p.startup_open_layout
|
|
123
|
+
assert_equal 2, p.startup_open_count
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_help_and_version_return_without_starting_ui
|
|
127
|
+
out = StringIO.new
|
|
128
|
+
err = StringIO.new
|
|
129
|
+
|
|
130
|
+
code = RuVim::CLI.run(["--version"], stdout: out, stderr: err, stdin: StringIO.new)
|
|
131
|
+
assert_equal 0, code
|
|
132
|
+
assert_match(/RuVim /, out.string)
|
|
133
|
+
assert_equal "", err.string
|
|
134
|
+
|
|
135
|
+
out = StringIO.new
|
|
136
|
+
err = StringIO.new
|
|
137
|
+
code = RuVim::CLI.run(["--help"], stdout: out, stderr: err, stdin: StringIO.new)
|
|
138
|
+
assert_equal 0, code
|
|
139
|
+
assert_match(/Usage: ruvim/, out.string)
|
|
140
|
+
assert_match(/-R\s+Open file readonly/, out.string)
|
|
141
|
+
assert_match(/-d\s+Diff mode requested/, out.string)
|
|
142
|
+
assert_match(/-q \{errorfile\}\s+Quickfix startup placeholder/, out.string)
|
|
143
|
+
assert_match(/-S \[session\]\s+Session startup placeholder/, out.string)
|
|
144
|
+
assert_match(/-M\s+Open file unmodifiable/, out.string)
|
|
145
|
+
assert_match(/-Z\s+Restricted mode/, out.string)
|
|
146
|
+
assert_match(/-V\[N\], --verbose/, out.string)
|
|
147
|
+
assert_match(/--startuptime FILE/, out.string)
|
|
148
|
+
assert_match(/--cmd \{cmd\}/, out.string)
|
|
149
|
+
assert_match(/-n\s+No-op/, out.string)
|
|
150
|
+
assert_match(/-o\[N\]/, out.string)
|
|
151
|
+
assert_match(/-O\[N\]/, out.string)
|
|
152
|
+
assert_match(/-p\[N\]/, out.string)
|
|
153
|
+
assert_equal "", err.string
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def test_run_returns_error_for_missing_config_file
|
|
157
|
+
out = StringIO.new
|
|
158
|
+
err = StringIO.new
|
|
159
|
+
|
|
160
|
+
code = RuVim::CLI.run(["-u", "/tmp/ruvim/no-such-config.rb"], stdout: out, stderr: err, stdin: StringIO.new)
|
|
161
|
+
|
|
162
|
+
assert_equal 2, code
|
|
163
|
+
assert_match(/config file not found/, err.string)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class ConfigDSLTest < Minitest::Test
|
|
4
|
+
FakeCommandSpec = Struct.new(:id, :call, :desc, :source, keyword_init: true)
|
|
5
|
+
|
|
6
|
+
class FakeCommandRegistry
|
|
7
|
+
attr_reader :specs
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@specs = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def register(id, call:, desc:, source:)
|
|
14
|
+
@specs[id.to_s] = FakeCommandSpec.new(id: id.to_s, call:, desc:, source:)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def fetch(id)
|
|
18
|
+
@specs.fetch(id.to_s)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class FakeExRegistry
|
|
23
|
+
def registered?(_name)
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def unregister(_name)
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def register(*)
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def setup
|
|
37
|
+
@command_registry = FakeCommandRegistry.new
|
|
38
|
+
@ex_registry = FakeExRegistry.new
|
|
39
|
+
@keymaps = RuVim::KeymapManager.new
|
|
40
|
+
@dsl = RuVim::ConfigDSL.new(
|
|
41
|
+
command_registry: @command_registry,
|
|
42
|
+
ex_registry: @ex_registry,
|
|
43
|
+
keymaps: @keymaps,
|
|
44
|
+
command_host: RuVim::GlobalCommands.instance
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_nmap_block_registers_inline_command_and_binds_key
|
|
49
|
+
@dsl.nmap("K", desc: "Show name") { |ctx, **| ctx.editor.echo(ctx.buffer.display_name) }
|
|
50
|
+
|
|
51
|
+
match = @keymaps.resolve(:normal, ["K"])
|
|
52
|
+
assert_equal :match, match.status
|
|
53
|
+
refute_nil match.invocation
|
|
54
|
+
assert match.invocation.id.start_with?("user.keymap.normal.")
|
|
55
|
+
|
|
56
|
+
spec = @command_registry.fetch(match.invocation.id)
|
|
57
|
+
assert_equal :user, spec.source
|
|
58
|
+
assert_equal "Show name", spec.desc
|
|
59
|
+
assert_respond_to spec.call, :call
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_imap_block_registers_insert_mode_binding
|
|
63
|
+
@dsl.imap("Q", desc: "Insert helper") { |_ctx, **| }
|
|
64
|
+
|
|
65
|
+
match = @keymaps.resolve(:insert, ["Q"])
|
|
66
|
+
assert_equal :match, match.status
|
|
67
|
+
assert match.invocation.id.start_with?("user.keymap.insert.")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_map_global_block_binds_fallback_layer_when_mode_nil
|
|
71
|
+
@dsl.map_global("Z", mode: nil, desc: "Global fallback") { |_ctx, **| }
|
|
72
|
+
|
|
73
|
+
editor = fresh_editor
|
|
74
|
+
match = @keymaps.resolve_with_context(:normal, ["Z"], editor: editor)
|
|
75
|
+
assert_equal :match, match.status
|
|
76
|
+
assert match.invocation.id.start_with?("user.keymap.global.")
|
|
77
|
+
end
|
|
78
|
+
end
|