Class: Clacky::GitignoreParser

Inherits:
Object
  • Object
show all
Defined in:
lib/clacky/utils/gitignore_parser.rb

Overview

Parser for .gitignore files to determine which files should be ignored

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(gitignore_path = nil) ⇒ GitignoreParser

Returns a new instance of GitignoreParser.



8
9
10
11
12
13
14
15
# File 'lib/clacky/utils/gitignore_parser.rb', line 8

def initialize(gitignore_path = nil)
  @patterns = []
  @negation_patterns = []
  
  if gitignore_path && File.exist?(gitignore_path)
    parse_gitignore(gitignore_path)
  end
end

Instance Attribute Details

#patternsObject (readonly)

Returns the value of attribute patterns.



6
7
8
# File 'lib/clacky/utils/gitignore_parser.rb', line 6

def patterns
  @patterns
end

Instance Method Details

#ignored?(path) ⇒ Boolean

Check if a file path should be ignored

Returns:

  • (Boolean)


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/clacky/utils/gitignore_parser.rb', line 18

def ignored?(path)
  relative_path = path.start_with?('./') ? path[2..] : path
  
  # Check negation patterns first (! prefix in .gitignore)
  @negation_patterns.each do |pattern|
    return false if match_pattern?(relative_path, pattern)
  end
  
  # Then check ignore patterns
  @patterns.each do |pattern|
    return true if match_pattern?(relative_path, pattern)
  end
  
  false
end

#match_pattern?(path, pattern_info) ⇒ Boolean

Returns:

  • (Boolean)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/clacky/utils/gitignore_parser.rb', line 71

def match_pattern?(path, pattern_info)
  pattern = pattern_info[:pattern]
  is_absolute = pattern_info[:is_absolute]
  
  # For absolute patterns (starting with /), remove the leading slash
  # These patterns match from the root of the repository
  if is_absolute
    pattern = pattern[1..]
    # Absolute patterns match exactly from the start of the path
    return true if path == pattern
    return true if path.start_with?("#{pattern}/")
  end
  
  # Handle directory patterns
  if pattern_info[:is_directory]
    # Directory patterns should match the directory and all its contents
    return true if path == pattern
    return true if path.start_with?("#{pattern}/")
    # Also check if any path component matches the directory pattern
    return true if path.split('/').include?(pattern)
  end
  
  # Handle different wildcard patterns
  if pattern_info[:has_double_star]
    # Convert ** to match any number of directories
    regex_pattern = Regexp.escape(pattern)
      .gsub('\*\*/', '(.*/)?')  # **/ matches zero or more directories
      .gsub('\*\*', '.*')        # ** at end matches anything
      .gsub('\*', '[^/]*')       # * matches anything except /
      .gsub('\?', '[^/]')        # ? matches single character except /
    
    regex = Regexp.new("^#{regex_pattern}$")
    return true if path.match?(regex)
    return true if path.split('/').any? { |part| part.match?(regex) }
  elsif pattern_info[:has_wildcard]
    # Convert glob pattern to regex
    regex_pattern = Regexp.escape(pattern)
      .gsub('\*', '[^/]*')
      .gsub('\?', '[^/]')
    
    regex = Regexp.new("^#{regex_pattern}$")
    return true if path.match?(regex)
    return true if File.basename(path).match?(regex)
  else
    # Exact match - pattern without wildcards
    # Match as basename or as path prefix
    return true if path == pattern
    return true if path.start_with?("#{pattern}/")
    return true if File.basename(path) == pattern
    # Also check if pattern matches any path component
    return true if path.split('/').include?(pattern)
  end
  
  false
end

#normalize_pattern(pattern) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/clacky/utils/gitignore_parser.rb', line 52

def normalize_pattern(pattern)
  pattern = pattern.strip
  
  # Remove trailing whitespace
  pattern = pattern.rstrip
  
  # Store original for directory detection
  is_directory = pattern.end_with?('/')
  pattern = pattern.chomp('/')
  
  {
    pattern: pattern,
    is_directory: is_directory,
    is_absolute: pattern.start_with?('/'),
    has_wildcard: pattern.include?('*') || pattern.include?('?'),
    has_double_star: pattern.include?('**')
  }
end

#parse_gitignore(path) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/clacky/utils/gitignore_parser.rb', line 35

def parse_gitignore(path)
  File.readlines(path, chomp: true).each do |line|
    # Skip comments and empty lines
    next if line.strip.empty? || line.start_with?('#')
    
    # Handle negation patterns (lines starting with !)
    if line.start_with?('!')
      @negation_patterns << normalize_pattern(line[1..])
    else
      @patterns << normalize_pattern(line)
    end
  end
rescue StandardError => e
  # If we can't parse .gitignore, just continue with empty patterns
  warn "Warning: Failed to parse .gitignore: #{e.message}"
end