ruvim 0.3.0 → 0.4.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +18 -6
  3. data/README.md +15 -1
  4. data/docs/binding.md +16 -0
  5. data/docs/command.md +78 -4
  6. data/docs/config.md +10 -2
  7. data/docs/spec.md +60 -9
  8. data/docs/tutorial.md +24 -0
  9. data/docs/vim_diff.md +18 -8
  10. data/lib/ruvim/app.rb +290 -8
  11. data/lib/ruvim/buffer.rb +14 -2
  12. data/lib/ruvim/cli.rb +6 -0
  13. data/lib/ruvim/editor.rb +12 -1
  14. data/lib/ruvim/file_watcher.rb +243 -0
  15. data/lib/ruvim/git/blame.rb +245 -0
  16. data/lib/ruvim/git/branch.rb +97 -0
  17. data/lib/ruvim/git/commit.rb +102 -0
  18. data/lib/ruvim/git/diff.rb +129 -0
  19. data/lib/ruvim/git/handler.rb +84 -0
  20. data/lib/ruvim/git/log.rb +41 -0
  21. data/lib/ruvim/git/status.rb +103 -0
  22. data/lib/ruvim/global_commands.rb +176 -42
  23. data/lib/ruvim/highlighter.rb +3 -1
  24. data/lib/ruvim/input.rb +1 -0
  25. data/lib/ruvim/lang/diff.rb +41 -0
  26. data/lib/ruvim/lang/json.rb +34 -0
  27. data/lib/ruvim/rich_view/json_renderer.rb +131 -0
  28. data/lib/ruvim/rich_view/jsonl_renderer.rb +57 -0
  29. data/lib/ruvim/rich_view.rb +16 -0
  30. data/lib/ruvim/screen.rb +9 -12
  31. data/lib/ruvim/version.rb +1 -1
  32. data/lib/ruvim.rb +10 -0
  33. data/test/app_completion_test.rb +25 -0
  34. data/test/app_scenario_test.rb +169 -0
  35. data/test/cli_test.rb +14 -0
  36. data/test/clipboard_test.rb +67 -0
  37. data/test/command_line_test.rb +118 -0
  38. data/test/config_dsl_test.rb +87 -0
  39. data/test/display_width_test.rb +41 -0
  40. data/test/file_watcher_test.rb +197 -0
  41. data/test/follow_test.rb +199 -0
  42. data/test/git_blame_test.rb +713 -0
  43. data/test/highlighter_test.rb +44 -0
  44. data/test/indent_test.rb +86 -0
  45. data/test/rich_view_test.rb +256 -0
  46. data/test/search_option_test.rb +19 -0
  47. data/test/test_helper.rb +9 -0
  48. metadata +17 -1
@@ -14,6 +14,12 @@ class HighlighterTest < Minitest::Test
14
14
  assert_equal "\e[33m", cols[6] # number start
15
15
  end
16
16
 
17
+ def test_jsonl_highlighter_reuses_json_colors
18
+ cols = RuVim::Highlighter.color_columns("jsonl", '{"a": 10}')
19
+ assert_equal "\e[36m", cols[1] # key chars
20
+ assert_equal "\e[33m", cols[6] # number start
21
+ end
22
+
17
23
  def test_ruby_highlighter_marks_instance_variables_and_constants
18
24
  cols = RuVim::Highlighter.color_columns("ruby", "@x = Foo")
19
25
  assert_equal "\e[93m", cols[0] # @x
@@ -134,4 +140,42 @@ class HighlighterTest < Minitest::Test
134
140
  cols = RuVim::Highlighter.color_columns("scheme", "")
135
141
  assert_empty cols
136
142
  end
