mui 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +163 -0
  3. data/CHANGELOG.md +448 -0
  4. data/README.md +309 -6
  5. data/docs/_config.yml +56 -0
  6. data/docs/configuration.md +301 -0
  7. data/docs/getting-started.md +140 -0
  8. data/docs/index.md +55 -0
  9. data/docs/jobs.md +297 -0
  10. data/docs/keybindings.md +229 -0
  11. data/docs/plugins.md +285 -0
  12. data/docs/syntax-highlighting.md +149 -0
  13. data/exe/mui +1 -2
  14. data/lib/mui/autocmd.rb +66 -0
  15. data/lib/mui/buffer.rb +275 -0
  16. data/lib/mui/buffer_word_cache.rb +131 -0
  17. data/lib/mui/buffer_word_completer.rb +77 -0
  18. data/lib/mui/color_manager.rb +136 -0
  19. data/lib/mui/color_scheme.rb +63 -0
  20. data/lib/mui/command_completer.rb +30 -0
  21. data/lib/mui/command_context.rb +90 -0
  22. data/lib/mui/command_history.rb +89 -0
  23. data/lib/mui/command_line.rb +167 -0
  24. data/lib/mui/command_registry.rb +44 -0
  25. data/lib/mui/completion_renderer.rb +84 -0
  26. data/lib/mui/completion_state.rb +58 -0
  27. data/lib/mui/config.rb +58 -0
  28. data/lib/mui/editor.rb +395 -0
  29. data/lib/mui/error.rb +29 -0
  30. data/lib/mui/file_completer.rb +51 -0
  31. data/lib/mui/floating_window.rb +161 -0
  32. data/lib/mui/handler_result.rb +107 -0
  33. data/lib/mui/highlight.rb +22 -0
  34. data/lib/mui/highlighters/base.rb +23 -0
  35. data/lib/mui/highlighters/search_highlighter.rb +27 -0
  36. data/lib/mui/highlighters/selection_highlighter.rb +48 -0
  37. data/lib/mui/highlighters/syntax_highlighter.rb +107 -0
  38. data/lib/mui/input.rb +17 -0
  39. data/lib/mui/insert_completion_renderer.rb +92 -0
  40. data/lib/mui/insert_completion_state.rb +77 -0
  41. data/lib/mui/job.rb +81 -0
  42. data/lib/mui/job_manager.rb +113 -0
  43. data/lib/mui/key_code.rb +30 -0
  44. data/lib/mui/key_handler/base.rb +187 -0
  45. data/lib/mui/key_handler/command_mode.rb +511 -0
  46. data/lib/mui/key_handler/insert_mode.rb +323 -0
  47. data/lib/mui/key_handler/motions/motion_handler.rb +56 -0
  48. data/lib/mui/key_handler/normal_mode.rb +552 -0
  49. data/lib/mui/key_handler/operators/base_operator.rb +134 -0
  50. data/lib/mui/key_handler/operators/change_operator.rb +179 -0
  51. data/lib/mui/key_handler/operators/delete_operator.rb +176 -0
  52. data/lib/mui/key_handler/operators/paste_operator.rb +119 -0
  53. data/lib/mui/key_handler/operators/yank_operator.rb +127 -0
  54. data/lib/mui/key_handler/search_mode.rb +191 -0
  55. data/lib/mui/key_handler/visual_line_mode.rb +20 -0
  56. data/lib/mui/key_handler/visual_mode.rb +402 -0
  57. data/lib/mui/key_handler/window_command.rb +112 -0
  58. data/lib/mui/key_handler.rb +16 -0
  59. data/lib/mui/key_notation_parser.rb +152 -0
  60. data/lib/mui/key_sequence.rb +67 -0
  61. data/lib/mui/key_sequence_buffer.rb +85 -0
  62. data/lib/mui/key_sequence_handler.rb +163 -0
  63. data/lib/mui/key_sequence_matcher.rb +79 -0
  64. data/lib/mui/layout/calculator.rb +15 -0
  65. data/lib/mui/layout/leaf_node.rb +33 -0
  66. data/lib/mui/layout/node.rb +29 -0
  67. data/lib/mui/layout/split_node.rb +132 -0
  68. data/lib/mui/line_renderer.rb +173 -0
  69. data/lib/mui/mode.rb +13 -0
  70. data/lib/mui/mode_manager.rb +186 -0
  71. data/lib/mui/motion.rb +139 -0
  72. data/lib/mui/plugin.rb +35 -0
  73. data/lib/mui/plugin_manager.rb +106 -0
  74. data/lib/mui/register.rb +110 -0
  75. data/lib/mui/screen.rb +103 -0
  76. data/lib/mui/search_completer.rb +50 -0
  77. data/lib/mui/search_input.rb +40 -0
  78. data/lib/mui/search_state.rb +121 -0
  79. data/lib/mui/selection.rb +55 -0
  80. data/lib/mui/status_line_renderer.rb +40 -0
  81. data/lib/mui/syntax/language_detector.rb +106 -0
  82. data/lib/mui/syntax/lexer_base.rb +106 -0
  83. data/lib/mui/syntax/lexers/c_lexer.rb +127 -0
  84. data/lib/mui/syntax/lexers/css_lexer.rb +121 -0
  85. data/lib/mui/syntax/lexers/go_lexer.rb +205 -0
  86. data/lib/mui/syntax/lexers/html_lexer.rb +118 -0
  87. data/lib/mui/syntax/lexers/javascript_lexer.rb +197 -0
  88. data/lib/mui/syntax/lexers/markdown_lexer.rb +210 -0
  89. data/lib/mui/syntax/lexers/ruby_lexer.rb +114 -0
  90. data/lib/mui/syntax/lexers/rust_lexer.rb +148 -0
  91. data/lib/mui/syntax/lexers/typescript_lexer.rb +203 -0
  92. data/lib/mui/syntax/token.rb +42 -0
  93. data/lib/mui/syntax/token_cache.rb +91 -0
  94. data/lib/mui/tab_bar_renderer.rb +87 -0
  95. data/lib/mui/tab_manager.rb +96 -0
  96. data/lib/mui/tab_page.rb +35 -0
  97. data/lib/mui/terminal_adapter/base.rb +92 -0
  98. data/lib/mui/terminal_adapter/curses.rb +164 -0
  99. data/lib/mui/terminal_adapter.rb +4 -0
  100. data/lib/mui/themes/default.rb +315 -0
  101. data/lib/mui/undo_manager.rb +83 -0
  102. data/lib/mui/undoable_action.rb +175 -0
  103. data/lib/mui/unicode_width.rb +100 -0
  104. data/lib/mui/version.rb +1 -1
  105. data/lib/mui/window.rb +201 -0
  106. data/lib/mui/window_manager.rb +256 -0
  107. data/lib/mui/wrap_cache.rb +40 -0
  108. data/lib/mui/wrap_helper.rb +84 -0
  109. data/lib/mui.rb +171 -2
  110. metadata +123 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f64c4815352bf9fcafa99092cea5126496eb9340f0e679b0408e70d59c248627
