mui 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +2 -22
  3. data/.ruby-lsp.yml +19 -0
  4. data/CHANGELOG.md +56 -0
  5. data/README.md +17 -0
  6. data/docs/configuration.md +40 -0
  7. data/docs/getting-started.md +1 -1
  8. data/docs/keybindings.md +4 -0
  9. data/lib/mui/buffer.rb +1 -1
  10. data/lib/mui/buffer_word_cache.rb +2 -3
  11. data/lib/mui/command_completer.rb +4 -2
  12. data/lib/mui/command_line.rb +7 -2
  13. data/lib/mui/completion_renderer.rb +1 -1
  14. data/lib/mui/config.rb +3 -1
  15. data/lib/mui/editor.rb +3 -0
  16. data/lib/mui/file_completer.rb +10 -10
  17. data/lib/mui/insert_completion_renderer.rb +1 -1
  18. data/lib/mui/key_code.rb +5 -0
  19. data/lib/mui/key_handler/base.rb +2 -0
  20. data/lib/mui/key_handler/command_mode.rb +1 -1
  21. data/lib/mui/key_handler/insert_mode.rb +30 -0
  22. data/lib/mui/key_handler/normal_mode.rb +22 -2
  23. data/lib/mui/key_handler/visual_mode.rb +3 -1
  24. data/lib/mui/key_notation_parser.rb +13 -4
  25. data/lib/mui/mode_manager.rb +15 -27
  26. data/lib/mui/register.rb +36 -0
  27. data/lib/mui/screen.rb +5 -3
  28. data/lib/mui/search_completer.rb +1 -1
  29. data/lib/mui/search_input.rb +3 -3
  30. data/lib/mui/search_state.rb +11 -2
  31. data/lib/mui/status_line_renderer.rb +3 -5
  32. data/lib/mui/syntax/lexers/markdown_lexer.rb +1 -1
  33. data/lib/mui/terminal_adapter/curses.rb +40 -46
  34. data/lib/mui/unicode_width.rb +1 -1
  35. data/lib/mui/version.rb +1 -1
  36. data/lib/mui/window.rb +8 -0
  37. data/lib/mui/window_manager.rb +2 -5
  38. data/lib/mui/wrap_helper.rb +3 -3
  39. data/sig/lib/mui/autocmd.rbs +12 -0
  40. data/sig/lib/mui/buffer.rbs +52 -0
  41. data/sig/lib/mui/buffer_word_cache.rbs +21 -0
  42. data/sig/lib/mui/buffer_word_completer.rbs +15 -0
  43. data/sig/lib/mui/color_manager.rbs +20 -0
  44. data/sig/lib/mui/color_scheme.rbs +14 -0
  45. data/sig/lib/mui/command_completer.rbs +11 -0
  46. data/sig/lib/mui/command_context.rbs +24 -0
  47. data/sig/lib/mui/command_history.rbs +20 -0
  48. data/sig/lib/mui/command_line.rbs +24 -0
  49. data/sig/lib/mui/command_registry.rbs +14 -0
  50. data/sig/lib/mui/completion_renderer.rbs +14 -0
  51. data/sig/lib/mui/completion_state.rbs +20 -0
  52. data/sig/lib/mui/config.rbs +20 -0
  53. data/sig/lib/mui/editor.rbs +54 -0
  54. data/sig/lib/mui/error.rbs +18 -0
  55. data/sig/lib/mui/file_completer.rbs +13 -0
  56. data/sig/lib/mui/floating_window.rbs +30 -0
  57. data/sig/lib/mui/handler_result.rbs +38 -0
  58. data/sig/lib/mui/highlight.rbs +14 -0
  59. data/sig/lib/mui/highlighters/base.rbs +15 -0
  60. data/sig/lib/mui/highlighters/search_highlighter.rbs +11 -0
  61. data/sig/lib/mui/highlighters/selection_highlighter.rbs +13 -0
  62. data/sig/lib/mui/highlighters/syntax_highlighter.rbs +20 -0
  63. data/sig/lib/mui/input.rbs +10 -0
  64. data/sig/lib/mui/insert_completion_renderer.rbs +15 -0
  65. data/sig/lib/mui/insert_completion_state.rbs +23 -0
  66. data/sig/lib/mui/job.rbs +27 -0
  67. data/sig/lib/mui/job_manager.rbs +20 -0
  68. data/sig/lib/mui/key_code.rbs +29 -0
  69. data/sig/lib/mui/key_handler/base.rbs +35 -0
  70. data/sig/lib/mui/key_handler/command_mode.rbs +56 -0
  71. data/sig/lib/mui/key_handler/insert_mode.rbs +42 -0
  72. data/sig/lib/mui/key_handler/motions/motion_handler.rbs +19 -0
  73. data/sig/lib/mui/key_handler/normal_mode.rbs +55 -0
  74. data/sig/lib/mui/key_handler/operators/base_operator.rbs +29 -0
  75. data/sig/lib/mui/key_handler/operators/change_operator.rbs +22 -0
  76. data/sig/lib/mui/key_handler/operators/delete_operator.rbs +22 -0
  77. data/sig/lib/mui/key_handler/operators/paste_operator.rbs +19 -0
  78. data/sig/lib/mui/key_handler/operators/yank_operator.rbs +20 -0
  79. data/sig/lib/mui/key_handler/search_mode.rbs +25 -0
  80. data/sig/lib/mui/key_handler/visual_line_mode.rbs +11 -0
  81. data/sig/lib/mui/key_handler/visual_mode.rbs +43 -0
  82. data/sig/lib/mui/key_handler/window_command.rbs +20 -0
  83. data/sig/lib/mui/key_handler.rbs +4 -0
  84. data/sig/lib/mui/key_notation_parser.rbs +16 -0
  85. data/sig/lib/mui/key_sequence.rbs +18 -0
  86. data/sig/lib/mui/key_sequence_buffer.rbs +18 -0
  87. data/sig/lib/mui/key_sequence_handler.rbs +24 -0
  88. data/sig/lib/mui/key_sequence_matcher.rbs +14 -0
  89. data/sig/lib/mui/layout/calculator.rbs +10 -0
  90. data/sig/lib/mui/layout/leaf_node.rbs +16 -0
  91. data/sig/lib/mui/layout/node.rbs +24 -0
  92. data/sig/lib/mui/layout/split_node.rbs +29 -0
  93. data/sig/lib/mui/line_renderer.rbs +18 -0
  94. data/sig/lib/mui/mode.rbs +14 -0
  95. data/sig/lib/mui/mode_manager.rbs +33 -0
  96. data/sig/lib/mui/motion.rbs +24 -0
  97. data/sig/lib/mui/plugin.rbs +17 -0
  98. data/sig/lib/mui/plugin_manager.rbs +20 -0
  99. data/sig/lib/mui/register.rbs +21 -0
  100. data/sig/lib/mui/screen.rbs +21 -0
  101. data/sig/lib/mui/search_completer.rbs +11 -0
  102. data/sig/lib/mui/search_input.rbs +17 -0
  103. data/sig/lib/mui/search_state.rbs +22 -0
  104. data/sig/lib/mui/selection.rbs +21 -0
  105. data/sig/lib/mui/status_line_renderer.rbs +12 -0
  106. data/sig/lib/mui/syntax/language_detector.rbs +16 -0
  107. data/sig/lib/mui/syntax/lexer_base.rbs +16 -0
  108. data/sig/lib/mui/syntax/lexers/c_lexer.rbs +19 -0
  109. data/sig/lib/mui/syntax/lexers/css_lexer.rbs +19 -0
  110. data/sig/lib/mui/syntax/lexers/go_lexer.rbs +26 -0
  111. data/sig/lib/mui/syntax/lexers/html_lexer.rbs +19 -0
  112. data/sig/lib/mui/syntax/lexers/javascript_lexer.rbs +26 -0
  113. data/sig/lib/mui/syntax/lexers/markdown_lexer.rbs +24 -0
  114. data/sig/lib/mui/syntax/lexers/ruby_lexer.rbs +18 -0
  115. data/sig/lib/mui/syntax/lexers/rust_lexer.rbs +21 -0
  116. data/sig/lib/mui/syntax/lexers/typescript_lexer.rbs +26 -0
  117. data/sig/lib/mui/syntax/token.rbs +16 -0
  118. data/sig/lib/mui/syntax/token_cache.rbs +16 -0
  119. data/sig/lib/mui/tab_bar_renderer.rbs +23 -0
  120. data/sig/lib/mui/tab_manager.rbs +23 -0
  121. data/sig/lib/mui/tab_page.rbs +16 -0
  122. data/sig/lib/mui/terminal_adapter/base.rbs +31 -0
  123. data/sig/lib/mui/terminal_adapter/curses.rbs +31 -0
  124. data/sig/lib/mui/terminal_adapter.rbs +4 -0
  125. data/sig/lib/mui/themes/default.rbs +15 -0
  126. data/sig/lib/mui/undo_manager.rbs +20 -0
  127. data/sig/lib/mui/undoable_action.rbs +56 -0
  128. data/sig/lib/mui/unicode_width.rbs +12 -0
  129. data/sig/lib/mui/version.rbs +6 -0
  130. data/sig/lib/mui/window.rbs +49 -0
  131. data/sig/lib/mui/window_manager.rbs +37 -0
  132. data/sig/lib/mui/wrap_cache.rbs +14 -0
  133. data/sig/lib/mui/wrap_helper.rbs +11 -0
  134. data/sig/lib/mui.rbs +24 -0
  135. data/sig/typeprof.rbs +42 -0
  136. data/typeprof.conf.jsonc +6 -0
  137. metadata +116 -4
  138. data/sig/mui.rbs +0 -4
