mui 0.2.0 → 0.4.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +18 -10
  3. data/CHANGELOG.md +162 -0
  4. data/README.md +309 -6
  5. data/docs/_config.yml +56 -0
  6. data/docs/configuration.md +314 -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 +155 -0
  13. data/lib/mui/color_manager.rb +140 -6
  14. data/lib/mui/color_scheme.rb +1 -0
  15. data/lib/mui/command_completer.rb +11 -2
  16. data/lib/mui/command_history.rb +89 -0
  17. data/lib/mui/command_line.rb +32 -2
  18. data/lib/mui/command_registry.rb +21 -2
  19. data/lib/mui/config.rb +3 -1
  20. data/lib/mui/editor.rb +90 -2
  21. data/lib/mui/floating_window.rb +53 -1
  22. data/lib/mui/handler_result.rb +13 -7
  23. data/lib/mui/highlighters/search_highlighter.rb +2 -1
  24. data/lib/mui/highlighters/syntax_highlighter.rb +4 -1
  25. data/lib/mui/insert_completion_state.rb +15 -2
  26. data/lib/mui/key_handler/base.rb +87 -0
  27. data/lib/mui/key_handler/command_mode.rb +68 -0
  28. data/lib/mui/key_handler/insert_mode.rb +10 -41
  29. data/lib/mui/key_handler/normal_mode.rb +24 -51
  30. data/lib/mui/key_handler/operators/paste_operator.rb +9 -3
  31. data/lib/mui/key_handler/search_mode.rb +10 -7
  32. data/lib/mui/key_handler/visual_mode.rb +15 -10
  33. data/lib/mui/key_notation_parser.rb +159 -0
  34. data/lib/mui/key_sequence.rb +67 -0
  35. data/lib/mui/key_sequence_buffer.rb +85 -0
  36. data/lib/mui/key_sequence_handler.rb +163 -0
  37. data/lib/mui/key_sequence_matcher.rb +79 -0
  38. data/lib/mui/line_renderer.rb +52 -1
  39. data/lib/mui/mode_manager.rb +3 -2
  40. data/lib/mui/screen.rb +30 -6
  41. data/lib/mui/search_state.rb +74 -27
  42. data/lib/mui/syntax/language_detector.rb +33 -1
  43. data/lib/mui/syntax/lexers/c_lexer.rb +2 -0
  44. data/lib/mui/syntax/lexers/css_lexer.rb +121 -0
  45. data/lib/mui/syntax/lexers/go_lexer.rb +207 -0
  46. data/lib/mui/syntax/lexers/html_lexer.rb +118 -0
  47. data/lib/mui/syntax/lexers/javascript_lexer.rb +219 -0
  48. data/lib/mui/syntax/lexers/markdown_lexer.rb +210 -0
  49. data/lib/mui/syntax/lexers/ruby_lexer.rb +3 -0
  50. data/lib/mui/syntax/lexers/rust_lexer.rb +150 -0
  51. data/lib/mui/syntax/lexers/typescript_lexer.rb +225 -0
  52. data/lib/mui/terminal_adapter/base.rb +21 -0
  53. data/lib/mui/terminal_adapter/curses.rb +37 -11
  54. data/lib/mui/themes/default.rb +263 -132
  55. data/lib/mui/version.rb +1 -1
  56. data/lib/mui/window.rb +105 -39
  57. data/lib/mui/window_manager.rb +7 -0
  58. data/lib/mui/wrap_cache.rb +40 -0
  59. data/lib/mui/wrap_helper.rb +84 -0
  60. data/lib/mui.rb +15 -0
  61. metadata +26 -3
