mui 0.3.0 → 0.4.1

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.
@@ -62,49 +62,62 @@ module Mui
62
62
  end
63
63
 
64
64
  # Get matches for a specific row in a specific buffer
65
+ # O(1) lookup using row_index
65
66
  def matches_for_row(row, buffer: nil)
66
67
  return [] if buffer.nil?
67
68
 
68
- matches = get_or_calculate_matches(buffer)
69
- matches.select { |m| m[:row] == row }
69
+ cache = get_or_calculate_cache(buffer)
70
+ cache[:row_index][row] || []
70
71
  end
71
72
 
72
73
  private
73
74
 
74
75
  def get_or_calculate_matches(buffer)
76
+ get_or_calculate_cache(buffer)[:matches]
77
+ end
78
+
79
+ def get_or_calculate_cache(buffer)
75
80
  buffer_id = buffer.object_id
76
81
  cached = @buffer_matches[buffer_id]
77
82
 
78
- # Return cached matches if valid (same pattern version and buffer hasn't changed)
79
- return cached[:matches] if cached && cached[:version] == @pattern_version && cached[:change_count] == buffer.change_count
83
+ # Return cached data if valid (same pattern version and buffer hasn't changed)
84
+ return cached if cached && cached[:version] == @pattern_version && cached[:change_count] == buffer.change_count
80
85
 
81
86
  # Calculate and cache matches for this buffer
82
- matches = calculate_matches(buffer)
87
+ matches, row_index = calculate_matches(buffer)
83
88
  @buffer_matches[buffer_id] = {
84
89
  version: @pattern_version,
85
90
  change_count: buffer.change_count,
86
- matches:
91
+ matches:,
92
+ row_index:
87
93
  }
88
- matches
94
+ @buffer_matches[buffer_id]
89
95
  end
90
96
 
91
97
  def calculate_matches(buffer)
92
- return [] if @pattern.nil? || @pattern.empty?
98
+ empty_result = [[], {}]
99
+ return empty_result if @pattern.nil? || @pattern.empty?
93
100
 
94
101
  matches = []
102
+ row_index = {}
95
103
  begin
96
104
  regex = Regexp.new(@pattern)
97
105
  buffer.line_count.times do |row|
98
106
  line = buffer.line(row)
99
- scan_line_matches(matches, line, row, regex)
107
+ row_matches = scan_line_matches(line, row, regex)
108
+ unless row_matches.empty?
109
+ matches.concat(row_matches)
110
+ row_index[row] = row_matches
111
+ end
100
112
  end
101
113
  rescue RegexpError
102
114
  # Invalid regex pattern - no matches
103
115
  end
104
- matches
116
+ [matches, row_index]
105
117
  end
106
118
 
107
- def scan_line_matches(matches, line, row, regex)
119
+ def scan_line_matches(line, row, regex)
120
+ matches = []
108
121
  offset = 0
109
122
  while (match_data = line.match(regex, offset))
110
123
  col = match_data.begin(0)
@@ -116,6 +129,7 @@ module Mui
116
129
  offset += 1 if match_data[0].empty?
117
130
  break if offset >= line.length
118
131
  end
132
+ matches
119
133
  end
120
134
  end
121
135
  end
@@ -30,6 +30,8 @@ module Mui
30
30
  [:type, /\G\b(?:char|double|float|int|long|short|signed|unsigned|void|_Bool|_Complex|_Imaginary)\b/],
31
31
  # Other keywords (if, for, return, const, static, etc.)
32
32
  [:keyword, /\G\b(?:auto|break|case|const|continue|default|do|else|enum|extern|for|goto|if|register|return|sizeof|static|struct|switch|typedef|union|volatile|while|inline|restrict|_Alignas|_Alignof|_Atomic|_Generic|_Noreturn|_Static_assert|_Thread_local)\b/],