@@ -47,11 +47,11 @@ module Mui
47
47
  when Mode::VISUAL, Mode::VISUAL_LINE
48
48
  handle_visual_transition(result)
49
49
  when Mode::INSERT
50
- handle_insert_transition(result)
50
+ handle_insert_transition(result, result.mode)
51
51
  when Mode::SEARCH_FORWARD
52
- handle_search_forward_transition
52
+ handle_search_transition("/", result.mode)
53
53
  when Mode::SEARCH_BACKWARD
54
- handle_search_backward_transition
54
+ handle_search_transition("?", result.mode)
55
55
  else
56
56
  @mode = result.mode
57
57
  end
@@ -81,11 +81,7 @@ module Mui
81
81
  @last_visual_selection[:end_row],
82
82
  @last_visual_selection[:end_col]
83
83
  )
84
- @visual_handler = if line_mode
85
- KeyHandler::VisualLineMode.new(self, @buffer, @selection, @register, undo_manager: @undo_manager)
86
- else
87
- KeyHandler::VisualMode.new(self, @buffer, @selection, @register, undo_manager: @undo_manager)
88
- end
84
+ @visual_handler = create_visual_handler(line_mode:)
89
85
 
90
86
  # Move cursor to end of selection
91
87
  active_window.cursor_row = @last_visual_selection[:end_row]