4
- data.tar.gz: 160afb5b1c9891413852c76357470864d3d1a9d3621497f87695313d480ca934
3
+ metadata.gz: f38d3d434535fc336c9b5394801e3fb49393f691b15878a1994758cfb73a6217
4
+ data.tar.gz: 220128d17afb0f711f2642cd7e64acc14364ca5e65de77c7130828e5a809796c
5
5
  SHA512:
6
- metadata.gz: cfd12998c552ac377bfb8be5c1b208cc73fc6d5897639e39be6cb29ce67d964dd6dbd71604727578ecd755a930c1a66feff4c800142c513a900b76ad7d8eb20c
7
- data.tar.gz: e09e98c9b835227152dc0ecfbf2d0207b5f5545bc300fbdd898fd0fdde79d5bb6906c113729e1a3dc7a1bbf12b467cd7a549cc5e19d04d948d3b94eb6f36885c
6
+ metadata.gz: 82468df468d286b1b672b4c47257565f348661cf38633fdabfdc021cf42639d3637f1b6a1b233bd7af65b47fa426b15da2cd52e270cbe7be987dd071e4d785db
7
+ data.tar.gz: dbf19ebadd262acc004320ebcf55d25f404ce22ad7a77beb08f4363d5154dc2f9241834f531fa15b4b953d8768bc3c1ec8213441d3150ec803081b1be32590b0
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,163 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2025-12-14 12:18:50 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: 4
16
+ # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
17
+ Lint/DuplicateBranch:
18
+ Exclude:
19
+ - 'lib/mui/editor.rb'
20
+ - 'lib/mui/key_handler/command_mode.rb'
21
+ - 'lib/mui/plugin_manager.rb'
22
+ - 'lib/mui/register.rb'
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
+ # Offense count: 1
32
+ # Configuration parameters: AllowedParentClasses.
33
+ Lint/MissingSuper:
34
+ Exclude:
35
+ - 'test/test_helper.rb'
36
+
37
+ # Offense count: 3
38
+ # This cop supports safe autocorrection (--autocorrect).
39
+ # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
40
+ # NotImplementedExceptions: NotImplementedError
41
+ Lint/UnusedMethodArgument:
42
+ Exclude:
43
+ - 'lib/mui/terminal_adapter/base.rb'
44
+ - 'test/mui/highlighters/test_search_highlighter.rb'
45
+
46
+ # Offense count: 1
47
+ Lint/UselessConstantScoping:
48
+ Exclude:
49
+ - 'lib/mui/buffer_word_cache.rb'
50
+
51
+ # Offense count: 33
52
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
53
+ Metrics/CyclomaticComplexity:
54
+ Max: 49
55
+
56
+ # Offense count: 2
57
+ # Configuration parameters: CountComments, CountAsOne.
58
+ Metrics/ModuleLength:
59
+ Max: 284
60
+
61
+ # Offense count: 10
62
+ # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
63
+ Metrics/ParameterLists:
64
+ Max: 9
65
+
66
+ # Offense count: 14
67
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
68
+ Metrics/PerceivedComplexity:
69
+ Max: 19
70
+
71
+ # Offense count: 3
72
+ Naming/AccessorMethodName:
73
+ Exclude:
74
+ - 'lib/mui/command_context.rb'
75
+ - 'lib/mui/search_input.rb'
76
+ - 'test/test_helper.rb'
77
+
78
+ # Offense count: 37
79
+ # Configuration parameters: EnforcedStyle, AllowedPatterns, ForbiddenIdentifiers, ForbiddenPatterns.
80
+ # SupportedStyles: snake_case, camelCase
81
+ # ForbiddenIdentifiers: __id__, __send__
82
+ Naming/MethodName:
83
+ Exclude:
84
+ - 'test/e2e/test_motion.rb'
85
+ - 'test/e2e/test_vim_operations.rb'
86
+ - 'test/e2e/test_visual_mode.rb'
87
+ - 'test/integration/test_editor_integration.rb'
88
+ - 'test/mui/editor/test_normal_mode.rb'
89
+ - 'test/mui/key_handler/test_normal_mode.rb'
90
+ - 'test/mui/key_handler/test_normal_mode_search.rb'
91
+ - 'test/mui/key_handler/test_visual_line_mode.rb'
92
+ - 'test/mui/key_handler/test_visual_mode.rb'
93
+ - 'test/mui/key_handler/test_window_command.rb'
94
+
95
+ # Offense count: 45
96
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
97
+ # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
98
+ Naming/MethodParameterName:
99
+ Exclude:
100
+ - 'lib/mui/buffer.rb'
101
+ - 'lib/mui/color_manager.rb'
102
+ - 'lib/mui/color_scheme.rb'
103
+ - 'lib/mui/layout/calculator.rb'
104
+ - 'lib/mui/line_renderer.rb'
105
+ - 'lib/mui/plugin.rb'
106
+ - 'lib/mui/screen.rb'
107
+ - 'lib/mui/terminal_adapter/base.rb'
108
+ - 'lib/mui/terminal_adapter/curses.rb'
109
+ - 'lib/mui/window.rb'
110
+ - 'test/e2e/test_helper.rb'
111
+ - 'test/test_helper.rb'
112
+
113
+ # Offense count: 11
114
+ # Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
115
+ # AllowedMethods: call
116
+ # WaywardPredicates: nonzero?
117
+ Naming/PredicateMethod:
118
+ Exclude:
119
+ - 'lib/mui/command_line.rb'
120
+ - 'lib/mui/editor.rb'
121
+ - 'lib/mui/key_handler/insert_mode.rb'
122
+ - 'lib/mui/key_sequence_buffer.rb'
123
+ - 'lib/mui/undo_manager.rb'
124
+ - 'lib/mui/window_manager.rb'
125
+
126
+ # Offense count: 1
127
+ # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs.
128
+ # NamePrefix: is_, has_, have_, does_
129
+ # ForbiddenPrefixes: is_, has_, have_, does_
130
+ # AllowedMethods: is_a?
131
+ # MethodDefinitionMacros: define_method, define_singleton_method
132
+ Naming/PredicatePrefix:
133
+ Exclude:
134
+ - 'spec/**/*'
135
+ - 'lib/mui/search_state.rb'
136
+
137
+ # Offense count: 2
138
+ # Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
139
+ # SupportedStyles: snake_case, normalcase, non_integer
140
+ # AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
141
+ Naming/VariableNumber:
142
+ Exclude:
143
+ - 'test/e2e/test_motion.rb'
144
+ - 'test/mui/test_register.rb'
145
+
146
+ # Offense count: 2
147
+ # This cop supports safe autocorrection (--autocorrect).
148
+ Style/ComparableClamp:
149
+ Exclude:
150
+ - 'lib/mui/screen.rb'
151
+
152
+ # Offense count: 25
153
+ # Configuration parameters: AllowedConstants.
154
+ Style/Documentation:
155
+ Enabled: false
156
+
157
+ # Offense count: 1
158
+ # This cop supports safe autocorrection (--autocorrect).
159
+ # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods.
160
+ # 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
161
+ Style/TrivialAccessors:
162
+ Exclude:
163
+ - 'test/mui/test_autocmd.rb'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,453 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2025-12-15
4
+
5
+ ### Added
6
+ - Additional syntax highlighting for 7 languages:
7
+ - Go: keywords, types, constants, raw strings (backtick), block comments
8
+ - Rust: macros (`println!`), lifetimes (`'a`), attributes (`#[derive]`), doc comments (`///`), raw strings (`r#"..."#`)
9
+ - JavaScript: template literals, regex literals, BigInt (`123n`), ES6+ keywords (`const`, `let`, `async`, `await`)
10
+ - TypeScript: extends JavaScript with type keywords (`interface`, `type`, `enum`, `declare`, `abstract`)
11
+ - Markdown: headings, emphasis (`*italic*`, `**bold**`), code blocks, links, lists, blockquotes
12
+ - HTML: tags, attributes, comments (`<!-- -->`), DOCTYPE, entities (`&amp;`)
13
+ - CSS: selectors (`.class`, `#id`, `:pseudo`), properties, hex colors, @rules, functions (`calc`, `rgb`, `var`)
14
+ - Supported extensions: `.go`, `.rs`, `.js`/`.mjs`/`.cjs`/`.jsx`, `.ts`/`.tsx`/`.mts`/`.cts`, `.md`/`.markdown`, `.html`/`.htm`/`.xhtml`, `.css`/`.scss`/`.sass`
15
+ - Multi-key sequence and leader key support (Vim-compatible):
16
+ - Leader key: `Mui.set :leader, "\\"` (default: backslash)
17
+ - `<Leader>` notation: `Mui.keymap :normal, "<Leader>gd" do ... end`
18
+ - Multi-key sequences: `<Leader>ff`, `<C-x><C-s>`, etc.
19
+ - Special key notation: `<Space>`, `<Tab>`, `<CR>`, `<Enter>`, `<Esc>`, `<BS>`
20
+ - Control key notation: `<C-x>`, `<Ctrl-x>`, `<C-S-x>` (Ctrl+Shift)
21
+ - Shift key notation: `<S-x>`, `<Shift-x>`
22
+ - Timeout setting: `Mui.set :timeoutlen, 1000` (milliseconds)
23
+ - All modes supported: Normal, Insert, Visual, Command, Search
24
+ - Built-in commands (dd, dw, gg, etc.) take priority over plugin keymaps
25
+ - Partial match waiting: typing `<Leader>g` waits for more keys if `<Leader>gd` is registered
26
+ - Exact match with longer possibilities: waits for timeout before executing
27
+ - Backward compatible with single-key keymaps
28
+ - External shell command execution (`:!cmd`):
29
+ - `:!echo hello` to run shell commands asynchronously
30
+ - Output displayed in `[Shell Output]` scratch buffer (readonly)
31
+ - stdout/stderr/exit status formatted and displayed
32
+ - "Running: cmd" message shown during execution
33
+ - Subsequent `:!cmd` updates existing scratch buffer (doesn't create new windows)
34
+ - Supports pipes, redirects, and all shell features via Open3
35
+ - Empty command (`:!` or `:! `) shows error: "E471: Argument required"
36
+ - Case-insensitive command completion:
37
+ - `:git` now matches `:Git`, `:lsp` matches `:LspHover`, `:LspDefinition`, etc.
38
+ - Original command case is preserved in completion results
39
+ - Only applies to command completion (file path completion remains case-sensitive)
40
+ - Command history with persistence:
41
+ - Up/Down arrow keys navigate through command history in command mode
42
+ - History saved to `~/.mui_history` (persists across sessions)
43
+ - Maximum 100 entries stored
44
+ - Duplicate commands move to most recent position
45
+ - Empty commands are not added to history
46
+ - Current input is preserved when browsing history (restored with Down at end)
47
+
48
+ ### Fixed
49
+ - Fixed long lines not displaying correctly by implementing line wrapping
50
+ - Lines exceeding screen width are now automatically wrapped to next screen line
51
+ - Unicode width aware wrapping (Japanese characters count as 2 width)
52
+ - Removed broken horizontal scrolling (`scroll_col`) in favor of wrap mode
53
+ - Cursor position correctly calculated across wrapped lines
54
+ - Selection and search highlights work correctly across wrap boundaries
55
+ - Added `WrapCache` for performance optimization (content-based caching)
56
+ - Added `WrapHelper` module for wrap calculations
57
+ - Fixed search returning "Pattern not found" after switching buffers via `:e`
58
+ - `execute_search` was using `@buffer` (initial buffer) instead of `buffer` method (current buffer)
59
+ - Now correctly searches in the current buffer after buffer switch
60
+ - Search highlight now recalculates correctly when switching buffers via `:e`
61
+ - Previously, search highlight from old buffer remained visible in new buffer
62
+ - Now calls `find_all_matches` on buffer switch to recalculate matches (Vim-compatible behavior)
63
+ - Search pattern is preserved, only match positions are updated for new buffer content
64
+ - Search highlight now recalculates correctly when opening files via `:sp`, `:vs`, `:tabnew`
65
+ - Previously, search highlight was not calculated for new buffers opened with split/tab commands
66
+ - Now calls `find_all_matches` when path is specified, consistent with `:e` behavior
67
+ - Improved Escape key response time in Insert mode
68
+ - Set `ESCDELAY = 10` to reduce escape sequence detection delay from 1000ms to 10ms
69
+ - Use `timeout = 0` instead of `nodelay` for `getch_nonblock`
70
+ - Insert mode completion now closes when pressing Left/Right arrow keys
71
+ - Previously, arrow keys would move cursor but completion popup remained open
72
+ - Now matches Vim behavior: completion closes on cursor movement
73
+ - Undo/Redo now works correctly for buffers opened via `:e`, `:sp`, `:vs`, `:tabnew`
74
+ - Previously, buffers opened with these commands had no UndoManager set
75
+ - Each buffer now gets its own UndoManager (Vim-compatible: independent undo history per buffer)
76
+ - Handlers now use dynamic `undo_manager` reference to support buffer switching
77
+ - Fixed screen remnants after `:e` command when new file has fewer lines
78
+ - Previously, lines from the old buffer would remain visible below the new content
79
+ - Now explicitly clears lines beyond buffer content during render
80
+ - Fixed background color not applied to lines beyond buffer content
81
+ - Previously, when opening a file with fewer lines than the visible height, lines beyond buffer were rendered without background color (transparent)
82
+ - Now uses `put_with_style` with `:normal` color scheme to ensure consistent background color
83
+ - Plugin commands now appear in command completion suggestions
84
+ - Previously, commands registered via `Mui.command` were not shown in Tab completion
85
+ - `CommandCompleter` now includes plugin commands from `Mui.config.commands`
86
+ - Multi-line paste now undos as a single action (Vim-compatible)
87
+ - Previously, pasting multiple lines would undo one line at a time
88
+ - Now uses `begin_group`/`end_group` to group all paste operations into a single undo unit
89
+ - Fixed direct array assignment to use `replace_line` for proper undo recording
90
+ - Search highlight now works correctly per buffer when switching tabs/windows
91
+ - Previously, search matches from old buffer remained when using `gt`/`gT` or `Ctrl+W` commands
92
+ - `SearchState` now caches matches per buffer (keyed by `object_id`) with lazy evaluation
93
+ - Each buffer maintains its own match positions while sharing the global search pattern
94
+ - Cache automatically invalidates when pattern changes or buffer content is modified
95
+ - Vim-compatible behavior: search pattern is global, highlights are buffer-specific
96
+ - Plugin commands now recognized by CommandRegistry for alias configuration
97
+ - Previously, `CommandRegistry#exists?` and `#find` only checked built-in commands
98
+ - Now also checks `Mui.config.commands` for plugin-registered commands
99
+ - Enables setting command aliases for plugin commands without errors
100
+ - Built-in commands take precedence over plugin commands with the same name
101
+
102
+ ## [0.2.0] - 2025-12-12
103
+
104
+ ### Added
105
+ - Insert mode completion support for LSP:
106
+ - `InsertCompletionState` class for managing LSP completion items
107
+ - `InsertCompletionRenderer` for displaying completion popup below cursor
108
+ - `Editor#start_insert_completion(items, prefix:)` API for plugins
109
+ - `Editor#insert_completion_active?` to check completion state
110
+ - Key bindings in Insert mode:
111
+ - `↑` / `Ctrl+P`: Select previous completion
112
+ - `↓` / `Ctrl+N`: Select next completion
113
+ - `Tab`: Confirm and insert selected completion
114
+ - `Esc`: Cancel completion popup
115
+ - Popup automatically repositions above cursor if space is limited
116
+ - Auto-trigger completion after `.`, `@`, and `::` characters
117
+ - Uses LSP `textEdit` for precise text replacement
118
+ - Buffer word completion (`Ctrl+N` / `Ctrl+P` in Insert mode):
119
+ - Collects words from current buffer as completion candidates
120
+ - `BufferWordCache` class for high-performance word caching
121
+ - Cache built once when entering Insert mode, updated incrementally on changes
122
+ - Auto-triggers while typing (1+ character prefix)
123
+ - Dynamic filtering: completion list updates as you type more characters
124
+ - Excludes word at cursor position from candidates
125
+ - Triggered when no LSP completion is active
126
+ - `InsertCompletion` autocmd event for plugins to hook into completion triggers
127
+ - `KeyCode::CTRL_N`, `KeyCode::CTRL_P`, and `KeyCode::CTRL_SPACE` constants
128
+ - `KeyHandler::Base#editor` helper method for accessing editor instance
129
+ - Insert mode plugin keymap support (allows plugins to define Insert mode key bindings)
130
+ - LSP configuration stub (`Mui.lsp` / `Mui::LspConfigStub`):
131
+ - Allows `.muirc` to call `Mui.lsp { ... }` before mui-lsp gem is loaded
132
+ - Stub stores configuration (preset servers via `use`, custom servers via `server`)
133
+ - When mui-lsp gem loads, configurations are automatically migrated to real `ConfigDsl`
134
+ - `Mui.lsp_server_configs` returns stored server configurations
135
+ - Floating window (popup) support:
136
+ - `FloatingWindow` class for displaying temporary content like hover info
137
+ - `editor.show_floating(content, max_width:, max_height:)` to show popup at cursor
138
+ - `editor.hide_floating` to close the popup
139
+ - Automatic positioning with screen bounds adjustment
140
+ - Unicode box-drawing border characters
141
+ - Scroll support for long content
142
+ - Auto-close on any key press (Escape only closes without processing)
143
+ - Configurable via `:floating_window` color scheme element
144
+ - Dynamic custom highlighter management for buffers:
145
+ - `Buffer#add_custom_highlighter(key, highlighter)` to add named highlighter
146
+ - `Buffer#remove_custom_highlighter(key)` to remove highlighter by key
147
+ - `Buffer#custom_highlighter?(key)` to check if highlighter exists
148
+ - `Window#refresh_highlighters` to rebuild line renderer after highlighter changes
149
+ - Enables plugins to dynamically add/remove highlighting (e.g., LSP diagnostics)
150
+ - Interactive command execution support for plugins:
151
+ - `TerminalAdapter#suspend` / `#resume` methods for temporarily exiting curses mode
152
+ - `Editor#suspend_ui` block helper for safe UI suspension
153
+ - `CommandContext#run_interactive_command(cmd)` to run external interactive commands (e.g., fzf)
154
+ - `CommandContext#command_exists?(cmd)` to check if external command is available
155
+ - Enables plugins to integrate with interactive CLI tools like fzf, less, etc.
156
+ - Custom highlighter support for buffers:
157
+ - `Buffer#custom_highlighters(color_scheme)` method for buffer-specific highlighting
158
+ - Window automatically applies custom highlighters from buffer
159
+ - Enables plugins to provide syntax highlighting for special buffers (e.g., diff output)
160
+ - Diff highlighting color definitions:
161
+ - Added `diff_add`, `diff_delete`, `diff_hunk`, `diff_header` to ColorScheme elements
162
+ - All themes now include diff highlighting colors
163
+ - Incremental search (incsearch):
164
+ - Search results highlight in real-time as you type
165
+ - Cursor moves to first match while typing
166
+ - Escape cancels search and restores original cursor position
167
+ - Backspace updates search results incrementally
168
+ - Works with both forward (`/`) and backward (`?`) search
169
+ - Word completion popup shows matching words from buffer as you type
170
+ - Tab/Shift+Tab to cycle through completion candidates
171
+ - Completions are extracted from buffer content (identifiers and words)
172
+ - Word search commands (`*` and `#`):
173
+ - Normal mode: `*` searches forward for word under cursor
174
+ - Normal mode: `#` searches backward for word under cursor
175
+ - Uses word boundaries (`\b`) for whole-word matching (Vim behavior)
176
+ - Visual mode: `*`/`#` searches for selected text
177
+ - Works in both Visual mode (`v`) and Visual Line mode (`V`)
178
+ - Special regex characters are escaped for literal matching
179
+ - Search state is updated so `n`/`N` continues to find matches
180
+ - Visual mode indent commands (Vim-compatible):
181
+ - `>` to add indent to selected lines (shiftwidth spaces or tab)
182
+ - `<` to remove indent from selected lines
183
+ - Works in both Visual mode (`v`) and Visual Line mode (`V`)
184
+ - Empty lines are skipped (Vim behavior)
185
+ - Multiple lines indented as single undo group
186
+ - Cursor moves to first selected line after operation
187
+ - Configuration options: `shiftwidth` (default: 2), `expandtab` (default: true), `tabstop` (default: 8)
188
+ - `reselect_after_indent` option (default: false): Keep selection after indent for continuous adjustment
189
+ - Reselect last visual selection (`gv` command):
190
+ - `gv` in Normal mode restores the previous visual selection
191
+ - Works with both Visual mode and Visual Line mode
192
+ - Cursor moves to end of selection
193
+ - Useful for repeating operations on the same selection (e.g., multiple indents)
194
+ - Asynchronous job execution system (JobManager):
195
+ - `JobManager` class for managing background tasks with Thread + Queue
196
+ - `Job` class with status tracking (pending, running, completed, failed, cancelled)
197
+ - Thread-safe implementation with Mutex protection
198
+ - `context.run_async { ... }` to run Ruby blocks asynchronously
199
+ - `context.run_shell_command("cmd")` to run external shell commands via Open3
200
+ - `context.jobs_running?` to check if any jobs are active
201
+ - `context.cancel_job(id)` to cancel a running job
202
+ - Callback support via `on_complete:` parameter
203
+ - Autocmd events: JobStarted, JobCompleted, JobFailed, JobCancelled
204
+ - Non-blocking event loop with polling for job results
205
+ - Scratch buffer support:
206
+ - `editor.open_scratch_buffer(name, content)` to display read-only results
207
+ - `context.open_scratch_buffer(name, content)` for plugins
208
+ - Opens in horizontal split window
209
+ - Ideal for displaying test results, linter output, etc.
210
+ - Readonly buffer protection:
211
+ - `buffer.readonly` flag to mark buffers as read-only
212
+ - Edit commands (`i`, `a`, `o`, `O`, `x`, `d`, `c`, `p`, `P`) blocked with error message
213
+ - `:w` and `:wq` commands blocked on readonly buffers
214
+ - Navigation and yank operations still allowed
215
+ - Command-line completion (real-time popup):
216
+ - Auto-completion popup appears as you type in command mode
217
+ - Command name completion for all Ex commands (`:w`, `:q`, `:tabnew`, etc.)
218
+ - File path completion for file commands (`:e`, `:w`, `:sp`, `:vs`, `:tabnew`)
219
+ - Tab to cycle forward through candidates and apply selection
220
+ - Shift+Tab to cycle backward through candidates
221
+ - Popup displays up to 10 candidates with scroll support
222
+ - Completion styles configurable in all 8 themes (`completion_popup`, `completion_popup_selected`)
223
+ - Syntax highlighting for Ruby and C:
224
+ - Token-based lexer architecture with extensible base class
225
+ - Ruby lexer with support for: keywords, strings, comments, numbers, symbols, constants, instance variables (`@foo`, `@@bar`), global variables (`$stdout`), method calls (`.to_i`, `.each`)
226
+ - C lexer with support for: keywords, strings, char literals, comments, numbers, preprocessor directives
227
+ - Multiline comment support: Ruby `=begin`/`=end`, C `/* */`
228
+ - Line-based token caching for performance
229
+ - Language auto-detection from file extension (`.rb`, `.rake`, `.gemspec`, `.c`, `.h`, `.y`)
230
+ - Syntax colors defined for all 8 built-in themes
231
+ - Configurable via `set :syntax, true/false` in `.muirc`
232
+ - Tab page functionality (Vim-compatible):
233
+ - Tab > Window hierarchy: each tab contains independent window layout
234
+ - `:tabnew` / `:tabe` / `:tabedit` to create new tab
235
+ - `:tabclose` / `:tabc` to close current tab
236
+ - `:tabnext` / `:tabn` to go to next tab
237
+ - `:tabprev` / `:tabp` to go to previous tab
238
+ - `:tabfirst` / `:tabf` to go to first tab
239
+ - `:tablast` / `:tabl` to go to last tab
240
+ - `:Ntabn` / `:tabnext N` to go to specific tab (1-indexed)
241
+ - `:tabmove N` / `:tabm N` to move current tab to position N
242
+ - `gt` to go to next tab (wraps around)
243
+ - `gT` to go to previous tab (wraps around)
244
+ - Tab bar at top of screen with separator line
245
+ - Tab bar colors configurable via color scheme (`tab_bar`, `tab_bar_active`)
246
+ - `:q` closes window → tab → editor (Vim-like behavior)
247
+ - Window split functionality:
248
+ - `:sp` / `:split` to split window horizontally (top/bottom)
249
+ - `:vs` / `:vsplit` to split window vertically (left/right)
250
+ - `:sp <filename>` / `:vs <filename>` to split and open file
251
+ - `Ctrl-w h/j/k/l` to navigate between split windows
252
+ - `Ctrl-w w` to cycle to next window
253
+ - `Ctrl-w c` / `:close` to close current window
254
+ - `Ctrl-w o` / `:only` to close all windows except current
255
+ - `:q` / `:wq` / `:q!` in split window closes only that window (Vim-compatible)
256
+ - `:e <filename>` opens file in current window only (doesn't affect other split windows)
257
+ - Separator lines between windows with configurable colors
258
+ - Command line area with configurable background color (no terminal transparency)
259
+ - Each window has independent cursor position, scroll state, and buffer
260
+ - Japanese and multibyte character input support:
261
+ - Full UTF-8 input handling in Insert mode via IME
262
+ - Proper display width calculation for CJK characters (East Asian Width)
263
+ - Cursor position correctly accounts for double-width characters
264
+ - UTF-8 byte sequence assembly in terminal adapter
265
+ - Plugin system:
266
+ - `Mui.use "gem_name"` to declare plugin gems
267
+ - Lazy gem installation via `bundler/inline` on editor startup
268
+ - Class-based plugins: `class MyPlugin < Mui::Plugin`
269
+ - DSL-based plugins: `Mui.define_plugin(:name) { ... }`
270
+ - Plugin API: `command`, `keymap`, `autocmd`
271
+ - Plugin dependencies with topological sort for load order
272
+ - CommandContext for plugin access to editor internals
273
+ - Autocmd event system:
274
+ - 14 events: BufEnter, BufLeave, BufWrite, BufWritePre, BufWritePost, ModeChanged, CursorMoved, TextChanged, InsertEnter, InsertLeave, JobStarted, JobCompleted, JobFailed, JobCancelled
275
+ - Pattern matching for file paths (e.g., `"*.rb"`)
276
+ - `Mui.autocmd :BufEnter, pattern: "*.rb" do |ctx| ... end`
277
+ - Custom commands:
278
+ - `Mui.command :name do |ctx| ... end`
279
+ - Execute via `:name` in command mode
280
+ - Custom keymaps:
281
+ - `Mui.keymap :normal, "key" do |ctx| ... end`
282
+ - Per-mode key bindings
283
+ - Configuration file support:
284
+ - `~/.muirc` for global settings
285
+ - `.lmuirc` for local (per-project) settings
286
+ - Local settings override global settings
287
+ - Ruby DSL format: `Mui.set :colorscheme, "theme_name"`
288
+ - Color scheme system:
289
+ - 256-color terminal support
290
+ - Built-in themes: `mui` (default), `solarized_dark`, `solarized_light`, `monokai`, `nord`, `gruvbox_dark`, `dracula`, `tokyo_night`
291
+ - Customizable UI elements: normal text, status line, search highlight, visual selection, line numbers, messages
292
+ - Search functionality:
293
+ - `/` to search forward (supports regular expressions)
294
+ - `?` to search backward (supports regular expressions)
295
+ - `n` to jump to next match (respects search direction)
296
+ - `N` to jump to previous match (reverses search direction)
297
+ - Wrap-around search (continues from beginning/end of file)
298
+ - Search match highlighting across visible lines
299
+ - Undo/Redo functionality:
300
+ - `u` to undo last change
301
+ - `Ctrl-r` to redo undone change
302
+ - Vim-compatible undo granularity: Insert mode session as single undo unit
303
+ - Support for all editing operations (insert, delete, change, visual mode operations)
304
+ - Maximum 1000 undo history entries
305
+ - Vim-compatible named registers:
306
+ - Named registers (`"a` - `"z`): 26 user-specified registers
307
+ - Unnamed register (`""`): Default register
308
+ - Yank register (`"0`): Stores last yank, not affected by delete
309
+ - Delete history registers (`"1` - `"9`): Stores delete history, shifts on each delete
310
+ - Black hole register (`"_`): Discards content without saving
311
+ - Usage: `"ayy` (yank to register a), `"ap` (paste from register a)
312
+ - Delete/change operators now save to registers (Vim-compatible behavior):
313
+ - `dd`, `dw`, etc. save deleted text to unnamed register and delete history
314
+ - `cc`, `cw`, etc. save changed text to unnamed register and delete history
315
+ - Visual mode `d`/`c` also save to registers
316
+ - Yank operator (`y`):
317
+ - `yy` to yank current line
318
+ - `y` + motion: `yw`, `ye`, `yb`, `y0`, `y$`, `ygg`, `yG`
319
+ - `yf{char}`, `yt{char}`, `yF{char}`, `yT{char}` to yank to/till character
320
+ - `y` in Visual mode to yank selection (character-wise and line-wise)
321
+ - `yw` behaves like `ye` (yanks to end of word) matching Vim behavior
322
+ - Paste commands:
323
+ - `p` to paste after cursor (character-wise) or below current line (line-wise)
324
+ - `P` to paste before cursor (character-wise) or above current line (line-wise)
325
+ - Multi-line character-wise paste support
326
+ - Register system for yank/paste operations
327
+ - Default register for storing yanked text
328
+ - Named registers support
329
+ - Linewise flag to distinguish line-wise vs character-wise yanks
330
+ - TerminalAdapter abstraction layer for custom terminal implementations
331
+ - `Mui::TerminalAdapter::Base` abstract base class defining the terminal interface
332
+ - `Mui::TerminalAdapter::Curses` default implementation using Curses library
333
+ - Users can create custom adapters by subclassing `Base` and passing to `Editor.new(adapter:)`
334
+ - Example: `Mui::Editor.new(file_path, adapter: MyCustomAdapter.new)`
335
+ - Initial release of Mui, a Vim-like text editor written in Ruby
336
+ - Vim-like modal editing with five modes:
337
+ - Normal mode: Navigation and text manipulation
338
+ - Insert mode: Text input
339
+ - Command mode: Ex commands
340
+ - Visual mode (`v`): Character-wise selection
341
+ - Visual Line mode (`V`): Line-wise selection
342
+ - Basic cursor movement with `h`, `j`, `k`, `l` and arrow keys in Normal mode
343
+ - Arrow key cursor movement in Insert mode
344
+ - Motion commands:
345
+ - Word movements: `w` (word forward), `b` (word backward), `e` (word end)
346
+ - Line movements: `0` (line start), `^` (first non-blank), `$` (line end)
347
+ - File movements: `gg` (file start), `G` (file end)
348
+ - Character search: `f{char}`, `F{char}`, `t{char}`, `T{char}`
349
+ - Text editing operations:
350
+ - `i` to insert before cursor
351
+ - `a` to append after cursor
352
+ - `o` to open new line below
353
+ - `O` to open new line above
354
+ - `x` to delete character at cursor
355
+ - Backspace to delete and join lines
356
+ - Delete operator (`d`):
357
+ - `dd` to delete current line
358
+ - `d` + motion: `dw`, `de`, `db`, `d0`, `d$`, `dgg`, `dG`
359
+ - `df{char}`, `dt{char}`, `dF{char}`, `dT{char}` to delete to/till character
360
+ - `d` in Visual mode to delete selection (character-wise and line-wise)
361
+ - Change operator (`c`):
362
+ - `cc` to change current line (clear and enter Insert mode)
363
+ - `c` + motion: `cw`, `ce`, `cb`, `c0`, `c$`, `cgg`, `cG`
364
+ - `cf{char}`, `ct{char}`, `cF{char}`, `cT{char}` to change to/till character
365
+ - `c` in Visual mode to change selection (character-wise and line-wise)
366
+ - `cw` behaves like `ce` (changes to end of word, preserving space) matching Vim behavior
367
+ - Ex commands:
368
+ - `:w` to save file
369
+ - `:w <filename>` to save as
370
+ - `:q` to quit (with unsaved changes protection)
371
+ - `:q!` to force quit
372
+ - `:wq` to save and quit
373
+ - `:e` to reload current file
374
+ - `:e <filename>` to open file (Vim-compatible: non-existent files open as new buffer)
375
+ - `:{number}` to jump to specific line (e.g., `:10` jumps to line 10)
376
+ - Line numbers are 1-indexed (`:1` = first line)
377
+ - Out-of-range values are clamped (`:0` → first line, `:999` on 100-line file → last line)
378
+ - Cursor moves to beginning of line (column 0)
379
+ - Curses-based terminal UI with:
380
+ - Buffer management
381
+ - Window with scrolling support
382
+ - Status line display
383
+ - Command line input
384
+ - Visual mode features:
385
+ - Selection highlighting with reverse video
386
+ - Toggle between Visual and Visual Line mode with `v`/`V`
387
+ - All motion commands supported (h, j, k, l, w, b, e, 0, ^, $, gg, G, f, F, t, T)
388
+ - Exit to Normal mode with `Esc`
389
+ - Comprehensive test suite for `Mui::Input` and `Mui::Editor` classes
390
+ - Unit tests for Buffer, CommandLine, Input, Screen, Window, Selection, and Editor modes
391
+ - Integration tests for component interactions
392
+ - E2E tests with ScriptRunner DSL for Vim operation scenarios including Visual mode
393
+ - Test infrastructure with Curses mock and `MuiTestHelper` module
394
+
395
+ ### Changed
396
+ - Reorganized test directory structure to follow standard gem conventions
397
+ - Unit tests moved to `test/mui/`
398
+ - Editor mode tests in `test/mui/editor/`
399
+ - Integration tests in `test/integration/`
400
+ - E2E tests in `test/e2e/`
401
+ - Refactored test files to use nested classes per method for better readability
402
+ - Improved syntax highlighting performance:
403
+ - Use `\G` anchor for position-specific regex matching to avoid substring allocation
404
+ - Pre-compile all regex patterns as class-level constants for one-time compilation at class load
405
+ - Optimize event sorting in line renderer using array tuples instead of hash arrays
406
+ - Add style resolution cache to avoid repeated hash merges
407
+ - Reduce hash lookups in token cache by inlining cache validation
408
+ - Improved buffer change detection performance:
409
+ - Added `Buffer#change_count` for O(1) change detection
410
+ - Replaced `buffer.lines.hash` (O(n)) with counter comparison in `TextChanged` event trigger
411
+ - Improved buffer word completion performance:
412
+ - Use `String#scan` with regex for fast word extraction instead of character-by-character iteration
413
+ - Direct ASCII code comparison for word character detection instead of regex match per character
414
+
415
+ ### Fixed
416
+ - Command-line completion Tab behavior:
417
+ - First Tab now confirms current selection without cycling to next candidate
418
+ - Second Tab and subsequent presses cycle through candidates
419
+ - Previously, first Tab would skip the initially displayed candidate
420
+ - Command-line completion cursor position:
421
+ - Cursor now moves to end of buffer after applying completion
422
+ - Previously, cursor remained at old position after completion
423
+ - Plugin keymap handler now correctly uses active window's buffer instead of initial buffer
424
+ - Fixes issue where buffer-specific keymaps didn't work in split windows
425
+ - Plugin keymap handler now properly returns handler result
426
+ - Allows buffer-specific keymaps to conditionally pass through to built-in handlers
427
+ - Plugin keymap now correctly handles Enter key (Curses::KEY_ENTER)
428
+ - Previously special key codes like Curses::KEY_ENTER (343) were not converted to keymap string
429
+ - Now Enter key variants (CR, LF, KEY_ENTER) are all mapped to `"\r"` for plugin keymaps
430
+ - Auto-indent on newline in Insert mode
431
+ - Pressing Enter preserves indentation from the current line
432
+ - Leading whitespace (spaces and tabs) is automatically inserted on the new line
433
+ - Whitespace-only lines are cleared when pressing Escape (Vim behavior)
434
+ - Register is now shared globally across tabs and split windows
435
+ - Yank in one tab/window can be pasted in another
436
+ - Previously each ModeManager had its own Register instance
437
+ - Visual mode operations now correctly use the active window's buffer
438
+ - Fixed yank/delete/change/indent operations using stale buffer reference after tab or window switch
439
+ - Operations now properly target the current active window's buffer
440
+ - Command mode cursor movement with arrow keys
441
+ - Left/Right arrow keys now move cursor within command line
442
+ - Previously arrow keys were interpreted as characters (e.g., Left inserted 'a')
443
+ - Backspace now deletes character before cursor position
444
+ - Character input now inserts at cursor position
445
+ - Empty command (`:` then Enter) no longer crashes
446
+ - Previously caused error when trying to look up empty string as plugin command
447
+ - Buffer word completion now works correctly after opening files with `:e` or `:tabe`
448
+ - `InsertMode` now uses active window's buffer for `BufferWordCache` instead of initial buffer
449
+ - Previously, completion would use stale buffer reference after switching files
450
+
3
451
  ## [0.1.0] - 2025-11-30
4
452
 
5
453
  - Initial release