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
data/lib/ruvim/window.rb
CHANGED
|
@@ -19,17 +19,17 @@ module RuVim
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def cursor_x=(value)
|
|
22
|
-
@cursor_x = value
|
|
22
|
+
@cursor_x = value
|
|
23
23
|
@preferred_x = nil
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def cursor_y=(value)
|
|
27
|
-
@cursor_y = value
|
|
27
|
+
@cursor_y = value
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def clamp_to_buffer(buffer, max_extra_col: 0)
|
|
31
31
|
@cursor_y = [[@cursor_y, 0].max, buffer.line_count - 1].min
|
|
32
|
-
max_col = buffer.line_length(@cursor_y) + [max_extra_col
|
|
32
|
+
max_col = buffer.line_length(@cursor_y) + [max_extra_col, 0].max
|
|
33
33
|
@cursor_x = [[@cursor_x, 0].max, max_col].min
|
|
34
34
|
self
|
|
35
35
|
end
|
|
@@ -71,7 +71,7 @@ module RuVim
|
|
|
71
71
|
|
|
72
72
|
def ensure_visible(buffer, height:, width:, tabstop: 2, scrolloff: 0, sidescrolloff: 0)
|
|
73
73
|
clamp_to_buffer(buffer)
|
|
74
|
-
so = [[scrolloff
|
|
74
|
+
so = [[scrolloff, 0].max, [height - 1, 0].max].min
|
|
75
75
|
|
|
76
76
|
top_target = @cursor_y - so
|
|
77
77
|
bottom_target = @cursor_y + so
|
|
@@ -82,7 +82,7 @@ module RuVim
|
|
|
82
82
|
line = buffer.line_at(@cursor_y)
|
|
83
83
|
cursor_screen_col = RuVim::TextMetrics.screen_col_for_char_index(line, @cursor_x, tabstop:)
|
|
84
84
|
offset_screen_col = RuVim::TextMetrics.screen_col_for_char_index(line, @col_offset, tabstop:)
|
|
85
|
-
sso = [[sidescrolloff
|
|
85
|
+
sso = [[sidescrolloff, 0].max, [width - 1, 0].max].min
|
|
86
86
|
|
|
87
87
|
if cursor_screen_col < offset_screen_col + sso
|
|
88
88
|
target_left = [cursor_screen_col - sso, 0].max
|
data/lib/ruvim.rb
CHANGED
|
@@ -13,12 +13,33 @@ require_relative "ruvim/display_width"
|
|
|
13
13
|
require_relative "ruvim/keyword_chars"
|
|
14
14
|
require_relative "ruvim/text_metrics"
|
|
15
15
|
require_relative "ruvim/clipboard"
|
|
16
|
+
require_relative "ruvim/browser"
|
|
17
|
+
require_relative "ruvim/lang/registry"
|
|
16
18
|
require_relative "ruvim/lang/base"
|
|
17
19
|
require_relative "ruvim/lang/markdown"
|
|
18
20
|
require_relative "ruvim/lang/ruby"
|
|
19
21
|
require_relative "ruvim/lang/json"
|
|
20
22
|
require_relative "ruvim/lang/scheme"
|
|
23
|
+
require_relative "ruvim/lang/c"
|
|
24
|
+
require_relative "ruvim/lang/cpp"
|
|
21
25
|
require_relative "ruvim/lang/diff"
|
|
26
|
+
require_relative "ruvim/lang/yaml"
|
|
27
|
+
require_relative "ruvim/lang/sh"
|
|
28
|
+
require_relative "ruvim/lang/python"
|
|
29
|
+
require_relative "ruvim/lang/javascript"
|
|
30
|
+
require_relative "ruvim/lang/typescript"
|
|
31
|
+
require_relative "ruvim/lang/html"
|
|
32
|
+
require_relative "ruvim/lang/toml"
|
|
33
|
+
require_relative "ruvim/lang/go"
|
|
34
|
+
require_relative "ruvim/lang/rust"
|
|
35
|
+
require_relative "ruvim/lang/makefile"
|
|
36
|
+
require_relative "ruvim/lang/dockerfile"
|
|
37
|
+
require_relative "ruvim/lang/sql"
|
|
38
|
+
require_relative "ruvim/lang/elixir"
|
|
39
|
+
require_relative "ruvim/lang/perl"
|
|
40
|
+
require_relative "ruvim/lang/lua"
|
|
41
|
+
require_relative "ruvim/lang/ocaml"
|
|
42
|
+
require_relative "ruvim/lang/erb"
|
|
22
43
|
require_relative "ruvim/highlighter"
|
|
23
44
|
require_relative "ruvim/context"
|
|
24
45
|
require_relative "ruvim/buffer"
|
|
@@ -32,6 +53,8 @@ require_relative "ruvim/git/diff"
|
|
|
32
53
|
require_relative "ruvim/git/log"
|
|
33
54
|
require_relative "ruvim/git/branch"
|
|
34
55
|
require_relative "ruvim/git/commit"
|
|
56
|
+
require_relative "ruvim/git/grep"
|
|
57
|
+
require_relative "ruvim/gh/link"
|
|
35
58
|
require_relative "ruvim/git/handler"
|
|
36
59
|
require_relative "ruvim/global_commands"
|
|
37
60
|
require_relative "ruvim/dispatcher"
|
|
@@ -40,12 +63,6 @@ require_relative "ruvim/command_line"
|
|
|
40
63
|
require_relative "ruvim/input"
|
|
41
64
|
require_relative "ruvim/terminal"
|
|
42
65
|
require_relative "ruvim/rich_view"
|
|
43
|
-
|
|
44
|
-
# Register renderers after RichView is defined
|
|
45
|
-
RuVim::RichView.register("markdown", RuVim::RichView::MarkdownRenderer)
|
|
46
|
-
RuVim::RichView.register("json", RuVim::RichView::JsonRenderer)
|
|
47
|
-
RuVim::RichView.register("jsonl", RuVim::RichView::JsonlRenderer)
|
|
48
|
-
|
|
49
66
|
require_relative "ruvim/lang/tsv"
|
|
50
67
|
require_relative "ruvim/lang/csv"
|
|
51
68
|
require_relative "ruvim/screen"
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class AppCommandTest < 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
|
+
# --- cursor commands ---
|
|
26
|
+
|
|
27
|
+
def test_cursor_line_end
|
|
28
|
+
buf.replace_all_lines!(["hello world"])
|
|
29
|
+
win.cursor_x = 0
|
|
30
|
+
feed("$")
|
|
31
|
+
assert_equal buf.line_length(0), win.cursor_x
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_cursor_buffer_start_gg
|
|
35
|
+
buf.replace_all_lines!(["line1", "line2", "line3"])
|
|
36
|
+
win.cursor_y = 2
|
|
37
|
+
feed("g", "g")
|
|
38
|
+
assert_equal 0, win.cursor_y
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# --- insert mode variants ---
|
|
42
|
+
|
|
43
|
+
def test_append_mode_a
|
|
44
|
+
buf.replace_all_lines!(["abc"])
|
|
45
|
+
win.cursor_x = 1
|
|
46
|
+
feed("a", "X", :escape)
|
|
47
|
+
assert_equal ["abXc"], buf.lines
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_insert_line_start_nonblank_I
|
|
51
|
+
buf.replace_all_lines!([" hello"])
|
|
52
|
+
win.cursor_x = 4
|
|
53
|
+
feed("I", "X", :escape)
|
|
54
|
+
assert_equal [" Xhello"], buf.lines
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_open_line_below_o
|
|
58
|
+
buf.replace_all_lines!(["hello", "world"])
|
|
59
|
+
win.cursor_y = 0
|
|
60
|
+
feed("o", "new", :escape)
|
|
61
|
+
assert_equal ["hello", "new", "world"], buf.lines
|
|
62
|
+
assert_equal :normal, @editor.mode
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_open_line_above_O
|
|
66
|
+
buf.replace_all_lines!(["hello", "world"])
|
|
67
|
+
win.cursor_y = 1
|
|
68
|
+
feed("O", "new", :escape)
|
|
69
|
+
assert_equal ["hello", "new", "world"], buf.lines
|
|
70
|
+
assert_equal :normal, @editor.mode
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# --- undo / redo ---
|
|
74
|
+
|
|
75
|
+
def test_undo_and_redo
|
|
76
|
+
buf.replace_all_lines!(["hello"])
|
|
77
|
+
feed("x")
|
|
78
|
+
assert_equal ["ello"], buf.lines
|
|
79
|
+
|
|
80
|
+
feed("u")
|
|
81
|
+
assert_equal ["hello"], buf.lines
|
|
82
|
+
assert_match(/Undo/, @editor.message)
|
|
83
|
+
|
|
84
|
+
feed(:ctrl_r)
|
|
85
|
+
assert_equal ["ello"], buf.lines
|
|
86
|
+
assert_match(/Redo/, @editor.message)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_undo_at_oldest
|
|
90
|
+
buf.replace_all_lines!(["hello"])
|
|
91
|
+
buf.instance_variable_get(:@undo_stack)&.clear
|
|
92
|
+
feed("u")
|
|
93
|
+
assert_match(/oldest/, @editor.message)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# --- search ---
|
|
97
|
+
|
|
98
|
+
def test_search_backward_mode
|
|
99
|
+
buf.replace_all_lines!(["hello world"])
|
|
100
|
+
feed("?")
|
|
101
|
+
assert_equal :command_line, @editor.mode
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def test_search_prev_N
|
|
105
|
+
buf.replace_all_lines!(["aaa", "bbb", "aaa"])
|
|
106
|
+
win.cursor_y = 0
|
|
107
|
+
feed("/", "b", "b", "b", :enter)
|
|
108
|
+
assert_equal 1, win.cursor_y
|
|
109
|
+
|
|
110
|
+
feed("N")
|
|
111
|
+
assert_operator win.cursor_y, :>=, 0
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_search_word_backward
|
|
115
|
+
buf.replace_all_lines!(["foo bar foo"])
|
|
116
|
+
win.cursor_x = 8
|
|
117
|
+
feed("#")
|
|
118
|
+
assert_equal 0, win.cursor_x
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def test_search_word_forward_partial
|
|
122
|
+
buf.replace_all_lines!(["foobar foo foobar"])
|
|
123
|
+
win.cursor_x = 0
|
|
124
|
+
feed("g", "*")
|
|
125
|
+
assert_operator win.cursor_x, :>, 0
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def test_search_word_backward_partial
|
|
129
|
+
buf.replace_all_lines!(["foobar foo foobar"])
|
|
130
|
+
win.cursor_x = 11
|
|
131
|
+
feed("g", "#")
|
|
132
|
+
assert_operator win.cursor_x, :<, 11
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# --- marks ---
|
|
136
|
+
|
|
137
|
+
def test_mark_set_and_jump
|
|
138
|
+
buf.replace_all_lines!(["line1", "line2", "line3"])
|
|
139
|
+
win.cursor_y = 1
|
|
140
|
+
feed("m", "a")
|
|
141
|
+
|
|
142
|
+
win.cursor_y = 0
|
|
143
|
+
feed("'", "a")
|
|
144
|
+
assert_equal 1, win.cursor_y
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def test_mark_jump_unset
|
|
148
|
+
buf.replace_all_lines!(["line1"])
|
|
149
|
+
feed("`", "z")
|
|
150
|
+
assert_match(/Mark not set/, @editor.message)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def test_mark_pending_escape_cancels
|
|
154
|
+
buf.replace_all_lines!(["hello"])
|
|
155
|
+
feed("m", "\e")
|
|
156
|
+
assert_equal :normal, @editor.mode
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def test_mark_pending_invalid_char
|
|
160
|
+
buf.replace_all_lines!(["hello"])
|
|
161
|
+
feed("m", " ")
|
|
162
|
+
assert_equal :normal, @editor.mode
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# --- jump list ---
|
|
166
|
+
|
|
167
|
+
def test_jump_older_and_newer
|
|
168
|
+
buf.replace_all_lines!((1..20).map { |i| "line#{i}" })
|
|
169
|
+
win.cursor_y = 0
|
|
170
|
+
|
|
171
|
+
feed("G")
|
|
172
|
+
last_line = buf.line_count - 1
|
|
173
|
+
assert_equal last_line, win.cursor_y
|
|
174
|
+
|
|
175
|
+
feed(:ctrl_o)
|
|
176
|
+
old_y = win.cursor_y
|
|
177
|
+
|
|
178
|
+
feed(:ctrl_i)
|
|
179
|
+
new_y = win.cursor_y
|
|
180
|
+
assert_operator new_y, :>=, old_y
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def test_jump_older_empty
|
|
184
|
+
buf.replace_all_lines!(["line1"])
|
|
185
|
+
@editor.instance_variable_get(:@jump_list)&.clear rescue nil
|
|
186
|
+
feed(:ctrl_o)
|
|
187
|
+
assert_match(/Jump list/, @editor.message.to_s) if @editor.message
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def test_backtick_backtick_jumps_older
|
|
191
|
+
buf.replace_all_lines!((1..10).map { |i| "line#{i}" })
|
|
192
|
+
win.cursor_y = 0
|
|
193
|
+
feed("G")
|
|
194
|
+
feed("`", "`")
|
|
195
|
+
assert_equal 0, win.cursor_y
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def test_jump_pending_escape_cancels
|
|
199
|
+
buf.replace_all_lines!(["hello"])
|
|
200
|
+
feed("'", "\e")
|
|
201
|
+
assert_equal :normal, @editor.mode
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def test_jump_pending_invalid_mark
|
|
205
|
+
buf.replace_all_lines!(["hello"])
|
|
206
|
+
feed("'", " ")
|
|
207
|
+
assert_equal :normal, @editor.mode
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# --- visual mode ---
|
|
211
|
+
|
|
212
|
+
def test_visual_line_yank
|
|
213
|
+
buf.replace_all_lines!(["aaa", "bbb", "ccc"])
|
|
214
|
+
win.cursor_y = 0
|
|
215
|
+
feed("V", "j", "y")
|
|
216
|
+
|
|
217
|
+
reg = @editor.get_register("\"")
|
|
218
|
+
assert_equal :normal, @editor.mode
|
|
219
|
+
assert_includes reg[:text], "aaa"
|
|
220
|
+
assert_includes reg[:text], "bbb"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def test_visual_line_delete
|
|
224
|
+
buf.replace_all_lines!(["aaa", "bbb", "ccc"])
|
|
225
|
+
win.cursor_y = 0
|
|
226
|
+
feed("V", "j", "d")
|
|
227
|
+
|
|
228
|
+
assert_equal ["ccc"], buf.lines
|
|
229
|
+
assert_equal :normal, @editor.mode
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def test_visual_char_delete
|
|
233
|
+
buf.replace_all_lines!(["abcdef"])
|
|
234
|
+
win.cursor_x = 1
|
|
235
|
+
feed("v", "l", "l", "d")
|
|
236
|
+
|
|
237
|
+
assert_equal ["aef"], buf.lines
|
|
238
|
+
assert_equal :normal, @editor.mode
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def test_visual_select_text_object_iw
|
|
242
|
+
buf.replace_all_lines!(["hello world"])
|
|
243
|
+
win.cursor_x = 0
|
|
244
|
+
feed("v", "i", "w")
|
|
245
|
+
assert_equal :visual_char, @editor.mode
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# --- delete operator ---
|
|
249
|
+
|
|
250
|
+
def test_delete_gg_motion
|
|
251
|
+
buf.replace_all_lines!(["aaa", "bbb", "ccc"])
|
|
252
|
+
win.cursor_y = 2
|
|
253
|
+
feed("d", "g", "g")
|
|
254
|
+
assert_equal [""], buf.lines
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def test_delete_j_motion
|
|
258
|
+
buf.replace_all_lines!(["aaa", "bbb", "ccc"])
|
|
259
|
+
win.cursor_y = 0
|
|
260
|
+
feed("d", "j")
|
|
261
|
+
assert_equal ["ccc"], buf.lines
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def test_delete_k_motion
|
|
265
|
+
buf.replace_all_lines!(["aaa", "bbb", "ccc"])
|
|
266
|
+
win.cursor_y = 1
|
|
267
|
+
feed("d", "k")
|
|
268
|
+
assert_equal ["ccc"], buf.lines
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def test_delete_word_dw
|
|
272
|
+
buf.replace_all_lines!(["hello world"])
|
|
273
|
+
win.cursor_x = 0
|
|
274
|
+
feed("d", "w")
|
|
275
|
+
assert_equal ["world"], buf.lines
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def test_delete_aw
|
|
279
|
+
buf.replace_all_lines!(["hello world"])
|
|
280
|
+
win.cursor_x = 0
|
|
281
|
+
feed("d", "a", "w")
|
|
282
|
+
assert_equal ["world"], buf.lines
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# --- yank operator ---
|
|
286
|
+
|
|
287
|
+
def test_yank_word_yw
|
|
288
|
+
buf.replace_all_lines!(["hello world"])
|
|
289
|
+
win.cursor_x = 0
|
|
290
|
+
feed("y", "w")
|
|
291
|
+
reg = @editor.get_register("\"")
|
|
292
|
+
assert_equal "hello ", reg[:text]
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def test_yank_gg_motion
|
|
296
|
+
buf.replace_all_lines!(["aaa", "bbb", "ccc"])
|
|
297
|
+
win.cursor_y = 2
|
|
298
|
+
feed("y", "g", "g")
|
|
299
|
+
reg = @editor.get_register("\"")
|
|
300
|
+
assert_includes reg[:text], "aaa"
|
|
301
|
+
assert_includes reg[:text], "ccc"
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def test_yank_iw
|
|
305
|
+
buf.replace_all_lines!(["hello world"])
|
|
306
|
+
win.cursor_x = 0
|
|
307
|
+
feed("y", "i", "w")
|
|
308
|
+
reg = @editor.get_register("\"")
|
|
309
|
+
assert_equal "hello", reg[:text]
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def test_yank_aw
|
|
313
|
+
buf.replace_all_lines!(["hello world"])
|
|
314
|
+
win.cursor_x = 0
|
|
315
|
+
feed("y", "a", "w")
|
|
316
|
+
reg = @editor.get_register("\"")
|
|
317
|
+
assert_equal "hello ", reg[:text]
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# --- indent operator ---
|
|
321
|
+
|
|
322
|
+
def test_indent_j_motion
|
|
323
|
+
buf.replace_all_lines!([" aaa", " bbb", "ccc"])
|
|
324
|
+
win.cursor_y = 0
|
|
325
|
+
feed("=", "j")
|
|
326
|
+
assert_equal :normal, @editor.mode
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def test_indent_gg_motion
|
|
330
|
+
buf.replace_all_lines!([" aaa", " bbb"])
|
|
331
|
+
win.cursor_y = 1
|
|
332
|
+
feed("=", "g", "g")
|
|
333
|
+
assert_equal :normal, @editor.mode
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def test_indent_k_motion
|
|
337
|
+
buf.replace_all_lines!(["aaa", " bbb", "ccc"])
|
|
338
|
+
win.cursor_y = 1
|
|
339
|
+
feed("=", "k")
|
|
340
|
+
assert_equal :normal, @editor.mode
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def test_indent_G_motion
|
|
344
|
+
buf.replace_all_lines!([" aaa", " bbb"])
|
|
345
|
+
win.cursor_y = 0
|
|
346
|
+
feed("=", "G")
|
|
347
|
+
assert_equal :normal, @editor.mode
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# --- arrow and page keys ---
|
|
351
|
+
|
|
352
|
+
def test_arrow_keys_in_normal_mode
|
|
353
|
+
buf.replace_all_lines!(["abc", "def"])
|
|
354
|
+
win.cursor_y = 0
|
|
355
|
+
win.cursor_x = 0
|
|
356
|
+
feed(:right)
|
|
357
|
+
assert_equal 1, win.cursor_x
|
|
358
|
+
feed(:down)
|
|
359
|
+
assert_equal 1, win.cursor_y
|
|
360
|
+
feed(:left)
|
|
361
|
+
assert_equal 0, win.cursor_x
|
|
362
|
+
feed(:up)
|
|
363
|
+
assert_equal 0, win.cursor_y
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def test_page_keys_in_normal_mode
|
|
367
|
+
buf.replace_all_lines!((1..30).map { |i| "line#{i}" })
|
|
368
|
+
@editor.current_window_view_height_hint = 10
|
|
369
|
+
win.cursor_y = 0
|
|
370
|
+
feed(:pagedown)
|
|
371
|
+
assert_operator win.cursor_y, :>, 0
|
|
372
|
+
feed(:pageup)
|
|
373
|
+
assert_equal 0, win.cursor_y
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# --- escape clears message ---
|
|
377
|
+
|
|
378
|
+
def test_escape_clears_message
|
|
379
|
+
@editor.echo("test message")
|
|
380
|
+
feed("\e")
|
|
381
|
+
end
|
|
382
|
+
end
|
data/test/app_completion_test.rb
CHANGED
|
@@ -6,6 +6,8 @@ class AppCompletionTest < Minitest::Test
|
|
|
6
6
|
def setup
|
|
7
7
|
@app = RuVim::App.new
|
|
8
8
|
@editor = @app.instance_variable_get(:@editor)
|
|
9
|
+
@key_handler = @app.instance_variable_get(:@key_handler)
|
|
10
|
+
@completion = @app.instance_variable_get(:@completion)
|
|
9
11
|
end
|
|
10
12
|
|
|
11
13
|
def test_app_starts_with_intro_buffer_without_path
|
|
@@ -20,7 +22,7 @@ class AppCompletionTest < Minitest::Test
|
|
|
20
22
|
cmd = @editor.command_line
|
|
21
23
|
cmd.replace_text("set nu")
|
|
22
24
|
|
|
23
|
-
@
|
|
25
|
+
@completion.command_line_complete
|
|
24
26
|
|
|
25
27
|
assert_equal "set number", cmd.text
|
|
26
28
|
end
|
|
@@ -33,7 +35,7 @@ class AppCompletionTest < Minitest::Test
|
|
|
33
35
|
@editor.current_window.cursor_x = 2
|
|
34
36
|
@editor.enter_insert_mode
|
|
35
37
|
|
|
36
|
-
@
|
|
38
|
+
@key_handler.send(:handle_insert_key, :ctrl_n)
|
|
37
39
|
|
|
38
40
|
assert_equal "foobar", b.line_at(0)
|
|
39
41
|
assert_equal 6, @editor.current_window.cursor_x
|
|
@@ -45,7 +47,7 @@ class AppCompletionTest < Minitest::Test
|
|
|
45
47
|
File.write(File.join(dir, "a.o"), "")
|
|
46
48
|
@editor.set_option("wildignore", "*.o", scope: :global)
|
|
47
49
|
|
|
48
|
-
matches = @
|
|
50
|
+
matches = @completion.send(:path_completion_candidates, File.join(dir, "a"))
|
|
49
51
|
|
|
50
52
|
assert_includes matches, File.join(dir, "a.rb")
|
|
51
53
|
refute_includes matches, File.join(dir, "a.o")
|
|
@@ -57,7 +59,7 @@ class AppCompletionTest < Minitest::Test
|
|
|
57
59
|
Dir.chdir(dir) do
|
|
58
60
|
FileUtils.mkdir_p("lib")
|
|
59
61
|
|
|
60
|
-
matches = @
|
|
62
|
+
matches = @completion.send(:path_completion_candidates, "li")
|
|
61
63
|
|
|
62
64
|
assert_includes matches, "lib/"
|
|
63
65
|
end
|
|
@@ -70,7 +72,7 @@ class AppCompletionTest < Minitest::Test
|
|
|
70
72
|
File.write(File.join(dir, "aaa.txt"), "")
|
|
71
73
|
File.write(File.join(dir, "bbb.txt"), "")
|
|
72
74
|
|
|
73
|
-
matches = @
|
|
75
|
+
matches = @completion.send(:path_completion_candidates, File.join(dir, ""))
|
|
74
76
|
|
|
75
77
|
assert_includes matches, File.join(dir, ".git/")
|
|
76
78
|
visible_idx = matches.index(File.join(dir, "aaa.txt"))
|
|
@@ -96,10 +98,10 @@ class AppCompletionTest < Minitest::Test
|
|
|
96
98
|
cmd = @editor.command_line
|
|
97
99
|
cmd.replace_text("e #{File.join(dir, "a")}")
|
|
98
100
|
|
|
99
|
-
@
|
|
101
|
+
@completion.command_line_complete
|
|
100
102
|
first = cmd.text.dup
|
|
101
103
|
first_msg = @editor.message.dup
|
|
102
|
-
@
|
|
104
|
+
@completion.command_line_complete
|
|
103
105
|
second = cmd.text.dup
|
|
104
106
|
second_msg = @editor.message.dup
|
|
105
107
|
|
|
@@ -125,10 +127,10 @@ class AppCompletionTest < Minitest::Test
|
|
|
125
127
|
cmd = @editor.command_line
|
|
126
128
|
cmd.replace_text("e #{File.join(dir, "a")}")
|
|
127
129
|
|
|
128
|
-
@
|
|
130
|
+
@completion.command_line_complete
|
|
129
131
|
assert_equal "e #{File.join(dir, "a")}", cmd.text
|
|
130
132
|
|
|
131
|
-
@
|
|
133
|
+
@completion.command_line_complete
|
|
132
134
|
assert([a, b].any? { |p| cmd.text.end_with?(p) })
|
|
133
135
|
end
|
|
134
136
|
end
|
|
@@ -148,9 +150,9 @@ class AppCompletionTest < Minitest::Test
|
|
|
148
150
|
cmd = @editor.command_line
|
|
149
151
|
cmd.replace_text("e #{File.join(dir, "a")}")
|
|
150
152
|
|
|
151
|
-
@
|
|
153
|
+
@completion.command_line_complete
|
|
152
154
|
first = cmd.text.dup
|
|
153
|
-
@
|
|
155
|
+
@completion.command_line_complete
|
|
154
156
|
second = cmd.text.dup
|
|
155
157
|
|
|
156
158
|
refute_equal first, second
|
|
@@ -168,7 +170,7 @@ class AppCompletionTest < Minitest::Test
|
|
|
168
170
|
cmd = @editor.command_line
|
|
169
171
|
cmd.replace_text("se")
|
|
170
172
|
|
|
171
|
-
@
|
|
173
|
+
@completion.command_line_complete
|
|
172
174
|
|
|
173
175
|
assert_equal "se", cmd.text
|
|
174
176
|
assert_includes @editor.message, "set"
|
|
@@ -186,11 +188,11 @@ class AppCompletionTest < Minitest::Test
|
|
|
186
188
|
@editor.current_window.cursor_x = 2
|
|
187
189
|
@editor.enter_insert_mode
|
|
188
190
|
|
|
189
|
-
@
|
|
191
|
+
@key_handler.send(:handle_insert_key, :ctrl_n)
|
|
190
192
|
assert_equal "fo", b.line_at(0)
|
|
191
193
|
assert_includes @editor.message, "..."
|
|
192
194
|
|
|
193
|
-
@
|
|
195
|
+
@key_handler.send(:handle_insert_key, :ctrl_n)
|
|
194
196
|
assert_equal "foobar", b.line_at(0)
|
|
195
197
|
end
|
|
196
198
|
|
|
@@ -203,11 +205,11 @@ class AppCompletionTest < Minitest::Test
|
|
|
203
205
|
@editor.current_window.cursor_x = 2
|
|
204
206
|
@editor.enter_insert_mode
|
|
205
207
|
|
|
206
|
-
@
|
|
208
|
+
@key_handler.send(:handle_insert_key, :ctrl_n)
|
|
207
209
|
assert_equal "fo", b.line_at(0)
|
|
208
210
|
assert_includes @editor.message, "["
|
|
209
211
|
|
|
210
|
-
@
|
|
212
|
+
@key_handler.send(:handle_insert_key, :ctrl_n)
|
|
211
213
|
assert_equal "foobar", b.line_at(0)
|
|
212
214
|
end
|
|
213
215
|
|
|
@@ -217,7 +219,7 @@ class AppCompletionTest < Minitest::Test
|
|
|
217
219
|
cmd = @editor.command_line
|
|
218
220
|
cmd.replace_text("git bl")
|
|
219
221
|
|
|
220
|
-
@
|
|
222
|
+
@completion.command_line_complete
|
|
221
223
|
|
|
222
224
|
# "bl" matches "blame", "blameback", "blamecommit", "blameprev", "branch"
|
|
223
225
|
# With multiple matches, wildmode applies (longest common prefix or cycle)
|
|
@@ -229,10 +231,32 @@ class AppCompletionTest < Minitest::Test
|
|
|
229
231
|
@editor.materialize_intro_buffer!
|
|
230
232
|
@editor.enter_command_line_mode(":")
|
|
231
233
|
cmd = @editor.command_line
|
|
232
|
-
cmd.replace_text("git
|
|
234
|
+
cmd.replace_text("git commi")
|
|
233
235
|
|
|
234
|
-
@
|
|
236
|
+
@completion.command_line_complete
|
|
235
237
|
|
|
236
238
|
assert_equal "git commit", cmd.text
|
|
237
239
|
end
|
|
240
|
+
|
|
241
|
+
def test_git_subcommand_completion_includes_external
|
|
242
|
+
@editor.materialize_intro_buffer!
|
|
243
|
+
@editor.enter_command_line_mode(":")
|
|
244
|
+
cmd = @editor.command_line
|
|
245
|
+
cmd.replace_text("git stas")
|
|
246
|
+
|
|
247
|
+
@completion.command_line_complete
|
|
248
|
+
|
|
249
|
+
assert_equal "git stash", cmd.text
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def test_gh_subcommand_completion_includes_external
|
|
253
|
+
@editor.materialize_intro_buffer!
|
|
254
|
+
@editor.enter_command_line_mode(":")
|
|
255
|
+
cmd = @editor.command_line
|
|
256
|
+
cmd.replace_text("gh issu")
|
|
257
|
+
|
|
258
|
+
@completion.command_line_complete
|
|
259
|
+
|
|
260
|
+
assert_equal "gh issue", cmd.text
|
|
261
|
+
end
|
|
238
262
|
end
|
data/test/app_dot_repeat_test.rb
CHANGED
|
@@ -4,13 +4,14 @@ class AppDotRepeatTest < 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_dot_repeats_x
|
|
@@ -57,11 +58,34 @@ class AppDotRepeatTest < Minitest::Test
|
|
|
57
58
|
@win.cursor_x = 0
|
|
58
59
|
|
|
59
60
|
press("s")
|
|
60
|
-
@
|
|
61
|
-
@
|
|
61
|
+
@key_handler.handle("X")
|
|
62
|
+
@key_handler.handle(:escape)
|
|
62
63
|
press("l")
|
|
63
64
|
press(".")
|
|
64
65
|
|
|
65
66
|
assert_equal "XbXd", @buffer.line_at(0)
|
|
66
67
|
end
|
|
68
|
+
|
|
69
|
+
def test_dot_repeats_counted_x
|
|
70
|
+
@buffer.replace_all_lines!(["abcdefgh"])
|
|
71
|
+
@win.cursor_x = 0
|
|
72
|
+
|
|
73
|
+
@key_handler.handle("3")
|
|
74
|
+
press("x")
|
|
75
|
+
assert_equal "defgh", @buffer.line_at(0)
|
|
76
|
+
|
|
77
|
+
press(".")
|
|
78
|
+
assert_equal "gh", @buffer.line_at(0)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_dot_repeats_counted_dd
|
|
82
|
+
@buffer.replace_all_lines!(["a", "b", "c", "d", "e", "f"])
|
|
83
|
+
|
|
84
|
+
@key_handler.handle("2")
|
|
85
|
+
press("d", "d")
|
|
86
|
+
assert_equal ["c", "d", "e", "f"], @buffer.lines
|
|
87
|
+
|
|
88
|
+
press(".")
|
|
89
|
+
assert_equal ["e", "f"], @buffer.lines
|
|
90
|
+
end
|
|
67
91
|
end
|