mui 0.4.1 → 0.5.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 +2 -22
- data/.ruby-lsp.yml +19 -0
- data/CHANGELOG.md +48 -0
- data/README.md +17 -0
- data/docs/configuration.md +40 -0
- data/docs/getting-started.md +1 -1
- data/docs/keybindings.md +4 -0
- data/lib/mui/buffer.rb +1 -1
- data/lib/mui/buffer_word_cache.rb +2 -3
- data/lib/mui/command_completer.rb +4 -2
- data/lib/mui/command_line.rb +7 -2
- data/lib/mui/completion_renderer.rb +1 -1
- data/lib/mui/config.rb +3 -1
- data/lib/mui/editor.rb +3 -0
- data/lib/mui/file_completer.rb +10 -10
- data/lib/mui/insert_completion_renderer.rb +1 -1
- data/lib/mui/key_code.rb +5 -0
- data/lib/mui/key_handler/base.rb +2 -0
- data/lib/mui/key_handler/command_mode.rb +1 -1
- data/lib/mui/key_handler/insert_mode.rb +30 -0
- data/lib/mui/key_handler/normal_mode.rb +21 -0
- data/lib/mui/key_handler/visual_mode.rb +3 -1
- data/lib/mui/key_notation_parser.rb +13 -4
- data/lib/mui/mode_manager.rb +15 -27
- data/lib/mui/register.rb +36 -0
- data/lib/mui/screen.rb +5 -3
- data/lib/mui/search_completer.rb +1 -1
- data/lib/mui/search_input.rb +3 -3
- data/lib/mui/search_state.rb +11 -2
- data/lib/mui/status_line_renderer.rb +3 -5
- data/lib/mui/syntax/lexers/markdown_lexer.rb +1 -1
- data/lib/mui/terminal_adapter/curses.rb +40 -46
- data/lib/mui/unicode_width.rb +1 -1
- data/lib/mui/version.rb +1 -1
- data/lib/mui/window.rb +8 -0
- data/lib/mui/window_manager.rb +2 -5
- data/lib/mui/wrap_helper.rb +3 -3
- data/sig/lib/mui/autocmd.rbs +12 -0
- data/sig/lib/mui/buffer.rbs +52 -0
- data/sig/lib/mui/buffer_word_cache.rbs +21 -0
- data/sig/lib/mui/buffer_word_completer.rbs +15 -0
- data/sig/lib/mui/color_manager.rbs +20 -0
- data/sig/lib/mui/color_scheme.rbs +14 -0
- data/sig/lib/mui/command_completer.rbs +11 -0
- data/sig/lib/mui/command_context.rbs +24 -0
- data/sig/lib/mui/command_history.rbs +20 -0
- data/sig/lib/mui/command_line.rbs +24 -0
- data/sig/lib/mui/command_registry.rbs +14 -0
- data/sig/lib/mui/completion_renderer.rbs +14 -0
- data/sig/lib/mui/completion_state.rbs +20 -0
- data/sig/lib/mui/config.rbs +20 -0
- data/sig/lib/mui/editor.rbs +54 -0
- data/sig/lib/mui/error.rbs +18 -0
- data/sig/lib/mui/file_completer.rbs +13 -0
- data/sig/lib/mui/floating_window.rbs +30 -0
- data/sig/lib/mui/handler_result.rbs +38 -0
- data/sig/lib/mui/highlight.rbs +14 -0
- data/sig/lib/mui/highlighters/base.rbs +15 -0
- data/sig/lib/mui/highlighters/search_highlighter.rbs +11 -0
- data/sig/lib/mui/highlighters/selection_highlighter.rbs +13 -0
- data/sig/lib/mui/highlighters/syntax_highlighter.rbs +20 -0
- data/sig/lib/mui/input.rbs +10 -0
- data/sig/lib/mui/insert_completion_renderer.rbs +15 -0
- data/sig/lib/mui/insert_completion_state.rbs +23 -0
- data/sig/lib/mui/job.rbs +27 -0
- data/sig/lib/mui/job_manager.rbs +20 -0
- data/sig/lib/mui/key_code.rbs +29 -0
- data/sig/lib/mui/key_handler/base.rbs +35 -0
- data/sig/lib/mui/key_handler/command_mode.rbs +56 -0
- data/sig/lib/mui/key_handler/insert_mode.rbs +42 -0
- data/sig/lib/mui/key_handler/motions/motion_handler.rbs +19 -0
- data/sig/lib/mui/key_handler/normal_mode.rbs +55 -0
- data/sig/lib/mui/key_handler/operators/base_operator.rbs +29 -0
- data/sig/lib/mui/key_handler/operators/change_operator.rbs +22 -0
- data/sig/lib/mui/key_handler/operators/delete_operator.rbs +22 -0
- data/sig/lib/mui/key_handler/operators/paste_operator.rbs +19 -0
- data/sig/lib/mui/key_handler/operators/yank_operator.rbs +20 -0
- data/sig/lib/mui/key_handler/search_mode.rbs +25 -0
- data/sig/lib/mui/key_handler/visual_line_mode.rbs +11 -0
- data/sig/lib/mui/key_handler/visual_mode.rbs +43 -0
- data/sig/lib/mui/key_handler/window_command.rbs +20 -0
- data/sig/lib/mui/key_handler.rbs +4 -0
- data/sig/lib/mui/key_notation_parser.rbs +16 -0
- data/sig/lib/mui/key_sequence.rbs +18 -0
- data/sig/lib/mui/key_sequence_buffer.rbs +18 -0
- data/sig/lib/mui/key_sequence_handler.rbs +24 -0
- data/sig/lib/mui/key_sequence_matcher.rbs +14 -0
- data/sig/lib/mui/layout/calculator.rbs +10 -0
- data/sig/lib/mui/layout/leaf_node.rbs +16 -0
- data/sig/lib/mui/layout/node.rbs +24 -0
- data/sig/lib/mui/layout/split_node.rbs +29 -0
- data/sig/lib/mui/line_renderer.rbs +18 -0
- data/sig/lib/mui/mode.rbs +14 -0
- data/sig/lib/mui/mode_manager.rbs +33 -0
- data/sig/lib/mui/motion.rbs +24 -0
- data/sig/lib/mui/plugin.rbs +17 -0
- data/sig/lib/mui/plugin_manager.rbs +20 -0
- data/sig/lib/mui/register.rbs +21 -0
- data/sig/lib/mui/screen.rbs +21 -0
- data/sig/lib/mui/search_completer.rbs +11 -0
- data/sig/lib/mui/search_input.rbs +17 -0
- data/sig/lib/mui/search_state.rbs +22 -0
- data/sig/lib/mui/selection.rbs +21 -0
- data/sig/lib/mui/status_line_renderer.rbs +12 -0
- data/sig/lib/mui/syntax/language_detector.rbs +16 -0
- data/sig/lib/mui/syntax/lexer_base.rbs +16 -0
- data/sig/lib/mui/syntax/lexers/c_lexer.rbs +19 -0
- data/sig/lib/mui/syntax/lexers/css_lexer.rbs +19 -0
- data/sig/lib/mui/syntax/lexers/go_lexer.rbs +26 -0
- data/sig/lib/mui/syntax/lexers/html_lexer.rbs +19 -0
- data/sig/lib/mui/syntax/lexers/javascript_lexer.rbs +26 -0
- data/sig/lib/mui/syntax/lexers/markdown_lexer.rbs +24 -0
- data/sig/lib/mui/syntax/lexers/ruby_lexer.rbs +18 -0
- data/sig/lib/mui/syntax/lexers/rust_lexer.rbs +21 -0
- data/sig/lib/mui/syntax/lexers/typescript_lexer.rbs +26 -0
- data/sig/lib/mui/syntax/token.rbs +16 -0
- data/sig/lib/mui/syntax/token_cache.rbs +16 -0
- data/sig/lib/mui/tab_bar_renderer.rbs +23 -0
- data/sig/lib/mui/tab_manager.rbs +23 -0
- data/sig/lib/mui/tab_page.rbs +16 -0
- data/sig/lib/mui/terminal_adapter/base.rbs +31 -0
- data/sig/lib/mui/terminal_adapter/curses.rbs +31 -0
- data/sig/lib/mui/terminal_adapter.rbs +4 -0
- data/sig/lib/mui/themes/default.rbs +15 -0
- data/sig/lib/mui/undo_manager.rbs +20 -0
- data/sig/lib/mui/undoable_action.rbs +56 -0
- data/sig/lib/mui/unicode_width.rbs +12 -0
- data/sig/lib/mui/version.rbs +6 -0
- data/sig/lib/mui/window.rbs +49 -0
- data/sig/lib/mui/window_manager.rbs +37 -0
- data/sig/lib/mui/wrap_cache.rbs +14 -0
- data/sig/lib/mui/wrap_helper.rbs +11 -0
- data/sig/lib/mui.rbs +24 -0
- data/sig/typeprof.rbs +42 -0
- data/typeprof.conf.jsonc +6 -0
- metadata +116 -4
- data/sig/mui.rbs +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 673cc3f184e93e2d2d0ce36bf723c2bdc8cabe688c8191df01dfbe8902c1b9f8
|
|
4
|
+
data.tar.gz: fa1b949c989cac09566b7f55dd7f6ac2f5d57fc593d638c69973d6b86c0be515
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d9bc8288b7d1947a7946d192243c9b30b307e05b6142699958e8b2b34a986324f2b07d5125d779e919eecff84a12965b23ca5558bf6048c386d26853484cebe
|
|
7
|
+
data.tar.gz: bb9fe3e3fbb7d0db5371ba822457e5d215198d1c5cd2c2269f73e9d65da834d699089425d8baed932f9fc0ec56deb23da8f453fbf8aac012e5f713d7499991c7
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2025-12-
|
|
3
|
+
# on 2025-12-24 03:48:47 UTC using RuboCop version 1.81.7.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
@@ -21,13 +21,6 @@ Lint/DuplicateBranch:
|
|
|
21
21
|
- 'lib/mui/plugin_manager.rb'
|
|
22
22
|
- 'lib/mui/register.rb'
|
|
23
23
|
|
|
24
|
-
# Offense count: 3
|
|
25
|
-
# Configuration parameters: AllowComments, AllowEmptyLambdas.
|
|
26
|
-
Lint/EmptyBlock:
|
|
27
|
-
Exclude:
|
|
28
|
-
- 'test/mui/test_autocmd.rb'
|
|
29
|
-
- 'test/mui/test_command_registry.rb'
|
|
30
|
-
|
|
31
24
|
# Offense count: 1
|
|
32
25
|
# Configuration parameters: AllowedParentClasses.
|
|
33
26
|
Lint/MissingSuper:
|
|
@@ -43,15 +36,10 @@ Lint/UnusedMethodArgument:
|
|
|
43
36
|
- 'lib/mui/terminal_adapter/base.rb'
|
|
44
37
|
- 'test/mui/highlighters/test_search_highlighter.rb'
|
|
45
38
|
|
|
46
|
-
# Offense count: 1
|
|
47
|
-
Lint/UselessConstantScoping:
|
|
48
|
-
Exclude:
|
|
49
|
-
- 'lib/mui/buffer_word_cache.rb'
|
|
50
|
-
|
|
51
39
|
# Offense count: 35
|
|
52
40
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
|
53
41
|
Metrics/CyclomaticComplexity:
|
|
54
|
-
Max:
|
|
42
|
+
Max: 51
|
|
55
43
|
|
|
56
44
|
# Offense count: 2
|
|
57
45
|
# Configuration parameters: CountComments, CountAsOne.
|
|
@@ -156,11 +144,3 @@ Style/ComparableClamp:
|
|
|
156
144
|
# Configuration parameters: AllowedConstants.
|
|
157
145
|
Style/Documentation:
|
|
158
146
|
Enabled: false
|
|
159
|
-
|
|
160
|
-
# Offense count: 1
|
|
161
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
162
|
-
# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods.
|
|
163
|
-
# 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
|
|
164
|
-
Style/TrivialAccessors:
|
|
165
|
-
Exclude:
|
|
166
|
-
- 'test/mui/test_autocmd.rb'
|
data/.ruby-lsp.yml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Ruby LSP configuration for Mui
|
|
2
|
+
# https://shopify.github.io/ruby-lsp/
|
|
3
|
+
|
|
4
|
+
indexing:
|
|
5
|
+
# Include all lib files for indexing
|
|
6
|
+
included_patterns:
|
|
7
|
+
- "lib/**/*.rb"
|
|
8
|
+
|
|
9
|
+
# Exclude test files from indexing to speed up
|
|
10
|
+
excluded_patterns:
|
|
11
|
+
- "test/**/*.rb"
|
|
12
|
+
- "test/fixtures/**/*.rb"
|
|
13
|
+
|
|
14
|
+
# Exclude development-only gems from indexing
|
|
15
|
+
excluded_gems:
|
|
16
|
+
- rubocop
|
|
17
|
+
- rubocop-rake
|
|
18
|
+
- rubocop-minitest
|
|
19
|
+
- rubocop-performance
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2025-12-26
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Ruby 4.0 support in CI matrix
|
|
7
|
+
- System clipboard integration:
|
|
8
|
+
- `clipboard` setting (`:unnamed` or `:unnamedplus`) to enable
|
|
9
|
+
- Yank operations (`yy`, `yw`, `y` in Visual mode) sync to system clipboard
|
|
10
|
+
- Delete operations (`dd`, `dw`, `d` in Visual mode) sync to system clipboard
|
|
11
|
+
- Paste operations (`p`, `P`) sync from system clipboard
|
|
12
|
+
- Linewise detection: trailing newline indicates line-wise content
|
|
13
|
+
- Named registers (`"a`-`"z`) and black hole register (`"_`) do not sync to clipboard
|
|
14
|
+
- Requires `clipboard` gem (automatically installed as dependency)
|
|
15
|
+
- YJIT support (Ruby 3.3+):
|
|
16
|
+
- `use_yjit` configuration option (default: `true`)
|
|
17
|
+
- Automatically enables YJIT at editor startup when available
|
|
18
|
+
- Disable with `Mui.set :use_yjit, false` in `.muirc`
|
|
19
|
+
- Shift+Arrow key navigation across lines:
|
|
20
|
+
- `Shift+Left` at line start moves to end of previous line
|
|
21
|
+
- `Shift+Right` at line end moves to start of next line
|
|
22
|
+
- Works in both Normal mode and Insert mode
|
|
23
|
+
- Boundary behavior: does nothing at first/last line
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- Fixed Shift+Tab handling causing `NoMethodError` in command mode completion:
|
|
27
|
+
- `:shift_tab` symbol was passed to `convert_key_to_string` which called `chr` on it
|
|
28
|
+
- Unified key representation: `KeyCode::SHIFT_TAB = 353` (same as `Curses::KEY_BTAB`)
|
|
29
|
+
- Added `SHIFT_CHARS` mapping similar to `CTRL_CHARS` for Shift key combinations
|
|
30
|
+
- All Shift+Tab representations now use `"\T"` string consistently
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- FileCompleter now includes hidden files (dotfiles) in completion candidates
|
|
34
|
+
- Previously, files starting with `.` were excluded from file path completion
|
|
35
|
+
- Now returns all entries including `.`, `..`, and hidden files like `.gitignore`
|
|
36
|
+
- Performance improvements across multiple modules:
|
|
37
|
+
- String concatenation: Use `<<` instead of `+` for buffer operations (`command_line.rb`, `search_input.rb`)
|
|
38
|
+
- Status line rendering: Use `ljust` instead of manual padding (`status_line_renderer.rb`)
|
|
39
|
+
- Text truncation: Add `ascii_only?` early return and index-based slicing (`screen.rb`)
|
|
40
|
+
- Method chains: Use `filter_map` instead of `select.map.map` (`file_completer.rb`)
|
|
41
|
+
- Max width calculation: Use `reduce` instead of `map.max` (`completion_renderer.rb`, `insert_completion_renderer.rb`)
|
|
42
|
+
- Regex caching: Cache compiled regex when pattern unchanged (`search_state.rb`)
|
|
43
|
+
- Regex optimization: Add `/o` option for static patterns (`search_completer.rb`)
|
|
44
|
+
- Distance calculation: Remove unnecessary `Math.sqrt` (`window_manager.rb`)
|
|
45
|
+
- Command list: Memoize `all_commands` array (`command_completer.rb`)
|
|
46
|
+
|
|
47
|
+
### Removed
|
|
48
|
+
- Dropped Ruby 3.2 support (EOL: 2025-03-31)
|
|
49
|
+
- Minimum required Ruby version is now 3.3.0
|
|
50
|
+
|
|
3
51
|
## [0.4.1] 2025-12-20
|
|
4
52
|
|
|
5
53
|
### Fixed
|
data/README.md
CHANGED
|
@@ -72,6 +72,20 @@ Supports 9 languages:
|
|
|
72
72
|
- Delete history: `"1` - `"9`
|
|
73
73
|
- Black hole register: `"_`
|
|
74
74
|
|
|
75
|
+
### Clipboard Integration
|
|
76
|
+
|
|
77
|
+
System clipboard integration (Vim's `clipboard` option):
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
# Enable clipboard integration in ~/.muirc
|
|
81
|
+
Mui.set :clipboard, :unnamedplus
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- Yank/delete operations sync to system clipboard
|
|
85
|
+
- Paste operations sync from system clipboard
|
|
86
|
+
- Named registers (`"a`-`"z`) are not affected
|
|
87
|
+
- Supports WSL, macOS, and Linux (X11/Wayland)
|
|
88
|
+
|
|
75
89
|
### Completion
|
|
76
90
|
|
|
77
91
|
- Command-line completion with popup
|
|
@@ -168,6 +182,9 @@ Mui.set :syntax, true
|
|
|
168
182
|
# Leader key
|
|
169
183
|
Mui.set :leader, " "
|
|
170
184
|
|
|
185
|
+
# YJIT (enabled by default on Ruby 3.3+)
|
|
186
|
+
Mui.set :use_yjit, true
|
|
187
|
+
|
|
171
188
|
# Custom keymaps
|
|
172
189
|
Mui.keymap :normal, "<Leader>w" do |ctx|
|
|
173
190
|
ctx.editor.execute_command("w")
|
data/docs/configuration.md
CHANGED
|
@@ -92,6 +92,40 @@ Mui.set :leader, " " # Space as leader
|
|
|
92
92
|
Mui.set :timeoutlen, 1000
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
### YJIT
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
# Enable/disable YJIT (default: true, Ruby 3.3+ only)
|
|
99
|
+
Mui.set :use_yjit, true
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
YJIT is Ruby's Just-In-Time compiler that improves performance. When enabled, Mui automatically activates YJIT at startup if your Ruby version supports it (Ruby 3.3+). On older Ruby versions, this setting is safely ignored.
|
|
103
|
+
|
|
104
|
+
### Clipboard
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
# Enable system clipboard integration
|
|
108
|
+
Mui.set :clipboard, :unnamedplus
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
| Value | Description |
|
|
112
|
+
|-------|-------------|
|
|
113
|
+
| `nil` | Disabled (default) |
|
|
114
|
+
| `:unnamed` | Sync with system clipboard |
|
|
115
|
+
| `:unnamedplus` | Sync with system clipboard (same as `:unnamed`) |
|
|
116
|
+
|
|
117
|
+
When enabled:
|
|
118
|
+
- **Yank** (`yy`, `yw`, `y` in Visual mode) copies to system clipboard
|
|
119
|
+
- **Delete** (`dd`, `dw`, `d` in Visual mode) copies to system clipboard
|
|
120
|
+
- **Paste** (`p`, `P`) reads from system clipboard
|
|
121
|
+
- **Named registers** (`"a`-`"z`) are not affected by clipboard sync
|
|
122
|
+
- **Black hole register** (`"_`) discards without clipboard sync
|
|
123
|
+
|
|
124
|
+
Platform support (via `clipboard` gem):
|
|
125
|
+
- **macOS**: Uses `pbcopy`/`pbpaste`
|
|
126
|
+
- **Linux**: Uses `xclip` or `xsel` (X11) / `wl-copy`/`wl-paste` (Wayland)
|
|
127
|
+
- **WSL**: Uses `clip.exe` and PowerShell
|
|
128
|
+
|
|
95
129
|
---
|
|
96
130
|
|
|
97
131
|
## Custom Key Mappings
|
|
@@ -279,6 +313,12 @@ Mui.set :expandtab, true
|
|
|
279
313
|
# Leader key
|
|
280
314
|
Mui.set :leader, " "
|
|
281
315
|
|
|
316
|
+
# YJIT (Ruby 3.3+)
|
|
317
|
+
Mui.set :use_yjit, true
|
|
318
|
+
|
|
319
|
+
# Clipboard integration
|
|
320
|
+
Mui.set :clipboard, :unnamedplus
|
|
321
|
+
|
|
282
322
|
# Plugins
|
|
283
323
|
Mui.use "mui-lsp"
|
|
284
324
|
Mui.use "mui-fzf"
|
data/docs/getting-started.md
CHANGED
data/docs/keybindings.md
CHANGED
|
@@ -28,6 +28,8 @@ nav_order: 3
|
|
|
28
28
|
| `0` | Move to line start |
|
|
29
29
|
| `^` | Move to first non-blank character |
|
|
30
30
|
| `$` | Move to line end |
|
|
31
|
+
| `Shift-Left` | Move to end of previous line |
|
|
32
|
+
| `Shift-Right` | Move to start of next line |
|
|
31
33
|
|
|
32
34
|
### Word Movement
|
|
33
35
|
|
|
@@ -144,6 +146,8 @@ Example: `"ayy` yanks line to register `a`, `"ap` pastes from register `a`.
|
|
|
144
146
|
| `Ctrl-p` | Previous completion candidate |
|
|
145
147
|
| `Tab` | Accept completion |
|
|
146
148
|
| Arrow keys | Move cursor |
|
|
149
|
+
| `Shift-Left` | Move to end of previous line |
|
|
150
|
+
| `Shift-Right` | Move to start of next line |
|
|
147
151
|
| `Backspace` | Delete character before cursor |
|
|
148
152
|
|
|
149
153
|
---
|
data/lib/mui/buffer.rb
CHANGED
|
@@ -8,6 +8,8 @@ module Mui
|
|
|
8
8
|
MIN_WORD_LENGTH = 2
|
|
9
9
|
# Maximum candidates to return (for performance)
|
|
10
10
|
MAX_CANDIDATES = 50
|
|
11
|
+
# Regex to match word characters (alphanumeric + underscore)
|
|
12
|
+
WORD_REGEX = /\w+/
|
|
11
13
|
|
|
12
14
|
def initialize(buffer)
|
|
13
15
|
@buffer = buffer
|
|
@@ -72,9 +74,6 @@ module Mui
|
|
|
72
74
|
|
|
73
75
|
private
|
|
74
76
|
|
|
75
|
-
# Regex to match word characters (alphanumeric + underscore)
|
|
76
|
-
WORD_REGEX = /\w+/
|
|
77
|
-
|
|
78
77
|
def build_cache
|
|
79
78
|
@buffer.lines.each do |line|
|
|
80
79
|
extract_words_fast(line)
|
|
@@ -13,8 +13,6 @@ module Mui
|
|
|
13
13
|
].freeze
|
|
14
14
|
|
|
15
15
|
def complete(prefix)
|
|
16
|
-
all_commands = COMMANDS + plugin_command_names
|
|
17
|
-
|
|
18
16
|
return all_commands.uniq.sort if prefix.empty?
|
|
19
17
|
|
|
20
18
|
prefix_downcase = prefix.downcase
|
|
@@ -23,6 +21,10 @@ module Mui
|
|
|
23
21
|
|
|
24
22
|
private
|
|
25
23
|
|
|
24
|
+
def all_commands
|
|
25
|
+
@all_commands ||= COMMANDS + plugin_command_names
|
|
26
|
+
end
|
|
27
|
+
|
|
26
28
|
def plugin_command_names
|
|
27
29
|
Mui.config.commands.keys.map(&:to_s)
|
|
28
30
|
end
|
data/lib/mui/command_line.rb
CHANGED
|
@@ -14,14 +14,19 @@ module Mui
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def input(char)
|
|
17
|
-
|
|
17
|
+
rest = @buffer[@cursor_pos..].to_s
|
|
18
|
+
@buffer = @buffer[0...@cursor_pos].to_s
|
|
19
|
+
@buffer << char
|
|
20
|
+
@buffer << rest
|
|
18
21
|
@cursor_pos += char.length
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def backspace
|
|
22
25
|
return if @cursor_pos.zero?
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
rest = @buffer[@cursor_pos..].to_s
|
|
28
|
+
@buffer = @buffer[0...(@cursor_pos - 1)].to_s
|
|
29
|
+
@buffer << rest
|
|
25
30
|
@cursor_pos -= 1
|
|
26
31
|
end
|
|
27
32
|
|
|
@@ -67,7 +67,7 @@ module Mui
|
|
|
67
67
|
def calculate_max_width(candidates)
|
|
68
68
|
return 0 if candidates.empty?
|
|
69
69
|
|
|
70
|
-
candidates.
|
|
70
|
+
candidates.reduce(0) { |max, c| [max, display_width(c)].max } + 2 # +2 for padding
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def display_width(text)
|
data/lib/mui/config.rb
CHANGED
|
@@ -13,7 +13,9 @@ module Mui
|
|
|
13
13
|
tabstop: 8, # Tab display width
|
|
14
14
|
reselect_after_indent: false, # Keep selection after > / < in visual mode
|
|
15
15
|
leader: "\\", # Leader key for key mappings (default: backslash)
|
|
16
|
-
timeoutlen: 1000 # Timeout for multi-key sequences in milliseconds
|
|
16
|
+
timeoutlen: 1000, # Timeout for multi-key sequences in milliseconds
|
|
17
|
+
use_yjit: true, # Enable YJIT
|
|
18
|
+
clipboard: nil # Clipboard integration (:unnamed or :unnamedplus to enable)
|
|
17
19
|
}
|
|
18
20
|
@plugins = []
|
|
19
21
|
@keymaps = {}
|
data/lib/mui/editor.rb
CHANGED
|
@@ -10,6 +10,9 @@ module Mui
|
|
|
10
10
|
def initialize(file_path = nil, adapter: TerminalAdapter::Curses.new, load_config: true)
|
|
11
11
|
Mui.load_config if load_config
|
|
12
12
|
|
|
13
|
+
# Enable YJIT for Ruby 3.3+
|
|
14
|
+
RubyVM::YJIT.enable if Mui.config.get(:use_yjit) && defined?(RubyVM::YJIT) && RubyVM::YJIT.respond_to?(:enable)
|
|
15
|
+
|
|
13
16
|
@adapter = adapter
|
|
14
17
|
@color_manager = ColorManager.new
|
|
15
18
|
@adapter.color_resolver = @color_manager
|
data/lib/mui/file_completer.rb
CHANGED
|
@@ -9,9 +9,9 @@ module Mui
|
|
|
9
9
|
dir, prefix = split_path(partial_path)
|
|
10
10
|
entries = list_directory(dir)
|
|
11
11
|
|
|
12
|
-
entries.
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
entries.filter_map do |entry|
|
|
13
|
+
format_entry_to_path(dir, entry) if entry.start_with?(prefix)
|
|
14
|
+
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
private
|
|
@@ -30,22 +30,22 @@ module Mui
|
|
|
30
30
|
target = dir.empty? ? "." : dir
|
|
31
31
|
return [] unless Dir.exist?(target)
|
|
32
32
|
|
|
33
|
-
Dir.entries(target)
|
|
34
|
-
.reject { |e| e.start_with?(".") }
|
|
35
|
-
.sort
|
|
33
|
+
Dir.entries(target).sort
|
|
36
34
|
end
|
|
37
35
|
|
|
38
36
|
def list_current_directory
|
|
39
37
|
list_directory("").map { |entry| format_path(entry) }
|
|
40
38
|
end
|
|
41
39
|
|
|
42
|
-
def join_path(dir, entry)
|
|
43
|
-
dir.empty? ? entry : File.join(dir, entry)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
40
|
def format_path(path)
|
|
47
41
|
full_path = path.start_with?("/") ? path : File.join(".", path)
|
|
48
42
|
File.directory?(full_path) ? "#{path}/" : path
|
|
49
43
|
end
|
|
44
|
+
|
|
45
|
+
def format_entry_to_path(dir, entry)
|
|
46
|
+
path = dir.empty? ? entry : File.join(dir, entry)
|
|
47
|
+
|
|
48
|
+
format_path(path)
|
|
49
|
+
end
|
|
50
50
|
end
|
|
51
51
|
end
|
|
@@ -70,7 +70,7 @@ module Mui
|
|
|
70
70
|
def calculate_max_width(items)
|
|
71
71
|
return 0 if items.empty?
|
|
72
72
|
|
|
73
|
-
items.
|
|
73
|
+
items.reduce(0) { |max, item| [max, display_width(item_label(item))].max } + 2 # +2 for padding
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def item_label(item)
|
data/lib/mui/key_code.rb
CHANGED
data/lib/mui/key_handler/base.rb
CHANGED
|
@@ -40,6 +40,10 @@ module Mui
|
|
|
40
40
|
handle_backspace
|
|
41
41
|
when KeyCode::ENTER_CR, KeyCode::ENTER_LF, Curses::KEY_ENTER
|
|
42
42
|
handle_enter
|
|
43
|
+
when KeyCode::SHIFT_LEFT
|
|
44
|
+
handle_shift_left
|
|
45
|
+
when KeyCode::SHIFT_RIGHT
|
|
46
|
+
handle_shift_right
|
|
43
47
|
else
|
|
44
48
|
handle_character_input(key)
|
|
45
49
|
end
|
|
@@ -91,6 +95,32 @@ module Mui
|
|
|
91
95
|
result
|
|
92
96
|
end
|
|
93
97
|
|
|
98
|
+
def handle_line_start
|
|
99
|
+
window.move_to_line_start
|
|
100
|
+
result
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def handle_line_end
|
|
104
|
+
window.move_to_line_end
|
|
105
|
+
result
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def handle_shift_left
|
|
109
|
+
return result unless cursor_row.positive?
|
|
110
|
+
|
|
111
|
+
window.move_up
|
|
112
|
+
window.move_to_line_end
|
|
113
|
+
result
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def handle_shift_right
|
|
117
|
+
return result unless cursor_row < buffer.line_count - 1
|
|
118
|
+
|
|
119
|
+
window.move_down
|
|
120
|
+
window.move_to_line_start
|
|
121
|
+
result
|
|
122
|
+
end
|
|
123
|
+
|
|
94
124
|
def handle_backspace
|
|
95
125
|
if cursor_col.positive?
|
|
96
126
|
self.cursor_col = cursor_col - 1
|
|
@@ -179,6 +179,10 @@ module Mui
|
|
|
179
179
|
when KeyCode::CTRL_W
|
|
180
180
|
@pending_motion = :window_command
|
|
181
181
|
result
|
|
182
|
+
when KeyCode::SHIFT_LEFT
|
|
183
|
+
handle_shift_left
|
|
184
|
+
when KeyCode::SHIFT_RIGHT
|
|
185
|
+
handle_shift_right
|
|
182
186
|
else
|
|
183
187
|
result
|
|
184
188
|
end
|
|
@@ -264,6 +268,23 @@ module Mui
|
|
|
264
268
|
result
|
|
265
269
|
end
|
|
266
270
|
|
|
271
|
+
def handle_shift_left
|
|
272
|
+
return result unless cursor_row.positive?
|
|
273
|
+
|
|
274
|
+
window.move_up
|
|
275
|
+
# Normal mode: position at last character (not after it)
|
|
276
|
+
self.cursor_col = [current_line_length - 1, 0].max
|
|
277
|
+
result
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def handle_shift_right
|
|
281
|
+
return result unless cursor_row < buffer.line_count - 1
|
|
282
|
+
|
|
283
|
+
window.move_down
|
|
284
|
+
window.move_to_line_start
|
|
285
|
+
result
|
|
286
|
+
end
|
|
287
|
+
|
|
267
288
|
# Edit handlers
|
|
268
289
|
def handle_append
|
|
269
290
|
self.cursor_col = cursor_col + 1 if current_line_length.positive?
|
|
@@ -6,6 +6,8 @@ module Mui
|
|
|
6
6
|
class VisualMode < Base
|
|
7
7
|
include Motions::MotionHandler
|
|
8
8
|
|
|
9
|
+
INDENT_PATTERN = [" ", "\t"].freeze
|
|
10
|
+
|
|
9
11
|
attr_reader :selection
|
|
10
12
|
|
|
11
13
|
def initialize(mode_manager, buffer, selection, register = nil, undo_manager: nil)
|
|
@@ -310,7 +312,7 @@ module Mui
|
|
|
310
312
|
|
|
311
313
|
while removed < width && !line.empty?
|
|
312
314
|
char = line[0]
|
|
313
|
-
break unless
|
|
315
|
+
break unless INDENT_PATTERN.include?(char)
|
|
314
316
|
|
|
315
317
|
char_width = char == "\t" ? Mui.config.get(:tabstop) : 1
|
|
316
318
|
break if removed + char_width > width && char == "\t"
|
|
@@ -10,8 +10,8 @@ module Mui
|
|
|
10
10
|
SPECIAL_KEYS = {
|
|
11
11
|
"space" => " ",
|
|
12
12
|
"tab" => "\t",
|
|
13
|
-
"s-tab" =>
|
|
14
|
-
"btab" =>
|
|
13
|
+
"s-tab" => "\T", # Shift+Tab (SHIFT_CHARS["tab"])
|
|
14
|
+
"btab" => "\T", # Back Tab = Shift+Tab
|
|
15
15
|
"cr" => "\r",
|
|
16
16
|
"enter" => "\r",
|
|
17
17
|
"return" => "\r",
|
|
@@ -64,6 +64,11 @@ module Mui
|
|
|
64
64
|
"_" => 31
|
|
65
65
|
}.freeze
|
|
66
66
|
|
|
67
|
+
# Shift key mappings for special keys
|
|
68
|
+
SHIFT_CHARS = {
|
|
69
|
+
"tab" => "\T" # Shift+Tab
|
|
70
|
+
}.freeze
|
|
71
|
+
|
|
67
72
|
class << self
|
|
68
73
|
# Parse a key notation string into an array of keys
|
|
69
74
|
# @param notation [String] Key notation (e.g., "<Leader>gd", "<C-x><C-s>")
|
|
@@ -129,6 +134,10 @@ module Mui
|
|
|
129
134
|
end
|
|
130
135
|
|
|
131
136
|
def parse_shift_key(char)
|
|
137
|
+
char_lower = char.downcase
|
|
138
|
+
# Check SHIFT_CHARS for special key mappings (e.g., Shift+Tab)
|
|
139
|
+
return SHIFT_CHARS[char_lower] if SHIFT_CHARS.key?(char_lower)
|
|
140
|
+
|
|
132
141
|
# Shift typically produces uppercase for letters
|
|
133
142
|
char.length == 1 ? char.upcase : char
|
|
134
143
|
end
|
|
@@ -141,8 +150,8 @@ module Mui
|
|
|
141
150
|
"\e"
|
|
142
151
|
when KeyCode::TAB
|
|
143
152
|
"\t"
|
|
144
|
-
when
|
|
145
|
-
|
|
153
|
+
when KeyCode::SHIFT_TAB
|
|
154
|
+
"\T"
|
|
146
155
|
when KeyCode::BACKSPACE
|
|
147
156
|
"\x7f"
|
|
148
157
|
when 0..31
|
data/lib/mui/mode_manager.rb
CHANGED
|
@@ -47,11 +47,11 @@ module Mui
|
|
|
47
47
|
when Mode::VISUAL, Mode::VISUAL_LINE
|
|
48
48
|
handle_visual_transition(result)
|
|
49
49
|
when Mode::INSERT
|
|
50
|
-
handle_insert_transition(result)
|
|
50
|
+
handle_insert_transition(result, result.mode)
|
|
51
51
|
when Mode::SEARCH_FORWARD
|
|
52
|
-
|
|
52
|
+
handle_search_transition("/", result.mode)
|
|
53
53
|
when Mode::SEARCH_BACKWARD
|
|
54
|
-
|
|
54
|
+
handle_search_transition("?", result.mode)
|
|
55
55
|
else
|
|
56
56
|
@mode = result.mode
|
|
57
57
|
end
|
|
@@ -81,11 +81,7 @@ module Mui
|
|
|
81
81
|
@last_visual_selection[:end_row],
|
|
82
82
|
@last_visual_selection[:end_col]
|
|
83
83
|
)
|
|
84
|
-
@visual_handler =
|
|
85
|
-
KeyHandler::VisualLineMode.new(self, @buffer, @selection, @register, undo_manager: @undo_manager)
|
|
86
|
-
else
|
|
87
|
-
KeyHandler::VisualMode.new(self, @buffer, @selection, @register, undo_manager: @undo_manager)
|
|
88
|
-
end
|
|
84
|
+
@visual_handler = create_visual_handler(line_mode:)
|
|
89
85
|
|
|
90
86
|
# Move cursor to end of selection
|
|
91
87
|
active_window.cursor_row = @last_visual_selection[:end_row]
|
|
@@ -110,10 +106,10 @@ module Mui
|
|
|
110
106
|
KeyHandler::InsertMode.new(self, @buffer, undo_manager: @undo_manager, group_started:)
|
|
111
107
|
end
|
|
112
108
|
|
|
113
|
-
def handle_insert_transition(result)
|
|
109
|
+
def handle_insert_transition(result, mode)
|
|
114
110
|
group_started = result.respond_to?(:group_started?) && result.group_started?
|
|
115
|
-
@key_handlers[
|
|
116
|
-
@mode =
|
|
111
|
+
@key_handlers[mode] = create_insert_handler(group_started:)
|
|
112
|
+
@mode = mode
|
|
117
113
|
end
|
|
118
114
|
|
|
119
115
|
def handle_visual_transition(result)
|
|
@@ -128,6 +124,7 @@ module Mui
|
|
|
128
124
|
|
|
129
125
|
def start_visual_mode(mode, line_mode)
|
|
130
126
|
@mode = mode
|
|
127
|
+
@selection = Selection.new(active_window.cursor_row, active_window.cursor_col, line_mode:)
|
|
131
128
|
@visual_handler = create_visual_handler(line_mode:)
|
|
132
129
|
end
|
|
133
130
|
|
|
@@ -152,16 +149,14 @@ module Mui
|
|
|
152
149
|
def toggle_visual_line_mode(new_mode)
|
|
153
150
|
return unless @selection
|
|
154
151
|
|
|
155
|
-
|
|
156
|
-
@selection = Selection.new(@selection.start_row, @selection.start_col, line_mode:
|
|
152
|
+
line_mode = new_mode == Mode::VISUAL_LINE
|
|
153
|
+
@selection = Selection.new(@selection.start_row, @selection.start_col, line_mode:)
|
|
157
154
|
@selection.update_end(active_window.cursor_row, active_window.cursor_col)
|
|
158
|
-
@visual_handler = create_visual_handler(line_mode:
|
|
155
|
+
@visual_handler = create_visual_handler(line_mode:)
|
|
159
156
|
@mode = new_mode
|
|
160
157
|
end
|
|
161
158
|
|
|
162
159
|
def create_visual_handler(line_mode:)
|
|
163
|
-
@selection = Selection.new(active_window.cursor_row, active_window.cursor_col, line_mode:)
|
|
164
|
-
|
|
165
160
|
if line_mode
|
|
166
161
|
KeyHandler::VisualLineMode.new(self, @buffer, @selection, @register, undo_manager: @undo_manager)
|
|
167
162
|
else
|
|
@@ -169,18 +164,11 @@ module Mui
|
|
|
169
164
|
end
|
|
170
165
|
end
|
|
171
166
|
|
|
172
|
-
def
|
|
167
|
+
def handle_search_transition(prompt, mode)
|
|
173
168
|
@search_input.clear
|
|
174
|
-
@search_input.set_prompt(
|
|
175
|
-
@key_handlers[
|
|
176
|
-
@mode =
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def handle_search_backward_transition
|
|
180
|
-
@search_input.clear
|
|
181
|
-
@search_input.set_prompt("?")
|
|
182
|
-
@key_handlers[Mode::SEARCH_BACKWARD].start_search
|
|
183
|
-
@mode = Mode::SEARCH_BACKWARD
|
|
169
|
+
@search_input.set_prompt(prompt)
|
|
170
|
+
@key_handlers[mode].start_search
|
|
171
|
+
@mode = mode
|
|
184
172
|
end
|
|
185
173
|
end
|
|
186
174
|
end
|