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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9515ecfb14d1a9ea5422a842fd22911c303f2465f35827b879117e3cce4d1551
|
|
4
|
+
data.tar.gz: e6daedc45ebb07f1e8b094f590ae9749bfae49e2ced12017cba16f1c1dc255c0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4f493037291b8801dd2fc8a6101fb714110c12653ebb432900eebaa8f135d91597c8c862d5cf2b380e332d689decbada6d06f406e7f8c8eae859819d63887845
|
|
7
|
+
data.tar.gz: de2b83ea5a999760a171bb037bc264035ef1b43878b806a2b0577099b5601e6e94bdbf6d711604fd9d5a7df01d381665ed734526e1d0f643f896b40e89954604
|
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2025-12-11 15:57:29 UTC using RuboCop version 1.81.7.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
|
8
|
+
|
|
9
|
+
# Offense count: 1
|
|
10
|
+
# Configuration parameters: Severity.
|
|
11
|
+
Gemspec/RequiredRubyVersion:
|
|
12
|
+
Exclude:
|
|
13
|
+
- 'test/fixtures/mui-test-plugin/mui-test-plugin.gemspec'
|
|
14
|
+
|
|
15
|
+
# Offense count: 3
|
|
16
|
+
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
|
|
17
|
+
Lint/DuplicateBranch:
|
|
18
|
+
Exclude:
|
|
19
|
+
- 'lib/mui/key_handler/command_mode.rb'
|
|
20
|
+
- 'lib/mui/plugin_manager.rb'
|
|
21
|
+
- 'lib/mui/register.rb'
|
|
22
|
+
|
|
23
|
+
# Offense count: 3
|
|
24
|
+
# Configuration parameters: AllowComments, AllowEmptyLambdas.
|
|
25
|
+
Lint/EmptyBlock:
|
|
26
|
+
Exclude:
|
|
27
|
+
- 'test/mui/test_autocmd.rb'
|
|
28
|
+
- 'test/mui/test_command_registry.rb'
|
|
29
|
+
|
|
30
|
+
# Offense count: 1
|
|
31
|
+
# Configuration parameters: AllowedParentClasses.
|
|
32
|
+
Lint/MissingSuper:
|
|
33
|
+
Exclude:
|
|
34
|
+
- 'test/test_helper.rb'
|
|
35
|
+
|
|
36
|
+
# Offense count: 2
|
|
37
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
38
|
+
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
|
|
39
|
+
# NotImplementedExceptions: NotImplementedError
|
|
40
|
+
Lint/UnusedMethodArgument:
|
|
41
|
+
Exclude:
|
|
42
|
+
- 'lib/mui/terminal_adapter/base.rb'
|
|
43
|
+
|
|
44
|
+
# Offense count: 1
|
|
45
|
+
Lint/UselessConstantScoping:
|
|
46
|
+
Exclude:
|
|
47
|
+
- 'lib/mui/buffer_word_cache.rb'
|
|
48
|
+
|
|
49
|
+
# Offense count: 32
|
|
50
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
51
|
+
Metrics/CyclomaticComplexity:
|
|
52
|
+
Max: 49
|
|
53
|
+
|
|
54
|
+
# Offense count: 1
|
|
55
|
+
# Configuration parameters: CountComments, CountAsOne.
|
|
56
|
+
Metrics/ModuleLength:
|
|
57
|
+
Max: 284
|
|
58
|
+
|
|
59
|
+
# Offense count: 10
|
|
60
|
+
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
|
|
61
|
+
Metrics/ParameterLists:
|
|
62
|
+
Max: 9
|
|
63
|
+
|
|
64
|
+
# Offense count: 12
|
|
65
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
66
|
+
Metrics/PerceivedComplexity:
|
|
67
|
+
Max: 19
|
|
68
|
+
|
|
69
|
+
# Offense count: 3
|
|
70
|
+
Naming/AccessorMethodName:
|
|
71
|
+
Exclude:
|
|
72
|
+
- 'lib/mui/command_context.rb'
|
|
73
|
+
- 'lib/mui/search_input.rb'
|
|
74
|
+
- 'test/test_helper.rb'
|
|
75
|
+
|
|
76
|
+
# Offense count: 37
|
|
77
|
+
# Configuration parameters: EnforcedStyle, AllowedPatterns, ForbiddenIdentifiers, ForbiddenPatterns.
|
|
78
|
+
# SupportedStyles: snake_case, camelCase
|
|
79
|
+
# ForbiddenIdentifiers: __id__, __send__
|
|
80
|
+
Naming/MethodName:
|
|
81
|
+
Exclude:
|
|
82
|
+
- 'test/e2e/test_motion.rb'
|
|
83
|
+
- 'test/e2e/test_vim_operations.rb'
|
|
84
|
+
- 'test/e2e/test_visual_mode.rb'
|
|
85
|
+
- 'test/integration/test_editor_integration.rb'
|
|
86
|
+
- 'test/mui/editor/test_normal_mode.rb'
|
|
87
|
+
- 'test/mui/key_handler/test_normal_mode.rb'
|
|
88
|
+
- 'test/mui/key_handler/test_normal_mode_search.rb'
|
|
89
|
+
- 'test/mui/key_handler/test_visual_line_mode.rb'
|
|
90
|
+
- 'test/mui/key_handler/test_visual_mode.rb'
|
|
91
|
+
- 'test/mui/key_handler/test_window_command.rb'
|
|
92
|
+
|
|
93
|
+
# Offense count: 41
|
|
94
|
+
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
|
95
|
+
# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
|
|
96
|
+
Naming/MethodParameterName:
|
|
97
|
+
Exclude:
|
|
98
|
+
- 'lib/mui/buffer.rb'
|
|
99
|
+
- 'lib/mui/color_manager.rb'
|
|
100
|
+
- 'lib/mui/color_scheme.rb'
|
|
101
|
+
- 'lib/mui/layout/calculator.rb'
|
|
102
|
+
- 'lib/mui/line_renderer.rb'
|
|
103
|
+
- 'lib/mui/plugin.rb'
|
|
104
|
+
- 'lib/mui/screen.rb'
|
|
105
|
+
- 'lib/mui/terminal_adapter/base.rb'
|
|
106
|
+
- 'lib/mui/terminal_adapter/curses.rb'
|
|
107
|
+
- 'lib/mui/window.rb'
|
|
108
|
+
- 'test/e2e/test_helper.rb'
|
|
109
|
+
- 'test/test_helper.rb'
|
|
110
|
+
|
|
111
|
+
# Offense count: 6
|
|
112
|
+
# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
|
|
113
|
+
# AllowedMethods: call
|
|
114
|
+
# WaywardPredicates: nonzero?
|
|
115
|
+
Naming/PredicateMethod:
|
|
116
|
+
Exclude:
|
|
117
|
+
- 'lib/mui/key_handler/insert_mode.rb'
|
|
118
|
+
- 'lib/mui/undo_manager.rb'
|
|
119
|
+
- 'lib/mui/window_manager.rb'
|
|
120
|
+
|
|
121
|
+
# Offense count: 1
|
|
122
|
+
# Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs.
|
|
123
|
+
# NamePrefix: is_, has_, have_, does_
|
|
124
|
+
# ForbiddenPrefixes: is_, has_, have_, does_
|
|
125
|
+
# AllowedMethods: is_a?
|
|
126
|
+
# MethodDefinitionMacros: define_method, define_singleton_method
|
|
127
|
+
Naming/PredicatePrefix:
|
|
128
|
+
Exclude:
|
|
129
|
+
- 'spec/**/*'
|
|
130
|
+
- 'lib/mui/search_state.rb'
|
|
131
|
+
|
|
132
|
+
# Offense count: 2
|
|
133
|
+
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
|
|
134
|
+
# SupportedStyles: snake_case, normalcase, non_integer
|
|
135
|
+
# AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
|
|
136
|
+
Naming/VariableNumber:
|
|
137
|
+
Exclude:
|
|
138
|
+
- 'test/e2e/test_motion.rb'
|
|
139
|
+
- 'test/mui/test_register.rb'
|
|
140
|
+
|
|
141
|
+
# Offense count: 2
|
|
142
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
143
|
+
Style/ComparableClamp:
|
|
144
|
+
Exclude:
|
|
145
|
+
- 'lib/mui/screen.rb'
|
|
146
|
+
|
|
147
|
+
# Offense count: 25
|
|
148
|
+
# Configuration parameters: AllowedConstants.
|
|
149
|
+
Style/Documentation:
|
|
150
|
+
Enabled: false
|
|
151
|
+
|
|
152
|
+
# Offense count: 1
|
|
153
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
154
|
+
# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods.
|
|
155
|
+
# AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
|
|
156
|
+
Style/TrivialAccessors:
|
|
157
|
+
Exclude:
|
|
158
|
+
- 'test/mui/test_autocmd.rb'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,354 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2025-12-12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Insert mode completion support for LSP:
|
|
7
|
+
- `InsertCompletionState` class for managing LSP completion items
|
|
8
|
+
- `InsertCompletionRenderer` for displaying completion popup below cursor
|
|
9
|
+
- `Editor#start_insert_completion(items, prefix:)` API for plugins
|
|
10
|
+
- `Editor#insert_completion_active?` to check completion state
|
|
11
|
+
- Key bindings in Insert mode:
|
|
12
|
+
- `↑` / `Ctrl+P`: Select previous completion
|
|
13
|
+
- `↓` / `Ctrl+N`: Select next completion
|
|
14
|
+
- `Tab`: Confirm and insert selected completion
|
|
15
|
+
- `Esc`: Cancel completion popup
|
|
16
|
+
- Popup automatically repositions above cursor if space is limited
|
|
17
|
+
- Auto-trigger completion after `.`, `@`, and `::` characters
|
|
18
|
+
- Uses LSP `textEdit` for precise text replacement
|
|
19
|
+
- Buffer word completion (`Ctrl+N` / `Ctrl+P` in Insert mode):
|
|
20
|
+
- Collects words from current buffer as completion candidates
|
|
21
|
+
- `BufferWordCache` class for high-performance word caching
|
|
22
|
+
- Cache built once when entering Insert mode, updated incrementally on changes
|
|
23
|
+
- Auto-triggers while typing (1+ character prefix)
|
|
24
|
+
- Dynamic filtering: completion list updates as you type more characters
|
|
25
|
+
- Excludes word at cursor position from candidates
|
|
26
|
+
- Triggered when no LSP completion is active
|
|
27
|
+
- `InsertCompletion` autocmd event for plugins to hook into completion triggers
|
|
28
|
+
- `KeyCode::CTRL_N`, `KeyCode::CTRL_P`, and `KeyCode::CTRL_SPACE` constants
|
|
29
|
+
- `KeyHandler::Base#editor` helper method for accessing editor instance
|
|
30
|
+
- Insert mode plugin keymap support (allows plugins to define Insert mode key bindings)
|
|
31
|
+
- LSP configuration stub (`Mui.lsp` / `Mui::LspConfigStub`):
|
|
32
|
+
- Allows `.muirc` to call `Mui.lsp { ... }` before mui-lsp gem is loaded
|
|
33
|
+
- Stub stores configuration (preset servers via `use`, custom servers via `server`)
|
|
34
|
+
- When mui-lsp gem loads, configurations are automatically migrated to real `ConfigDsl`
|
|
35
|
+
- `Mui.lsp_server_configs` returns stored server configurations
|
|
36
|
+
- Floating window (popup) support:
|
|
37
|
+
- `FloatingWindow` class for displaying temporary content like hover info
|
|
38
|
+
- `editor.show_floating(content, max_width:, max_height:)` to show popup at cursor
|
|
39
|
+
- `editor.hide_floating` to close the popup
|
|
40
|
+
- Automatic positioning with screen bounds adjustment
|
|
41
|
+
- Unicode box-drawing border characters
|
|
42
|
+
- Scroll support for long content
|
|
43
|
+
- Auto-close on any key press (Escape only closes without processing)
|
|
44
|
+
- Configurable via `:floating_window` color scheme element
|
|
45
|
+
- Dynamic custom highlighter management for buffers:
|
|
46
|
+
- `Buffer#add_custom_highlighter(key, highlighter)` to add named highlighter
|
|
47
|
+
- `Buffer#remove_custom_highlighter(key)` to remove highlighter by key
|
|
48
|
+
- `Buffer#custom_highlighter?(key)` to check if highlighter exists
|
|
49
|
+
- `Window#refresh_highlighters` to rebuild line renderer after highlighter changes
|
|
50
|
+
- Enables plugins to dynamically add/remove highlighting (e.g., LSP diagnostics)
|
|
51
|
+
- Interactive command execution support for plugins:
|
|
52
|
+
- `TerminalAdapter#suspend` / `#resume` methods for temporarily exiting curses mode
|
|
53
|
+
- `Editor#suspend_ui` block helper for safe UI suspension
|
|
54
|
+
- `CommandContext#run_interactive_command(cmd)` to run external interactive commands (e.g., fzf)
|
|
55
|
+
- `CommandContext#command_exists?(cmd)` to check if external command is available
|
|
56
|
+
- Enables plugins to integrate with interactive CLI tools like fzf, less, etc.
|
|
57
|
+
- Custom highlighter support for buffers:
|
|
58
|
+
- `Buffer#custom_highlighters(color_scheme)` method for buffer-specific highlighting
|
|
59
|
+
- Window automatically applies custom highlighters from buffer
|
|
60
|
+
- Enables plugins to provide syntax highlighting for special buffers (e.g., diff output)
|
|
61
|
+
- Diff highlighting color definitions:
|
|
62
|
+
- Added `diff_add`, `diff_delete`, `diff_hunk`, `diff_header` to ColorScheme elements
|
|
63
|
+
- All themes now include diff highlighting colors
|
|
64
|
+
- Incremental search (incsearch):
|
|
65
|
+
- Search results highlight in real-time as you type
|
|
66
|
+
- Cursor moves to first match while typing
|
|
67
|
+
- Escape cancels search and restores original cursor position
|
|
68
|
+
- Backspace updates search results incrementally
|
|
69
|
+
- Works with both forward (`/`) and backward (`?`) search
|
|
70
|
+
- Word completion popup shows matching words from buffer as you type
|
|
71
|
+
- Tab/Shift+Tab to cycle through completion candidates
|
|
72
|
+
- Completions are extracted from buffer content (identifiers and words)
|
|
73
|
+
- Word search commands (`*` and `#`):
|
|
74
|
+
- Normal mode: `*` searches forward for word under cursor
|
|
75
|
+
- Normal mode: `#` searches backward for word under cursor
|
|
76
|
+
- Uses word boundaries (`\b`) for whole-word matching (Vim behavior)
|
|
77
|
+
- Visual mode: `*`/`#` searches for selected text
|
|
78
|
+
- Works in both Visual mode (`v`) and Visual Line mode (`V`)
|
|
79
|
+
- Special regex characters are escaped for literal matching
|
|
80
|
+
- Search state is updated so `n`/`N` continues to find matches
|
|
81
|
+
- Visual mode indent commands (Vim-compatible):
|
|
82
|
+
- `>` to add indent to selected lines (shiftwidth spaces or tab)
|
|
83
|
+
- `<` to remove indent from selected lines
|
|
84
|
+
- Works in both Visual mode (`v`) and Visual Line mode (`V`)
|
|
85
|
+
- Empty lines are skipped (Vim behavior)
|
|
86
|
+
- Multiple lines indented as single undo group
|
|
87
|
+
- Cursor moves to first selected line after operation
|
|
88
|
+
- Configuration options: `shiftwidth` (default: 2), `expandtab` (default: true), `tabstop` (default: 8)
|
|
89
|
+
- `reselect_after_indent` option (default: false): Keep selection after indent for continuous adjustment
|
|
90
|
+
- Reselect last visual selection (`gv` command):
|
|
91
|
+
- `gv` in Normal mode restores the previous visual selection
|
|
92
|
+
- Works with both Visual mode and Visual Line mode
|
|
93
|
+
- Cursor moves to end of selection
|
|
94
|
+
- Useful for repeating operations on the same selection (e.g., multiple indents)
|
|
95
|
+
- Asynchronous job execution system (JobManager):
|
|
96
|
+
- `JobManager` class for managing background tasks with Thread + Queue
|
|
97
|
+
- `Job` class with status tracking (pending, running, completed, failed, cancelled)
|
|
98
|
+
- Thread-safe implementation with Mutex protection
|
|
99
|
+
- `context.run_async { ... }` to run Ruby blocks asynchronously
|
|
100
|
+
- `context.run_shell_command("cmd")` to run external shell commands via Open3
|
|
101
|
+
- `context.jobs_running?` to check if any jobs are active
|
|
102
|
+
- `context.cancel_job(id)` to cancel a running job
|
|
103
|
+
- Callback support via `on_complete:` parameter
|
|
104
|
+
- Autocmd events: JobStarted, JobCompleted, JobFailed, JobCancelled
|
|
105
|
+
- Non-blocking event loop with polling for job results
|
|
106
|
+
- Scratch buffer support:
|
|
107
|
+
- `editor.open_scratch_buffer(name, content)` to display read-only results
|
|
108
|
+
- `context.open_scratch_buffer(name, content)` for plugins
|
|
109
|
+
- Opens in horizontal split window
|
|
110
|
+
- Ideal for displaying test results, linter output, etc.
|
|
111
|
+
- Readonly buffer protection:
|
|
112
|
+
- `buffer.readonly` flag to mark buffers as read-only
|
|
113
|
+
- Edit commands (`i`, `a`, `o`, `O`, `x`, `d`, `c`, `p`, `P`) blocked with error message
|
|
114
|
+
- `:w` and `:wq` commands blocked on readonly buffers
|
|
115
|
+
- Navigation and yank operations still allowed
|
|
116
|
+
- Command-line completion (real-time popup):
|
|
117
|
+
- Auto-completion popup appears as you type in command mode
|
|
118
|
+
- Command name completion for all Ex commands (`:w`, `:q`, `:tabnew`, etc.)
|
|
119
|
+
- File path completion for file commands (`:e`, `:w`, `:sp`, `:vs`, `:tabnew`)
|
|
120
|
+
- Tab to cycle forward through candidates and apply selection
|
|
121
|
+
- Shift+Tab to cycle backward through candidates
|
|
122
|
+
- Popup displays up to 10 candidates with scroll support
|
|
123
|
+
- Completion styles configurable in all 8 themes (`completion_popup`, `completion_popup_selected`)
|
|
124
|
+
- Syntax highlighting for Ruby and C:
|
|
125
|
+
- Token-based lexer architecture with extensible base class
|
|
126
|
+
- Ruby lexer with support for: keywords, strings, comments, numbers, symbols, constants, instance variables (`@foo`, `@@bar`), global variables (`$stdout`), method calls (`.to_i`, `.each`)
|
|
127
|
+
- C lexer with support for: keywords, strings, char literals, comments, numbers, preprocessor directives
|
|
128
|
+
- Multiline comment support: Ruby `=begin`/`=end`, C `/* */`
|
|
129
|
+
- Line-based token caching for performance
|
|
130
|
+
- Language auto-detection from file extension (`.rb`, `.rake`, `.gemspec`, `.c`, `.h`, `.y`)
|
|
131
|
+
- Syntax colors defined for all 8 built-in themes
|
|
132
|
+
- Configurable via `set :syntax, true/false` in `.muirc`
|
|
133
|
+
- Tab page functionality (Vim-compatible):
|
|
134
|
+
- Tab > Window hierarchy: each tab contains independent window layout
|
|
135
|
+
- `:tabnew` / `:tabe` / `:tabedit` to create new tab
|
|
136
|
+
- `:tabclose` / `:tabc` to close current tab
|
|
137
|
+
- `:tabnext` / `:tabn` to go to next tab
|
|
138
|
+
- `:tabprev` / `:tabp` to go to previous tab
|
|
139
|
+
- `:tabfirst` / `:tabf` to go to first tab
|
|
140
|
+
- `:tablast` / `:tabl` to go to last tab
|
|
141
|
+
- `:Ntabn` / `:tabnext N` to go to specific tab (1-indexed)
|
|
142
|
+
- `:tabmove N` / `:tabm N` to move current tab to position N
|
|
143
|
+
- `gt` to go to next tab (wraps around)
|
|
144
|
+
- `gT` to go to previous tab (wraps around)
|
|
145
|
+
- Tab bar at top of screen with separator line
|
|
146
|
+
- Tab bar colors configurable via color scheme (`tab_bar`, `tab_bar_active`)
|
|
147
|
+
- `:q` closes window → tab → editor (Vim-like behavior)
|
|
148
|
+
- Window split functionality:
|
|
149
|
+
- `:sp` / `:split` to split window horizontally (top/bottom)
|
|
150
|
+
- `:vs` / `:vsplit` to split window vertically (left/right)
|
|
151
|
+
- `:sp <filename>` / `:vs <filename>` to split and open file
|
|
152
|
+
- `Ctrl-w h/j/k/l` to navigate between split windows
|
|
153
|
+
- `Ctrl-w w` to cycle to next window
|
|
154
|
+
- `Ctrl-w c` / `:close` to close current window
|
|
155
|
+
- `Ctrl-w o` / `:only` to close all windows except current
|
|
156
|
+
- `:q` / `:wq` / `:q!` in split window closes only that window (Vim-compatible)
|
|
157
|
+
- `:e <filename>` opens file in current window only (doesn't affect other split windows)
|
|
158
|
+
- Separator lines between windows with configurable colors
|
|
159
|
+
- Command line area with configurable background color (no terminal transparency)
|
|
160
|
+
- Each window has independent cursor position, scroll state, and buffer
|
|
161
|
+
- Japanese and multibyte character input support:
|
|
162
|
+
- Full UTF-8 input handling in Insert mode via IME
|
|
163
|
+
- Proper display width calculation for CJK characters (East Asian Width)
|
|
164
|
+
- Cursor position correctly accounts for double-width characters
|
|
165
|
+
- UTF-8 byte sequence assembly in terminal adapter
|
|
166
|
+
- Plugin system:
|
|
167
|
+
- `Mui.use "gem_name"` to declare plugin gems
|
|
168
|
+
- Lazy gem installation via `bundler/inline` on editor startup
|
|
169
|
+
- Class-based plugins: `class MyPlugin < Mui::Plugin`
|
|
170
|
+
- DSL-based plugins: `Mui.define_plugin(:name) { ... }`
|
|
171
|
+
- Plugin API: `command`, `keymap`, `autocmd`
|
|
172
|
+
- Plugin dependencies with topological sort for load order
|
|
173
|
+
- CommandContext for plugin access to editor internals
|
|
174
|
+
- Autocmd event system:
|
|
175
|
+
- 14 events: BufEnter, BufLeave, BufWrite, BufWritePre, BufWritePost, ModeChanged, CursorMoved, TextChanged, InsertEnter, InsertLeave, JobStarted, JobCompleted, JobFailed, JobCancelled
|
|
176
|
+
- Pattern matching for file paths (e.g., `"*.rb"`)
|
|
177
|
+
- `Mui.autocmd :BufEnter, pattern: "*.rb" do |ctx| ... end`
|
|
178
|
+
- Custom commands:
|
|
179
|
+
- `Mui.command :name do |ctx| ... end`
|
|
180
|
+
- Execute via `:name` in command mode
|
|
181
|
+
- Custom keymaps:
|
|
182
|
+
- `Mui.keymap :normal, "key" do |ctx| ... end`
|
|
183
|
+
- Per-mode key bindings
|
|
184
|
+
- Configuration file support:
|
|
185
|
+
- `~/.muirc` for global settings
|
|
186
|
+
- `.lmuirc` for local (per-project) settings
|
|
187
|
+
- Local settings override global settings
|
|
188
|
+
- Ruby DSL format: `Mui.set :colorscheme, "theme_name"`
|
|
189
|
+
- Color scheme system:
|
|
190
|
+
- 256-color terminal support
|
|
191
|
+
- Built-in themes: `mui` (default), `solarized_dark`, `solarized_light`, `monokai`, `nord`, `gruvbox_dark`, `dracula`, `tokyo_night`
|
|
192
|
+
- Customizable UI elements: normal text, status line, search highlight, visual selection, line numbers, messages
|
|
193
|
+
- Search functionality:
|
|
194
|
+
- `/` to search forward (supports regular expressions)
|
|
195
|
+
- `?` to search backward (supports regular expressions)
|
|
196
|
+
- `n` to jump to next match (respects search direction)
|
|
197
|
+
- `N` to jump to previous match (reverses search direction)
|
|
198
|
+
- Wrap-around search (continues from beginning/end of file)
|
|
199
|
+
- Search match highlighting across visible lines
|
|
200
|
+
- Undo/Redo functionality:
|
|
201
|
+
- `u` to undo last change
|
|
202
|
+
- `Ctrl-r` to redo undone change
|
|
203
|
+
- Vim-compatible undo granularity: Insert mode session as single undo unit
|
|
204
|
+
- Support for all editing operations (insert, delete, change, visual mode operations)
|
|
205
|
+
- Maximum 1000 undo history entries
|
|
206
|
+
- Vim-compatible named registers:
|
|
207
|
+
- Named registers (`"a` - `"z`): 26 user-specified registers
|
|
208
|
+
- Unnamed register (`""`): Default register
|
|
209
|
+
- Yank register (`"0`): Stores last yank, not affected by delete
|
|
210
|
+
- Delete history registers (`"1` - `"9`): Stores delete history, shifts on each delete
|
|
211
|
+
- Black hole register (`"_`): Discards content without saving
|
|
212
|
+
- Usage: `"ayy` (yank to register a), `"ap` (paste from register a)
|
|
213
|
+
- Delete/change operators now save to registers (Vim-compatible behavior):
|
|
214
|
+
- `dd`, `dw`, etc. save deleted text to unnamed register and delete history
|
|
215
|
+
- `cc`, `cw`, etc. save changed text to unnamed register and delete history
|
|
216
|
+
- Visual mode `d`/`c` also save to registers
|
|
217
|
+
- Yank operator (`y`):
|
|
218
|
+
- `yy` to yank current line
|
|
219
|
+
- `y` + motion: `yw`, `ye`, `yb`, `y0`, `y$`, `ygg`, `yG`
|
|
220
|
+
- `yf{char}`, `yt{char}`, `yF{char}`, `yT{char}` to yank to/till character
|
|
221
|
+
- `y` in Visual mode to yank selection (character-wise and line-wise)
|
|
222
|
+
- `yw` behaves like `ye` (yanks to end of word) matching Vim behavior
|
|
223
|
+
- Paste commands:
|
|
224
|
+
- `p` to paste after cursor (character-wise) or below current line (line-wise)
|
|
225
|
+
- `P` to paste before cursor (character-wise) or above current line (line-wise)
|
|
226
|
+
- Multi-line character-wise paste support
|
|
227
|
+
- Register system for yank/paste operations
|
|
228
|
+
- Default register for storing yanked text
|
|
229
|
+
- Named registers support
|
|
230
|
+
- Linewise flag to distinguish line-wise vs character-wise yanks
|
|
231
|
+
- TerminalAdapter abstraction layer for custom terminal implementations
|
|
232
|
+
- `Mui::TerminalAdapter::Base` abstract base class defining the terminal interface
|
|
233
|
+
- `Mui::TerminalAdapter::Curses` default implementation using Curses library
|
|
234
|
+
- Users can create custom adapters by subclassing `Base` and passing to `Editor.new(adapter:)`
|
|
235
|
+
- Example: `Mui::Editor.new(file_path, adapter: MyCustomAdapter.new)`
|
|
236
|
+
- Initial release of Mui, a Vim-like text editor written in Ruby
|
|
237
|
+
- Vim-like modal editing with five modes:
|
|
238
|
+
- Normal mode: Navigation and text manipulation
|
|
239
|
+
- Insert mode: Text input
|
|
240
|
+
- Command mode: Ex commands
|
|
241
|
+
- Visual mode (`v`): Character-wise selection
|
|
242
|
+
- Visual Line mode (`V`): Line-wise selection
|
|
243
|
+
- Basic cursor movement with `h`, `j`, `k`, `l` and arrow keys in Normal mode
|
|
244
|
+
- Arrow key cursor movement in Insert mode
|
|
245
|
+
- Motion commands:
|
|
246
|
+
- Word movements: `w` (word forward), `b` (word backward), `e` (word end)
|
|
247
|
+
- Line movements: `0` (line start), `^` (first non-blank), `$` (line end)
|
|
248
|
+
- File movements: `gg` (file start), `G` (file end)
|
|
249
|
+
- Character search: `f{char}`, `F{char}`, `t{char}`, `T{char}`
|
|
250
|
+
- Text editing operations:
|
|
251
|
+
- `i` to insert before cursor
|
|
252
|
+
- `a` to append after cursor
|
|
253
|
+
- `o` to open new line below
|
|
254
|
+
- `O` to open new line above
|
|
255
|
+
- `x` to delete character at cursor
|
|
256
|
+
- Backspace to delete and join lines
|
|
257
|
+
- Delete operator (`d`):
|
|
258
|
+
- `dd` to delete current line
|
|
259
|
+
- `d` + motion: `dw`, `de`, `db`, `d0`, `d$`, `dgg`, `dG`
|
|
260
|
+
- `df{char}`, `dt{char}`, `dF{char}`, `dT{char}` to delete to/till character
|
|
261
|
+
- `d` in Visual mode to delete selection (character-wise and line-wise)
|
|
262
|
+
- Change operator (`c`):
|
|
263
|
+
- `cc` to change current line (clear and enter Insert mode)
|
|
264
|
+
- `c` + motion: `cw`, `ce`, `cb`, `c0`, `c$`, `cgg`, `cG`
|
|
265
|
+
- `cf{char}`, `ct{char}`, `cF{char}`, `cT{char}` to change to/till character
|
|
266
|
+
- `c` in Visual mode to change selection (character-wise and line-wise)
|
|
267
|
+
- `cw` behaves like `ce` (changes to end of word, preserving space) matching Vim behavior
|
|
268
|
+
- Ex commands:
|
|
269
|
+
- `:w` to save file
|
|
270
|
+
- `:w <filename>` to save as
|
|
271
|
+
- `:q` to quit (with unsaved changes protection)
|
|
272
|
+
- `:q!` to force quit
|
|
273
|
+
- `:wq` to save and quit
|
|
274
|
+
- `:e` to reload current file
|
|
275
|
+
- `:e <filename>` to open file (Vim-compatible: non-existent files open as new buffer)
|
|
276
|
+
- `:{number}` to jump to specific line (e.g., `:10` jumps to line 10)
|
|
277
|
+
- Line numbers are 1-indexed (`:1` = first line)
|
|
278
|
+
- Out-of-range values are clamped (`:0` → first line, `:999` on 100-line file → last line)
|
|
279
|
+
- Cursor moves to beginning of line (column 0)
|
|
280
|
+
- Curses-based terminal UI with:
|
|
281
|
+
- Buffer management
|
|
282
|
+
- Window with scrolling support
|
|
283
|
+
- Status line display
|
|
284
|
+
- Command line input
|
|
285
|
+
- Visual mode features:
|
|
286
|
+
- Selection highlighting with reverse video
|
|
287
|
+
- Toggle between Visual and Visual Line mode with `v`/`V`
|
|
288
|
+
- All motion commands supported (h, j, k, l, w, b, e, 0, ^, $, gg, G, f, F, t, T)
|
|
289
|
+
- Exit to Normal mode with `Esc`
|
|
290
|
+
- Comprehensive test suite for `Mui::Input` and `Mui::Editor` classes
|
|
291
|
+
- Unit tests for Buffer, CommandLine, Input, Screen, Window, Selection, and Editor modes
|
|
292
|
+
- Integration tests for component interactions
|
|
293
|
+
- E2E tests with ScriptRunner DSL for Vim operation scenarios including Visual mode
|
|
294
|
+
- Test infrastructure with Curses mock and `MuiTestHelper` module
|
|
295
|
+
|
|
296
|
+
### Changed
|
|
297
|
+
- Reorganized test directory structure to follow standard gem conventions
|
|
298
|
+
- Unit tests moved to `test/mui/`
|
|
299
|
+
- Editor mode tests in `test/mui/editor/`
|
|
300
|
+
- Integration tests in `test/integration/`
|
|
301
|
+
- E2E tests in `test/e2e/`
|
|
302
|
+
- Refactored test files to use nested classes per method for better readability
|
|
303
|
+
- Improved syntax highlighting performance:
|
|
304
|
+
- Use `\G` anchor for position-specific regex matching to avoid substring allocation
|
|
305
|
+
- Pre-compile all regex patterns as class-level constants for one-time compilation at class load
|
|
306
|
+
- Optimize event sorting in line renderer using array tuples instead of hash arrays
|
|
307
|
+
- Add style resolution cache to avoid repeated hash merges
|
|
308
|
+
- Reduce hash lookups in token cache by inlining cache validation
|
|
309
|
+
- Improved buffer change detection performance:
|
|
310
|
+
- Added `Buffer#change_count` for O(1) change detection
|
|
311
|
+
- Replaced `buffer.lines.hash` (O(n)) with counter comparison in `TextChanged` event trigger
|
|
312
|
+
- Improved buffer word completion performance:
|
|
313
|
+
- Use `String#scan` with regex for fast word extraction instead of character-by-character iteration
|
|
314
|
+
- Direct ASCII code comparison for word character detection instead of regex match per character
|
|
315
|
+
|
|
316
|
+
### Fixed
|
|
317
|
+
- Command-line completion Tab behavior:
|
|
318
|
+
- First Tab now confirms current selection without cycling to next candidate
|
|
319
|
+
- Second Tab and subsequent presses cycle through candidates
|
|
320
|
+
- Previously, first Tab would skip the initially displayed candidate
|
|
321
|
+
- Command-line completion cursor position:
|
|
322
|
+
- Cursor now moves to end of buffer after applying completion
|
|
323
|
+
- Previously, cursor remained at old position after completion
|
|
324
|
+
- Plugin keymap handler now correctly uses active window's buffer instead of initial buffer
|
|
325
|
+
- Fixes issue where buffer-specific keymaps didn't work in split windows
|
|
326
|
+
- Plugin keymap handler now properly returns handler result
|
|
327
|
+
- Allows buffer-specific keymaps to conditionally pass through to built-in handlers
|
|
328
|
+
- Plugin keymap now correctly handles Enter key (Curses::KEY_ENTER)
|
|
329
|
+
- Previously special key codes like Curses::KEY_ENTER (343) were not converted to keymap string
|
|
330
|
+
- Now Enter key variants (CR, LF, KEY_ENTER) are all mapped to `"\r"` for plugin keymaps
|
|
331
|
+
- Auto-indent on newline in Insert mode
|
|
332
|
+
- Pressing Enter preserves indentation from the current line
|
|
333
|
+
- Leading whitespace (spaces and tabs) is automatically inserted on the new line
|
|
334
|
+
- Whitespace-only lines are cleared when pressing Escape (Vim behavior)
|
|
335
|
+
- Register is now shared globally across tabs and split windows
|
|
336
|
+
- Yank in one tab/window can be pasted in another
|
|
337
|
+
- Previously each ModeManager had its own Register instance
|
|
338
|
+
- Visual mode operations now correctly use the active window's buffer
|
|
339
|
+
- Fixed yank/delete/change/indent operations using stale buffer reference after tab or window switch
|
|
340
|
+
- Operations now properly target the current active window's buffer
|
|
341
|
+
- Command mode cursor movement with arrow keys
|
|
342
|
+
- Left/Right arrow keys now move cursor within command line
|
|
343
|
+
- Previously arrow keys were interpreted as characters (e.g., Left inserted 'a')
|
|
344
|
+
- Backspace now deletes character before cursor position
|
|
345
|
+
- Character input now inserts at cursor position
|
|
346
|
+
- Empty command (`:` then Enter) no longer crashes
|
|
347
|
+
- Previously caused error when trying to look up empty string as plugin command
|
|
348
|
+
- Buffer word completion now works correctly after opening files with `:e` or `:tabe`
|
|
349
|
+
- `InsertMode` now uses active window's buffer for `BufferWordCache` instead of initial buffer
|
|
350
|
+
- Previously, completion would use stale buffer reference after switching files
|
|
351
|
+
|
|
3
352
|
## [0.1.0] - 2025-11-30
|
|
4
353
|
|
|
5
354
|
- Initial release
|
data/exe/mui
CHANGED
data/lib/mui/autocmd.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mui
|
|
4
|
+
# Event-driven hook system for plugins
|
|
5
|
+
class Autocmd
|
|
6
|
+
EVENTS = %i[
|
|
7
|
+
BufEnter
|
|
8
|
+
BufLeave
|
|
9
|
+
BufWrite
|
|
10
|
+
BufWritePre
|
|
11
|
+
BufWritePost
|
|
12
|
+
ModeChanged
|
|
13
|
+
CursorMoved
|
|
14
|
+
TextChanged
|
|
15
|
+
InsertEnter
|
|
16
|
+
InsertLeave
|
|
17
|
+
InsertCompletion
|
|
18
|
+
JobStarted
|
|
19
|
+
JobCompleted
|
|
20
|
+
JobFailed
|
|
21
|
+
JobCancelled
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
def initialize
|
|
25
|
+
@handlers = {}
|
|
26
|
+
EVENTS.each { |e| @handlers[e] = [] }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def register(event, pattern: nil, &block)
|
|
30
|
+
event = event.to_sym
|
|
31
|
+
raise ArgumentError, "Unknown event: #{event}" unless EVENTS.include?(event)
|
|
32
|
+
|
|
33
|
+
@handlers[event] << { pattern:, handler: block }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def trigger(event, context = nil, **kwargs)
|
|
37
|
+
event = event.to_sym
|
|
38
|
+
return unless @handlers[event]
|
|
39
|
+
|
|
40
|
+
@handlers[event].each do |entry|
|
|
41
|
+
# Skip pattern matching for non-buffer events (like Job events)
|
|
42
|
+
next if context && entry[:pattern] && !match_pattern?(context, entry[:pattern])
|
|
43
|
+
|
|
44
|
+
# Pass context or kwargs depending on what's available
|
|
45
|
+
entry[:handler].call(context || kwargs)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def match_pattern?(context, pattern)
|
|
52
|
+
return true unless pattern
|
|
53
|
+
|
|
54
|
+
file_path = context.buffer.file_path
|
|
55
|
+
|
|
56
|
+
case pattern
|
|
57
|
+
when Regexp
|
|
58
|
+
file_path&.match?(pattern)
|
|
59
|
+
when String
|
|
60
|
+
File.fnmatch(pattern, file_path || "")
|
|
61
|
+
else
|
|
62
|
+
false
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|