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
data/lib/ruvim/dispatcher.rb
CHANGED
|
@@ -21,9 +21,9 @@ module RuVim
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def dispatch_ex(editor, line)
|
|
24
|
-
raw = line.
|
|
24
|
+
raw = line.strip
|
|
25
25
|
if raw.start_with?("!")
|
|
26
|
-
command = raw[1..].
|
|
26
|
+
command = raw[1..].strip
|
|
27
27
|
invocation = CommandInvocation.new(id: "__shell__", argv: [command])
|
|
28
28
|
ctx = Context.new(editor:, invocation:)
|
|
29
29
|
@command_host.ex_shell(ctx, command:)
|
|
@@ -52,15 +52,21 @@ module RuVim
|
|
|
52
52
|
return if parsed.nil?
|
|
53
53
|
|
|
54
54
|
spec = @ex_registry.fetch(parsed.name)
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
argv = parsed.argv
|
|
56
|
+
if spec.raw_args
|
|
57
|
+
# Re-extract raw text after command name (preserving shell quoting)
|
|
58
|
+
raw_rest = rest.strip.sub(/\A\S+\s*/, "")
|
|
59
|
+
argv = raw_rest.empty? ? [] : [raw_rest]
|
|
60
|
+
end
|
|
61
|
+
validate_ex_args!(spec, argv, parsed.bang)
|
|
62
|
+
invocation = CommandInvocation.new(id: spec.name, argv: argv, bang: parsed.bang)
|
|
57
63
|
ctx = Context.new(editor:, invocation:)
|
|
58
64
|
range_kwargs = {}
|
|
59
65
|
if range_result
|
|
60
66
|
range_kwargs[:range_start] = range_result[:range_start]
|
|
61
67
|
range_kwargs[:range_end] = range_result[:range_end]
|
|
62
68
|
end
|
|
63
|
-
@command_host.call(spec.call, ctx, argv:
|
|
69
|
+
@command_host.call(spec.call, ctx, argv: argv, bang: parsed.bang, count: 1, kwargs: range_kwargs)
|
|
64
70
|
rescue StandardError => e
|
|
65
71
|
editor.echo_error("Error: #{e.message}")
|
|
66
72
|
ensure
|
|
@@ -68,7 +74,7 @@ module RuVim
|
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
def parse_ex(line)
|
|
71
|
-
raw = line.
|
|
77
|
+
raw = line.strip
|
|
72
78
|
return nil if raw.empty?
|
|
73
79
|
|
|
74
80
|
tokens = Shellwords.shellsplit(raw)
|
|
@@ -85,7 +91,7 @@ module RuVim
|
|
|
85
91
|
# Parse a substitute command: s/pat/repl/flags
|
|
86
92
|
# Returns {pattern:, replacement:, flags_str:} or nil
|
|
87
93
|
def parse_substitute(line)
|
|
88
|
-
raw = line.
|
|
94
|
+
raw = line.strip
|
|
89
95
|
return nil unless raw.match?(/\As[^a-zA-Z]/)
|
|
90
96
|
return nil if raw.length < 2
|
|
91
97
|
|
|
@@ -189,7 +195,7 @@ module RuVim
|
|
|
189
195
|
# Parse a range from the beginning of raw.
|
|
190
196
|
# Returns {range_start:, range_end:, rest:} or nil.
|
|
191
197
|
def parse_range(raw, editor)
|
|
192
|
-
str = raw
|
|
198
|
+
str = raw
|
|
193
199
|
return nil if str.empty?
|
|
194
200
|
|
|
195
201
|
# % = whole file
|
data/lib/ruvim/display_width.rb
CHANGED
|
@@ -1,24 +1,103 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Try loading the C extension (built by rake compile)
|
|
4
|
+
begin
|
|
5
|
+
require_relative "ruvim_ext"
|
|
6
|
+
rescue LoadError
|
|
7
|
+
# C extension not available — pure Ruby fallback below
|
|
8
|
+
end
|
|
9
|
+
|
|
3
10
|
module RuVim
|
|
4
11
|
module DisplayWidth
|
|
5
12
|
module_function
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
|
|
14
|
+
if defined?(RuVim::DisplayWidthExt)
|
|
15
|
+
# ---- C extension paths ----
|
|
16
|
+
|
|
17
|
+
def cell_width(ch, col: 0, tabstop: 2)
|
|
18
|
+
sync_ambiguous_width
|
|
19
|
+
DisplayWidthExt.cell_width(ch, col:, tabstop:)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def display_width(str, tabstop: 2, start_col: 0)
|
|
23
|
+
sync_ambiguous_width
|
|
24
|
+
DisplayWidthExt.display_width(str, tabstop:, start_col:)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def expand_tabs(str, tabstop: 2, start_col: 0)
|
|
28
|
+
sync_ambiguous_width
|
|
29
|
+
DisplayWidthExt.expand_tabs(str, tabstop:, start_col:)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ambiguous_width
|
|
33
|
+
sync_ambiguous_width
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def sync_ambiguous_width
|
|
37
|
+
env = ::ENV["RUVIM_AMBIGUOUS_WIDTH"]
|
|
38
|
+
if !defined?(@ambiguous_width_cached) || @ambiguous_width_env != env
|
|
39
|
+
@ambiguous_width_env = env
|
|
40
|
+
@ambiguous_width_cached = (env == "2" ? 2 : 1)
|
|
41
|
+
DisplayWidthExt.set_ambiguous_width(@ambiguous_width_cached)
|
|
42
|
+
end
|
|
43
|
+
@ambiguous_width_cached
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
# ---- Pure Ruby fallback ----
|
|
47
|
+
|
|
48
|
+
def cell_width(ch, col: 0, tabstop: 2)
|
|
49
|
+
return 1 if ch.nil? || ch.empty?
|
|
50
|
+
|
|
51
|
+
if ch == "\t"
|
|
52
|
+
width = tabstop - (col % tabstop)
|
|
53
|
+
return width.zero? ? tabstop : width
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Fast path: byte length 1 means ASCII — always width 1
|
|
57
|
+
return 1 if ch.bytesize == 1
|
|
58
|
+
|
|
59
|
+
code = ch.ord
|
|
60
|
+
return cached_codepoint_width(code) if codepoint_cacheable?(code)
|
|
9
61
|
|
|
10
|
-
|
|
11
|
-
width = tabstop - (col % tabstop)
|
|
12
|
-
return width.zero? ? tabstop : width
|
|
62
|
+
uncached_codepoint_width(code)
|
|
13
63
|
end
|
|
14
64
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
65
|
+
def display_width(str, tabstop: 2, start_col: 0)
|
|
66
|
+
col = start_col
|
|
67
|
+
str.to_s.each_char { |ch| col += cell_width(ch, col:, tabstop:) }
|
|
68
|
+
col - start_col
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def expand_tabs(str, tabstop: 2, start_col: 0)
|
|
72
|
+
col = start_col
|
|
73
|
+
out = +""
|
|
74
|
+
str.to_s.each_char do |ch|
|
|
75
|
+
if ch == "\t"
|
|
76
|
+
n = cell_width(ch, col:, tabstop:)
|
|
77
|
+
out << (" " * n)
|
|
78
|
+
col += n
|
|
79
|
+
else
|
|
80
|
+
out << ch
|
|
81
|
+
col += cell_width(ch, col:, tabstop:)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
out
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def ambiguous_width
|
|
88
|
+
env = ::ENV["RUVIM_AMBIGUOUS_WIDTH"]
|
|
89
|
+
if !defined?(@ambiguous_width_cached) || @ambiguous_width_env != env
|
|
90
|
+
@ambiguous_width_env = env
|
|
91
|
+
@ambiguous_width_cached = (env == "2" ? 2 : 1)
|
|
92
|
+
@codepoint_width_cache = {}
|
|
93
|
+
end
|
|
18
94
|
|
|
19
|
-
|
|
95
|
+
@ambiguous_width_cached
|
|
96
|
+
end
|
|
20
97
|
end
|
|
21
98
|
|
|
99
|
+
# Shared helpers (used by pure Ruby path; kept available for tests)
|
|
100
|
+
|
|
22
101
|
def codepoint_cacheable?(code)
|
|
23
102
|
!code.nil? && !code.zero?
|
|
24
103
|
end
|
|
@@ -43,28 +122,6 @@ module RuVim
|
|
|
43
122
|
1
|
|
44
123
|
end
|
|
45
124
|
|
|
46
|
-
def display_width(str, tabstop: 2, start_col: 0)
|
|
47
|
-
col = start_col
|
|
48
|
-
str.to_s.each_char { |ch| col += cell_width(ch, col:, tabstop:) }
|
|
49
|
-
col - start_col
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def expand_tabs(str, tabstop: 2, start_col: 0)
|
|
53
|
-
col = start_col
|
|
54
|
-
out = +""
|
|
55
|
-
str.to_s.each_char do |ch|
|
|
56
|
-
if ch == "\t"
|
|
57
|
-
n = cell_width(ch, col:, tabstop:)
|
|
58
|
-
out << (" " * n)
|
|
59
|
-
col += n
|
|
60
|
-
else
|
|
61
|
-
out << ch
|
|
62
|
-
col += cell_width(ch, col:, tabstop:)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
out
|
|
66
|
-
end
|
|
67
|
-
|
|
68
125
|
def combining_mark?(code)
|
|
69
126
|
(0x0300..0x036F).cover?(code) ||
|
|
70
127
|
(0x1AB0..0x1AFF).cover?(code) ||
|
|
@@ -74,9 +131,9 @@ module RuVim
|
|
|
74
131
|
end
|
|
75
132
|
|
|
76
133
|
def zero_width_codepoint?(code)
|
|
77
|
-
(0x200D..0x200D).cover?(code) ||
|
|
78
|
-
(0xFE00..0xFE0F).cover?(code) ||
|
|
79
|
-
(0xE0100..0xE01EF).cover?(code)
|
|
134
|
+
(0x200D..0x200D).cover?(code) ||
|
|
135
|
+
(0xFE00..0xFE0F).cover?(code) ||
|
|
136
|
+
(0xE0100..0xE01EF).cover?(code)
|
|
80
137
|
end
|
|
81
138
|
|
|
82
139
|
def wide_codepoint?(code)
|
|
@@ -121,16 +178,5 @@ module RuVim
|
|
|
121
178
|
(0x2460..0x24E9).cover?(code) ||
|
|
122
179
|
(0x2500..0x257F).cover?(code)
|
|
123
180
|
end
|
|
124
|
-
|
|
125
|
-
def ambiguous_width
|
|
126
|
-
env = ::ENV["RUVIM_AMBIGUOUS_WIDTH"]
|
|
127
|
-
if !defined?(@ambiguous_width_cached) || @ambiguous_width_env != env
|
|
128
|
-
@ambiguous_width_env = env
|
|
129
|
-
@ambiguous_width_cached = (env == "2" ? 2 : 1)
|
|
130
|
-
@codepoint_width_cache = {}
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
@ambiguous_width_cached
|
|
134
|
-
end
|
|
135
181
|
end
|
|
136
182
|
end
|
data/lib/ruvim/editor.rb
CHANGED
|
@@ -54,24 +54,12 @@ module RuVim
|
|
|
54
54
|
"filetype" => { default_scope: :buffer, type: :string, default: nil },
|
|
55
55
|
"onsavehook" => { default_scope: :buffer, type: :bool, default: true },
|
|
56
56
|
"grepprg" => { default_scope: :global, type: :string, default: "grep -nH" },
|
|
57
|
-
"grepformat" => { default_scope: :global, type: :string, default: "%f:%l:%m" }
|
|
57
|
+
"grepformat" => { default_scope: :global, type: :string, default: "%f:%l:%m" },
|
|
58
|
+
"runprg" => { default_scope: :buffer, type: :string, default: nil }
|
|
58
59
|
}.freeze
|
|
59
|
-
SHEBANG_FILETYPE_RULES = [
|
|
60
|
-
[/\Aruby(?:\d+(?:\.\d+)*)?\z/, "ruby"],
|
|
61
|
-
[/\Apython(?:\d+(?:\.\d+)*)?\z/, "python"],
|
|
62
|
-
["node", "javascript"],
|
|
63
|
-
["nodejs", "javascript"],
|
|
64
|
-
["deno", "javascript"],
|
|
65
|
-
["bash", "sh"],
|
|
66
|
-
["sh", "sh"],
|
|
67
|
-
["zsh", "sh"],
|
|
68
|
-
["ksh", "sh"],
|
|
69
|
-
["dash", "sh"],
|
|
70
|
-
[/\Aperl(?:\d+(?:\.\d+)*)?\z/, "perl"]
|
|
71
|
-
].freeze
|
|
72
60
|
|
|
73
61
|
attr_reader :buffers, :windows, :layout_tree
|
|
74
|
-
attr_accessor :current_window_id, :mode, :message, :pending_count, :alternate_buffer_id, :restricted_mode, :current_window_view_height_hint, :
|
|
62
|
+
attr_accessor :current_window_id, :mode, :message, :pending_count, :alternate_buffer_id, :restricted_mode, :current_window_view_height_hint, :open_path_handler, :keymap_manager, :app_action_handler, :git_stream_handler, :git_stream_stop_handler, :shell_executor, :run_stream_handler
|
|
75
63
|
|
|
76
64
|
def initialize
|
|
77
65
|
@buffers = {}
|
|
@@ -93,13 +81,13 @@ module RuVim
|
|
|
93
81
|
@restricted_mode = false
|
|
94
82
|
@current_window_view_height_hint = 1
|
|
95
83
|
@running = true
|
|
96
|
-
@stdin_stream_stop_handler = nil
|
|
97
84
|
@open_path_handler = nil
|
|
98
85
|
@keymap_manager = nil
|
|
99
86
|
@app_action_handler = nil
|
|
100
87
|
@global_options = default_global_options
|
|
101
88
|
@command_line = CommandLine.new
|
|
102
89
|
@last_search = nil
|
|
90
|
+
@hlsearch_suppressed = false
|
|
103
91
|
@last_find = nil
|
|
104
92
|
@registers = {}
|
|
105
93
|
@active_register_name = nil
|
|
@@ -116,6 +104,8 @@ module RuVim
|
|
|
116
104
|
@arglist = []
|
|
117
105
|
@arglist_index = 0
|
|
118
106
|
@hit_enter_lines = nil
|
|
107
|
+
@run_history = {} # buffer_id => last run command (unexpanded)
|
|
108
|
+
@run_output_buffer_id = nil
|
|
119
109
|
end
|
|
120
110
|
|
|
121
111
|
def running?
|
|
@@ -130,6 +120,18 @@ module RuVim
|
|
|
130
120
|
@running = false
|
|
131
121
|
end
|
|
132
122
|
|
|
123
|
+
def run_history
|
|
124
|
+
@run_history
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def run_output_buffer_id
|
|
128
|
+
@run_output_buffer_id
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def run_output_buffer_id=(id)
|
|
132
|
+
@run_output_buffer_id = id
|
|
133
|
+
end
|
|
134
|
+
|
|
133
135
|
def command_line
|
|
134
136
|
@command_line
|
|
135
137
|
end
|
|
@@ -151,11 +153,20 @@ module RuVim
|
|
|
151
153
|
end
|
|
152
154
|
|
|
153
155
|
def set_last_search(pattern:, direction:)
|
|
154
|
-
@last_search = { pattern: pattern
|
|
156
|
+
@last_search = { pattern: pattern, direction: direction }
|
|
157
|
+
@hlsearch_suppressed = false
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def suppress_hlsearch!
|
|
161
|
+
@hlsearch_suppressed = true
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def hlsearch_suppressed?
|
|
165
|
+
@hlsearch_suppressed
|
|
155
166
|
end
|
|
156
167
|
|
|
157
168
|
def set_last_find(char:, direction:, till:)
|
|
158
|
-
@last_find = { char: char
|
|
169
|
+
@last_find = { char: char, direction: direction, till: !!till }
|
|
159
170
|
end
|
|
160
171
|
|
|
161
172
|
def current_window
|
|
@@ -166,19 +177,18 @@ module RuVim
|
|
|
166
177
|
@buffers.fetch(current_window.buffer_id)
|
|
167
178
|
end
|
|
168
179
|
|
|
169
|
-
def
|
|
170
|
-
handler =
|
|
180
|
+
def stream_stop_or_cancel!
|
|
181
|
+
handler = current_buffer&.stream&.stop_handler
|
|
171
182
|
return false unless handler
|
|
172
183
|
|
|
173
184
|
handler.call
|
|
174
|
-
true
|
|
175
185
|
end
|
|
176
186
|
|
|
177
187
|
def invoke_app_action(name, **kwargs)
|
|
178
188
|
handler = @app_action_handler
|
|
179
189
|
return false unless handler
|
|
180
190
|
|
|
181
|
-
handler.call(name
|
|
191
|
+
handler.call(name, **kwargs)
|
|
182
192
|
true
|
|
183
193
|
end
|
|
184
194
|
|
|
@@ -203,7 +213,7 @@ module RuVim
|
|
|
203
213
|
|
|
204
214
|
def get_option(name, scope: :effective, window: current_window, buffer: current_buffer)
|
|
205
215
|
key = name.to_s
|
|
206
|
-
case scope
|
|
216
|
+
case scope
|
|
207
217
|
when :global
|
|
208
218
|
@global_options[key]
|
|
209
219
|
when :buffer
|
|
@@ -218,7 +228,7 @@ module RuVim
|
|
|
218
228
|
def set_option(name, value, scope: :auto, window: current_window, buffer: current_buffer)
|
|
219
229
|
key = name.to_s
|
|
220
230
|
value = coerce_option_value(key, value)
|
|
221
|
-
actual_scope = (scope
|
|
231
|
+
actual_scope = (scope == :auto ? option_default_scope(key) : scope)
|
|
222
232
|
case actual_scope
|
|
223
233
|
when :global
|
|
224
234
|
@global_options[key] = value
|
|
@@ -252,33 +262,13 @@ module RuVim
|
|
|
252
262
|
return nil if p.empty?
|
|
253
263
|
|
|
254
264
|
base = File.basename(p)
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
".js" => "javascript",
|
|
263
|
-
".mjs" => "javascript",
|
|
264
|
-
".cjs" => "javascript",
|
|
265
|
-
".ts" => "typescript",
|
|
266
|
-
".tsx" => "typescriptreact",
|
|
267
|
-
".jsx" => "javascriptreact",
|
|
268
|
-
".json" => "json",
|
|
269
|
-
".yml" => "yaml",
|
|
270
|
-
".yaml" => "yaml",
|
|
271
|
-
".md" => "markdown",
|
|
272
|
-
".txt" => "text",
|
|
273
|
-
".html" => "html",
|
|
274
|
-
".css" => "css",
|
|
275
|
-
".sh" => "sh",
|
|
276
|
-
".tsv" => "tsv",
|
|
277
|
-
".csv" => "csv",
|
|
278
|
-
".scm" => "scheme",
|
|
279
|
-
".ss" => "scheme",
|
|
280
|
-
".sld" => "scheme"
|
|
281
|
-
}[File.extname(base).downcase]
|
|
265
|
+
|
|
266
|
+
# Basename-based detection (exact match, then prefix)
|
|
267
|
+
base_ft = Lang::Registry.detect_by_basename(base)
|
|
268
|
+
return base_ft if base_ft
|
|
269
|
+
|
|
270
|
+
# Extension-based detection
|
|
271
|
+
ext_ft = Lang::Registry.detect_by_extension(File.extname(base))
|
|
282
272
|
return ext_ft if ext_ft
|
|
283
273
|
|
|
284
274
|
detect_filetype_from_shebang(p)
|
|
@@ -290,9 +280,9 @@ module RuVim
|
|
|
290
280
|
|
|
291
281
|
def set_register(name = "\"", text:, type: :charwise)
|
|
292
282
|
key = name.to_s
|
|
293
|
-
return { text: text
|
|
283
|
+
return { text: text, type: type } if key == "_"
|
|
294
284
|
|
|
295
|
-
payload = write_register_payload(key, text: text
|
|
285
|
+
payload = write_register_payload(key, text: text, type: type)
|
|
296
286
|
write_clipboard_register(key, payload)
|
|
297
287
|
if key == "\""
|
|
298
288
|
if (default_clip = clipboard_default_register_key)
|
|
@@ -305,14 +295,14 @@ module RuVim
|
|
|
305
295
|
end
|
|
306
296
|
|
|
307
297
|
def store_operator_register(name = "\"", text:, type:, kind:)
|
|
308
|
-
key = (name || "\"")
|
|
309
|
-
payload = { text: text
|
|
298
|
+
key = (name || "\"")
|
|
299
|
+
payload = { text: text, type: type }
|
|
310
300
|
return payload if key == "_"
|
|
311
301
|
|
|
312
302
|
written = set_register(key, text: payload[:text], type: payload[:type])
|
|
313
303
|
op_payload = dup_register_payload(written)
|
|
314
304
|
|
|
315
|
-
case kind
|
|
305
|
+
case kind
|
|
316
306
|
when :yank
|
|
317
307
|
@registers["0"] = dup_register_payload(op_payload)
|
|
318
308
|
when :delete, :change
|
|
@@ -474,9 +464,9 @@ module RuVim
|
|
|
474
464
|
end
|
|
475
465
|
|
|
476
466
|
def enter_visual(mode)
|
|
477
|
-
@mode = mode
|
|
467
|
+
@mode = mode
|
|
478
468
|
@visual_state = {
|
|
479
|
-
mode: mode
|
|
469
|
+
mode: mode,
|
|
480
470
|
anchor_y: current_window.cursor_y,
|
|
481
471
|
anchor_x: current_window.cursor_x
|
|
482
472
|
}
|
|
@@ -634,10 +624,10 @@ module RuVim
|
|
|
634
624
|
win.row_offset = src.row_offset
|
|
635
625
|
win.col_offset = src.col_offset
|
|
636
626
|
|
|
637
|
-
split_type = (layout
|
|
627
|
+
split_type = (layout == :vertical ? :vsplit : :hsplit)
|
|
638
628
|
new_leaf = { type: :window, id: win.id }
|
|
639
629
|
|
|
640
|
-
@layout_tree = tree_split_leaf(@layout_tree, src.id, split_type, new_leaf, place
|
|
630
|
+
@layout_tree = tree_split_leaf(@layout_tree, src.id, split_type, new_leaf, place)
|
|
641
631
|
|
|
642
632
|
@current_window_id = win.id
|
|
643
633
|
save_current_tabpage_state! unless @suspend_tab_autosave
|
|
@@ -838,8 +828,7 @@ module RuVim
|
|
|
838
828
|
end
|
|
839
829
|
|
|
840
830
|
def delete_buffer(buffer_id)
|
|
841
|
-
|
|
842
|
-
buffer = @buffers[id]
|
|
831
|
+
buffer = @buffers[buffer_id]
|
|
843
832
|
return nil unless buffer
|
|
844
833
|
|
|
845
834
|
if @buffers.length <= 1
|
|
@@ -852,13 +841,13 @@ module RuVim
|
|
|
852
841
|
if replacement
|
|
853
842
|
replacement.id
|
|
854
843
|
else
|
|
855
|
-
candidates = @buffers.keys.reject { |bid| bid ==
|
|
844
|
+
candidates = @buffers.keys.reject { |bid| bid == buffer_id }
|
|
856
845
|
alt = @alternate_buffer_id
|
|
857
|
-
(alt && alt !=
|
|
846
|
+
(alt && alt != buffer_id && @buffers.key?(alt)) ? alt : candidates.first
|
|
858
847
|
end
|
|
859
848
|
|
|
860
849
|
@windows.each_value do |win|
|
|
861
|
-
next unless win.buffer_id ==
|
|
850
|
+
next unless win.buffer_id == buffer_id
|
|
862
851
|
next unless fallback_id
|
|
863
852
|
|
|
864
853
|
win.buffer_id = fallback_id
|
|
@@ -868,9 +857,9 @@ module RuVim
|
|
|
868
857
|
win.col_offset = 0
|
|
869
858
|
end
|
|
870
859
|
|
|
871
|
-
@buffers.delete(
|
|
872
|
-
@local_marks.delete(
|
|
873
|
-
@alternate_buffer_id = nil if @alternate_buffer_id ==
|
|
860
|
+
@buffers.delete(buffer_id)
|
|
861
|
+
@local_marks.delete(buffer_id)
|
|
862
|
+
@alternate_buffer_id = nil if @alternate_buffer_id == buffer_id
|
|
874
863
|
save_current_tabpage_state! unless @suspend_tab_autosave
|
|
875
864
|
ensure_bootstrap_buffer! if @buffers.empty?
|
|
876
865
|
true
|
|
@@ -1012,11 +1001,10 @@ module RuVim
|
|
|
1012
1001
|
end
|
|
1013
1002
|
|
|
1014
1003
|
def find_window_ids_by_buffer_kind(kind)
|
|
1015
|
-
sym = kind.to_sym
|
|
1016
1004
|
window_order.select do |wid|
|
|
1017
1005
|
win = @windows[wid]
|
|
1018
1006
|
buf = win && @buffers[win.buffer_id]
|
|
1019
|
-
buf && buf.kind ==
|
|
1007
|
+
buf && buf.kind == kind
|
|
1020
1008
|
end
|
|
1021
1009
|
end
|
|
1022
1010
|
|
|
@@ -1154,15 +1142,24 @@ module RuVim
|
|
|
1154
1142
|
def assign_filetype(buffer, ft)
|
|
1155
1143
|
buffer.options["filetype"] = ft
|
|
1156
1144
|
buffer.lang_module = resolve_lang_module(ft)
|
|
1145
|
+
apply_filetype_defaults(buffer, ft)
|
|
1146
|
+
end
|
|
1147
|
+
|
|
1148
|
+
def apply_filetype_defaults(buffer, ft)
|
|
1149
|
+
unless buffer.options.key?("runprg")
|
|
1150
|
+
runprg = filetype_default_runprg(ft)
|
|
1151
|
+
buffer.options["runprg"] = runprg if runprg
|
|
1152
|
+
end
|
|
1153
|
+
end
|
|
1154
|
+
|
|
1155
|
+
def filetype_default_runprg(ft)
|
|
1156
|
+
Lang::Registry.runprg_for(ft)
|
|
1157
1157
|
end
|
|
1158
1158
|
|
|
1159
1159
|
private
|
|
1160
1160
|
|
|
1161
1161
|
def resolve_lang_module(ft)
|
|
1162
|
-
|
|
1163
|
-
when "ruby" then Lang::Ruby
|
|
1164
|
-
else Lang::Base
|
|
1165
|
-
end
|
|
1162
|
+
Lang::Registry.resolve_module(ft)
|
|
1166
1163
|
end
|
|
1167
1164
|
|
|
1168
1165
|
def detect_filetype_from_shebang(path)
|
|
@@ -1172,10 +1169,7 @@ module RuVim
|
|
|
1172
1169
|
cmd = shebang_command_name(line)
|
|
1173
1170
|
return nil if cmd.nil? || cmd.empty?
|
|
1174
1171
|
|
|
1175
|
-
|
|
1176
|
-
matcher.is_a?(Regexp) ? matcher.match?(cmd) : matcher.to_s == cmd
|
|
1177
|
-
end
|
|
1178
|
-
rule && rule[1]
|
|
1172
|
+
Lang::Registry.detect_by_shebang(cmd)
|
|
1179
1173
|
rescue StandardError
|
|
1180
1174
|
nil
|
|
1181
1175
|
end
|
|
@@ -1211,7 +1205,7 @@ module RuVim
|
|
|
1211
1205
|
prog = tokens[i].to_s
|
|
1212
1206
|
end
|
|
1213
1207
|
|
|
1214
|
-
File.basename(prog
|
|
1208
|
+
File.basename(prog)
|
|
1215
1209
|
end
|
|
1216
1210
|
|
|
1217
1211
|
def default_global_options
|
|
@@ -1266,7 +1260,7 @@ module RuVim
|
|
|
1266
1260
|
def dup_register_payload(payload)
|
|
1267
1261
|
return nil unless payload
|
|
1268
1262
|
|
|
1269
|
-
{ text: payload[:text].
|
|
1263
|
+
{ text: payload[:text].dup, type: payload[:type] }
|
|
1270
1264
|
end
|
|
1271
1265
|
|
|
1272
1266
|
def assign_detected_filetype(buffer)
|
|
@@ -9,6 +9,7 @@ module RuVim
|
|
|
9
9
|
:desc,
|
|
10
10
|
:nargs,
|
|
11
11
|
:bang,
|
|
12
|
+
:raw_args,
|
|
12
13
|
:source,
|
|
13
14
|
keyword_init: true
|
|
14
15
|
)
|
|
@@ -20,7 +21,7 @@ module RuVim
|
|
|
20
21
|
@lookup = {}
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
def register(name, call:, aliases: [], desc: "", nargs: :any, bang: false, source: :builtin)
|
|
24
|
+
def register(name, call:, aliases: [], desc: "", nargs: :any, bang: false, raw_args: false, source: :builtin)
|
|
24
25
|
canonical = name.to_s
|
|
25
26
|
if @specs.key?(canonical)
|
|
26
27
|
raise RuVim::CommandError, "Ex command already exists: #{canonical}"
|
|
@@ -32,6 +33,7 @@ module RuVim
|
|
|
32
33
|
desc: desc,
|
|
33
34
|
nargs: nargs,
|
|
34
35
|
bang: bang,
|
|
36
|
+
raw_args: raw_args,
|
|
35
37
|
source: source
|
|
36
38
|
)
|
|
37
39
|
|