@@ -110,10 +106,10 @@ module Mui
110
106
  KeyHandler::InsertMode.new(self, @buffer, undo_manager: @undo_manager, group_started:)
111
107
  end
112
108
 
113
- def handle_insert_transition(result)
109
+ def handle_insert_transition(result, mode)
114
110
  group_started = result.respond_to?(:group_started?) && result.group_started?
115
- @key_handlers[Mode::INSERT] = create_insert_handler(group_started:)
116
- @mode = Mode::INSERT
111
+ @key_handlers[mode] = create_insert_handler(group_started:)
112
+ @mode = mode
117
113
  end
118
114
 
119
115
  def handle_visual_transition(result)
@@ -128,6 +124,7 @@ module Mui
128
124
 
129
125
  def start_visual_mode(mode, line_mode)
130
126
  @mode = mode
127
+ @selection = Selection.new(active_window.cursor_row, active_window.cursor_col, line_mode:)
131
128
  @visual_handler = create_visual_handler(line_mode:)
132
129
  end
133
130
 
@@ -152,16 +149,14 @@ module Mui
152
149
  def toggle_visual_line_mode(new_mode)
153
150
  return unless @selection
154
151
 
155
- new_line_mode = new_mode == Mode::VISUAL_LINE
156
- @selection = Selection.new(@selection.start_row, @selection.start_col, line_mode: new_line_mode)
152
+ line_mode = new_mode == Mode::VISUAL_LINE
153
+ @selection = Selection.new(@selection.start_row, @selection.start_col, line_mode:)
157
154
  @selection.update_end(active_window.cursor_row, active_window.cursor_col)
158
- @visual_handler = create_visual_handler(line_mode: new_line_mode)
155
+ @visual_handler = create_visual_handler(line_mode:)
159
156
  @mode = new_mode
160
157
  end
161
158
 
162
159
  def create_visual_handler(line_mode:)
163
- @selection = Selection.new(active_window.cursor_row, active_window.cursor_col, line_mode:)
164
-
165
160
  if line_mode
166
161
  KeyHandler::VisualLineMode.new(self, @buffer, @selection, @register, undo_manager: @undo_manager)
167
162
  else
@@ -169,18 +164,11 @@ module Mui
169
164
  end
170
165
  end
171
166
 
172
- def handle_search_forward_transition
167
+ def handle_search_transition(prompt, mode)
173
168
  @search_input.clear
174
- @search_input.set_prompt("/")
175
- @key_handlers[Mode::SEARCH_FORWARD].start_search
176
- @mode = Mode::SEARCH_FORWARD
177
- end
178
-
179
- def handle_search_backward_transition
180
- @search_input.clear
181
- @search_input.set_prompt("?")
182
- @key_handlers[Mode::SEARCH_BACKWARD].start_search
183
- @mode = Mode::SEARCH_BACKWARD
169
+ @search_input.set_prompt(prompt)
170
+ @key_handlers[mode].start_search
171
+ @mode = mode
184
172
  end
185
173
  end
186
174
  end
data/lib/mui/register.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "clipboard"
4
+
3
5
  module Mui
4
6
  # Manages yank/delete registers for copy/paste operations
5
7
  # Supports Vim-compatible registers:
@@ -24,6 +26,7 @@ module Mui
24
26
 
25
27
  # Store text from yank operation
26
28
  # Saves to unnamed register and "0 (yank register)
29
+ # Also syncs to system clipboard if enabled
27
30
  def yank(text, linewise: false, name: nil)
28
31
  return if name == BLACK_HOLE_REGISTER
29
32
 
@@ -32,11 +35,13 @@ module Mui
32
35
  else
33
36
  @unnamed = { content: text, linewise: }
34
37
  @yank_register = { content: text, linewise: }
38
+ sync_to_clipboard(text, linewise:)
35
39
  end
36
40
  end
37
41
 
38
42
  # Store text from delete operation
39
43
  # Saves to unnamed register and shifts delete history ("1-"9)
44
+ # Also syncs to system clipboard if enabled
40
45
  def delete(text, linewise: false, name: nil)
41
46
  return if name == BLACK_HOLE_REGISTER
42
47
 
@@ -45,6 +50,7 @@ module Mui
45
50
  else
46
51
  @unnamed = { content: text, linewise: }
47
52
  shift_delete_history(text, linewise)
