ruvim 0.2.0 → 0.3.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/.github/workflows/test.yml +4 -0
- data/AGENTS.md +84 -0
- data/CLAUDE.md +1 -0
- data/docs/binding.md +23 -0
- data/docs/command.md +85 -0
- data/docs/config.md +2 -2
- data/docs/done.md +21 -0
- data/docs/spec.md +157 -12
- data/docs/todo.md +1 -5
- data/docs/vim_diff.md +94 -172
- data/lib/ruvim/app.rb +882 -69
- data/lib/ruvim/buffer.rb +35 -1
- data/lib/ruvim/cli.rb +12 -3
- data/lib/ruvim/clipboard.rb +2 -0
- data/lib/ruvim/command_invocation.rb +3 -1
- data/lib/ruvim/command_line.rb +2 -0
- data/lib/ruvim/command_registry.rb +2 -0
- data/lib/ruvim/config_dsl.rb +2 -0
- data/lib/ruvim/config_loader.rb +2 -0
- data/lib/ruvim/context.rb +2 -0
- data/lib/ruvim/dispatcher.rb +143 -13
- data/lib/ruvim/display_width.rb +3 -0
- data/lib/ruvim/editor.rb +455 -71
- data/lib/ruvim/ex_command_registry.rb +2 -0
- data/lib/ruvim/global_commands.rb +890 -63
- data/lib/ruvim/highlighter.rb +16 -21
- data/lib/ruvim/input.rb +39 -28
- data/lib/ruvim/keymap_manager.rb +83 -0
- data/lib/ruvim/keyword_chars.rb +2 -0
- data/lib/ruvim/lang/base.rb +25 -0
- data/lib/ruvim/lang/csv.rb +18 -0
- data/lib/ruvim/lang/json.rb +18 -0
- data/lib/ruvim/lang/markdown.rb +170 -0
- data/lib/ruvim/lang/ruby.rb +236 -0
- data/lib/ruvim/lang/scheme.rb +44 -0
- data/lib/ruvim/lang/tsv.rb +19 -0
- data/lib/ruvim/rich_view/markdown_renderer.rb +248 -0
- data/lib/ruvim/rich_view/table_renderer.rb +176 -0
- data/lib/ruvim/rich_view.rb +93 -0
- data/lib/ruvim/screen.rb +503 -106
- data/lib/ruvim/terminal.rb +18 -1
- data/lib/ruvim/text_metrics.rb +2 -0
- data/lib/ruvim/version.rb +1 -1
- data/lib/ruvim/window.rb +2 -0
- data/lib/ruvim.rb +14 -0
- data/test/app_completion_test.rb +73 -0
- data/test/app_dot_repeat_test.rb +13 -0
- data/test/app_motion_test.rb +13 -0
- data/test/app_scenario_test.rb +729 -1
- data/test/app_startup_test.rb +187 -0
- data/test/arglist_test.rb +113 -0
- data/test/buffer_test.rb +49 -30
- data/test/dispatcher_test.rb +322 -0
- data/test/editor_register_test.rb +23 -0
- data/test/highlighter_test.rb +121 -0
- data/test/indent_test.rb +201 -0
- data/test/input_screen_integration_test.rb +40 -2
- data/test/markdown_renderer_test.rb +279 -0
- data/test/on_save_hook_test.rb +150 -0
- data/test/rich_view_test.rb +478 -0
- data/test/screen_test.rb +304 -0
- metadata +33 -2
data/test/app_startup_test.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require_relative "test_helper"
|
|
2
2
|
require "tempfile"
|
|
3
3
|
require "stringio"
|
|
4
|
+
require "tmpdir"
|
|
4
5
|
|
|
5
6
|
class AppStartupTest < Minitest::Test
|
|
6
7
|
def test_startup_ex_command_runs_after_boot
|
|
@@ -107,6 +108,34 @@ class AppStartupTest < Minitest::Test
|
|
|
107
108
|
end
|
|
108
109
|
end
|
|
109
110
|
|
|
111
|
+
def test_startup_horizontal_split_focuses_first_file
|
|
112
|
+
Tempfile.create(["ruvim-a", ".txt"]) do |a|
|
|
113
|
+
Tempfile.create(["ruvim-b", ".txt"]) do |b|
|
|
114
|
+
a.write("a\n"); a.flush
|
|
115
|
+
b.write("b\n"); b.flush
|
|
116
|
+
|
|
117
|
+
app = RuVim::App.new(paths: [a.path, b.path], clean: true, startup_open_layout: :horizontal)
|
|
118
|
+
editor = app.instance_variable_get(:@editor)
|
|
119
|
+
|
|
120
|
+
assert_equal a.path, editor.current_buffer.path
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_startup_vertical_split_focuses_first_file
|
|
126
|
+
Tempfile.create(["ruvim-a", ".txt"]) do |a|
|
|
127
|
+
Tempfile.create(["ruvim-b", ".txt"]) do |b|
|
|
128
|
+
a.write("a\n"); a.flush
|
|
129
|
+
b.write("b\n"); b.flush
|
|
130
|
+
|
|
131
|
+
app = RuVim::App.new(paths: [a.path, b.path], clean: true, startup_open_layout: :vertical)
|
|
132
|
+
editor = app.instance_variable_get(:@editor)
|
|
133
|
+
|
|
134
|
+
assert_equal a.path, editor.current_buffer.path
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
110
139
|
def test_startup_tab_layout_opens_multiple_tabs
|
|
111
140
|
Tempfile.create(["ruvim-a", ".txt"]) do |a|
|
|
112
141
|
Tempfile.create(["ruvim-b", ".txt"]) do |b|
|
|
@@ -121,6 +150,21 @@ class AppStartupTest < Minitest::Test
|
|
|
121
150
|
end
|
|
122
151
|
end
|
|
123
152
|
|
|
153
|
+
def test_startup_tab_layout_focuses_first_tab
|
|
154
|
+
Tempfile.create(["ruvim-a", ".txt"]) do |a|
|
|
155
|
+
Tempfile.create(["ruvim-b", ".txt"]) do |b|
|
|
156
|
+
a.write("a\n"); a.flush
|
|
157
|
+
b.write("b\n"); b.flush
|
|
158
|
+
|
|
159
|
+
app = RuVim::App.new(paths: [a.path, b.path], clean: true, startup_open_layout: :tab)
|
|
160
|
+
editor = app.instance_variable_get(:@editor)
|
|
161
|
+
|
|
162
|
+
assert_equal 1, editor.current_tabpage_number
|
|
163
|
+
assert_equal a.path, editor.current_buffer.path
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
124
168
|
def test_restricted_mode_disables_ex_ruby
|
|
125
169
|
app = RuVim::App.new(clean: true, restricted: true)
|
|
126
170
|
editor = app.instance_variable_get(:@editor)
|
|
@@ -206,4 +250,147 @@ class AppStartupTest < Minitest::Test
|
|
|
206
250
|
assert_match(/startup_actions\.done/, text)
|
|
207
251
|
end
|
|
208
252
|
end
|
|
253
|
+
|
|
254
|
+
def test_stdin_stream_mode_uses_stream_buffer_and_appends_input
|
|
255
|
+
app = RuVim::App.new(
|
|
256
|
+
clean: true,
|
|
257
|
+
stdin: StringIO.new("line1\nline2\n"),
|
|
258
|
+
ui_stdin: StringIO.new,
|
|
259
|
+
stdin_stream_mode: true
|
|
260
|
+
)
|
|
261
|
+
editor = app.instance_variable_get(:@editor)
|
|
262
|
+
|
|
263
|
+
20.times do
|
|
264
|
+
app.send(:drain_stream_events!)
|
|
265
|
+
thread = app.instance_variable_get(:@stream_reader_thread)
|
|
266
|
+
break unless thread&.alive?
|
|
267
|
+
sleep 0.005
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
buf = editor.current_buffer
|
|
271
|
+
assert_equal :stream, buf.kind
|
|
272
|
+
assert_equal "[stdin]", buf.display_name
|
|
273
|
+
assert_equal true, buf.readonly?
|
|
274
|
+
assert_equal false, buf.modifiable?
|
|
275
|
+
assert_includes [:live, :closed], buf.stream_state
|
|
276
|
+
assert_equal ["line1", "line2", ""], buf.lines
|
|
277
|
+
assert_match(/\[stdin\] (follow|EOF)/, editor.message)
|
|
278
|
+
ensure
|
|
279
|
+
app&.send(:shutdown_stream_reader!)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def test_large_file_threshold_uses_async_loader
|
|
283
|
+
prev = ENV["RUVIM_ASYNC_FILE_THRESHOLD_BYTES"]
|
|
284
|
+
app = nil
|
|
285
|
+
Tempfile.create(["ruvim-large-async", ".txt"]) do |f|
|
|
286
|
+
begin
|
|
287
|
+
f.write("a\nb\nc\n")
|
|
288
|
+
f.flush
|
|
289
|
+
|
|
290
|
+
ENV["RUVIM_ASYNC_FILE_THRESHOLD_BYTES"] = "1"
|
|
291
|
+
app = RuVim::App.new(clean: true)
|
|
292
|
+
editor = app.instance_variable_get(:@editor)
|
|
293
|
+
|
|
294
|
+
buf = editor.open_path(f.path)
|
|
295
|
+
assert_match(/loading/i, editor.message)
|
|
296
|
+
assert_includes [:live, :closed], buf.loading_state
|
|
297
|
+
assert_equal false, buf.modifiable?
|
|
298
|
+
|
|
299
|
+
100.times do
|
|
300
|
+
app.send(:drain_stream_events!)
|
|
301
|
+
break if buf.loading_state != :live
|
|
302
|
+
state = app.instance_variable_get(:@async_file_loads)[buf.id]
|
|
303
|
+
break unless state && state[:thread]&.alive?
|
|
304
|
+
sleep 0.005
|
|
305
|
+
end
|
|
306
|
+
app.send(:drain_stream_events!)
|
|
307
|
+
|
|
308
|
+
assert_equal :closed, buf.loading_state
|
|
309
|
+
assert_equal true, buf.modifiable?
|
|
310
|
+
assert_equal %w[a b c], buf.lines
|
|
311
|
+
assert_match(/#{Regexp.escape(f.path)}/, editor.message)
|
|
312
|
+
ensure
|
|
313
|
+
ENV["RUVIM_ASYNC_FILE_THRESHOLD_BYTES"] = prev
|
|
314
|
+
app&.send(:shutdown_background_readers!)
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def test_very_large_file_shows_prefix_then_appends_rest
|
|
320
|
+
prev_async = ENV["RUVIM_ASYNC_FILE_THRESHOLD_BYTES"]
|
|
321
|
+
prev_prefix = ENV["RUVIM_ASYNC_FILE_PREFIX_BYTES"]
|
|
322
|
+
app = nil
|
|
323
|
+
Tempfile.create(["ruvim-large-prefix", ".txt"]) do |f|
|
|
324
|
+
begin
|
|
325
|
+
f.write("ab\ncd\nef\n")
|
|
326
|
+
f.flush
|
|
327
|
+
|
|
328
|
+
ENV["RUVIM_ASYNC_FILE_THRESHOLD_BYTES"] = "1"
|
|
329
|
+
ENV["RUVIM_ASYNC_FILE_PREFIX_BYTES"] = "4"
|
|
330
|
+
app = RuVim::App.new(clean: true)
|
|
331
|
+
editor = app.instance_variable_get(:@editor)
|
|
332
|
+
|
|
333
|
+
buf = editor.open_path(f.path)
|
|
334
|
+
assert_equal :live, buf.loading_state
|
|
335
|
+
assert_equal ["ab", "c"], buf.lines
|
|
336
|
+
assert_match(/showing first/i, editor.message)
|
|
337
|
+
|
|
338
|
+
100.times do
|
|
339
|
+
app.send(:drain_stream_events!)
|
|
340
|
+
break if buf.loading_state != :live
|
|
341
|
+
sleep 0.005
|
|
342
|
+
end
|
|
343
|
+
app.send(:drain_stream_events!)
|
|
344
|
+
|
|
345
|
+
assert_equal :closed, buf.loading_state
|
|
346
|
+
assert_equal %w[ab cd ef], buf.lines
|
|
347
|
+
ensure
|
|
348
|
+
ENV["RUVIM_ASYNC_FILE_THRESHOLD_BYTES"] = prev_async
|
|
349
|
+
ENV["RUVIM_ASYNC_FILE_PREFIX_BYTES"] = prev_prefix
|
|
350
|
+
app&.send(:shutdown_background_readers!)
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def test_command_line_history_persists_in_xdg_state_home
|
|
356
|
+
Dir.mktmpdir("ruvim-history-xdg") do |dir|
|
|
357
|
+
prev_xdg = ENV["XDG_STATE_HOME"]
|
|
358
|
+
prev_home = ENV["HOME"]
|
|
359
|
+
ENV["XDG_STATE_HOME"] = dir
|
|
360
|
+
ENV["HOME"] = dir
|
|
361
|
+
|
|
362
|
+
app1 = RuVim::App.new(clean: true)
|
|
363
|
+
app1.send(:push_command_line_history, ":", "set number")
|
|
364
|
+
app1.send(:push_command_line_history, "/", "foo")
|
|
365
|
+
app1.send(:push_command_line_history, "?", "bar")
|
|
366
|
+
app1.send(:save_command_line_history!)
|
|
367
|
+
|
|
368
|
+
path = File.join(dir, "ruvim", "history.json")
|
|
369
|
+
assert_equal true, File.file?(path)
|
|
370
|
+
|
|
371
|
+
app2 = RuVim::App.new(clean: true)
|
|
372
|
+
hist = app2.instance_variable_get(:@cmdline_history)
|
|
373
|
+
assert_equal ["set number"], hist[":"]
|
|
374
|
+
assert_equal ["foo"], hist["/"]
|
|
375
|
+
assert_equal ["bar"], hist["?"]
|
|
376
|
+
ensure
|
|
377
|
+
ENV["XDG_STATE_HOME"] = prev_xdg
|
|
378
|
+
ENV["HOME"] = prev_home
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def test_command_line_history_path_falls_back_to_home_dot_ruvim
|
|
383
|
+
Dir.mktmpdir("ruvim-history-home") do |dir|
|
|
384
|
+
prev_xdg = ENV["XDG_STATE_HOME"]
|
|
385
|
+
prev_home = ENV["HOME"]
|
|
386
|
+
ENV.delete("XDG_STATE_HOME")
|
|
387
|
+
ENV["HOME"] = dir
|
|
388
|
+
|
|
389
|
+
app = RuVim::App.new(clean: true)
|
|
390
|
+
assert_equal File.join(dir, ".ruvim", "history.json"), app.send(:command_line_history_file_path)
|
|
391
|
+
ensure
|
|
392
|
+
ENV["XDG_STATE_HOME"] = prev_xdg
|
|
393
|
+
ENV["HOME"] = prev_home
|
|
394
|
+
end
|
|
395
|
+
end
|
|
209
396
|
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class ArglistTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@editor = RuVim::Editor.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def test_initial_arglist_is_empty
|
|
9
|
+
assert_empty @editor.arglist
|
|
10
|
+
assert_equal 0, @editor.arglist_index
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_set_arglist
|
|
14
|
+
paths = %w[file1.txt file2.txt file3.txt]
|
|
15
|
+
@editor.set_arglist(paths)
|
|
16
|
+
|
|
17
|
+
assert_equal paths, @editor.arglist
|
|
18
|
+
assert_equal 0, @editor.arglist_index
|
|
19
|
+
assert_equal "file1.txt", @editor.arglist_current
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_arglist_next
|
|
23
|
+
@editor.set_arglist(%w[file1.txt file2.txt file3.txt])
|
|
24
|
+
|
|
25
|
+
assert_equal "file2.txt", @editor.arglist_next
|
|
26
|
+
assert_equal 1, @editor.arglist_index
|
|
27
|
+
|
|
28
|
+
assert_equal "file3.txt", @editor.arglist_next
|
|
29
|
+
assert_equal 2, @editor.arglist_index
|
|
30
|
+
|
|
31
|
+
error = assert_raises(RuVim::CommandError) do
|
|
32
|
+
@editor.arglist_next
|
|
33
|
+
end
|
|
34
|
+
assert_equal "Already at last argument", error.message
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_arglist_prev
|
|
38
|
+
@editor.set_arglist(%w[file1.txt file2.txt file3.txt])
|
|
39
|
+
@editor.arglist_next(2) # Move to index 2
|
|
40
|
+
|
|
41
|
+
assert_equal "file2.txt", @editor.arglist_prev
|
|
42
|
+
assert_equal 1, @editor.arglist_index
|
|
43
|
+
|
|
44
|
+
assert_equal "file1.txt", @editor.arglist_prev
|
|
45
|
+
assert_equal 0, @editor.arglist_index
|
|
46
|
+
|
|
47
|
+
error = assert_raises(RuVim::CommandError) do
|
|
48
|
+
@editor.arglist_prev
|
|
49
|
+
end
|
|
50
|
+
assert_equal "Already at first argument", error.message
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_arglist_first
|
|
54
|
+
@editor.set_arglist(%w[file1.txt file2.txt file3.txt])
|
|
55
|
+
@editor.arglist_next(2) # Move to index 2
|
|
56
|
+
|
|
57
|
+
assert_equal "file1.txt", @editor.arglist_first
|
|
58
|
+
assert_equal 0, @editor.arglist_index
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_arglist_last
|
|
62
|
+
@editor.set_arglist(%w[file1.txt file2.txt file3.txt])
|
|
63
|
+
|
|
64
|
+
assert_equal "file3.txt", @editor.arglist_last
|
|
65
|
+
assert_equal 2, @editor.arglist_index
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_arglist_next_with_count
|
|
69
|
+
@editor.set_arglist(%w[file1.txt file2.txt file3.txt file4.txt])
|
|
70
|
+
|
|
71
|
+
assert_equal "file3.txt", @editor.arglist_next(2)
|
|
72
|
+
assert_equal 2, @editor.arglist_index
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_arglist_prev_with_count
|
|
76
|
+
@editor.set_arglist(%w[file1.txt file2.txt file3.txt file4.txt])
|
|
77
|
+
@editor.arglist_last
|
|
78
|
+
|
|
79
|
+
assert_equal "file2.txt", @editor.arglist_prev(2)
|
|
80
|
+
assert_equal 1, @editor.arglist_index
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_arglist_empty_operations
|
|
84
|
+
assert_nil @editor.arglist_current
|
|
85
|
+
assert_nil @editor.arglist_first
|
|
86
|
+
assert_nil @editor.arglist_last
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_startup_multiple_files_creates_buffers
|
|
90
|
+
# Simulate what open_startup_paths! does without layout option:
|
|
91
|
+
# all files should be loaded as buffers so they appear in :ls
|
|
92
|
+
paths = [File.expand_path("../../t.md", __FILE__),
|
|
93
|
+
File.expand_path("../../t.rb", __FILE__)]
|
|
94
|
+
existing = paths.select { |p| File.exist?(p) }
|
|
95
|
+
skip "need t.md and t.rb in project root" if existing.length < 2
|
|
96
|
+
|
|
97
|
+
@editor.ensure_bootstrap_buffer!
|
|
98
|
+
@editor.set_arglist(paths)
|
|
99
|
+
|
|
100
|
+
# Open first file (displayed in current window)
|
|
101
|
+
first_buf = @editor.add_buffer_from_file(paths[0])
|
|
102
|
+
@editor.switch_to_buffer(first_buf.id)
|
|
103
|
+
|
|
104
|
+
# Load remaining files as buffers (not displayed, but registered)
|
|
105
|
+
paths[1..].each { |p| @editor.add_buffer_from_file(p) }
|
|
106
|
+
|
|
107
|
+
# All files should be in the buffer list
|
|
108
|
+
all_paths = @editor.buffer_ids.map { |id| @editor.buffers[id].path }
|
|
109
|
+
paths.each do |p|
|
|
110
|
+
assert_includes all_paths, p, "#{p} should appear in buffer list"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
data/test/buffer_test.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative "test_helper"
|
|
2
|
+
require "tmpdir"
|
|
2
3
|
|
|
3
4
|
class BufferTest < Minitest::Test
|
|
4
5
|
def test_insert_and_undo_redo_group
|
|
@@ -25,48 +26,66 @@ class BufferTest < Minitest::Test
|
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def test_reload_from_file_clears_modified_and_history
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
Dir.mktmpdir do |dir|
|
|
30
|
+
path = File.join(dir, "ruvim_buffer_reload_test.txt")
|
|
31
|
+
File.write(path, "one\n")
|
|
32
|
+
b = RuVim::Buffer.from_file(id: 1, path: path)
|
|
33
|
+
b.insert_char(0, 0, "X")
|
|
34
|
+
assert b.modified?
|
|
35
|
+
assert b.can_undo?
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
b.reload_from_file!
|
|
38
|
+
assert_equal ["one"], b.lines
|
|
39
|
+
refute b.modified?
|
|
40
|
+
refute b.can_undo?
|
|
41
|
+
end
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
def test_utf8_file_is_loaded_as_utf8_text_not_binary_bytes
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
Dir.mktmpdir do |dir|
|
|
46
|
+
path = File.join(dir, "ruvim_utf8_test.txt")
|
|
47
|
+
File.binwrite(path, "bar 日本語 編集\n".encode("UTF-8"))
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
b = RuVim::Buffer.from_file(id: 1, path: path)
|
|
50
|
+
assert_equal Encoding::UTF_8, b.line_at(0).encoding
|
|
51
|
+
assert_equal "bar 日本語 編集", b.line_at(0)
|
|
52
|
+
assert_equal 10, b.line_length(0)
|
|
53
|
+
end
|
|
49
54
|
end
|
|
50
55
|
|
|
51
56
|
def test_invalid_bytes_are_decoded_to_valid_utf8_with_replacement
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
Dir.mktmpdir do |dir|
|
|
58
|
+
path = File.join(dir, "ruvim_invalid_bytes_test.txt")
|
|
59
|
+
File.binwrite(path, "A\xFFB\n".b)
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
b = RuVim::Buffer.from_file(id: 1, path: path)
|
|
62
|
+
line = b.line_at(0)
|
|
63
|
+
assert_equal Encoding::UTF_8, line.encoding
|
|
64
|
+
assert_equal true, line.valid_encoding?
|
|
65
|
+
assert_match(/A.*B/, line)
|
|
66
|
+
end
|
|
60
67
|
end
|
|
61
68
|
|
|
62
69
|
def test_write_to_saves_utf8_text
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
Dir.mktmpdir do |dir|
|
|
71
|
+
path = File.join(dir, "ruvim_write_utf8_test.txt")
|
|
72
|
+
b = RuVim::Buffer.new(id: 1, lines: ["日本語", "abc"])
|
|
73
|
+
b.write_to(path)
|
|
74
|
+
|
|
75
|
+
data = File.binread(path)
|
|
76
|
+
text = data.force_encoding(Encoding::UTF_8)
|
|
77
|
+
assert_equal true, text.valid_encoding?
|
|
78
|
+
assert_equal "日本語\nabc", text
|
|
79
|
+
end
|
|
80
|
+
end
|
|
66
81
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
82
|
+
def test_append_stream_text_updates_lines_without_marking_modified
|
|
83
|
+
b = RuVim::Buffer.new(id: 1, lines: [""])
|
|
84
|
+
b.append_stream_text!("a\n")
|
|
85
|
+
b.append_stream_text!("b")
|
|
86
|
+
b.append_stream_text!("\n\nc\n")
|
|
87
|
+
|
|
88
|
+
assert_equal ["a", "b", "", "c", ""], b.lines
|
|
89
|
+
refute b.modified?
|
|
71
90
|
end
|
|
72
91
|
end
|