33
+ # Function call/definition (identifier followed by parenthesis)
34
+ [:function_definition, /\G\b[a-zA-Z_][a-zA-Z0-9_]*(?=\s*\()/],
33
35
  # Identifiers
34
36
  [:identifier, /\G\b[a-zA-Z_][a-zA-Z0-9_]*\b/],
35
37
  # Operators
@@ -51,6 +51,8 @@ module Mui
51
51
  [:type, /\G\b(?:bool|byte|complex64|complex128|error|float32|float64|int|int8|int16|int32|int64|rune|string|uint|uint8|uint16|uint32|uint64|uintptr|any|comparable)\b/],
52
52
  # Keywords
53
53
  [:keyword, /\G\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/],
54
+ # Function definition names (func の後)
55
+ [:function_definition, /\G(?<=func )[a-z_][a-zA-Z0-9_]*/],
54
56
  # Exported identifiers (start with uppercase)
55
57
  [:constant, /\G\b[A-Z][a-zA-Z0-9_]*\b/],
56
58
  # Regular identifiers
@@ -61,6 +61,28 @@ module Mui
61
61
  TEMPLATE_LITERAL_START = /\A`/
62
62
  TEMPLATE_LITERAL_END = /`/
63
63
 
64
+ # Override tokenize to post-process function definitions
65
+ def tokenize(line, state = nil)
66
+ tokens, new_state = super
67
+
68
+ # Convert identifiers after 'function' keyword to function_definition
69
+ tokens.each_with_index do |token, i|
70
+ next unless i.positive? &&
71
+ tokens[i - 1].type == :keyword &&
72
+ tokens[i - 1].text == "function" &&
73
+ token.type == :identifier
74
+
75
+ tokens[i] = Token.new(
76
+ type: :function_definition,
77
+ start_col: token.start_col,
78
+ end_col: token.end_col,
79
+ text: token.text
80
+ )
81
+ end
82
+
83
+ [tokens, new_state]
84
+ end
85
+
64
86
  protected
65
87
 
66
88
  def compiled_patterns
@@ -47,6 +47,9 @@ module Mui
47
47
  [:global_variable, /\G\$[a-zA-Z_][a-zA-Z0-9_]*/],
48
48
  # Method calls (.to_i, .each, .map!, .empty?, etc.)
49
49
  [:method_call, /\G\.[a-z_][a-zA-Z0-9_]*[?!]?/],
50
+ # Function/method definition names (def の後)
51
+ # Note: def self.method_name is handled by method_call pattern (same color)
52
+ [:function_definition, /\G(?<=def )[a-z_][a-zA-Z0-9_]*[?!=]?/],
50
53
  # Identifiers (including method names with ? or !)
51
54
  [:identifier, /\G\b[a-z_][a-zA-Z0-9_]*[?!]?/],
52
55
  # Operators
@@ -59,6 +59,8 @@ module Mui
59
59
  [:type, /\G\b(?:bool|char|str|i8|i16|i32|i64|i128|isize|u8|u16|u32|u64|u128|usize|f32|f64)\b/],
60
60
  # Keywords
61
61
  [:keyword, /\G\b(?:as|async|await|break|const|continue|crate|dyn|else|enum|extern|false|fn|for|if|impl|in|let|loop|match|mod|move|mut|pub|ref|return|self|Self|static|struct|super|trait|true|type|unsafe|use|where|while)\b/],
62
+ # Function definition names (fn の後)
63
+ [:function_definition, /\G(?<=fn )[a-z_][a-zA-Z0-9_]*/],
62
64
  # Type names (start with uppercase)
63
65
  [:constant, /\G\b[A-Z][a-zA-Z0-9_]*\b/],
64
66
  # Regular identifiers
@@ -67,6 +67,28 @@ module Mui
67
67
  TEMPLATE_LITERAL_START = /\A`/
68
68
  TEMPLATE_LITERAL_END = /`/
69
69
 
70
+ # Override tokenize to post-process function definitions
71
+ def tokenize(line, state = nil)
72
+ tokens, new_state = super
73
+
74
+ # Convert identifiers after 'function' keyword to function_definition
75
+ tokens.each_with_index do |token, i|
76
+ next unless i.positive? &&
77
+ tokens[i - 1].type == :keyword &&
78
+ tokens[i - 1].text == "function" &&
79
+ token.type == :identifier
80
+
81
+ tokens[i] = Token.new(
82
+ type: :function_definition,
83
+ start_col: token.start_col,
84
+ end_col: token.end_col,
85
+ text: token.text
86
+ )
87
+ end
88
+
89
+ [tokens, new_state]
90
+ end
91
+
70
92
  protected
71
93
 
72
94
  def compiled_patterns
@@ -87,6 +87,27 @@ module Mui
87
87
  def resume
88
88
  raise MethodNotOverriddenError, __method__
89
89
  end
90
+
91
+ # Check if terminal supports colors
92
+ def has_colors?
93
+ raise MethodNotOverriddenError, __method__
94
+ end
95
+
96
+ # Get available color count (8, 256, etc.)
97
+ def colors
98
+ raise MethodNotOverriddenError, __method__
99
+ end
100
+
101
+ # Get available color pair count
102
+ def color_pairs
103
+ raise MethodNotOverriddenError, __method__
104
+ end
105
+
106
+ # Force a complete redraw of the screen
107
+ # This is needed when multibyte characters may have been corrupted
108
+ def touchwin
109
+ # Default implementation does nothing (for mock adapters)
110
+ end
90
111
  end
91
112
  end
92
113
  end
@@ -17,8 +17,25 @@ module Mui
17
17
  end
18
18
 
19
19
  def init_colors
20
+ return unless ::Curses.has_colors?
21
+
20
22
  ::Curses.start_color
21
23
  ::Curses.use_default_colors
24
+ @has_colors = true
25
+ @available_colors = ::Curses.respond_to?(:colors) ? ::Curses.colors : 8
26
+ @max_pairs = ::Curses.respond_to?(:color_pairs) ? ::Curses.color_pairs : 64
27
+ end
28
+
29
+ def has_colors?
30
+ @has_colors || false
31
+ end
32
+
33
+ def colors
34
+ @available_colors || 0
35
+ end
36
+
37
+ def color_pairs
38
+ @max_pairs || 0
22
39
  end
23
40
 
24
41
  def init_color_pair(pair_index, fg, bg)
@@ -159,6 +176,13 @@ module Mui
159
176
  ::Curses.refresh
160
177
  ::Curses.curs_set(1)
161
178
  end
179
+
180
+ # Force a complete redraw of the screen
181
+ # This is needed when multibyte characters may have been corrupted
182
+ def touchwin
183
+ ::Curses.stdscr.redraw
184
+ ::Curses.refresh
185
+ end
162
186
  end
163
187
  end
164
188
  end