53
+ sync_to_clipboard(text, linewise:)
48
54
  end
49
55
  end
50
56
 
@@ -60,6 +66,7 @@ module Mui
60
66
  def get(name: nil)
61
67
  case name
62
68
  when nil, UNNAMED_REGISTER
69
+ sync_from_clipboard
63
70
  @unnamed[:content]
64
71
  when YANK_REGISTER
65
72
  @yank_register[:content]
@@ -76,6 +83,7 @@ module Mui
76
83
  def linewise?(name: nil)
77
84
  case name
78
85
  when nil, UNNAMED_REGISTER
86
+ sync_from_clipboard
79
87
  @unnamed[:linewise]
80
88
  when YANK_REGISTER
81
89
  @yank_register[:linewise]
@@ -106,5 +114,33 @@ module Mui
106
114
  @delete_history.unshift({ content: text, linewise: })
107
115
  @delete_history = @delete_history.first(9)
108
116
  end
117
+
118
+ def sync_to_clipboard(text, linewise:)
119
+ return unless clipboard_enabled?
120
+
121
+ clipboard_text = linewise ? "#{text}\n" : text
122
+ Clipboard.copy(clipboard_text)
123
+ rescue StandardError
124
+ # Silently ignore clipboard errors
125
+ end
126
+
127
+ def sync_from_clipboard
128
+ return unless clipboard_enabled?
129
+
130
+ content = Clipboard.paste
131
+ return if content.nil? || content.empty?
132
+ return if content == @unnamed[:content]
133
+
134
+ # Determine linewise based on trailing newline (Vim heuristic)
135
+ linewise = content.end_with?("\n")
136
+ @unnamed = { content: linewise ? content.chomp : content, linewise: }
137
+ rescue StandardError
138
+ # Silently ignore clipboard errors
139
+ end
140
+
141
+ def clipboard_enabled?
142
+ setting = Mui.config.get(:clipboard)
143
+ %i[unnamed unnamedplus].include?(setting)
144
+ end
109
145
  end
110
146
  end
data/lib/mui/screen.rb CHANGED
@@ -92,18 +92,20 @@ module Mui
92
92
  def truncate_to_width(text, max_width)
93
93
  return text if max_width <= 0
94
94
 
95
+ return text[0, max_width] if text.ascii_only?
96
+
95
97
  current_width = 0
96
- result = String.new
98
+ end_index = 0
97
99
 
98
100
  text.each_char do |char|
99
101
  char_w = UnicodeWidth.char_width(char)
100
102
  break if current_width + char_w > max_width
101
103
 
102
- result << char
103
104
  current_width += char_w
105
+ end_index += 1
104
106
  end
105
107
 
106
- result
108
+ text[0, end_index]
107
109
  end
108
110
  end
109
111
  end
@@ -35,7 +35,7 @@ module Mui
35
35
  buffer.line_count.times do |row|
36
36
  line = buffer.line(row)
37
37
  # Extract words (alphanumeric + underscore, minimum 2 characters)
38
- line.scan(/\b[a-zA-Z_][a-zA-Z0-9_]+\b/) do |word|
38
+ line.scan(/\b[a-zA-Z_][a-zA-Z0-9_]+\b/o) do |word|
39
39
  words.add(word)
40
40
  end
41
41
  end
@@ -5,12 +5,12 @@ module Mui
5
5
  attr_reader :buffer, :prompt
6
6
 
7
7
  def initialize(prompt = "/")
8
- @buffer = ""
8
+ @buffer = +""
9
9
  @prompt = prompt
10
10
  end
11
11
 
12
12
  def input(char)
13
- @buffer += char
13
+ @buffer << char
14
14
  end
15
15
 
16
16
  def backspace
@@ -18,7 +18,7 @@ module Mui
18
18
  end
19
19
 
20
20
  def clear
21
- @buffer = ""
21
+ @buffer = +""
22
22
  end
23
23
 
24
24
  def set_prompt(prompt)
@@ -53,6 +53,8 @@ module Mui
53
53
 
54
54
  def clear
55
55
  @pattern = nil
56
+ @last_pattern = nil
57
+ @regex = nil
56
58
  @pattern_version += 1
57
59
  @buffer_matches.clear
58
60
  end
@@ -94,6 +96,12 @@ module Mui
94
96
  @buffer_matches[buffer_id]
95
97
  end
96
98
 
99
+ def generate_regex
100
+ return @regex if @last_pattern == @pattern
101
+
102
+ @regex = Regexp.new(@pattern)
103
+ end
104
+
97
105
  def calculate_matches(buffer)
98
106
  empty_result = [[], {}]
99
107
  return empty_result if @pattern.nil? || @pattern.empty?
@@ -101,10 +109,11 @@ module Mui
101
109
  matches = []
102
110
  row_index = {}
103
111
  begin
104
- regex = Regexp.new(@pattern)
112
+ generate_regex
113
+
105
114
  buffer.line_count.times do |row|
106
115
  line = buffer.line(row)
107
- row_matches = scan_line_matches(line, row, regex)
116
+ row_matches = scan_line_matches(line, row, @regex)
108
117
  unless row_matches.empty?