143
+
144
+ # --- Diff ---
145
+
146
+ def test_diff_add_line_green
147
+ cols = RuVim::Highlighter.color_columns("diff", "+added line")
148
+ refute_empty cols
149
+ assert_equal "\e[32m", cols[0]
150
+ assert_equal "\e[32m", cols[5]
151
+ end
152
+
153
+ def test_diff_delete_line_red
154
+ cols = RuVim::Highlighter.color_columns("diff", "-removed line")
155
+ refute_empty cols
156
+ assert_equal "\e[31m", cols[0]
157
+ end
158
+
159
+ def test_diff_hunk_header_cyan
160
+ cols = RuVim::Highlighter.color_columns("diff", "@@ -1,3 +1,4 @@ def foo")
161
+ refute_empty cols
162
+ assert_equal "\e[36m", cols[0]
163
+ end
164
+
165
+ def test_diff_header_bold
166
+ cols = RuVim::Highlighter.color_columns("diff", "diff --git a/foo.rb b/foo.rb")
167
+ refute_empty cols
168
+ assert_equal "\e[1m", cols[0]
169
+ end
170
+
171
+ def test_diff_context_line_no_color
172
+ cols = RuVim::Highlighter.color_columns("diff", " context line")
173
+ assert_empty cols
174
+ end
175
+
176
+ def test_diff_meta_line_yellow
177
+ cols = RuVim::Highlighter.color_columns("diff", "index abc..def 100644")
178
+ refute_empty cols
179
+ assert_equal "\e[33m", cols[0]
180
+ end
137
181
  end
data/test/indent_test.rb CHANGED
@@ -1,5 +1,91 @@
1
1
  require_relative "test_helper"
2
2
 
3
+ class JsonIndentTest < Minitest::Test
4
+ def calc(lines, target_row, sw = 2)
5
+ RuVim::Lang::Json.calculate_indent(lines, target_row, sw)
6
+ end
7
+
8
+ def test_first_line_is_zero
9
+ assert_equal 0, calc(["{"], 0)
10
+ end
11
+
12
+ def test_after_open_brace
13
+ lines = ["{", ' "key": "value"']
14
+ assert_equal 2, calc(lines, 1)
15
+ end
16
+
17
+ def test_close_brace
18
+ lines = ["{", ' "key": "value"', "}"]
19
+ assert_equal 0, calc(lines, 2)
20
+ end
21
+
22
+ def test_after_open_bracket
23
+ lines = ["[", " 1"]
24
+ assert_equal 2, calc(lines, 1)
25
+ end
26
+
27
+ def test_close_bracket
28
+ lines = ["[", " 1,", " 2", "]"]
29
+ assert_equal 0, calc(lines, 3)
30
+ end
31
+
32
+ def test_nested_objects
33
+ lines = [
34
+ "{",
35
+ ' "a": {',
36
+ ' "b": 1',
37
+ " }",
38
+ "}"
39
+ ]
40
+ assert_equal 2, calc(lines, 1)
41
+ assert_equal 4, calc(lines, 2)
42
+ assert_equal 2, calc(lines, 3)
43
+ assert_equal 0, calc(lines, 4)
44
+ end
45
+
46
+ def test_array_in_object
47
+ lines = [
48
+ "{",
49
+ ' "items": [',
50
+ " 1,",
51
+ " 2",
52
+ " ]",
53
+ "}"
54
+ ]
55
+ assert_equal 2, calc(lines, 1)
56
+ assert_equal 4, calc(lines, 2)
57
+ assert_equal 4, calc(lines, 3)
58
+ assert_equal 2, calc(lines, 4)
59
+ assert_equal 0, calc(lines, 5)
60
+ end
61
+
62
+ def test_shiftwidth_4
63
+ lines = ["{", ' "key": 1', "}"]
64
+ assert_equal 4, calc(lines, 1, 4)
65
+ assert_equal 0, calc(lines, 2, 4)
66
+ end
67
+
68
+ def test_indent_trigger_open_brace
69
+ assert RuVim::Lang::Json.indent_trigger?("{")
70
+ assert RuVim::Lang::Json.indent_trigger?(' "key": {')
71
+ assert RuVim::Lang::Json.indent_trigger?(' "key": [')
72
+ end
73
+
74
+ def test_indent_trigger_no_trigger
75
+ refute RuVim::Lang::Json.indent_trigger?(' "key": "value"')
76
+ refute RuVim::Lang::Json.indent_trigger?("}")
77
+ end
78
+
79
+ def test_dedent_trigger_close_brace
80
+ assert_kind_of Regexp, RuVim::Lang::Json.dedent_trigger("}")
81
+ assert_kind_of Regexp, RuVim::Lang::Json.dedent_trigger("]")
82
+ end
83
+
84
+ def test_dedent_trigger_no_trigger
85
+ assert_nil RuVim::Lang::Json.dedent_trigger("a")
86
+ end
87
+ end
88
+
3
89
  class RubyIndentTest < Minitest::Test
4
90
  def calc(lines, target_row, sw = 2)
