ruvim 0.3.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 +68 -7
- data/README.md +30 -7
- 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 +18 -1
- data/docs/command.md +156 -10
- data/docs/config.md +10 -2
- data/docs/done.md +23 -0
- data/docs/spec.md +162 -25
- data/docs/todo.md +9 -0
- data/docs/tutorial.md +33 -1
- data/docs/vim_diff.md +31 -8
- data/ext/ruvim/extconf.rb +5 -0
- data/ext/ruvim/ruvim_ext.c +519 -0
- data/lib/ruvim/app.rb +246 -2525
- data/lib/ruvim/browser.rb +104 -0
- data/lib/ruvim/buffer.rb +43 -20
- data/lib/ruvim/cli.rb +6 -0
- 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 +74 -80
- data/lib/ruvim/ex_command_registry.rb +3 -1
- data/lib/ruvim/file_watcher.rb +243 -0
- data/lib/ruvim/gh/link.rb +207 -0
- data/lib/ruvim/git/blame.rb +255 -0
- data/lib/ruvim/git/branch.rb +112 -0
- data/lib/ruvim/git/commit.rb +102 -0
- data/lib/ruvim/git/diff.rb +129 -0
- data/lib/ruvim/git/grep.rb +107 -0
- data/lib/ruvim/git/handler.rb +125 -0
- data/lib/ruvim/git/log.rb +41 -0
- data/lib/ruvim/git/status.rb +103 -0
- data/lib/ruvim/global_commands.rb +351 -77
- data/lib/ruvim/highlighter.rb +4 -11
- data/lib/ruvim/input.rb +1 -0
- 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 +43 -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 +40 -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/json_renderer.rb +131 -0
- data/lib/ruvim/rich_view/jsonl_renderer.rb +57 -0
- data/lib/ruvim/rich_view/table_renderer.rb +3 -3
- data/lib/ruvim/rich_view.rb +30 -7
- data/lib/ruvim/screen.rb +135 -84
- 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 +31 -4
- data/test/app_command_test.rb +382 -0
- data/test/app_completion_test.rb +65 -16
- 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 +182 -8
- 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 +77 -0
- data/test/clipboard_test.rb +67 -0
- data/test/command_invocation_test.rb +33 -0
- data/test/command_line_test.rb +118 -0
- data/test/config_dsl_test.rb +134 -0
- data/test/dispatcher_test.rb +74 -4
- data/test/display_width_test.rb +41 -0
- data/test/ex_command_registry_test.rb +106 -0
- data/test/file_watcher_test.rb +197 -0
- data/test/follow_test.rb +198 -0
- data/test/gh_link_test.rb +141 -0
- data/test/git_blame_test.rb +792 -0
- data/test/git_grep_test.rb +64 -0
- data/test/highlighter_test.rb +169 -0
- data/test/indent_test.rb +223 -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 +279 -23
- data/test/run_command_test.rb +307 -0
- data/test/screen_test.rb +68 -5
- data/test/search_option_test.rb +19 -0
- data/test/stream_test.rb +165 -0
- data/test/test_helper.rb +9 -0
- data/test/window_test.rb +59 -0
- metadata +68 -2
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class ClipboardTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
RuVim::Clipboard.reset_backend!
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def teardown
|
|
9
|
+
RuVim::Clipboard.reset_backend!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_available_returns_true_when_backend_exists
|
|
13
|
+
RuVim::Clipboard.instance_variable_set(:@backend, { write: %w[echo], read: %w[echo] })
|
|
14
|
+
assert RuVim::Clipboard.available?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_available_returns_false_when_no_backend
|
|
18
|
+
RuVim::Clipboard.instance_variable_set(:@backend, false)
|
|
19
|
+
refute RuVim::Clipboard.available?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_read_returns_nil_when_no_read_cmd
|
|
23
|
+
RuVim::Clipboard.instance_variable_set(:@backend, { write: nil, read: nil })
|
|
24
|
+
assert_nil RuVim::Clipboard.read
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_read_returns_output_on_success
|
|
28
|
+
RuVim::Clipboard.instance_variable_set(:@backend, { write: %w[true], read: %w[echo hello] })
|
|
29
|
+
result = RuVim::Clipboard.read
|
|
30
|
+
assert_equal "hello\n", result
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_read_returns_nil_on_command_failure
|
|
34
|
+
RuVim::Clipboard.instance_variable_set(:@backend, { write: %w[true], read: %w[false] })
|
|
35
|
+
assert_nil RuVim::Clipboard.read
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_write_returns_false_when_no_write_cmd
|
|
39
|
+
RuVim::Clipboard.instance_variable_set(:@backend, { write: nil, read: nil })
|
|
40
|
+
refute RuVim::Clipboard.write("test")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_write_sends_text_to_command
|
|
44
|
+
RuVim::Clipboard.instance_variable_set(:@backend, { write: %w[cat], read: %w[true] })
|
|
45
|
+
assert RuVim::Clipboard.write("hello")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_pbcopy_backend_format
|
|
49
|
+
expected = { write: %w[pbcopy], read: %w[pbpaste] }
|
|
50
|
+
assert_equal expected, RuVim::Clipboard.pbcopy_backend
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_wayland_backend_format
|
|
54
|
+
expected = { write: %w[wl-copy], read: %w[wl-paste -n] }
|
|
55
|
+
assert_equal expected, RuVim::Clipboard.wayland_backend
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_xclip_backend_format
|
|
59
|
+
expected = { write: %w[xclip -selection clipboard -in], read: %w[xclip -selection clipboard -out] }
|
|
60
|
+
assert_equal expected, RuVim::Clipboard.xclip_backend
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_xsel_backend_format
|
|
64
|
+
expected = { write: %w[xsel --clipboard --input], read: %w[xsel --clipboard --output] }
|
|
65
|
+
assert_equal expected, RuVim::Clipboard.xsel_backend
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class CommandInvocationTest < Minitest::Test
|
|
4
|
+
def test_bang_defaults_to_false
|
|
5
|
+
inv = RuVim::CommandInvocation.new(id: "test")
|
|
6
|
+
assert_equal false, inv.bang
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def test_bang_true
|
|
10
|
+
inv = RuVim::CommandInvocation.new(id: "test", bang: true)
|
|
11
|
+
assert_equal true, inv.bang
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_bang_false
|
|
15
|
+
inv = RuVim::CommandInvocation.new(id: "test", bang: false)
|
|
16
|
+
assert_equal false, inv.bang
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_argv_defaults_to_empty_array
|
|
20
|
+
inv = RuVim::CommandInvocation.new(id: "test")
|
|
21
|
+
assert_equal [], inv.argv
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_kwargs_defaults_to_empty_hash
|
|
25
|
+
inv = RuVim::CommandInvocation.new(id: "test")
|
|
26
|
+
assert_equal({}, inv.kwargs)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_count_defaults_to_nil
|
|
30
|
+
inv = RuVim::CommandInvocation.new(id: "test")
|
|
31
|
+
assert_nil inv.count
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require_relative "test_helper"
|
|
2
|
+
|
|
3
|
+
class CommandLineTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@cl = RuVim::CommandLine.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def test_initial_state
|
|
9
|
+
assert_equal ":", @cl.prefix
|
|
10
|
+
assert_equal "", @cl.text
|
|
11
|
+
assert_equal 0, @cl.cursor
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_reset_with_custom_prefix
|
|
15
|
+
@cl.reset(prefix: "/")
|
|
16
|
+
assert_equal "/", @cl.prefix
|
|
17
|
+
assert_equal "", @cl.text
|
|
18
|
+
assert_equal 0, @cl.cursor
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_insert_appends_at_cursor
|
|
22
|
+
@cl.insert("abc")
|
|
23
|
+
assert_equal "abc", @cl.text
|
|
24
|
+
assert_equal 3, @cl.cursor
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_insert_at_middle
|
|
28
|
+
@cl.insert("ac")
|
|
29
|
+
@cl.instance_variable_set(:@cursor, 1)
|
|
30
|
+
@cl.insert("b")
|
|
31
|
+
assert_equal "abc", @cl.text
|
|
32
|
+
assert_equal 2, @cl.cursor
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_backspace_at_zero_does_nothing
|
|
36
|
+
@cl.backspace
|
|
37
|
+
assert_equal "", @cl.text
|
|
38
|
+
assert_equal 0, @cl.cursor
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_backspace_deletes_character
|
|
42
|
+
@cl.insert("abc")
|
|
43
|
+
@cl.backspace
|
|
44
|
+
assert_equal "ab", @cl.text
|
|
45
|
+
assert_equal 2, @cl.cursor
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_move_left
|
|
49
|
+
@cl.insert("abc")
|
|
50
|
+
@cl.move_left
|
|
51
|
+
assert_equal 2, @cl.cursor
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_move_left_at_zero_stays
|
|
55
|
+
@cl.move_left
|
|
56
|
+
assert_equal 0, @cl.cursor
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_move_right
|
|
60
|
+
@cl.insert("abc")
|
|
61
|
+
@cl.instance_variable_set(:@cursor, 1)
|
|
62
|
+
@cl.move_right
|
|
63
|
+
assert_equal 2, @cl.cursor
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_move_right_at_end_stays
|
|
67
|
+
@cl.insert("abc")
|
|
68
|
+
@cl.move_right
|
|
69
|
+
assert_equal 3, @cl.cursor
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_content_includes_prefix
|
|
73
|
+
@cl.insert("hello")
|
|
74
|
+
assert_equal ":hello", @cl.content
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_content_with_custom_prefix
|
|
78
|
+
@cl.reset(prefix: "/")
|
|
79
|
+
@cl.insert("search")
|
|
80
|
+
assert_equal "/search", @cl.content
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_clear_resets_text_and_cursor
|
|
84
|
+
@cl.insert("hello")
|
|
85
|
+
@cl.clear
|
|
86
|
+
assert_equal "", @cl.text
|
|
87
|
+
assert_equal 0, @cl.cursor
|
|
88
|
+
assert_equal ":", @cl.prefix
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_replace_text
|
|
92
|
+
@cl.insert("old")
|
|
93
|
+
@cl.replace_text("new text")
|
|
94
|
+
assert_equal "new text", @cl.text
|
|
95
|
+
assert_equal 8, @cl.cursor
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def test_replace_span_end_cursor
|
|
99
|
+
@cl.insert("hello world")
|
|
100
|
+
@cl.replace_span(6, 11, "ruby")
|
|
101
|
+
assert_equal "hello ruby", @cl.text
|
|
102
|
+
assert_equal 10, @cl.cursor
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_replace_span_start_cursor
|
|
106
|
+
@cl.insert("hello world")
|
|
107
|
+
@cl.replace_span(6, 11, "ruby", cursor_at: :start)
|
|
108
|
+
assert_equal "hello ruby", @cl.text
|
|
109
|
+
assert_equal 6, @cl.cursor
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_replace_span_integer_cursor
|
|
113
|
+
@cl.insert("hello world")
|
|
114
|
+
@cl.replace_span(6, 11, "ruby", cursor_at: 8)
|
|
115
|
+
assert_equal "hello ruby", @cl.text
|
|
116
|
+
assert_equal 8, @cl.cursor
|
|
117
|
+
end
|
|
118
|
+
end
|
data/test/config_dsl_test.rb
CHANGED
|
@@ -75,4 +75,138 @@ class ConfigDSLTest < Minitest::Test
|
|
|
75
75
|
assert_equal :match, match.status
|
|
76
76
|
assert match.invocation.id.start_with?("user.keymap.global.")
|
|
77
77
|
end
|
|
78
|
+
|
|
79
|
+
def test_nmap_with_command_id_string
|
|
80
|
+
@command_registry.register("test.cmd", call: ->(_ctx, **) {}, desc: "test", source: :builtin)
|
|
81
|
+
@dsl.nmap("T", "test.cmd")
|
|
82
|
+
match = @keymaps.resolve(:normal, ["T"])
|
|
83
|
+
assert_equal :match, match.status
|
|
84
|
+
assert_equal "test.cmd", match.invocation.id
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_nmap_without_command_id_or_block_raises
|
|
88
|
+
# ConfigDSL < BasicObject, so raise becomes NoMethodError for ::ArgumentError
|
|
89
|
+
assert_raises(NoMethodError, ArgumentError) { @dsl.nmap("T") }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_imap_without_block_or_id_raises
|
|
93
|
+
assert_raises(NoMethodError, ArgumentError) { @dsl.imap("T") }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_command_registers_user_command
|
|
97
|
+
@dsl.command("my.cmd", desc: "custom") { |_ctx, **| }
|
|
98
|
+
spec = @command_registry.fetch("my.cmd")
|
|
99
|
+
assert_equal :user, spec.source
|
|
100
|
+
assert_equal "custom", spec.desc
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def test_command_without_block_raises
|
|
104
|
+
assert_raises(NoMethodError, ArgumentError) { @dsl.command("my.cmd") }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_nmap_with_filetype
|
|
108
|
+
dsl = RuVim::ConfigDSL.new(
|
|
109
|
+
command_registry: @command_registry,
|
|
110
|
+
ex_registry: @ex_registry,
|
|
111
|
+
keymaps: @keymaps,
|
|
112
|
+
command_host: RuVim::GlobalCommands.instance,
|
|
113
|
+
filetype: "ruby"
|
|
114
|
+
)
|
|
115
|
+
dsl.nmap("K", desc: "ft test") { |_ctx, **| }
|
|
116
|
+
editor = fresh_editor
|
|
117
|
+
editor.current_buffer.options["filetype"] = "ruby"
|
|
118
|
+
match = @keymaps.resolve_with_context(:normal, ["K"], editor: editor)
|
|
119
|
+
assert_equal :match, match.status
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def test_set_option_requires_editor
|
|
123
|
+
assert_raises(NoMethodError, ArgumentError) { @dsl.set("number") }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_set_boolean_option
|
|
127
|
+
editor = fresh_editor
|
|
128
|
+
dsl = RuVim::ConfigDSL.new(
|
|
129
|
+
command_registry: @command_registry,
|
|
130
|
+
ex_registry: @ex_registry,
|
|
131
|
+
keymaps: @keymaps,
|
|
132
|
+
command_host: RuVim::GlobalCommands.instance,
|
|
133
|
+
editor: editor
|
|
134
|
+
)
|
|
135
|
+
dsl.set("number")
|
|
136
|
+
assert editor.get_option("number")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_set_no_prefix_disables_option
|
|
140
|
+
editor = fresh_editor
|
|
141
|
+
dsl = RuVim::ConfigDSL.new(
|
|
142
|
+
command_registry: @command_registry,
|
|
143
|
+
ex_registry: @ex_registry,
|
|
144
|
+
keymaps: @keymaps,
|
|
145
|
+
command_host: RuVim::GlobalCommands.instance,
|
|
146
|
+
editor: editor
|
|
147
|
+
)
|
|
148
|
+
dsl.set("number")
|
|
149
|
+
dsl.set("nonumber")
|
|
150
|
+
refute editor.get_option("number")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def test_set_with_value
|
|
154
|
+
editor = fresh_editor
|
|
155
|
+
dsl = RuVim::ConfigDSL.new(
|
|
156
|
+
command_registry: @command_registry,
|
|
157
|
+
ex_registry: @ex_registry,
|
|
158
|
+
keymaps: @keymaps,
|
|
159
|
+
command_host: RuVim::GlobalCommands.instance,
|
|
160
|
+
editor: editor
|
|
161
|
+
)
|
|
162
|
+
dsl.set("tabstop=4")
|
|
163
|
+
assert_equal 4, editor.get_option("tabstop")
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_ex_command_registers_and_allows_re_register
|
|
167
|
+
dsl = RuVim::ConfigDSL.new(
|
|
168
|
+
command_registry: @command_registry,
|
|
169
|
+
ex_registry: RuVim::ExCommandRegistry.instance,
|
|
170
|
+
keymaps: @keymaps,
|
|
171
|
+
command_host: RuVim::GlobalCommands.instance
|
|
172
|
+
)
|
|
173
|
+
saved_specs = RuVim::ExCommandRegistry.instance.instance_variable_get(:@specs).dup
|
|
174
|
+
saved_lookup = RuVim::ExCommandRegistry.instance.instance_variable_get(:@lookup).dup
|
|
175
|
+
|
|
176
|
+
dsl.ex_command("testusercmd", desc: "test") { |_ctx, **| }
|
|
177
|
+
assert RuVim::ExCommandRegistry.instance.registered?("testusercmd")
|
|
178
|
+
|
|
179
|
+
# Re-registering should not raise (unregisters first)
|
|
180
|
+
dsl.ex_command("testusercmd", desc: "test v2") { |_ctx, **| }
|
|
181
|
+
assert RuVim::ExCommandRegistry.instance.registered?("testusercmd")
|
|
182
|
+
ensure
|
|
183
|
+
RuVim::ExCommandRegistry.instance.instance_variable_set(:@specs, saved_specs)
|
|
184
|
+
RuVim::ExCommandRegistry.instance.instance_variable_set(:@lookup, saved_lookup)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def test_setlocal_sets_local_scope
|
|
188
|
+
editor = fresh_editor
|
|
189
|
+
dsl = RuVim::ConfigDSL.new(
|
|
190
|
+
command_registry: @command_registry,
|
|
191
|
+
ex_registry: @ex_registry,
|
|
192
|
+
keymaps: @keymaps,
|
|
193
|
+
command_host: RuVim::GlobalCommands.instance,
|
|
194
|
+
editor: editor
|
|
195
|
+
)
|
|
196
|
+
dsl.setlocal("number")
|
|
197
|
+
assert editor.get_option("number")
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def test_setglobal_sets_global_scope
|
|
201
|
+
editor = fresh_editor
|
|
202
|
+
dsl = RuVim::ConfigDSL.new(
|
|
203
|
+
command_registry: @command_registry,
|
|
204
|
+
ex_registry: @ex_registry,
|
|
205
|
+
keymaps: @keymaps,
|
|
206
|
+
command_host: RuVim::GlobalCommands.instance,
|
|
207
|
+
editor: editor
|
|
208
|
+
)
|
|
209
|
+
dsl.setglobal("ignorecase")
|
|
210
|
+
assert editor.get_option("ignorecase")
|
|
211
|
+
end
|
|
78
212
|
end
|
data/test/dispatcher_test.rb
CHANGED
|
@@ -119,19 +119,89 @@ class DispatcherTest < Minitest::Test
|
|
|
119
119
|
assert_includes body, "42"
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
-
def
|
|
122
|
+
def test_dispatch_ex_shell_uses_shell_executor_when_available
|
|
123
|
+
executed_command = nil
|
|
124
|
+
fake_status = Struct.new(:exitstatus).new(0)
|
|
125
|
+
@editor.shell_executor = ->(cmd) { executed_command = cmd; fake_status }
|
|
126
|
+
|
|
127
|
+
@dispatcher.dispatch_ex(@editor, "!echo hello")
|
|
128
|
+
|
|
129
|
+
assert_equal "echo hello", executed_command
|
|
130
|
+
assert_equal "shell exit 0", @editor.message
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def test_dispatch_ex_shell_falls_back_to_capture_without_executor
|
|
134
|
+
@editor.shell_executor = nil
|
|
123
135
|
@dispatcher.dispatch_ex(@editor, "!echo out; echo err 1>&2")
|
|
124
136
|
|
|
125
137
|
assert_equal "[Shell Output]", @editor.message
|
|
126
138
|
assert_equal :help, @editor.current_buffer.kind
|
|
127
139
|
body = @editor.current_buffer.lines.join("\n")
|
|
128
|
-
assert_includes body, "[command]"
|
|
129
|
-
assert_includes body, "echo out; echo err 1>&2"
|
|
130
140
|
assert_includes body, "[stdout]"
|
|
131
141
|
assert_includes body, "out"
|
|
132
142
|
assert_includes body, "[stderr]"
|
|
133
143
|
assert_includes body, "err"
|
|
134
|
-
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def test_dispatch_ex_read_file_inserts_after_cursor_line
|
|
147
|
+
@editor.materialize_intro_buffer!
|
|
148
|
+
Dir.mktmpdir do |dir|
|
|
149
|
+
path = File.join(dir, "data.txt")
|
|
150
|
+
File.write(path, "aaa\nbbb\n")
|
|
151
|
+
@editor.current_buffer.replace_all_lines!(%w[line1 line2 line3])
|
|
152
|
+
@editor.current_window.cursor_y = 1 # on line2
|
|
153
|
+
|
|
154
|
+
@dispatcher.dispatch_ex(@editor, "r #{path}")
|
|
155
|
+
|
|
156
|
+
assert_equal %w[line1 line2 aaa bbb line3], @editor.current_buffer.lines
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def test_dispatch_ex_read_with_range_inserts_after_specified_line
|
|
161
|
+
@editor.materialize_intro_buffer!
|
|
162
|
+
Dir.mktmpdir do |dir|
|
|
163
|
+
path = File.join(dir, "data.txt")
|
|
164
|
+
File.write(path, "inserted\n")
|
|
165
|
+
@editor.current_buffer.replace_all_lines!(%w[line1 line2 line3])
|
|
166
|
+
|
|
167
|
+
@dispatcher.dispatch_ex(@editor, "2r #{path}")
|
|
168
|
+
|
|
169
|
+
assert_equal %w[line1 line2 inserted line3], @editor.current_buffer.lines
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def test_dispatch_ex_read_shell_command_inserts_output
|
|
174
|
+
@editor.materialize_intro_buffer!
|
|
175
|
+
@editor.current_buffer.replace_all_lines!(%w[line1 line2])
|
|
176
|
+
@editor.current_window.cursor_y = 0
|
|
177
|
+
|
|
178
|
+
@dispatcher.dispatch_ex(@editor, "r !echo hello")
|
|
179
|
+
|
|
180
|
+
assert_equal %w[line1 hello line2], @editor.current_buffer.lines
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def test_dispatch_ex_write_to_shell_command
|
|
184
|
+
@editor.materialize_intro_buffer!
|
|
185
|
+
@editor.current_buffer.replace_all_lines!(%w[hello world])
|
|
186
|
+
|
|
187
|
+
Dir.mktmpdir do |dir|
|
|
188
|
+
outfile = File.join(dir, "out.txt")
|
|
189
|
+
@dispatcher.dispatch_ex(@editor, "w !cat > #{outfile}")
|
|
190
|
+
|
|
191
|
+
assert_equal "hello\nworld\n", File.read(outfile)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def test_dispatch_ex_write_range_to_shell_command
|
|
196
|
+
@editor.materialize_intro_buffer!
|
|
197
|
+
@editor.current_buffer.replace_all_lines!(%w[aaa bbb ccc])
|
|
198
|
+
|
|
199
|
+
Dir.mktmpdir do |dir|
|
|
200
|
+
outfile = File.join(dir, "out.txt")
|
|
201
|
+
@dispatcher.dispatch_ex(@editor, "2,3w !cat > #{outfile}")
|
|
202
|
+
|
|
203
|
+
assert_equal "bbb\nccc\n", File.read(outfile)
|
|
204
|
+
end
|
|
135
205
|
end
|
|
136
206
|
|
|
137
207
|
def test_dispatch_ex_set_commands
|
data/test/display_width_test.rb
CHANGED
|
@@ -15,4 +15,45 @@ class DisplayWidthTest < Minitest::Test
|
|
|
15
15
|
ENV["RUVIM_AMBIGUOUS_WIDTH"] = prev
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
|
+
|
|
19
|
+
def test_zero_codepoint_returns_zero
|
|
20
|
+
assert_equal 0, RuVim::DisplayWidth.uncached_codepoint_width(0)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_expand_tabs_basic
|
|
24
|
+
assert_equal " hello", RuVim::DisplayWidth.expand_tabs("\thello", tabstop: 2)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_expand_tabs_mid_column
|
|
28
|
+
assert_equal "a hello", RuVim::DisplayWidth.expand_tabs("a\thello", tabstop: 2)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_expand_tabs_with_tabstop_4
|
|
32
|
+
assert_equal " hello", RuVim::DisplayWidth.expand_tabs("\thello", tabstop: 4)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_expand_tabs_preserves_non_tab_chars
|
|
36
|
+
assert_equal "hello", RuVim::DisplayWidth.expand_tabs("hello", tabstop: 2)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_expand_tabs_with_start_col
|
|
40
|
+
result = RuVim::DisplayWidth.expand_tabs("\tx", tabstop: 4, start_col: 1)
|
|
41
|
+
assert_equal " x", result
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_wide_codepoint_cjk
|
|
45
|
+
assert_equal 2, RuVim::DisplayWidth.cell_width("漢")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_wide_codepoint_fullwidth_form
|
|
49
|
+
assert_equal 2, RuVim::DisplayWidth.cell_width("A")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_combining_mark_returns_zero
|
|
53
|
+
assert_equal 0, RuVim::DisplayWidth.cell_width("\u0300")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_zero_width_joiner_returns_zero
|
|
57
|
+
assert_equal 0, RuVim::DisplayWidth.cell_width("\u200D")
|
|
58
|
+
end
|
|
18
59
|
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class ExCommandRegistryTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@registry = RuVim::ExCommandRegistry.instance
|
|
8
|
+
@saved_specs = @registry.instance_variable_get(:@specs).dup
|
|
9
|
+
@saved_lookup = @registry.instance_variable_get(:@lookup).dup
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def teardown
|
|
13
|
+
@registry.instance_variable_set(:@specs, @saved_specs)
|
|
14
|
+
@registry.instance_variable_set(:@lookup, @saved_lookup)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_register_and_resolve
|
|
18
|
+
@registry.clear!
|
|
19
|
+
spec = @registry.register("testcmd", call: ->(_ctx) {}, desc: "test")
|
|
20
|
+
assert_equal "testcmd", spec.name
|
|
21
|
+
|
|
22
|
+
resolved = @registry.resolve("testcmd")
|
|
23
|
+
assert_equal spec, resolved
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_register_with_aliases
|
|
27
|
+
@registry.clear!
|
|
28
|
+
spec = @registry.register("testcmd", call: ->(_ctx) {}, aliases: ["tc", "tcmd"])
|
|
29
|
+
|
|
30
|
+
assert_equal @registry.resolve("tc"), spec
|
|
31
|
+
assert_equal @registry.resolve("tcmd"), spec
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_register_duplicate_raises
|
|
35
|
+
@registry.clear!
|
|
36
|
+
@registry.register("testcmd", call: ->(_ctx) {})
|
|
37
|
+
|
|
38
|
+
assert_raises(RuVim::CommandError) do
|
|
39
|
+
@registry.register("testcmd", call: ->(_ctx) {})
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_register_alias_collision_raises
|
|
44
|
+
@registry.clear!
|
|
45
|
+
@registry.register("cmd1", call: ->(_ctx) {}, aliases: ["shared"])
|
|
46
|
+
|
|
47
|
+
assert_raises(RuVim::CommandError) do
|
|
48
|
+
@registry.register("cmd2", call: ->(_ctx) {}, aliases: ["shared"])
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_resolve_returns_nil_for_unknown
|
|
53
|
+
@registry.clear!
|
|
54
|
+
assert_nil @registry.resolve("nonexistent")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_fetch_raises_for_unknown
|
|
58
|
+
@registry.clear!
|
|
59
|
+
assert_raises(RuVim::CommandError) do
|
|
60
|
+
@registry.fetch("nonexistent")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_fetch_returns_spec
|
|
65
|
+
@registry.clear!
|
|
66
|
+
spec = @registry.register("testcmd", call: ->(_ctx) {})
|
|
67
|
+
assert_equal spec, @registry.fetch("testcmd")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_registered?
|
|
71
|
+
@registry.clear!
|
|
72
|
+
refute @registry.registered?("testcmd")
|
|
73
|
+
@registry.register("testcmd", call: ->(_ctx) {})
|
|
74
|
+
assert @registry.registered?("testcmd")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_all_returns_sorted
|
|
78
|
+
@registry.clear!
|
|
79
|
+
@registry.register("beta", call: ->(_ctx) {})
|
|
80
|
+
@registry.register("alpha", call: ->(_ctx) {})
|
|
81
|
+
names = @registry.all.map(&:name)
|
|
82
|
+
assert_equal ["alpha", "beta"], names
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_unregister
|
|
86
|
+
@registry.clear!
|
|
87
|
+
@registry.register("testcmd", call: ->(_ctx) {}, aliases: ["tc"])
|
|
88
|
+
|
|
89
|
+
removed = @registry.unregister("testcmd")
|
|
90
|
+
assert_equal "testcmd", removed.name
|
|
91
|
+
assert_nil @registry.resolve("testcmd")
|
|
92
|
+
assert_nil @registry.resolve("tc")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_unregister_unknown_returns_nil
|
|
96
|
+
@registry.clear!
|
|
97
|
+
assert_nil @registry.unregister("nonexistent")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_clear!
|
|
101
|
+
@registry.clear!
|
|
102
|
+
@registry.register("testcmd", call: ->(_ctx) {})
|
|
103
|
+
@registry.clear!
|
|
104
|
+
assert_equal [], @registry.all
|
|
105
|
+
end
|
|
106
|
+
end
|