109
118
  matches.concat(row_matches)
110
119
  row_index[row] = row_matches
@@ -22,7 +22,7 @@ module Mui
22
22
 
23
23
  def build_status_text
24
24
  status = " #{@buffer.name}"
25
- status += " [+]" if @buffer.modified
25
+ status << " [+]" if @buffer.modified
26
26
  status
27
27
  end
28
28
 
@@ -31,10 +31,8 @@ module Mui
31
31
  end
32
32
 
33
33
  def format_status_line(status, position)
34
- padding = @window.width - status.length - position.length
35
- padding = 0 if padding.negative?
36
- full_status = status + (" " * padding) + position
37
- full_status[0, @window.width]
34
+ target_length = @window.width - position.length
35
+ "#{status.ljust(target_length)}#{position}"[0, @window.width]
38
36
  end
39
37
  end
40
38
  end
@@ -92,7 +92,7 @@ module Mui
92
92
  # Process rest of line with inline patterns
93
93
  while pos < line.length
94
94
  # Skip whitespace
95
- if line[pos] =~ /\s/
95
+ if /\s/.match?(line[pos])
96
96
  pos += 1
97
97
  next
98
98
  end
@@ -54,18 +54,6 @@ module Mui
54
54
  result
55
55
  end
56
56
 
57
- private
58
-
59
- def color_code(color)
60
- return -1 if color.nil?
61
- return color if color.is_a?(Integer)
62
- return @color_resolver.resolve(color) if @color_resolver
63
-
64
- -1
65
- end
66
-
67
- public
68
-
69
57
  def close
70
58
  ::Curses.close_screen
71
59
  end
@@ -116,40 +104,6 @@ module Mui
116
104
  end
117
105
  end
118
106
 
119
- private
120
-
121
- def read_utf8_char(first_byte)
122
- bytes = [first_byte]
123
-
124
- # Determine expected byte count from first byte
125
- if (first_byte & 0xE0) == 0xC0
126
- # 2-byte sequence (110xxxxx)
127
- expected = 2
128
- elsif (first_byte & 0xF0) == 0xE0
129
- # 3-byte sequence (1110xxxx) - CJK characters
130
- expected = 3
131
- elsif (first_byte & 0xF8) == 0xF0
132
- # 4-byte sequence (11110xxx) - emoji, etc.
133
- expected = 4
134
- else
135
- # Invalid or continuation byte, return as-is
136
- return first_byte
137
- end
138
-
139
- # Read remaining bytes
140
- (expected - 1).times do
141
- next_byte = ::Curses.getch
142
- break unless next_byte.is_a?(Integer) && (next_byte & 0xC0) == 0x80
143
-
144
- bytes << next_byte
145
- end
146
-
147
- # Convert to UTF-8 string
148
- bytes.pack("C*").force_encoding(Encoding::UTF_8)
149
- end
150
-
151
- public
152
-
153
107
  def getch_nonblock
154
108
  ::Curses.stdscr.timeout = 0
155
109
 
@@ -183,6 +137,46 @@ module Mui
183
137
  ::Curses.stdscr.redraw
184
138
  ::Curses.refresh
185
139
  end
140
+
141
+ private
142
+
143
+ def color_code(color)
144
+ return -1 if color.nil?
145
+ return color if color.is_a?(Integer)
146
+ return @color_resolver.resolve(color) if @color_resolver
147
+
148
+ -1
149
+ end
150
+
151
+ def read_utf8_char(first_byte)
152
+ bytes = [first_byte]
153
+
154
+ # Determine expected byte count from first byte
155
+ if (first_byte & 0xE0) == 0xC0
156
+ # 2-byte sequence (110xxxxx)
157
+ expected = 2
158
+ elsif (first_byte & 0xF0) == 0xE0
159
+ # 3-byte sequence (1110xxxx) - CJK characters
160
+ expected = 3
161
+ elsif (first_byte & 0xF8) == 0xF0
162
+ # 4-byte sequence (11110xxx) - emoji, etc.
163
+ expected = 4
164
+ else
165
+ # Invalid or continuation byte, return as-is
166
+ return first_byte
167
+ end
168
+
169
+ # Read remaining bytes
170
+ (expected - 1).times do
171
+ next_byte = ::Curses.getch
172
+ break unless next_byte.is_a?(Integer) && (next_byte & 0xC0) == 0x80
173
+
174
+ bytes << next_byte
175
+ end
176
+
177
+ # Convert to UTF-8 string
178
+ bytes.pack("C*").force_encoding(Encoding::UTF_8)
179
+ end
186
180
  end
187
181
  end
188
182
  end
@@ -38,7 +38,7 @@ module Mui
38
38
  def width_to_col(str, col)
39
39
  return 0 if str.nil? || col <= 0
40
40
 
41
- str.chars.take(col).sum { |c| char_width(c) }
41
+ str[0...col].chars.sum { |c| char_width(c) }
42
42
  end
43
43
 
44
44
  # Returns the character index for a given display width position
data/lib/mui/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mui
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/mui/window.rb CHANGED
@@ -147,6 +147,14 @@ module Mui
147
147
  clamp_cursor_col