5
91
  RuVim::Lang::Ruby.calculate_indent(lines, target_row, sw)
@@ -459,6 +459,161 @@ class RichViewTest < Minitest::Test
459
459
  assert_equal dc0, dc1, "Second field start should align across CJK and ASCII rows"
460
460
  end
461
461
 
462
+ # --- JSON Rich View tests ---
463
+
464
+ def test_json_registered
465
+ assert RuVim::RichView.renderer_for("json")
466
+ end
467
+
468
+ def test_json_open_creates_virtual_buffer
469
+ editor = fresh_editor
470
+ buf = editor.current_buffer
471
+ buf.replace_all_lines!(['{"a":1,"b":[2,3]}'])
472
+ buf.options["filetype"] = "json"
473
+ count_before = editor.buffers.length
474
+
475
+ RuVim::RichView.open!(editor, format: "json")
476
+ assert_equal count_before + 1, editor.buffers.length
477
+ new_buf = editor.current_buffer
478
+ refute_equal buf.id, new_buf.id
479
+ assert_equal :json_formatted, new_buf.kind
480
+ assert new_buf.readonly?
481
+ end
482
+
483
+ def test_json_open_binds_close_keys
484
+ editor = fresh_editor
485
+ editor.keymap_manager = RuVim::KeymapManager.new
486
+ buf = editor.current_buffer
487
+ buf.replace_all_lines!(['{"a":1}'])
488
+ buf.options["filetype"] = "json"
489
+
490
+ RuVim::RichView.open!(editor, format: "json")
491
+ result = editor.keymap_manager.resolve_with_context(:normal, ["\e"], editor: editor)
492
+ assert_equal "rich.close_buffer", result.invocation.id
493
+ end
494
+
495
+ def test_json_open_pretty_prints
496
+ editor = fresh_editor
497
+ buf = editor.current_buffer
498
+ buf.replace_all_lines!(['{"a":1,"b":[2,3]}'])
499
+ buf.options["filetype"] = "json"
500
+
501
+ RuVim::RichView.open!(editor, format: "json")
502
+ new_buf = editor.current_buffer
503
+ lines = new_buf.lines
504
+ assert lines.length > 1, "Minified JSON should be expanded to multiple lines"
505
+ assert_equal "{", lines.first.strip
506
+ assert_equal "}", lines.last.strip
507
+ end
508
+
509
+ def test_json_open_multiline_buffer
510
+ editor = fresh_editor
511
+ buf = editor.current_buffer
512
+ buf.replace_all_lines!(['{', '"key": "value"', '}'])
513
+ buf.options["filetype"] = "json"
514
+
515
+ RuVim::RichView.open!(editor, format: "json")
516
+ new_buf = editor.current_buffer
517
+ lines = new_buf.lines
518
+ assert lines.length >= 3
519
+ end
520
+
521
+ def test_json_open_invalid_json_shows_error
522
+ editor = fresh_editor
523
+ buf = editor.current_buffer
524
+ buf.replace_all_lines!(['{"invalid json'])
525
+ buf.options["filetype"] = "json"
526
+
527
+ RuVim::RichView.open!(editor, format: "json")
528
+ # Should stay on original buffer
529
+ assert_equal buf.id, editor.current_buffer.id
530
+ assert_match(/JSON/, editor.message.to_s)
531
+ end
532
+
533
+ def test_json_open_does_not_enter_rich_mode
534
+ editor = fresh_editor
535
+ buf = editor.current_buffer
536
+ buf.replace_all_lines!(['{"a":1}'])
537
+ buf.options["filetype"] = "json"
538
+
539
+ RuVim::RichView.open!(editor, format: "json")
540
+ # Virtual buffer approach — no rich mode
541
+ assert_equal :normal, editor.mode
542
+ assert_nil editor.rich_state
543
+ end
544
+
545
+ def test_json_cursor_maps_to_formatted_line
546
+ editor = fresh_editor
547
+ buf = editor.current_buffer
548
+ # {"a":1,"b":{"c":2}}
549
+ buf.replace_all_lines!(['{"a":1,"b":{"c":2}}'])
550
+ buf.options["filetype"] = "json"
551
+
552
+ # Place cursor at "c" key — find its offset
553
+ line = buf.line_at(0)
554
+ idx = line.index('"c"')
555
+ editor.current_window.cursor_x = idx
556
+
557
+ RuVim::RichView.open!(editor, format: "json")
558
+ new_buf = editor.current_buffer
559
+ # Cursor should be on the line containing "c"
560
+ cursor_line = new_buf.line_at(editor.current_window.cursor_y)
561
+ assert_match(/"c"/, cursor_line, "Cursor should be on the line with \"c\" key")
562
+ end
563
+
564
+ def test_json_cursor_maps_multiline_source
565
+ editor = fresh_editor
566
+ buf = editor.current_buffer
567
+ buf.replace_all_lines!(['{', ' "x": [1, 2, 3]', '}'])
568
+ buf.options["filetype"] = "json"
569
+
570
+ # Place cursor on line 1 at the "x" key (col 2 = opening quote)
571
+ editor.current_window.cursor_y = 1
572
+ editor.current_window.cursor_x = 2
573
+
574
+ RuVim::RichView.open!(editor, format: "json")
575
+ new_buf = editor.current_buffer
576
+ cursor_line = new_buf.line_at(editor.current_window.cursor_y)
577
+ assert_match(/"x"/, cursor_line, "Cursor should be on the line with \"x\" key")
578
+ end
579
+
580
+ def test_json_cursor_at_start_stays_at_start
581
+ editor = fresh_editor
582
+ buf = editor.current_buffer
583
+ buf.replace_all_lines!(['{"a":1}'])
584
+ buf.options["filetype"] = "json"
585
+ editor.current_window.cursor_x = 0
586
+
587
+ RuVim::RichView.open!(editor, format: "json")
588
+ assert_equal 0, editor.current_window.cursor_y
589
+ end
590
+
591
+ def test_json_significant_offset
592
+ r = RuVim::RichView::JsonRenderer
593
+ # {"a" — 4 significant chars: { " a "
594
+ assert_equal 4, r.significant_char_count('{"a"', 4)
595
+ # { "a" — space outside string skipped, still 4 significant
596
+ assert_equal 4, r.significant_char_count('{ "a"', 5)
597
+ end
598
+
599
+ def test_json_line_for_significant_offset
600
+ formatted = "{\n \"a\": 1\n}"
601
+ r = RuVim::RichView::JsonRenderer
602
+ # count 0 → line 0 (before any char)
603
+ assert_equal 0, r.line_for_significant_count(formatted, 0)
604
+ # count 1 → line 0 ({ is the 1st significant char, on line 0)
605
+ assert_equal 0, r.line_for_significant_count(formatted, 1)
606
+ # count 2 → line 1 (" opening quote of "a" is on line 1)
607
+ assert_equal 1, r.line_for_significant_count(formatted, 2)
608
+ end
609
+
610
+ def test_json_filetype_detected
611
+ editor = fresh_editor
612
+ buf = editor.current_buffer
613
+ buf.options["filetype"] = "json"
614
+ assert_equal "json", RuVim::RichView.detect_format(buf)
615
+ end
616
+
462
617
  # --- Filetype detection tests ---
