mui 0.1.0 → 0.2.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/.rubocop_todo.yml +158 -0
- data/CHANGELOG.md +349 -0
- data/exe/mui +1 -2
- data/lib/mui/autocmd.rb +66 -0
- data/lib/mui/buffer.rb +275 -0
- data/lib/mui/buffer_word_cache.rb +131 -0
- data/lib/mui/buffer_word_completer.rb +77 -0
- data/lib/mui/color_manager.rb +136 -0
- data/lib/mui/color_scheme.rb +63 -0
- data/lib/mui/command_completer.rb +21 -0
- data/lib/mui/command_context.rb +90 -0
- data/lib/mui/command_line.rb +137 -0
- data/lib/mui/command_registry.rb +25 -0
- data/lib/mui/completion_renderer.rb +84 -0
- data/lib/mui/completion_state.rb +58 -0
- data/lib/mui/config.rb +56 -0
- data/lib/mui/editor.rb +319 -0
- data/lib/mui/error.rb +29 -0
- data/lib/mui/file_completer.rb +51 -0
- data/lib/mui/floating_window.rb +161 -0
- data/lib/mui/handler_result.rb +101 -0
- data/lib/mui/highlight.rb +22 -0
- data/lib/mui/highlighters/base.rb +23 -0
- data/lib/mui/highlighters/search_highlighter.rb +26 -0
- data/lib/mui/highlighters/selection_highlighter.rb +48 -0
- data/lib/mui/highlighters/syntax_highlighter.rb +105 -0
- data/lib/mui/input.rb +17 -0
- data/lib/mui/insert_completion_renderer.rb +92 -0
- data/lib/mui/insert_completion_state.rb +77 -0
- data/lib/mui/job.rb +81 -0
- data/lib/mui/job_manager.rb +113 -0
- data/lib/mui/key_code.rb +30 -0
- data/lib/mui/key_handler/base.rb +100 -0
- data/lib/mui/key_handler/command_mode.rb +443 -0
- data/lib/mui/key_handler/insert_mode.rb +354 -0
- data/lib/mui/key_handler/motions/motion_handler.rb +56 -0
- data/lib/mui/key_handler/normal_mode.rb +579 -0
- data/lib/mui/key_handler/operators/base_operator.rb +134 -0
- data/lib/mui/key_handler/operators/change_operator.rb +179 -0
- data/lib/mui/key_handler/operators/delete_operator.rb +176 -0
- data/lib/mui/key_handler/operators/paste_operator.rb +113 -0
- data/lib/mui/key_handler/operators/yank_operator.rb +127 -0
- data/lib/mui/key_handler/search_mode.rb +188 -0
- data/lib/mui/key_handler/visual_line_mode.rb +20 -0
- data/lib/mui/key_handler/visual_mode.rb +397 -0
- data/lib/mui/key_handler/window_command.rb +112 -0
- data/lib/mui/key_handler.rb +16 -0
- data/lib/mui/layout/calculator.rb +15 -0
- data/lib/mui/layout/leaf_node.rb +33 -0
- data/lib/mui/layout/node.rb +29 -0
- data/lib/mui/layout/split_node.rb +132 -0
- data/lib/mui/line_renderer.rb +122 -0
- data/lib/mui/mode.rb +13 -0
- data/lib/mui/mode_manager.rb +185 -0
- data/lib/mui/motion.rb +139 -0
- data/lib/mui/plugin.rb +35 -0
- data/lib/mui/plugin_manager.rb +106 -0
- data/lib/mui/register.rb +110 -0
- data/lib/mui/screen.rb +85 -0
- data/lib/mui/search_completer.rb +50 -0
- data/lib/mui/search_input.rb +40 -0
- data/lib/mui/search_state.rb +88 -0
- data/lib/mui/selection.rb +55 -0
- data/lib/mui/status_line_renderer.rb +40 -0
- data/lib/mui/syntax/language_detector.rb +74 -0
- data/lib/mui/syntax/lexer_base.rb +106 -0
- data/lib/mui/syntax/lexers/c_lexer.rb +127 -0
- data/lib/mui/syntax/lexers/ruby_lexer.rb +114 -0
- data/lib/mui/syntax/token.rb +42 -0
- data/lib/mui/syntax/token_cache.rb +91 -0
- data/lib/mui/tab_bar_renderer.rb +87 -0
- data/lib/mui/tab_manager.rb +96 -0
- data/lib/mui/tab_page.rb +35 -0
- data/lib/mui/terminal_adapter/base.rb +92 -0
- data/lib/mui/terminal_adapter/curses.rb +162 -0
- data/lib/mui/terminal_adapter.rb +4 -0
- data/lib/mui/themes/default.rb +315 -0
- data/lib/mui/undo_manager.rb +83 -0
- data/lib/mui/undoable_action.rb +175 -0
- data/lib/mui/unicode_width.rb +100 -0
- data/lib/mui/version.rb +1 -1
- data/lib/mui/window.rb +158 -0
- data/lib/mui/window_manager.rb +249 -0
- data/lib/mui.rb +156 -2
- metadata +98 -3
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mui
|
|
4
|
+
# Provides plugin access to editor internals
|
|
5
|
+
class CommandContext
|
|
6
|
+
attr_reader :buffer, :window, :editor
|
|
7
|
+
|
|
8
|
+
def initialize(editor:, buffer:, window:)
|
|
9
|
+
@editor = editor
|
|
10
|
+
@buffer = buffer
|
|
11
|
+
@window = window
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def cursor
|
|
15
|
+
{ line: @buffer.cursor_y, col: @buffer.cursor_x }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def current_line
|
|
19
|
+
@buffer.current_line
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def insert(text)
|
|
23
|
+
@buffer.insert_text(text)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def set_message(msg)
|
|
27
|
+
@editor.message = msg
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def quit
|
|
31
|
+
@editor.running = false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def run_command(name, *)
|
|
35
|
+
@editor.command_registry.execute(name, self, *)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def run_async(on_complete: nil, &)
|
|
39
|
+
@editor.job_manager.run_async(on_complete:, &)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def run_shell_command(cmd, on_complete: nil)
|
|
43
|
+
@editor.job_manager.run_command(cmd, on_complete:)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def jobs_running?
|
|
47
|
+
@editor.job_manager.busy?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def cancel_job(id)
|
|
51
|
+
@editor.job_manager.cancel(id)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def open_scratch_buffer(name, content)
|
|
55
|
+
@editor.open_scratch_buffer(name, content)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Run an interactive command that needs terminal access (e.g., fzf)
|
|
59
|
+
# Suspends Curses UI, runs command, resumes UI
|
|
60
|
+
def run_interactive_command(cmd)
|
|
61
|
+
require "tempfile"
|
|
62
|
+
|
|
63
|
+
@editor.suspend_ui do
|
|
64
|
+
output_file = Tempfile.new("mui_interactive")
|
|
65
|
+
begin
|
|
66
|
+
# Use shell redirection to capture output while keeping stdin/stderr connected to terminal
|
|
67
|
+
# rubocop:disable Style/SpecialGlobalVars
|
|
68
|
+
success = system("#{cmd} > #{output_file.path}")
|
|
69
|
+
status = $?
|
|
70
|
+
# rubocop:enable Style/SpecialGlobalVars
|
|
71
|
+
exit_status = status&.exitstatus || 1
|
|
72
|
+
{
|
|
73
|
+
stdout: File.read(output_file.path),
|
|
74
|
+
stderr: "",
|
|
75
|
+
exit_status:,
|
|
76
|
+
success: success == true && exit_status.zero?
|
|
77
|
+
}
|
|
78
|
+
ensure
|
|
79
|
+
output_file.close
|
|
80
|
+
output_file.unlink
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Check if a command exists in PATH
|
|
86
|
+
def command_exists?(cmd)
|
|
87
|
+
system("which #{cmd} > /dev/null 2>&1")
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mui
|
|
4
|
+
class CommandLine
|
|
5
|
+
# Commands that accept file path arguments
|
|
6
|
+
FILE_COMMANDS = %w[e w sp split vs vsplit tabnew tabe tabedit].freeze
|
|
7
|
+
|
|
8
|
+
attr_reader :buffer, :cursor_pos
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@buffer = ""
|
|
12
|
+
@cursor_pos = 0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def input(char)
|
|
16
|
+
@buffer = @buffer[0...@cursor_pos].to_s + char + @buffer[@cursor_pos..].to_s
|
|
17
|
+
@cursor_pos += char.length
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def backspace
|
|
21
|
+
return if @cursor_pos.zero?
|
|
22
|
+
|
|
23
|
+
@buffer = @buffer[0...(@cursor_pos - 1)].to_s + @buffer[@cursor_pos..].to_s
|
|
24
|
+
@cursor_pos -= 1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def clear
|
|
28
|
+
@buffer = ""
|
|
29
|
+
@cursor_pos = 0
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def move_cursor_left
|
|
33
|
+
@cursor_pos -= 1 if @cursor_pos.positive?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def move_cursor_right
|
|
37
|
+
@cursor_pos += 1 if @cursor_pos < @buffer.length
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def execute
|
|
41
|
+
result = parse(@buffer)
|
|
42
|
+
@buffer = ""
|
|
43
|
+
@cursor_pos = 0
|
|
44
|
+
result
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_s
|
|
48
|
+
":#{@buffer}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Determine completion context based on current buffer
|
|
52
|
+
def completion_context
|
|
53
|
+
# Check if buffer contains a space (command + argument)
|
|
54
|
+
if @buffer.include?(" ")
|
|
55
|
+
# Command with argument
|
|
56
|
+
parts = @buffer.split(/\s+/, 2)
|
|
57
|
+
command = parts[0]
|
|
58
|
+
arg = parts[1] || ""
|
|
59
|
+
|
|
60
|
+
return { type: :file, command:, prefix: arg } if FILE_COMMANDS.include?(command)
|
|
61
|
+
|
|
62
|
+
# Return nil for commands that don't support file completion
|
|
63
|
+
return nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# No space -> command completion
|
|
67
|
+
{ type: :command, prefix: @buffer.strip }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Apply completion result to buffer
|
|
71
|
+
def apply_completion(text, context)
|
|
72
|
+
@buffer = if context[:type] == :command
|
|
73
|
+
text
|
|
74
|
+
else
|
|
75
|
+
"#{context[:command]} #{text}"
|
|
76
|
+
end
|
|
77
|
+
@cursor_pos = @buffer.length
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def parse(cmd)
|
|
83
|
+
case cmd.strip
|
|
84
|
+
when ""
|
|
85
|
+
{ action: :no_op }
|
|
86
|
+
when "e"
|
|
87
|
+
{ action: :open }
|
|
88
|
+
when /^e\s+(.+)/
|
|
89
|
+
{ action: :open_as, path: ::Regexp.last_match(1) }
|
|
90
|
+
when "w"
|
|
91
|
+
{ action: :write }
|
|
92
|
+
when "q"
|
|
93
|
+
{ action: :quit }
|
|
94
|
+
when "wq"
|
|
95
|
+
{ action: :write_quit }
|
|
96
|
+
when "q!"
|
|
97
|
+
{ action: :force_quit }
|
|
98
|
+
when /^w\s+(.+)/
|
|
99
|
+
{ action: :write_as, path: ::Regexp.last_match(1) }
|
|
100
|
+
when "sp", "split"
|
|
101
|
+
{ action: :split_horizontal }
|
|
102
|
+
when /^sp\s+(.+)/, /^split\s+(.+)/
|
|
103
|
+
{ action: :split_horizontal, path: ::Regexp.last_match(1) }
|
|
104
|
+
when "vs", "vsplit"
|
|
105
|
+
{ action: :split_vertical }
|
|
106
|
+
when /^vs\s+(.+)/, /^vsplit\s+(.+)/
|
|
107
|
+
{ action: :split_vertical, path: ::Regexp.last_match(1) }
|
|
108
|
+
when "close"
|
|
109
|
+
{ action: :close_window }
|
|
110
|
+
when "only"
|
|
111
|
+
{ action: :only_window }
|
|
112
|
+
when "tabnew", "tabe", "tabedit"
|
|
113
|
+
{ action: :tab_new }
|
|
114
|
+
when /^tabnew\s+(.+)/, /^tabe\s+(.+)/, /^tabedit\s+(.+)/
|
|
115
|
+
{ action: :tab_new, path: ::Regexp.last_match(1) }
|
|
116
|
+
when "tabclose", "tabc"
|
|
117
|
+
{ action: :tab_close }
|
|
118
|
+
when "tabnext", "tabn"
|
|
119
|
+
{ action: :tab_next }
|
|
120
|
+
when "tabprev", "tabp", "tabprevious"
|
|
121
|
+
{ action: :tab_prev }
|
|
122
|
+
when "tabfirst", "tabf", "tabrewind", "tabr"
|
|
123
|
+
{ action: :tab_first }
|
|
124
|
+
when "tablast", "tabl"
|
|
125
|
+
{ action: :tab_last }
|
|
126
|
+
when /^tabmove\s+(\d+)/, /^tabm\s+(\d+)/
|
|
127
|
+
{ action: :tab_move, position: ::Regexp.last_match(1).to_i }
|
|
128
|
+
when /^(\d+)tabn(?:ext)?/, /^tabn(?:ext)?\s+(\d+)/
|
|
129
|
+
{ action: :tab_go, index: ::Regexp.last_match(1).to_i - 1 }
|
|
130
|
+
when /^(\d+)$/
|
|
131
|
+
{ action: :goto_line, line_number: ::Regexp.last_match(1).to_i }
|
|
132
|
+
else
|
|
133
|
+
{ action: :unknown, command: cmd }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mui
|
|
4
|
+
# Registry for Ex commands
|
|
5
|
+
class CommandRegistry
|
|
6
|
+
def initialize
|
|
7
|
+
@commands = {}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def register(name, &block)
|
|
11
|
+
@commands[name.to_sym] = block
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def execute(name, context, *)
|
|
15
|
+
command = @commands[name.to_sym]
|
|
16
|
+
raise UnknownCommandError, name unless command
|
|
17
|
+
|
|
18
|
+
command.call(context, *)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def exists?(name)
|
|
22
|
+
@commands.key?(name.to_sym)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mui
|
|
4
|
+
# Renders completion popup menu
|
|
5
|
+
class CompletionRenderer
|
|
6
|
+
MAX_VISIBLE_ITEMS = 10
|
|
7
|
+
|
|
8
|
+
def initialize(screen, color_scheme)
|
|
9
|
+
@screen = screen
|
|
10
|
+
@color_scheme = color_scheme
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render(completion_state, base_row, base_col)
|
|
14
|
+
return unless completion_state.active?
|
|
15
|
+
|
|
16
|
+
candidates = completion_state.candidates
|
|
17
|
+
selected_index = completion_state.selected_index
|
|
18
|
+
|
|
19
|
+
# Calculate visible window
|
|
20
|
+
visible_start, visible_end = calculate_visible_range(candidates.length, selected_index)
|
|
21
|
+
visible_candidates = candidates[visible_start...visible_end]
|
|
22
|
+
|
|
23
|
+
# Calculate popup dimensions
|
|
24
|
+
max_width = calculate_max_width(visible_candidates)
|
|
25
|
+
popup_height = visible_candidates.length
|
|
26
|
+
|
|
27
|
+
# Calculate position (popup appears above the command line)
|
|
28
|
+
popup_row = base_row - popup_height
|
|
29
|
+
popup_col = base_col
|
|
30
|
+
|
|
31
|
+
# Ensure popup stays within screen bounds
|
|
32
|
+
popup_col = [@screen.width - max_width - 1, popup_col].min
|
|
33
|
+
popup_col = [0, popup_col].max
|
|
34
|
+
popup_row = [0, popup_row].max
|
|
35
|
+
|
|
36
|
+
# Render each visible candidate
|
|
37
|
+
visible_candidates.each_with_index do |candidate, i|
|
|
38
|
+
actual_index = visible_start + i
|
|
39
|
+
is_selected = actual_index == selected_index
|
|
40
|
+
|
|
41
|
+
render_item(
|
|
42
|
+
candidate,
|
|
43
|
+
popup_row + i,
|
|
44
|
+
popup_col,
|
|
45
|
+
max_width,
|
|
46
|
+
is_selected
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def calculate_visible_range(total_count, selected_index)
|
|
54
|
+
return [0, total_count] if total_count <= MAX_VISIBLE_ITEMS
|
|
55
|
+
|
|
56
|
+
# Try to center the selected item
|
|
57
|
+
half = MAX_VISIBLE_ITEMS / 2
|
|
58
|
+
start_index = selected_index - half
|
|
59
|
+
start_index = [0, start_index].max
|
|
60
|
+
end_index = start_index + MAX_VISIBLE_ITEMS
|
|
61
|
+
end_index = [total_count, end_index].min
|
|
62
|
+
start_index = end_index - MAX_VISIBLE_ITEMS
|
|
63
|
+
|
|
64
|
+
[start_index, end_index]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def calculate_max_width(candidates)
|
|
68
|
+
return 0 if candidates.empty?
|
|
69
|
+
|
|
70
|
+
candidates.map { |c| display_width(c) }.max + 2 # +2 for padding
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def display_width(text)
|
|
74
|
+
UnicodeWidth.string_width(text)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def render_item(text, row, col, width, selected)
|
|
78
|
+
style_key = selected ? :completion_popup_selected : :completion_popup
|
|
79
|
+
style = @color_scheme[style_key]
|
|
80
|
+
padded_text = " #{text}".ljust(width)
|
|
81
|
+
@screen.put_with_style(row, col, padded_text, style)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mui
|
|
4
|
+
# Manages completion popup state
|
|
5
|
+
class CompletionState
|
|
6
|
+
attr_reader :candidates, :selected_index, :original_input, :completion_type
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
reset
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def reset
|
|
13
|
+
@candidates = []
|
|
14
|
+
@selected_index = 0
|
|
15
|
+
@original_input = nil
|
|
16
|
+
@completion_type = nil # :command or :file
|
|
17
|
+
@confirmed = false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def active?
|
|
21
|
+
!@candidates.empty?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def start(candidates, original_input, type)
|
|
25
|
+
@candidates = candidates
|
|
26
|
+
@selected_index = 0
|
|
27
|
+
@original_input = original_input
|
|
28
|
+
@completion_type = type
|
|
29
|
+
@confirmed = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def confirm
|
|
33
|
+
@confirmed = true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def confirmed?
|
|
37
|
+
@confirmed
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def select_next
|
|
41
|
+
return unless active?
|
|
42
|
+
|
|
43
|
+
@selected_index = (@selected_index + 1) % @candidates.length
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def select_previous
|
|
47
|
+
return unless active?
|
|
48
|
+
|
|
49
|
+
@selected_index = (@selected_index - 1) % @candidates.length
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def current_candidate
|
|
53
|
+
return nil unless active?
|
|
54
|
+
|
|
55
|
+
@candidates[@selected_index]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/mui/config.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mui
|
|
4
|
+
class Config
|
|
5
|
+
attr_reader :options, :plugins, :keymaps, :commands, :autocmds
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@options = {
|
|
9
|
+
colorscheme: "mui",
|
|
10
|
+
syntax: true, # Enable/disable syntax highlighting
|
|
11
|
+
shiftwidth: 2, # Indent width for > and < commands
|
|
12
|
+
expandtab: true, # Use spaces instead of tabs
|
|
13
|
+
tabstop: 8, # Tab display width
|
|
14
|
+
reselect_after_indent: false # Keep selection after > / < in visual mode
|
|
15
|
+
}
|
|
16
|
+
@plugins = []
|
|
17
|
+
@keymaps = {}
|
|
18
|
+
@commands = {}
|
|
19
|
+
@autocmds = {}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def set(key, value)
|
|
23
|
+
@options[key.to_sym] = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def get(key)
|
|
27
|
+
@options[key.to_sym]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def load_file(path)
|
|
31
|
+
return unless File.exist?(path)
|
|
32
|
+
|
|
33
|
+
instance_eval(File.read(path), path)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Stub for future plugin support
|
|
37
|
+
def use_plugin(gem_name, version = nil)
|
|
38
|
+
@plugins << { gem: gem_name, version: }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Stub for future keymap support
|
|
42
|
+
def add_keymap(mode, key, block)
|
|
43
|
+
@keymaps[mode] ||= {}
|
|
44
|
+
@keymaps[mode][key] = block
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def add_command(name, block)
|
|
48
|
+
@commands[name.to_sym] = block
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def add_autocmd(event, pattern, block)
|
|
52
|
+
@autocmds[event.to_sym] ||= []
|
|
53
|
+
@autocmds[event.to_sym] << { pattern:, handler: block }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|