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
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,149 @@
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_variable` | Variables |
148
+ | `syntax_constant` | Constants |
149
+ | `syntax_operator` | Operators |
data/exe/mui CHANGED
@@ -3,5 +3,4 @@
3
3
 
4
4
  require "mui"
5
5
 
6
- puts "Mui v#{Mui::VERSION} - A Vim-like TUI editor written in Ruby"
7
- puts "Coming soon..."
6
+ Mui::Editor.new(ARGV[0]).run
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mui
4
+ # Event-driven hook system for plugins
5
+ class Autocmd
6
+ EVENTS = %i[
7
+ BufEnter
8
+ BufLeave
9
+ BufWrite
10
+ BufWritePre
11
+ BufWritePost
12
+ ModeChanged
13
+ CursorMoved
14
+ TextChanged
15
+ InsertEnter
16
+ InsertLeave
17
+ InsertCompletion
18
+ JobStarted
19
+ JobCompleted
20
+ JobFailed
21
+ JobCancelled
22
+ ].freeze
23
+
24
+ def initialize
25
+ @handlers = {}
26
+ EVENTS.each { |e| @handlers[e] = [] }
27
+ end
28
+
29
+ def register(event, pattern: nil, &block)
30
+ event = event.to_sym
31
+ raise ArgumentError, "Unknown event: #{event}" unless EVENTS.include?(event)
32
+
33
+ @handlers[event] << { pattern:, handler: block }
34
+ end
35
+
36
+ def trigger(event, context = nil, **kwargs)
37
+ event = event.to_sym
38
+ return unless @handlers[event]
39
+
40
+ @handlers[event].each do |entry|
41
+ # Skip pattern matching for non-buffer events (like Job events)
42
+ next if context && entry[:pattern] && !match_pattern?(context, entry[:pattern])
43
+
44
+ # Pass context or kwargs depending on what's available
45
+ entry[:handler].call(context || kwargs)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def match_pattern?(context, pattern)
52
+ return true unless pattern
53
+
54
+ file_path = context.buffer.file_path
55
+
56
+ case pattern
57
+ when Regexp
58
+ file_path&.match?(pattern)
59
+ when String
60
+ File.fnmatch(pattern, file_path || "")
61
+ else
62
+ false
63
+ end
64
+ end
65
+ end
66
+ end