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/jobs.md ADDED
@@ -0,0 +1,297 @@
1
+ ---
2
+ title: Jobs
3
+ layout: default
4
+ nav_order: 6
5
+ ---
6
+
7
+ # Asynchronous Jobs
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 job system allows plugins to run background tasks without blocking the editor. This is essential for:
21
+
22
+ - Running tests
23
+ - Executing linters/formatters
24
+ - Making HTTP requests
25
+ - Any long-running operation
26
+
27
+ ## Running Async Ruby Code
28
+
29
+ Use `ctx.run_async` to execute Ruby code in a background thread:
30
+
31
+ ```ruby
32
+ command :slow_task do |ctx|
33
+ ctx.set_message("Starting task...")
34
+
35
+ ctx.run_async do
36
+ # This runs in a background thread
37
+ sleep 5 # Simulate slow operation
38
+ "Task completed!"
39
+ end
40
+ end
41
+ ```
42
+
43
+ ### With Callback
44
+
45
+ ```ruby
46
+ command :fetch_data do |ctx|
47
+ ctx.run_async(on_complete: ->(result) {
48
+ ctx.set_message("Result: #{result}")
49
+ }) do
50
+ # Perform async work
51
+ fetch_from_api
52
+ end
53
+ end
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Running Shell Commands
59
+
60
+ Use `ctx.run_shell_command` for external processes:
61
+
62
+ ```ruby
63
+ command :run_tests do |ctx|
64
+ ctx.set_message("Running tests...")
65
+
66
+ ctx.run_shell_command("bundle exec rake test") do |result|
67
+ if result[:success]
68
+ ctx.open_scratch_buffer("[Test Results]", result[:stdout])
69
+ ctx.set_message("Tests passed!")
70
+ else
71
+ ctx.open_scratch_buffer("[Test Errors]", result[:stderr])
72
+ ctx.set_error("Tests failed!")
73
+ end
74
+ end
75
+ end
76
+ ```
77
+
78
+ ### Result Object
79
+
80
+ The callback receives a hash with:
81
+
82
+ | Key | Description |
83
+ |-----|-------------|
84
+ | `:success` | Boolean indicating exit status |
85
+ | `:stdout` | Standard output as string |
86
+ | `:stderr` | Standard error as string |
87
+ | `:exit_status` | Process exit code |
88
+
89
+ ### Example: Linter
90
+
91
+ ```ruby
92
+ command :lint do |ctx|
93
+ file = ctx.buffer.file_path
94
+
95
+ ctx.run_shell_command("rubocop #{file}") do |result|
96
+ if result[:success]
97
+ ctx.set_message("No lint errors!")
98
+ else
99
+ ctx.open_scratch_buffer("[Lint Results]", result[:stdout])
100
+ end
101
+ end
102
+ end
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Job Management
108
+
109
+ ### Checking Job Status
110
+
111
+ ```ruby
112
+ command :status do |ctx|
113
+ if ctx.jobs_running?
114
+ ctx.set_message("Jobs are running...")
115
+ else
116
+ ctx.set_message("No active jobs")
117
+ end
118
+ end
119
+ ```
120
+
121
+ ### Cancelling Jobs
122
+
123
+ ```ruby
124
+ command :start_job do |ctx|
125
+ job_id = ctx.run_async do
126
+ # Long running task
127
+ loop do
128
+ sleep 1
129
+ end
130
+ end
131
+
132
+ # Store job_id for later cancellation
133
+ @current_job = job_id
134
+ end
135
+
136
+ command :cancel_job do |ctx|
137
+ if @current_job
138
+ ctx.cancel_job(@current_job)
139
+ ctx.set_message("Job cancelled")
140
+ end
141
+ end
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Scratch Buffers
147
+
148
+ Display job results in a scratch buffer:
149
+
150
+ ```ruby
151
+ ctx.open_scratch_buffer(name, content)
152
+ ```
153
+
154
+ - Opens in a horizontal split
155
+ - Buffer is read-only
156
+ - Subsequent calls with same name update existing buffer
157
+
158
+ ### Example: Test Runner
159
+
160
+ ```ruby
161
+ command :test do |ctx|
162
+ ctx.run_shell_command("rake test") do |result|
163
+ output = []
164
+ output << "=== Test Results ==="
165
+ output << ""
166
+ output << result[:stdout]
167
+
168
+ if result[:stderr].length > 0
169
+ output << ""
170
+ output << "=== Errors ==="
171
+ output << result[:stderr]
172
+ end
173
+
174
+ output << ""
175
+ output << "Exit status: #{result[:exit_status]}"
176
+
177
+ ctx.open_scratch_buffer("[Test Output]", output.join("\n"))
178
+ end
179
+ end
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Job Events
185
+
186
+ React to job lifecycle with autocmd:
187
+
188
+ ```ruby
189
+ autocmd :JobStarted do |ctx|
190
+ ctx.set_message("Job started")
191
+ end
192
+
193
+ autocmd :JobCompleted do |ctx|
194
+ ctx.set_message("Job completed successfully")
195
+ end
196
+
197
+ autocmd :JobFailed do |ctx|
198
+ ctx.set_error("Job failed!")
199
+ end
200
+
201
+ autocmd :JobCancelled do |ctx|
202
+ ctx.set_message("Job was cancelled")
203
+ end
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Complete Example: Build Plugin
209
+
210
+ ```ruby
211
+ class BuildPlugin < Mui::Plugin
212
+ name :build
213
+ version "1.0.0"
214
+ description "Build and test runner"
215
+
216
+ def setup
217
+ command :build do |ctx|
218
+ run_build(ctx)
219
+ end
220
+
221
+ command :test do |ctx|
222
+ run_tests(ctx)
223
+ end
224
+
225
+ command :check do |ctx|
226
+ run_build(ctx)
227
+ run_tests(ctx)
228
+ end
229
+
230
+ keymap :normal, "<Leader>b" do |ctx|
231
+ run_build(ctx)
232
+ end
233
+
234
+ keymap :normal, "<Leader>t" do |ctx|
235
+ run_tests(ctx)
236
+ end
237
+
238
+ autocmd :JobCompleted do |ctx|
239
+ # Could trigger notifications, update status line, etc.
240
+ end
241
+ end
242
+
243
+ private
244
+
245
+ def run_build(ctx)
246
+ ctx.set_message("Building...")
247
+
248
+ ctx.run_shell_command("make build") do |result|
249
+ if result[:success]
250
+ ctx.set_message("Build succeeded!")
251
+ else
252
+ ctx.open_scratch_buffer("[Build Errors]", result[:stderr])
253
+ ctx.set_error("Build failed!")
254
+ end
255
+ end
256
+ end
257
+
258
+ def run_tests(ctx)
259
+ ctx.set_message("Running tests...")
260
+
261
+ ctx.run_shell_command("make test") do |result|
262
+ ctx.open_scratch_buffer("[Test Results]", result[:stdout])
263
+
264
+ if result[:success]
265
+ ctx.set_message("All tests passed!")
266
+ else
267
+ ctx.set_error("Tests failed!")
268
+ end
269
+ end
270
+ end
271
+ end
272
+ ```
273
+
274
+ ---
275
+
276
+ ## Interactive Commands
277
+
278
+ For commands that require user interaction (like fzf), use `run_interactive_command`:
279
+
280
+ ```ruby
281
+ command :find_file do |ctx|
282
+ unless ctx.command_exists?("fzf")
283
+ ctx.set_error("fzf is not installed")
284
+ return
285
+ end
286
+
287
+ # This temporarily exits curses mode
288
+ result = ctx.run_interactive_command("find . -type f | fzf")
289
+
290
+ if result && !result.empty?
291
+ ctx.editor.execute_command("e #{result.strip}")
292
+ end
293
+ end
294
+ ```
295
+
296
+ {: .note }
297
+ `run_interactive_command` blocks and waits for the command to complete. Use this only for interactive CLI tools that require terminal access.
@@ -0,0 +1,229 @@
1
+ ---
2
+ title: Key Bindings
3
+ layout: default
4
+ nav_order: 3
5
+ ---
6
+
7
+ # Key Bindings
8
+ {: .no_toc }
9
+
10
+ ## Table of contents
11
+ {: .no_toc .text-delta }
12
+
13
+ 1. TOC
14
+ {:toc}
15
+
16
+ ---
17
+
18
+ ## Normal Mode
19
+
20
+ ### Cursor Movement
21
+
22
+ | Key | Action |
23
+ |-----|--------|
24
+ | `h` | Move left |
25
+ | `j` | Move down |
26
+ | `k` | Move up |
27
+ | `l` | Move right |
28
+ | `0` | Move to line start |
29
+ | `^` | Move to first non-blank character |
30
+ | `$` | Move to line end |
31
+
32
+ ### Word Movement
33
+
34
+ | Key | Action |
35
+ |-----|--------|
36
+ | `w` | Move to next word start |
37
+ | `b` | Move to previous word start |
38
+ | `e` | Move to word end |
39
+
40
+ ### File Movement
41
+
42
+ | Key | Action |
43
+ |-----|--------|
44
+ | `gg` | Go to first line |
45
+ | `G` | Go to last line |
46
+ | `:{number}` | Go to line number |
47
+
48
+ ### Character Search
49
+
50
+ | Key | Action |
51
+ |-----|--------|
52
+ | `f{char}` | Find character forward (on character) |
53
+ | `F{char}` | Find character backward (on character) |
54
+ | `t{char}` | Find character forward (before character) |
55
+ | `T{char}` | Find character backward (after character) |
56
+
57
+ ### Search
58
+
59
+ | Key | Action |
60
+ |-----|--------|
61
+ | `/pattern` | Search forward |
62
+ | `?pattern` | Search backward |
63
+ | `n` | Next match |
64
+ | `N` | Previous match |
65
+ | `*` | Search word under cursor (forward) |
66
+ | `#` | Search word under cursor (backward) |
67
+
68
+ ### Entering Insert Mode
69
+
70
+ | Key | Action |
71
+ |-----|--------|
72
+ | `i` | Insert before cursor |
73
+ | `a` | Append after cursor |
74
+ | `I` | Insert at line start |
75
+ | `A` | Append at line end |
76
+ | `o` | Open line below |
77
+ | `O` | Open line above |
78
+
79
+ ### Operators
80
+
81
+ Operators can be combined with motions (e.g., `dw` = delete word).
82
+
83
+ | Key | Action |
84
+ |-----|--------|
85
+ | `d` | Delete |
86
+ | `c` | Change (delete and enter Insert mode) |
87
+ | `y` | Yank (copy) |
88
+
89
+ ### Common Operations
90
+
91
+ | Key | Action |
92
+ |-----|--------|
93
+ | `dd` | Delete line |
94
+ | `cc` | Change line |
95
+ | `yy` | Yank line |
96
+ | `x` | Delete character at cursor |
97
+ | `p` | Paste after cursor |
98
+ | `P` | Paste before cursor |
99
+ | `u` | Undo |
100
+ | `Ctrl-r` | Redo |
101
+
102
+ ### Visual Mode
103
+
104
+ | Key | Action |
105
+ |-----|--------|
106
+ | `v` | Enter Visual mode |
107
+ | `V` | Enter Visual Line mode |
108
+ | `gv` | Reselect last visual selection |
109
+
110
+ ### Tabs and Windows
111
+
112
+ | Key | Action |
113
+ |-----|--------|
114
+ | `gt` | Go to next tab |
115
+ | `gT` | Go to previous tab |
116
+ | `Ctrl-w h` | Go to left window |
117
+ | `Ctrl-w j` | Go to below window |
118
+ | `Ctrl-w k` | Go to above window |
119
+ | `Ctrl-w l` | Go to right window |
120
+ | `Ctrl-w w` | Cycle to next window |
121
+ | `Ctrl-w c` | Close current window |
122
+ | `Ctrl-w o` | Close all other windows |
123
+
124
+ ### Registers
125
+
126
+ | Key | Action |
127
+ |-----|--------|
128
+ | `"a` - `"z` | Use named register |
129
+ | `""` | Use unnamed register |
130
+ | `"0` | Use yank register |
131
+ | `"1` - `"9` | Use delete history |
132
+ | `"_` | Use black hole register (discard) |
133
+
134
+ Example: `"ayy` yanks line to register `a`, `"ap` pastes from register `a`.
135
+
136
+ ---
137
+
138
+ ## Insert Mode
139
+
140
+ | Key | Action |
141
+ |-----|--------|
142
+ | `Esc` | Return to Normal mode |
143
+ | `Ctrl-n` | Next completion candidate |
144
+ | `Ctrl-p` | Previous completion candidate |
145
+ | `Tab` | Accept completion |
146
+ | Arrow keys | Move cursor |
147
+ | `Backspace` | Delete character before cursor |
148
+
149
+ ---
150
+
151
+ ## Visual Mode
152
+
153
+ | Key | Action |
154
+ |-----|--------|
155
+ | `Esc` | Return to Normal mode |
156
+ | `d` | Delete selection |
157
+ | `c` | Change selection |
158
+ | `y` | Yank selection |
159
+ | `>` | Indent selection |
160
+ | `<` | Unindent selection |
161
+ | `*` | Search for selected text (forward) |
162
+ | `#` | Search for selected text (backward) |
163
+ | All motion keys | Extend selection |
164
+
165
+ ---
166
+
167
+ ## Command Mode
168
+
169
+ Enter with `:` from Normal mode.
170
+
171
+ ### File Commands
172
+
173
+ | Command | Action |
174
+ |---------|--------|
175
+ | `:w` | Save file |
176
+ | `:w filename` | Save as filename |
177
+ | `:q` | Quit |
178
+ | `:q!` | Force quit |
179
+ | `:wq` | Save and quit |
180
+ | `:e filename` | Open file |
181
+
182
+ ### Window Commands
183
+
184
+ | Command | Action |
185
+ |---------|--------|
186
+ | `:sp [filename]` | Horizontal split |
187
+ | `:vs [filename]` | Vertical split |
188
+ | `:close` | Close window |
189
+ | `:only` | Close all other windows |
190
+
191
+ ### Tab Commands
192
+
193
+ | Command | Action |
194
+ |---------|--------|
195
+ | `:tabnew [filename]` | New tab |
196
+ | `:tabclose` | Close tab |
197
+ | `:tabnext` | Next tab |
198
+ | `:tabprev` | Previous tab |
199
+ | `:tabfirst` | First tab |
200
+ | `:tablast` | Last tab |
201
+ | `:tabmove N` | Move tab to position N |
202
+
203
+ ### Shell Commands
204
+
205
+ | Command | Action |
206
+ |---------|--------|
207
+ | `:!cmd` | Run shell command |
208
+
209
+ ### Navigation
210
+
211
+ | Command | Action |
212
+ |---------|--------|
213
+ | `:{number}` | Go to line number |
214
+
215
+ ---
216
+
217
+ ## Search Mode
218
+
219
+ Enter with `/` or `?` from Normal mode.
220
+
221
+ | Key | Action |
222
+ |-----|--------|
223
+ | `Enter` | Execute search |
224
+ | `Esc` | Cancel search |
225
+ | `Tab` | Cycle through completion |
226
+ | `Shift-Tab` | Cycle backward |
227
+ | `Backspace` | Delete character |
228
+
229
+ Search supports regular expressions.