data/docs/plugins.md ADDED
@@ -0,0 +1,285 @@
1
+ ---
2
+ title: Plugins
3
+ layout: default
4
+ nav_order: 5
5
+ ---
6
+
7
+ # Plugins
8
+ {: .no_toc }
9
+
10
+ ## Table of contents
11
+ {: .no_toc .text-delta }
12
+
13
+ 1. TOC
14
+ {:toc}
15
+
16
+ ---
17
+
18
+ ## Overview
19
+
20
+ Mui's plugin system allows you to extend the editor's functionality using Ruby gems. Plugins can:
21
+
22
+ - Add custom commands
23
+ - Define key mappings
24
+ - React to events (autocmd)
25
+ - Integrate with external tools
26
+ - Add syntax highlighting
27
+
28
+ ## Official Plugins
29
+
30
+ | Plugin | Description | Install |
31
+ |--------|-------------|---------|
32
+ | [mui-lsp](https://github.com/S-H-GAMELINKS/mui-lsp) | LSP support | `gem install mui-lsp` |
33
+ | [mui-git](https://github.com/S-H-GAMELINKS/mui-git) | Git integration | `gem install mui-git` |
34
+ | [mui-fzf](https://github.com/S-H-GAMELINKS/mui-fzf) | fzf integration | `gem install mui-fzf` |
35
+
36
+ ## Using Plugins
37
+
38
+ ### Loading Plugins
39
+
40
+ Add plugins to your `~/.muirc`:
41
+
42
+ ```ruby
43
+ Mui.use "mui-lsp"
44
+ Mui.use "mui-git"
45
+ Mui.use "mui-fzf"
46
+ ```
47
+
48
+ Plugins are automatically installed via `bundler/inline` on first startup.
49
+
50
+ ### LSP Configuration
51
+
52
+ Configure LSP servers with `Mui.lsp`:
53
+
54
+ ```ruby
55
+ Mui.use "mui-lsp"
56
+
57
+ Mui.lsp do
58
+ # Use preset configuration
59
+ use :ruby
60
+
61
+ # Custom server configuration
62
+ server :typescript,
63
+ command: ["typescript-language-server", "--stdio"],
64
+ filetypes: ["typescript", "typescriptreact"]
65
+ end
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Creating Plugins
71
+
72
+ ### Class-based Plugin
73
+
74
+ ```ruby
75
+ class MyPlugin < Mui::Plugin
76
+ name :my_plugin
77
+ version "1.0.0"
78
+ description "My awesome plugin"
79
+
80
+ # Optional: declare dependencies
81
+ depends_on :other_plugin
82
+
83
+ def setup
84
+ # Define commands, keymaps, autocmds here
85
+ end
86
+ end
87
+ ```
88
+
89
+ ### DSL-based Plugin
90
+
91
+ ```ruby
92
+ Mui.define_plugin(:my_plugin) do
93
+ # Define commands, keymaps, autocmds here
94
+ end
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Plugin API
100
+
101
+ ### Commands
102
+
103
+ ```ruby
104
+ def setup
105
+ command :greet do |ctx|
106
+ ctx.set_message("Hello!")
107
+ end
108
+
109
+ command :greet_name do |ctx, name|
110
+ ctx.set_message("Hello, #{name}!")
111
+ end
112
+ end
113
+ ```
114
+
115
+ ### Key Mappings
116
+
117
+ ```ruby
118
+ def setup
119
+ # Normal mode mapping
120
+ keymap :normal, "<Leader>g" do |ctx|
121
+ ctx.editor.execute_command("greet")
122
+ end
123
+
124
+ # Insert mode mapping
125
+ keymap :insert, "<C-g>" do |ctx|
126
+ ctx.insert_text("Generated text")
127
+ end
128
+
129
+ # Multi-key sequence
130
+ keymap :normal, "<Leader>gg" do |ctx|
131
+ ctx.set_message("Leader g g pressed!")
132
+ end
133
+ end
134
+ ```
135
+
136
+ ### Autocmd Events
137
+
138
+ ```ruby
139
+ def setup
140
+ autocmd :BufEnter, pattern: "*.rb" do |ctx|
141
+ ctx.set_message("Opened Ruby file: #{ctx.buffer.file_path}")
142
+ end
143
+
144
+ autocmd :BufWritePre do |ctx|
145
+ # Run before every file save
146
+ end
147
+
148
+ autocmd :InsertLeave do |ctx|
149
+ # Run when leaving insert mode
150
+ end
151
+ end
152
+ ```
153
+
154
+ ---
155
+
156
+ ## CommandContext API
157
+
158
+ The `ctx` object passed to handlers provides access to editor internals:
159
+
160
+ ### Messages
161
+
162
+ ```ruby
163
+ ctx.set_message("Info message")
164
+ ctx.set_error("Error message")
165
+ ```
166
+
167
+ ### Editor Access
168
+
169
+ ```ruby
170
+ ctx.editor # Editor instance
171
+ ctx.buffer # Current buffer
172
+ ctx.window # Current window
173
+ ctx.mode # Current mode (:normal, :insert, etc.)
174
+ ```
175
+
176
+ ### Mode Control
177
+
178
+ ```ruby
179
+ ctx.change_mode(:normal)
180
+ ctx.change_mode(:insert)
181
+ ```
182
+
183
+ ### Text Manipulation
184
+
185
+ ```ruby
186
+ ctx.insert_text("text") # Insert at cursor
187
+ ctx.buffer.lines # Get all lines
188
+ ctx.buffer.line(n) # Get line n
189
+ ```
190
+
191
+ ### Cursor
192
+
193
+ ```ruby
194
+ ctx.cursor_row # Current row (0-indexed)
195
+ ctx.cursor_col # Current column (0-indexed)
196
+ ctx.move_cursor(row, col) # Move cursor
197
+ ```
198
+
199
+ ### File Operations
200
+
201
+ ```ruby
202
+ ctx.buffer.file_path # Current file path
203
+ ctx.buffer.modified? # Has unsaved changes?
204
+ ctx.editor.execute_command("w") # Save file
205
+ ctx.editor.execute_command("e filename") # Open file
206
+ ```
207
+
208
+ ### Scratch Buffers
209
+
210
+ ```ruby
211
+ ctx.open_scratch_buffer("[Results]", "Content here")
212
+ ```
213
+
214
+ ### Jobs
215
+
216
+ See [Jobs]({{ site.baseurl }}/jobs) for async job execution.
217
+
218
+ ### Interactive Commands
219
+
220
+ ```ruby
221
+ if ctx.command_exists?("fzf")
222
+ result = ctx.run_interactive_command("fzf")
223
+ ctx.editor.execute_command("e #{result}") if result
224
+ end
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Publishing Plugins
230
+
231
+ ### Gem Structure
232
+
233
+ ```
234
+ mui-myplugin/
235
+ ├── lib/
236
+ │ └── mui_myplugin.rb
237
+ ├── mui-myplugin.gemspec
238
+ └── README.md
239
+ ```
240
+
241
+ ### Gemspec
242
+
243
+ ```ruby
244
+ Gem::Specification.new do |spec|
245
+ spec.name = "mui-myplugin"
246
+ spec.version = "1.0.0"
247
+ spec.summary = "My Mui plugin"
248
+ spec.files = Dir["lib/**/*.rb"]
249
+ spec.require_paths = ["lib"]
250
+
251
+ spec.add_dependency "mui", ">= 0.2.0"
252
+ end
253
+ ```
254
+
255
+ ### Plugin File
256
+
257
+ ```ruby
258
+ # lib/mui_myplugin.rb
259
+ require "mui"
260
+
261
+ class MuiMyplugin < Mui::Plugin
262
+ name :myplugin
263
+ version "1.0.0"
264
+
265
+ def setup
266
+ command :my_command do |ctx|
267
+ ctx.set_message("Hello from myplugin!")
268
+ end
269
+ end
270
+ end
271
+ ```
272
+
273
+ ### Publishing
274
+
275
+ ```bash
276
+ gem build mui-myplugin.gemspec
277
+ gem push mui-myplugin-1.0.0.gem
278
+ ```
279
+
280
+ Users can then install with:
281
+
282
+ ```ruby
283
+ # ~/.muirc
284
+ Mui.use "mui-myplugin"
285
+ ```
@@ -0,0 +1,155 @@
1
+ ---
2
+ title: Syntax Highlighting
3
+ layout: default
4
+ nav_order: 7
5
+ ---
6
+
7
+ # Syntax Highlighting
8
+ {: .no_toc }
9
+
10
+ ## Table of contents
11
+ {: .no_toc .text-delta }
12
+
13
+ 1. TOC
14
+ {:toc}
15
+
16
+ ---
17
+
18
+ ## Overview
19
+
20
+ Mui includes built-in syntax highlighting for 9 programming languages. Highlighting is automatic based on file extension.
21
+
22
+ ## Supported Languages
23
+
24
+ | Language | Extensions |
25
+ |----------|------------|
26
+ | Ruby | `.rb`, `.rake`, `.gemspec` |
27
+ | C | `.c`, `.h`, `.y` |
28
+ | Go | `.go` |
29
+ | Rust | `.rs` |
30
+ | JavaScript | `.js`, `.mjs`, `.cjs`, `.jsx` |
31
+ | TypeScript | `.ts`, `.tsx`, `.mts`, `.cts` |
32
+ | Markdown | `.md`, `.markdown` |
33
+ | HTML | `.html`, `.htm`, `.xhtml` |
34
+ | CSS | `.css`, `.scss`, `.sass` |
35
+
36
+ ## Configuration
37
+
38
+ ### Enable/Disable
39
+
40
+ ```ruby
41
+ # ~/.muirc
42
+ Mui.set :syntax, true # Enable (default)
43
+ Mui.set :syntax, false # Disable
44
+ ```
45
+
46
+ ## Language Features
47
+
48
+ ### Ruby
49
+
50
+ - Keywords (`def`, `class`, `module`, `if`, `end`, etc.)
51
+ - Strings (single, double, heredoc)
52
+ - Comments (`#`, `=begin`/`=end`)
53
+ - Numbers
54
+ - Symbols (`:symbol`)
55
+ - Constants (`CONSTANT`, `ClassName`)
56
+ - Instance variables (`@foo`, `@@bar`)
57
+ - Global variables (`$stdout`)
58
+ - Method calls (`.to_s`, `.each`)
59
+
60
+ ### C
61
+
62
+ - Keywords (`int`, `char`, `struct`, `if`, `for`, etc.)
63
+ - Strings and character literals
64
+ - Comments (`//`, `/* */`)
65
+ - Numbers
66
+ - Preprocessor directives (`#include`, `#define`)
67
+
68
+ ### Go
69
+
70
+ - Keywords (`func`, `package`, `import`, `go`, `defer`, etc.)
71
+ - Types (`int`, `string`, `bool`, etc.)
72
+ - Constants (`true`, `false`, `nil`, `iota`)
73
+ - Strings (regular and raw backtick strings)
74
+ - Comments (`//`, `/* */`)
75
+
76
+ ### Rust
77
+
78
+ - Keywords (`fn`, `let`, `mut`, `impl`, `trait`, etc.)
79
+ - Macros (`println!`, `vec!`)
80
+ - Lifetimes (`'a`, `'static`)
81
+ - Attributes (`#[derive]`, `#[cfg]`)
82
+ - Doc comments (`///`, `//!`)
83
+ - Raw strings (`r#"..."#`)
84
+
85
+ ### JavaScript
86
+
87
+ - ES6+ keywords (`const`, `let`, `async`, `await`, `class`)
88
+ - Template literals (`` `template ${expr}` ``)
89
+ - Regex literals (`/pattern/flags`)
90
+ - BigInt (`123n`)
91
+ - Strings and comments
92
+
93
+ ### TypeScript
94
+
95
+ All JavaScript features plus:
96
+ - Type keywords (`interface`, `type`, `enum`, `declare`, `abstract`)
97
+ - Type annotations
98
+
99
+ ### Markdown
100
+
101
+ - Headings (`#`, `##`, etc.)
102
+ - Emphasis (`*italic*`, `**bold**`)
103
+ - Code blocks (fenced and indented)
104
+ - Links (`[text](url)`)
105
+ - Lists (ordered and unordered)
106
+ - Blockquotes (`>`)
107
+
108
+ ### HTML
109
+
110
+ - Tags (`<div>`, `</div>`, `<br/>`)
111
+ - Attributes (`class="..."`, `id="..."`)
112
+ - Comments (`<!-- -->`)
113
+ - DOCTYPE
114
+ - Entities (`&amp;`, `&lt;`)
115
+
116
+ ### CSS
117
+
118
+ - Selectors (`.class`, `#id`, `:pseudo`, `::pseudo-element`)
119
+ - Properties and values
120
+ - Hex colors (`#fff`, `#ffffff`)
121
+ - At-rules (`@media`, `@import`, `@keyframes`)
122
+ - Functions (`calc()`, `rgb()`, `var()`)
123
+
124
+ ## Theme Colors
125
+
126
+ Syntax colors are defined per theme. All 8 built-in themes include syntax highlighting colors:
127
+
128
+ - `mui` (default)
129
+ - `solarized_dark`
130
+ - `solarized_light`
131
+ - `monokai`
132
+ - `nord`
133
+ - `gruvbox_dark`
134
+ - `dracula`
135
+ - `tokyo_night`
136
+
137
+ Each theme defines colors for:
138
+
139
+ | Element | Description |
140
+ |---------|-------------|
141
+ | `syntax_keyword` | Language keywords |
142
+ | `syntax_string` | String literals |
143
+ | `syntax_comment` | Comments |
144
+ | `syntax_number` | Numeric literals |
145
+ | `syntax_type` | Types and classes |
146
+ | `syntax_function` | Function names |
147
+ | `syntax_function_definition` | Function definition names |
148
+ | `syntax_variable` | Variables |
149
+ | `syntax_constant` | Constants |
150
+ | `syntax_operator` | Operators |
151
+ | `diagnostic_error` | LSP diagnostic errors |
152
+ | `diagnostic_warning` | LSP diagnostic warnings |
153
+ | `diagnostic_info` | LSP diagnostic information |
154
+ | `diagnostic_hint` | LSP diagnostic hints |
155
+ | `floating_window` | Floating window background |
@@ -17,8 +17,27 @@ module Mui
17
17
  # 256-color palette extended colors
18
18
  # Use https://www.ditig.com/256-colors-cheat-sheet for reference
19
19
  EXTENDED_COLOR_MAP = {
20
- # mui theme
21
- darkgray: 235, # #262626 (~#2b2b2b)
20
+ # mui theme - Eye-friendly gray-based theme
21
+ mui_bg: 236, # #303030 - Calm dark gray background
22
+ mui_fg: 253, # #dadada - Soft white (easy on the eyes)
23
+ mui_comment: 102, # #878787 - Subtle gray (for comments)
24
+ mui_constant: 110, # #87afd7 - Calm blue (constants/strings/numbers)
25
+ mui_identifier: 174, # #d78787 - Soft salmon pink
26
+ mui_statement: 186, # #d7d787 - Subtle yellow (keywords)
27
+ mui_preproc: 173, # #d7875f - Orange/brown (preprocessor)
28
+ mui_type: 109, # #87afaf - Calm cyan (types)
29
+ mui_special: 180, # #d7af87 - Soft beige (symbols)
30
+ mui_function: 216, # #ffaf87 - Peach/orange (functions)
31
+ # UI colors
32
+ mui_line_number: 243, # #767676 - Subtle gray
33
+ mui_status_bg: 238, # #444444 - Status bar background
34
+ mui_visual: 239, # #4e4e4e - Selection background
35
+ mui_search: 222, # #ffd787 - Search highlight (prominent yellow)
36
+ mui_tab_bg: 237, # #3a3a3a - Tab bar background
37
+ mui_tab_active: 110, # #87afd7 - Active tab
38
+ mui_error: 167, # #d75f5f - Error messages
39
+ mui_info: 109, # #87afaf - Info messages
40
+ darkgray: 235, # #262626 (~#2b2b2b) - Kept for backward compatibility
22
41
 
23
42
  # solarized
24
43
  solarized_base03: 234, # #1c1c1c (~#002b36)
@@ -41,6 +60,7 @@ module Mui
41
60
  # monokai
42
61
  monokai_bg: 235, # #262626 (~#272822)
43
62
  monokai_fg: 231, # #ffffff (~#f8f8f2)
63
+ monokai_comment: 101, # #87875f (~#75715e) - Olive gray for comments
44
64
  monokai_pink: 197, # #ff005f (~#f92672)
45
65
  monokai_green: 148, # #afd700 (~#a6e22e)
46
66
  monokai_orange: 208, # #ff8700 (~#fd971f)
@@ -104,18 +124,95 @@ module Mui
104
124
  tokyo_yellow: 223 # #ffd7af (~#e0af68)
105
125
  }.freeze
106
126
 
107
- attr_reader :pairs
127
+ # Fallback map: 256-color to 8-color
128
+ FALLBACK_MAP = {
129
+ # mui theme
130
+ mui_bg: :black,
131
+ mui_fg: :white,
132
+ mui_comment: :white,
133
+ mui_constant: :cyan,
134
+ mui_identifier: :red,
135
+ mui_statement: :yellow,
136
+ mui_preproc: :yellow,
137
+ mui_type: :cyan,
138
+ mui_special: :yellow,
139
+ mui_function: :yellow,
140
+ mui_line_number: :white,
141
+ mui_status_bg: :blue,
142
+ mui_visual: :magenta,
143
+ mui_search: :yellow,
144
+ mui_tab_bg: :blue,
145
+ mui_tab_active: :cyan,
146
+ mui_error: :red,
147
+ mui_info: :cyan,
148
+ darkgray: :black,
149
+ # solarized
150
+ solarized_base03: :black, solarized_base02: :black,
151
+ solarized_base01: :white, solarized_base00: :white,
152
+ solarized_base0: :white, solarized_base1: :white,
153
+ solarized_base2: :white, solarized_base3: :white,
154
+ solarized_yellow: :yellow, solarized_orange: :red,
155
+ solarized_red: :red, solarized_magenta: :magenta,
156
+ solarized_violet: :blue, solarized_blue: :blue,
157
+ solarized_cyan: :cyan, solarized_green: :green,
158
+ # monokai
159
+ monokai_bg: :black, monokai_fg: :white, monokai_comment: :white,
160
+ monokai_pink: :magenta, monokai_green: :green,
161
+ monokai_orange: :yellow, monokai_purple: :magenta,
162
+ monokai_cyan: :cyan, monokai_yellow: :yellow,
163
+ # nord
164
+ nord_polar0: :black, nord_polar1: :black,
165
+ nord_polar2: :black, nord_polar3: :white,
166
+ nord_snow0: :white, nord_snow1: :white, nord_snow2: :white,
167
+ nord_frost0: :cyan, nord_frost1: :cyan,
168
+ nord_frost2: :blue, nord_frost3: :blue,
169
+ nord_aurora_red: :red, nord_aurora_orange: :yellow,
170
+ nord_aurora_yellow: :yellow, nord_aurora_green: :green,
171
+ nord_aurora_purple: :magenta,
172
+ # gruvbox
173
+ gruvbox_bg: :black, gruvbox_fg: :white,
174
+ gruvbox_red: :red, gruvbox_green: :green,
175
+ gruvbox_yellow: :yellow, gruvbox_blue: :blue,
176
+ gruvbox_purple: :magenta, gruvbox_aqua: :cyan,
177
+ gruvbox_orange: :yellow, gruvbox_gray: :white,
178
+ # dracula
179
+ dracula_bg: :black, dracula_fg: :white,
180
+ dracula_selection: :black, dracula_comment: :blue,
181
+ dracula_cyan: :cyan, dracula_green: :green,
182
+ dracula_orange: :yellow, dracula_pink: :magenta,
183
+ dracula_purple: :magenta, dracula_red: :red,
184
+ dracula_yellow: :yellow,
185
+ # tokyo night
186
+ tokyo_bg: :black, tokyo_fg: :white,
187
+ tokyo_comment: :blue, tokyo_cyan: :cyan,
188
+ tokyo_blue: :blue, tokyo_purple: :magenta,
189
+ tokyo_green: :green, tokyo_orange: :yellow,
190
+ tokyo_red: :red, tokyo_yellow: :yellow
191
+ }.freeze
108
192
 
109
- def initialize
193
+ attr_reader :pairs, :supports_256_colors
194
+
195
+ def initialize(adapter: nil)
110
196
  @pair_index = 1
111
197
  @pairs = {}
198
+ @pair_order = []
199
+ @adapter = adapter
200
+ configure_color_capability
112
201
  end
113
202
 
114
203
  def register_pair(fg, bg)
115
204
  key = [fg, bg]
116
- return @pairs[key] if @pairs[key]
205
+
206
+ if @pairs[key]
207
+ touch_pair(key)
208
+ return @pairs[key]
209
+ end
210
+
211
+ # Check pair limit and evict oldest if needed
212
+ evict_oldest_pair if @max_pairs.positive? && @pair_index >= @max_pairs
117
213
 
118
214
  @pairs[key] = @pair_index
215
+ @pair_order << key
119
216
  @pair_index += 1
120
217
  @pairs[key]
121
218
  end
@@ -128,9 +225,46 @@ module Mui
128
225
  return -1 if color.nil?
129
226
  return color if color.is_a?(Integer)
130
227
 
131
- COLOR_MAP[color] || EXTENDED_COLOR_MAP[color] || -1
228
+ resolved_color = resolve_with_fallback(color)
229
+ COLOR_MAP[resolved_color] || EXTENDED_COLOR_MAP[resolved_color] || -1
132
230
  end
133
231
 
134
232
  alias resolve color_code
233
+
234
+ private
235
+
236
+ def configure_color_capability
237
+ if @adapter.nil?
238
+ # Backward compatibility: assume 256 colors when adapter is not specified
239
+ @available_colors = 256
240
+ @max_pairs = 256
241
+ @supports_256_colors = true
242
+ elsif @adapter.has_colors?
243
+ @available_colors = @adapter.colors
244
+ @max_pairs = [@adapter.color_pairs, 256].min
245
+ @supports_256_colors = @available_colors >= 256
246
+ else
247
+ @available_colors = 0
248
+ @max_pairs = 0
249
+ @supports_256_colors = false
250
+ end
251
+ end
252
+
253
+ def resolve_with_fallback(color)
254
+ return color if @supports_256_colors
255
+ return color if COLOR_MAP.key?(color)
256
+
257
+ FALLBACK_MAP[color] || :white
258
+ end
259
+
260
+ def touch_pair(key)
261
+ @pair_order.delete(key)
262
+ @pair_order << key
263
+ end
264
+
265
+ def evict_oldest_pair
266
+ oldest_key = @pair_order.shift
267
+ @pairs.delete(oldest_key) if oldest_key
268
+ end
135
269
  end
136
270
  end
@@ -27,6 +27,7 @@ module Mui
27
27
  syntax_instance_variable
28
28
  syntax_global_variable
29
29
  syntax_method_call
30
+ syntax_function_definition
30
31
  syntax_type
31
32
  diff_add
32
33
  diff_delete
@@ -13,9 +13,18 @@ module Mui
13
13
  ].freeze
14
14
 
15
15
  def complete(prefix)
16
- return COMMANDS.sort if prefix.empty?
16
+ all_commands = COMMANDS + plugin_command_names
17
17
 
18
- COMMANDS.select { |cmd| cmd.start_with?(prefix) }.sort
18
+ return all_commands.uniq.sort if prefix.empty?
19
+
20
+ prefix_downcase = prefix.downcase
21
+ all_commands.select { |cmd| cmd.downcase.start_with?(prefix_downcase) }.uniq.sort
22
+ end
23
+
24
+ private
25
+
26
+ def plugin_command_names
27
+ Mui.config.commands.keys.map(&:to_s)
19
28
  end
20
29
  end
21
30
  end