463
618
 
464
619
  def test_detect_filetype_tsv
@@ -475,4 +630,105 @@ class RichViewTest < Minitest::Test
475
630
  editor = RuVim::Editor.new
476
631
  assert_equal "tsv", editor.detect_filetype("DATA.TSV")
477
632
  end
633
+
634
+ def test_detect_filetype_jsonl
635
+ editor = RuVim::Editor.new
636
+ assert_equal "jsonl", editor.detect_filetype("data.jsonl")
637
+ end
638
+
639
+ # --- JSONL Rich View tests ---
640
+
641
+ def test_jsonl_registered
642
+ assert RuVim::RichView.renderer_for("jsonl")
643
+ end
644
+
645
+ def test_jsonl_open_creates_virtual_buffer
646
+ editor = fresh_editor
647
+ buf = editor.current_buffer
648
+ buf.replace_all_lines!(['{"a":1}', '{"b":2}'])
649
+ buf.options["filetype"] = "jsonl"
650
+ count_before = editor.buffers.length
651
+
652
+ RuVim::RichView.open!(editor, format: "jsonl")
653
+ assert_equal count_before + 1, editor.buffers.length
654
+ new_buf = editor.current_buffer
655
+ refute_equal buf.id, new_buf.id
656
+ assert_equal :jsonl_formatted, new_buf.kind
657
+ assert new_buf.readonly?
658
+ end
659
+
660
+ def test_jsonl_open_binds_close_keys
661
+ editor = fresh_editor
662
+ editor.keymap_manager = RuVim::KeymapManager.new
663
+ buf = editor.current_buffer
664
+ buf.replace_all_lines!(['{"a":1}', '{"b":2}'])
665
+ buf.options["filetype"] = "jsonl"
666
+
667
+ RuVim::RichView.open!(editor, format: "jsonl")
668
+ result = editor.keymap_manager.resolve_with_context(:normal, ["\e"], editor: editor)
669
+ assert_equal "rich.close_buffer", result.invocation.id
670
+ end
671
+
672
+ def test_jsonl_open_pretty_prints_each_line
673
+ editor = fresh_editor
674
+ buf = editor.current_buffer
675
+ buf.replace_all_lines!(['{"a":1,"b":[2,3]}', '{"c":4}'])
676
+ buf.options["filetype"] = "jsonl"
677
+
678
+ RuVim::RichView.open!(editor, format: "jsonl")
679
+ new_buf = editor.current_buffer
680
+ lines = new_buf.lines
681
+ # Each JSON object should be expanded; separated by "---"
682
+ assert lines.length > 2, "JSONL should be expanded to multiple lines"
683
+ assert lines.any? { |l| l.include?("---") }, "Entries should be separated"
684
+ end
685
+
686
+ def test_jsonl_open_maps_cursor_to_correct_entry
687
+ editor = fresh_editor
688
+ buf = editor.current_buffer
689
+ buf.replace_all_lines!(['{"a":1}', '{"b":2}', '{"c":3}'])
690
+ buf.options["filetype"] = "jsonl"
691
+ editor.current_window.cursor_y = 1 # on second entry
692
+
693
+ RuVim::RichView.open!(editor, format: "jsonl")
694
+ new_buf = editor.current_buffer
695
+ cy = editor.current_window.cursor_y
696
+ # Cursor should be within the second entry's formatted block
697
+ nearby = (cy..[cy + 2, new_buf.lines.length - 1].min).map { |r| new_buf.line_at(r) }.join("\n")
698
+ assert_match(/"b"/, nearby, "Cursor should be near the entry with \"b\"")
699
+ end
700
+
701
+ def test_jsonl_open_skips_blank_lines
702
+ editor = fresh_editor
703
+ buf = editor.current_buffer
704
+ buf.replace_all_lines!(['{"a":1}', '', '{"b":2}'])
705
+ buf.options["filetype"] = "jsonl"
706
+
707
+ RuVim::RichView.open!(editor, format: "jsonl")
708
+ new_buf = editor.current_buffer
709
+ lines = new_buf.lines
710
+ # Should contain both entries
711
+ assert lines.any? { |l| l.include?('"a"') }
712
+ assert lines.any? { |l| l.include?('"b"') }
713
+ end
714
+
715
+ def test_jsonl_open_shows_parse_error_inline
716
+ editor = fresh_editor
717
+ buf = editor.current_buffer
718
+ buf.replace_all_lines!(['{"a":1}', 'bad json', '{"b":2}'])
719
+ buf.options["filetype"] = "jsonl"
720
+
721
+ RuVim::RichView.open!(editor, format: "jsonl")
722
+ new_buf = editor.current_buffer
723
+ lines = new_buf.lines
724
+ # Invalid line should show an error marker
725
+ assert lines.any? { |l| l.include?("PARSE ERROR") }, "Invalid JSON line should show error"
726
+ end
727
+
728
+ def test_jsonl_filetype_detected
729
+ editor = fresh_editor
730
+ buf = editor.current_buffer
731
+ buf.options["filetype"] = "jsonl"
732
+ assert_equal "jsonl", RuVim::RichView.detect_format(buf)
733
+ end
478
734
  end
