Module: Ace::Search::Atoms::PatternAnalyzer

Defined in:
lib/ace/search/atoms/pattern_analyzer.rb

Overview

PatternAnalyzer provides intelligent analysis of search patterns for DWIM mode selection This is an atom - pure function for pattern analysis

Constant Summary collapse

FILE_GLOB_PATTERNS =

File glob indicators

[
  /^\*+\./,                    # *.extension
  /\*\*\/\*/,                  # **/* recursive patterns
  /^\w+\/\*+/,                 # dir/* patterns
  /\.\w+$/,                    # .extension endings
  /\/\*+$/,                    # ending with /*
  /^\w+\*+\w*$/                # word* or *word patterns for filenames
].freeze
CONTENT_REGEX_PATTERNS =

Content regex indicators

[
  /[|+?{}()\[\]\\]/,           # Regex metacharacters (removed * from here)
  /def\s+\w+/,                 # Method definitions
  /class\s+\w+/,               # Class definitions
  /function\s+\w+/,            # Function definitions
  /import\s+/,                 # Import statements
  /require\s+/,                # Require statements
  /\b(TODO|FIXME|BUG|HACK)\b/, # Code annotations
  /^\^/,                       # Line start anchor
  /\$$/,                       # Line end anchor
  /\\[bBdDsSwW]/               # Character class escapes
].freeze
LITERAL_INDICATORS =

Literal text indicators

[
  /^\w+$/,                     # Single word
  /^[\w\s]+$/,                 # Words with spaces
  /^"[^"]*"$/,                 # Quoted strings
  /^'[^']*'$/                  # Single quoted strings
].freeze

Class Method Summary collapse

Class Method Details

.analyze_pattern(pattern) ⇒ Hash

Analyze a pattern and determine its most likely type

Parameters:

  • pattern (String)

    Pattern to analyze

Returns:

  • (Hash)

    Analysis result with type and confidence



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/ace/search/atoms/pattern_analyzer.rb', line 46

def analyze_pattern(pattern)
  return {type: :invalid, confidence: 0.0, reason: "Pattern is nil"} if pattern.nil?
  return {type: :invalid, confidence: 0.0, reason: "Pattern is empty"} if pattern.empty?

  clean_pattern = pattern.strip

  # Check for file glob patterns
  if file_glob_pattern?(clean_pattern)
    return {
      type: :file_glob,
      confidence: calculate_file_glob_confidence(clean_pattern),
      reason: "Contains file glob patterns",
      suggested_tool: "fd"
    }
  end

  # Check for content regex patterns
  if content_regex_pattern?(clean_pattern)
    return {
      type: :content_regex,
      confidence: calculate_content_regex_confidence(clean_pattern),
      reason: "Contains regex metacharacters or code patterns",
      suggested_tool: "rg"
    }
  end

  # Check for literal patterns
  if literal_pattern?(clean_pattern)
    return {
      type: :literal,
      confidence: calculate_literal_confidence(clean_pattern),
      reason: "Simple literal text pattern",
      suggested_tool: "rg"
    }
  end

  # Default to hybrid if unclear
  {
    type: :hybrid,
    confidence: 0.5,
    reason: "Pattern could match files or content",
    suggested_tool: "both"
  }
end

.calculate_content_regex_confidence(pattern) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
# File 'lib/ace/search/atoms/pattern_analyzer.rb', line 152

def calculate_content_regex_confidence(pattern)
  confidence = 0.5

  metachar_count = pattern.scan(/[|+?{}()\[\]\\]/).length
  confidence += metachar_count * 0.1

  confidence += 0.2 if pattern.match?(/\b(def|class|function|import|require)\s+/)
  confidence += 0.1 if pattern.match?(/^\^|\$$/)

  [confidence, 1.0].min
end

.calculate_file_glob_confidence(pattern) ⇒ Object



142
143
144
145
146
147
148
149
150
# File 'lib/ace/search/atoms/pattern_analyzer.rb', line 142

def calculate_file_glob_confidence(pattern)
  confidence = 0.5

  confidence += 0.3 if pattern.include?("*")
  confidence += 0.2 if pattern.match?(/\.\w+$/)
  confidence += 0.1 if pattern.include?("/")

  [confidence, 1.0].min
end

.calculate_literal_confidence(pattern) ⇒ Object



164
165
166
167
168
169
170
171
172
# File 'lib/ace/search/atoms/pattern_analyzer.rb', line 164

def calculate_literal_confidence(pattern)
  confidence = 0.6

  confidence += 0.2 if pattern.match?(/^\w+$/)
  confidence += 0.1 if pattern.length < 20
  confidence += 0.3 if pattern.match?(/^["'][^"']*["']$/)

  [confidence, 1.0].min
end

.content_regex_pattern?(pattern) ⇒ Boolean

Check if pattern looks like content regex

Returns:

  • (Boolean)


97
98
99
# File 'lib/ace/search/atoms/pattern_analyzer.rb', line 97

def content_regex_pattern?(pattern)
  CONTENT_REGEX_PATTERNS.any? { |regex| pattern.match?(regex) }
end

.extract_extensions(pattern) ⇒ Object

Extract file extensions from a pattern if present



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ace/search/atoms/pattern_analyzer.rb', line 126

def extract_extensions(pattern)
  extensions = []

  # Match patterns like *.rb, **/*.js, etc.
  extension_matches = pattern.scan(/\*+\.(\w+)/)
  extensions.concat(extension_matches.flatten)

  # Match explicit .ext at the end
  if pattern.match?(/\.(\w+)$/)
    extension_match = pattern.match(/\.(\w+)$/)
    extensions << extension_match[1]
  end

  extensions.uniq
end

.file_glob_pattern?(pattern) ⇒ Boolean

Check if pattern looks like a file glob

Returns:

  • (Boolean)


92
93
94
# File 'lib/ace/search/atoms/pattern_analyzer.rb', line 92

def file_glob_pattern?(pattern)
  FILE_GLOB_PATTERNS.any? { |regex| pattern.match?(regex) }
end

.literal_pattern?(pattern) ⇒ Boolean

Check if pattern looks like literal text

Returns:

  • (Boolean)


102
103
104
# File 'lib/ace/search/atoms/pattern_analyzer.rb', line 102

def literal_pattern?(pattern)
  LITERAL_INDICATORS.any? { |regex| pattern.match?(regex) }
end

.suggest_search_mode(pattern, flags = {}) ⇒ Object

Suggest search mode based on pattern analysis



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/ace/search/atoms/pattern_analyzer.rb', line 107

def suggest_search_mode(pattern, flags = {})
  return :files if flags[:files_only] || flags[:name_only]
  return :content if flags[:content_only]

  analysis = analyze_pattern(pattern)

  case analysis[:type]
  when :file_glob
    :files
  when :content_regex, :literal
    :content
  when :hybrid
    :both
  else
    :content
  end
end