148
148
  end
149
149
 
150
+ def move_to_line_start
151
+ @cursor_col = 0
152
+ end
153
+
154
+ def move_to_line_end
155
+ @cursor_col = @buffer.line(@cursor_row).length
156
+ end
157
+
150
158
  def clamp_cursor_to_line(buffer)
151
159
  max_col = [buffer.line(@cursor_row).length - 1, 0].max
152
160
  @cursor_col = max_col if @cursor_col > max_col
@@ -195,21 +195,18 @@ module Mui
195
195
  end
196
196
 
197
197
  def find_window_in_direction(direction)
198
- return nil unless @active_window
199
-
200
198
  predicate = DIRECTION_PREDICATES[direction]
201
199
  return nil unless predicate
202
200
 
203
201
  current_x = @active_window.x + (@active_window.width / 2)
204
202
  current_y = @active_window.y + (@active_window.height / 2)
205
203
 
206
- candidates = windows.reject { |w| w == @active_window }
207
- .select { |w| predicate.call(w, @active_window) }
204
+ candidates = windows.select { |w| w != @active_window && predicate.call(w, @active_window) }
208
205
 
209
206
  candidates.min_by do |w|
210
207
  wx = w.x + (w.width / 2)
211
208
  wy = w.y + (w.height / 2)
212
- Math.sqrt(((wx - current_x)**2) + ((wy - current_y)**2))
209
+ ((wx - current_x)**2) + ((wy - current_y)**2)
213
210
  end
214
211
  end
215
212
 
@@ -53,7 +53,7 @@ module Mui
53
53
  def compute_wrap(line, width)
54
54
  result = []
55
55
  chars = line.chars
56
- current_text = String.new
56
+ current_text = +""
57
57
  current_width = 0
58
58
  start_col = 0
59
59
  col = 0
@@ -64,7 +64,7 @@ module Mui
64
64
  # Check if adding this character would exceed width
65
65
  if current_width + char_w > width && !current_text.empty?
66
66
  result << { text: current_text, start_col:, end_col: col }
67
- current_text = String.new
67
+ current_text = +""
68
68
  current_width = 0
69
69
  start_col = col
70
70
  end
@@ -77,7 +77,7 @@ module Mui
77
77
  # Add remaining text
78
78
  result << { text: current_text, start_col:, end_col: col } unless current_text.empty?
79
79
 
80
- result.empty? ? [{ text: String.new, start_col: 0, end_col: 0 }] : result
80
+ result.empty? ? [{ text: +"", start_col: 0, end_col: 0 }] : result
81
81
  end
82
82
  end
83
83
  end