@@ -36,4 +36,23 @@ class SearchOptionTest < Minitest::Test
36
36
  cols = screen.send(:search_highlight_source_cols, @editor, "foo bar", source_col_offset: 0)
37
37
  assert_equal({}, cols)
38
38
  end
39
+
40
+ def test_nohlsearch_suppresses_highlight_until_next_search
41
+ screen = RuVim::Screen.new(terminal: TerminalStub.new([10, 40]))
42
+ @editor.set_last_search(pattern: "foo", direction: :forward)
43
+
44
+ # highlight is active
45
+ cols = screen.send(:search_highlight_source_cols, @editor, "foo bar", source_col_offset: 0)
46
+ assert_equal true, cols[0]
47
+
48
+ # suppress via nohlsearch
49
+ @editor.suppress_hlsearch!
50
+ cols = screen.send(:search_highlight_source_cols, @editor, "foo bar", source_col_offset: 0)
51
+ assert_equal({}, cols)
52
+
53
+ # next search restores highlight
54
+ @editor.set_last_search(pattern: "bar", direction: :forward)
55
+ cols = screen.send(:search_highlight_source_cols, @editor, "foo bar", source_col_offset: 0)
56
+ assert_equal true, cols[4]
57
+ end
39
58
  end
data/test/test_helper.rb CHANGED
@@ -1,3 +1,12 @@
1
+ # Suppress "system temporary path is not writable" warnings in sandboxed environments
2
+ unless ENV["TMPDIR"] && File.writable?(ENV["TMPDIR"])
3
+ candidates = ["/tmp", "/var/tmp"]
4
+ # Claude Code sandbox uses /tmp/claude-<uid>
5
+ candidates.unshift("/tmp/claude-#{Process.uid}") if Dir.exist?("/tmp/claude-#{Process.uid}")
6
+ found = candidates.find { |d| File.writable?(d) }
7
+ ENV["TMPDIR"] = found if found
8
+ end
9
+
1
10
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
2
11
  require "minitest/autorun"