@@ -0,0 +1,12 @@
1
+ # TypeProf 0.31.1
2
+
3
+ # lib/mui/autocmd.rb
4
+ module Mui
5
+ class Mui::Autocmd
6
+ Mui::Autocmd::EVENTS: Array[:BufEnter | :BufLeave | :BufWrite | :BufWritePost | :BufWritePre | :CursorMoved | :InsertCompletion | :InsertEnter | :InsertLeave | :JobCancelled | :JobCompleted | :JobFailed | :JobStarted | :ModeChanged | :TextChanged]
7
+ def initialize: -> void
8
+ def register: (untyped, ?pattern: nil) { (untyped) -> untyped } -> []
9
+ def trigger: (untyped, ?nil, **untyped) -> Array[Hash[:handler | :pattern, <Proc>?]]?
10
+ def match_pattern?: (untyped, nil) -> bool
11
+ end
12
+ end
@@ -0,0 +1,52 @@
1
+ # TypeProf 0.31.1
2
+
3
+ # lib/mui/buffer.rb
4
+ module Mui
5
+ class Mui::Buffer
6
+ def lines: -> (Array[String] | [String])
7
+ def change_count: -> Integer
8
+ def name: -> String
9
+ def name=: (untyped) -> String
10
+ def modified: -> bool
11
+ def modified=: (untyped) -> bool
12
+ def undo_manager: -> nil
13
+ def undo_manager=: (untyped) -> nil
14
+ def readonly: -> false
15
+ def readonly=: (untyped) -> false
16
+ def initialize: (?String) -> void
17
+ def readonly?: -> false
18
+ def custom_highlighters: (untyped) -> Array[untyped]
19
+ def add_custom_highlighter: (untyped, untyped) -> untyped
20
+ def remove_custom_highlighter: (untyped) -> nil
21
+ def custom_highlighter?: (untyped) -> bool
22
+ def content=: (untyped) -> false
23
+ def reload: -> false?
24
+ def load: (String) -> false
25
+ def save: (?String) -> false
26
+ def line_count: -> Integer
27
+ def line: (untyped) -> String
28
+ def insert_char: (untyped, untyped, untyped) -> Integer?
29
+ def delete_char: (untyped, untyped) -> Integer?
30
+ def insert_line: (untyped, ?nil) -> Integer?
31
+ def delete_line: (untyped) -> Integer?
32
+ def split_line: (untyped, untyped) -> Integer?
33
+ def join_lines: (untyped) -> Integer?
34
+ def delete_range: (untyped, untyped, untyped, untyped) -> Integer?
35
+ def replace_line: (untyped, untyped) -> Integer?
36
+ def insert_char_without_record: (untyped, untyped, untyped) -> Integer
37
+ def delete_char_without_record: (untyped, untyped) -> Integer?
38
+ def insert_line_without_record: (untyped, ?String?) -> Integer
39
+ def delete_line_without_record: (untyped) -> Integer
40
+ def split_line_without_record: (untyped, untyped) -> Integer?
41
+ def join_lines_without_record: (untyped) -> Integer?
42
+ def delete_range_without_record: (untyped, untyped, untyped, untyped) -> Integer
43
+ def replace_line_without_record: (untyped, untyped) -> Integer
44
+ def restore_range: (untyped, untyped, untyped) -> Integer?
45
+ def with_undo: (untyped) { () -> Integer? } -> Integer?
46
+ def valid_char_position?: (untyped, untyped) -> String
47
+ def empty_line: -> String
48
+ def capture_range: (untyped, untyped, untyped, untyped) -> ([String] | [])
49
+ def delete_within_line: (untyped, untyped, untyped) -> untyped
50
+ def delete_across_lines: (untyped, untyped, untyped, untyped) -> untyped
51
+ end
52
+ end
@@ -0,0 +1,21 @@
1
+ # TypeProf 0.31.1
2
+
3
+ # lib/mui/buffer_word_cache.rb
4
+ module Mui
5
+ class Mui::BufferWordCache
6
+ Mui::BufferWordCache::MIN_WORD_LENGTH: Integer
7
+ Mui::BufferWordCache::MAX_CANDIDATES: Integer
8
+ Mui::BufferWordCache::WORD_REGEX: Regexp
9
+ def initialize: (untyped) -> void
10
+ def complete: (untyped, untyped, untyped) -> (Array[untyped] | [])
11
+ def word_at_position: (untyped, untyped) -> nil
12
+ def mark_dirty: (untyped) -> Set[untyped]
13
+ def add_word: (untyped) -> Set[untyped]?
14
+ def prefix_at: (untyped, untyped) -> String
15
+ def build_cache: -> untyped
16
+ def process_dirty_rows: (untyped, untyped) -> Set[untyped]?
17
+ def extract_words_fast: (untyped) -> untyped
18
+ def extract_words_with_exclusion: (untyped, untyped, untyped, untyped) -> untyped
19
+ def word_char?: (untyped) -> false
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # TypeProf 0.31.1
2
+
3
+ # lib/mui/buffer_word_completer.rb
4
+ module Mui
5
+ class Mui::BufferWordCompleter
6
+ Mui::BufferWordCompleter::MIN_WORD_LENGTH: Integer
7
+ Mui::BufferWordCompleter::MAX_CANDIDATES: Integer
8
+ def initialize: (untyped) -> void
9
+ def complete: (untyped, untyped, untyped) -> (Array[String] | [])
10
+ def prefix_at: (untyped, untyped) -> String
11
+ def extract_matching_words: (untyped, untyped, untyped, untyped, untyped, untyped, Set[String]) -> Set[String]?
12
+ def check_and_add_word: (String, Integer, untyped, untyped, untyped, untyped, untyped, untyped, Set[String]) -> Set[String]?
13
+ def word_char?: (untyped) -> untyped
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ # TypeProf 0.31.1
2
+
3
+ # lib/mui/color_manager.rb
4
+ module Mui
5
+ class Mui::ColorManager
6
+ Mui::ColorManager::COLOR_MAP: Hash[:black | :blue | :cyan | :green | :magenta | :red | :white | :yellow, Integer]
7
+ Mui::ColorManager::EXTENDED_COLOR_MAP: Hash[:black | :blue | :cyan | :darkgray | :dracula_bg | :dracula_comment | :dracula_cyan | :dracula_fg | :dracula_green | :dracula_orange | :dracula_pink | :dracula_purple | :dracula_red | :dracula_selection | :dracula_yellow | :green | :gruvbox_aqua | :gruvbox_bg | :gruvbox_blue | :gruvbox_fg | :gruvbox_gray | :gruvbox_green | :gruvbox_orange | :gruvbox_purple | :gruvbox_red | :gruvbox_yellow | :magenta | :monokai_bg | :monokai_comment | :monokai_cyan | :monokai_fg | :monokai_green | :monokai_orange | :monokai_pink | :monokai_purple | :monokai_yellow | :mui_bg | :mui_comment | :mui_constant | :mui_error | :mui_fg | :mui_function | :mui_identifier | :mui_info | :mui_line_number | :mui_preproc | :mui_search | :mui_special | :mui_statement | :mui_status_bg | :mui_tab_active | :mui_tab_bg | :mui_type | :mui_visual | :nord_aurora_green | :nord_aurora_orange | :nord_aurora_purple | :nord_aurora_red | :nord_aurora_yellow | :nord_frost0 | :nord_frost1 | :nord_frost2 | :nord_frost3 | :nord_polar0 | :nord_polar1 | :nord_polar2 | :nord_polar3 | :nord_snow0 | :nord_snow1 | :nord_snow2 | :red | :solarized_base0 | :solarized_base00 | :solarized_base01 | :solarized_base02 | :solarized_base03 | :solarized_base1 | :solarized_base2 | :solarized_base3 | :solarized_blue | :solarized_cyan | :solarized_green | :solarized_magenta | :solarized_orange | :solarized_red | :solarized_violet | :solarized_yellow | :tokyo_bg | :tokyo_blue | :tokyo_comment | :tokyo_cyan | :tokyo_fg | :tokyo_green | :tokyo_orange | :tokyo_purple | :tokyo_red | :tokyo_yellow | :white | :yellow, Integer]
8
+ Mui::ColorManager::FALLBACK_MAP: Hash[:darkgray | :dracula_bg | :dracula_comment | :dracula_cyan | :dracula_fg | :dracula_green | :dracula_orange | :dracula_pink | :dracula_purple | :dracula_red | :dracula_selection | :dracula_yellow | :gruvbox_aqua | :gruvbox_bg | :gruvbox_blue | :gruvbox_fg | :gruvbox_gray | :gruvbox_green | :gruvbox_orange | :gruvbox_purple | :gruvbox_red | :gruvbox_yellow | :monokai_bg | :monokai_comment | :monokai_cyan | :monokai_fg | :monokai_green | :monokai_orange | :monokai_pink | :monokai_purple | :monokai_yellow | :mui_bg | :mui_comment | :mui_constant | :mui_error | :mui_fg | :mui_function | :mui_identifier | :mui_info | :mui_line_number | :mui_preproc | :mui_search | :mui_special | :mui_statement | :mui_status_bg | :mui_tab_active | :mui_tab_bg | :mui_type | :mui_visual | :nord_aurora_green | :nord_aurora_orange | :nord_aurora_purple | :nord_aurora_red | :nord_aurora_yellow | :nord_frost0 | :nord_frost1 | :nord_frost2 | :nord_frost3 | :nord_polar0 | :nord_polar1 | :nord_polar2 | :nord_polar3 | :nord_snow0 | :nord_snow1 | :nord_snow2 | :solarized_base0 | :solarized_base00 | :solarized_base01 | :solarized_base02 | :solarized_base03 | :solarized_base1 | :solarized_base2 | :solarized_base3 | :solarized_blue | :solarized_cyan | :solarized_green | :solarized_magenta | :solarized_orange | :solarized_red | :solarized_violet | :solarized_yellow | :tokyo_bg | :tokyo_blue | :tokyo_comment | :tokyo_cyan | :tokyo_fg | :tokyo_green | :tokyo_orange | :tokyo_purple | :tokyo_red | :tokyo_yellow, :black | :blue | :cyan | :green | :magenta | :red | :white | :yellow]
9
+ def pairs: -> Hash[[untyped, untyped], Integer]
10
+ def supports_256_colors: -> bool
11
+ def initialize: (?adapter: nil) -> void
12
+ def register_pair: (untyped, untyped) -> Integer
13
+ def get_pair_index: (untyped, untyped) -> Integer
14
+ def color_code: (untyped) -> Integer
15
+ def configure_color_capability: -> bool
16
+ def resolve_with_fallback: (untyped) -> (:black | :blue | :cyan | :green | :magenta | :red | :white | :yellow)
17
+ def touch_pair: ([untyped, untyped]) -> []
18
+ def evict_oldest_pair: -> Integer?
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ # TypeProf 0.31.1
2
+
3
+ # lib/mui/color_scheme.rb
4
+ module Mui
5
+ class Mui::ColorScheme
6
+ Mui::ColorScheme::ELEMENTS: Array[:completion_popup | :completion_popup_selected | :diff_add | :diff_delete | :diff_header | :diff_hunk | :line_number | :message_error | :message_info | :normal | :search_highlight | :status_line | :status_line_mode | :syntax_comment | :syntax_constant | :syntax_function_definition | :syntax_global_variable | :syntax_identifier | :syntax_instance_variable | :syntax_keyword | :syntax_method_call | :syntax_number | :syntax_operator | :syntax_preprocessor | :syntax_string | :syntax_symbol | :syntax_type | :tab_bar | :tab_bar_active | :visual_selection]
7
+ def name: -> untyped
8
+ def colors: -> Hash[untyped, Hash[:bg | :bold | :fg | :underline, false?]]
9
+ def initialize: (untyped) -> void
10
+ def define: (untyped, fg: untyped, ?bg: nil, ?bold: false, ?underline: false) -> Hash[:bg | :bold | :fg | :underline, false?]
11
+ def []: (untyped) -> (Hash[:bg | :bold | :fg | :underline, (:white | false)?] | Hash[:bg | :bold | :fg | :underline, false?])
12
+ def default_color: -> Hash[:bg | :bold | :fg | :underline, (:white | false)?]
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # TypeProf 0.31.1
2
+
3
+ # lib/mui/command_completer.rb
4
+ module Mui
5
+ class Mui::CommandCompleter
6
+ Mui::CommandCompleter::COMMANDS: Array[String]
7
+ def complete: (untyped) -> Array[String]
8
+ def all_commands: -> Array[String]
9
+ def plugin_command_names: -> untyped
10
+ end
11
+ end