3
12
  require "ruvim"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruvim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Sasada
@@ -62,6 +62,14 @@ files:
62
62
  - lib/ruvim/display_width.rb
63
63
  - lib/ruvim/editor.rb
64
64
  - lib/ruvim/ex_command_registry.rb
65
+ - lib/ruvim/file_watcher.rb
66
+ - lib/ruvim/git/blame.rb
67
+ - lib/ruvim/git/branch.rb
68
+ - lib/ruvim/git/commit.rb
69
+ - lib/ruvim/git/diff.rb
70
+ - lib/ruvim/git/handler.rb
71
+ - lib/ruvim/git/log.rb
72
+ - lib/ruvim/git/status.rb
65
73
  - lib/ruvim/global_commands.rb
66
74
  - lib/ruvim/highlighter.rb
67
75
  - lib/ruvim/input.rb
@@ -69,12 +77,15 @@ files:
69
77
  - lib/ruvim/keyword_chars.rb
70
78
  - lib/ruvim/lang/base.rb
71
79
  - lib/ruvim/lang/csv.rb
80
+ - lib/ruvim/lang/diff.rb
72
81
  - lib/ruvim/lang/json.rb
73
82
  - lib/ruvim/lang/markdown.rb
74
83
  - lib/ruvim/lang/ruby.rb
75
84
  - lib/ruvim/lang/scheme.rb
76
85
  - lib/ruvim/lang/tsv.rb
77
86
  - lib/ruvim/rich_view.rb
87
+ - lib/ruvim/rich_view/json_renderer.rb
88
+ - lib/ruvim/rich_view/jsonl_renderer.rb
78
89
  - lib/ruvim/rich_view/markdown_renderer.rb
79
90
  - lib/ruvim/rich_view/table_renderer.rb
80
91
  - lib/ruvim/screen.rb
@@ -94,15 +105,20 @@ files:
94
105
  - test/arglist_test.rb
95
106
  - test/buffer_test.rb
96
107
  - test/cli_test.rb
108
+ - test/clipboard_test.rb
109
+ - test/command_line_test.rb
97
110
  - test/config_dsl_test.rb
98
111
  - test/config_loader_test.rb
99
112
  - test/dispatcher_test.rb
100
113
  - test/display_width_test.rb
101
114
  - test/editor_mark_test.rb
102
115
  - test/editor_register_test.rb
116
+ - test/file_watcher_test.rb
103
117
  - test/fixtures/render_basic_snapshot.txt
104
118
  - test/fixtures/render_basic_snapshot_nonumber.txt
105
119
  - test/fixtures/render_unicode_scrolled_snapshot.txt
120
+ - test/follow_test.rb
121
+ - test/git_blame_test.rb
106
122
  - test/highlighter_test.rb
107
123
  - test/indent_test.rb
108
124
  - test/input_screen_integration_test.rb