fast_ignore 0.12.1 → 0.15.2

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.
@@ -2,12 +2,12 @@
2
2
 
3
3
  class FastIgnore
4
4
  module Backports
5
- ruby_major, ruby_minor = RUBY_VERSION.split('.', 2)
5
+ ruby_major, ruby_minor = ::RUBY_VERSION.split('.', 2)
6
6
  unless ruby_major.to_i > 2 || ruby_major.to_i == 2 && ruby_minor.to_i > 5
7
7
  module DirEachChild
8
8
  refine ::Dir.singleton_class do
9
9
  def children(path)
10
- Dir.entries(path) - ['.', '..']
10
+ ::Dir.entries(path) - ['.', '..']
11
11
  end
12
12
  end
13
13
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class FileRoot
5
+ # :nocov:
6
+ using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
7
+ # :nocov:
8
+
9
+ def self.build(file_path, project_root)
10
+ file_root = "#{::File.dirname(file_path)}/".delete_prefix(project_root)
11
+
12
+ new(file_root) unless file_root.empty?
13
+ end
14
+
15
+ def initialize(file_root)
16
+ @file_root = file_root
17
+ end
18
+
19
+ def shebang_path_pattern
20
+ @shebang_path_pattern ||= /\A#{escaped}./
21
+ end
22
+
23
+ def escaped
24
+ @escaped ||= ::Regexp.escape(@file_root)
25
+ end
26
+
27
+ def escaped_segments
28
+ @escaped_segments ||= escaped.split('/')
29
+ end
30
+
31
+ def escaped_segments_length
32
+ @escaped_segments_length ||= escaped_segments.length
33
+ end
34
+
35
+ def escaped_segments_joined
36
+ @escaped_segments_joined ||= escaped_segments.join('(?:/') + '(?:/'
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class GitignoreIncludeRuleBuilder < GitignoreRuleBuilder
5
+ # :nocov:
6
+ using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
7
+ # :nocov:
8
+
9
+ def initialize(rule, file_path, expand_path_from = nil)
10
+ super(rule, file_path)
11
+
12
+ @parent_segments = []
13
+ @negation = true
14
+ @expand_path_from = expand_path_from
15
+ end
16
+
17
+ def expand_rule_path
18
+ anchored! unless @s.match?(/\*/)
19
+ return unless @s.match?(%r{(?:[~/]|\.{1,2}/|.*/\.\./)})
20
+
21
+ dir_only! if @s.match?(%r{.*/\s*\z})
22
+ @s.string.replace(::File.expand_path(@s.rest))
23
+ @s.string.delete_prefix!(@expand_path_from)
24
+ @s.pos = 0
25
+ end
26
+
27
+ def negated!
28
+ @negation = false
29
+ end
30
+
31
+ def unmatchable_rule!
32
+ throw :abort_build, ::FastIgnore::UnmatchableRule
33
+ end
34
+
35
+ def nothing_emitted?
36
+ @re.empty? && @parent_segments.empty?
37
+ end
38
+
39
+ def emit_dir
40
+ anchored!
41
+
42
+ @parent_segments << @re
43
+ @re = ::FastIgnore::GitignoreRuleRegexpBuilder.new
44
+ end
45
+
46
+ def emit_end
47
+ @dir_only || @re.append_end_dir_or_anchor
48
+ break!
49
+ end
50
+
51
+ def parent_dir_re # rubocop:disable Metrics/MethodLength
52
+ segment_joins_count = @parent_segments.length
53
+ parent_prefix = if @file_path
54
+ segment_joins_count += @file_path.escaped_segments_length
55
+
56
+ if @anchored
57
+ "\\A#{@file_path.escaped_segments_joined}"
58
+ else
59
+ "\\A#{@file_path.escaped_segments_joined}(?:.*/)?"
60
+ end
61
+ else
62
+ prefix
63
+ end
64
+
65
+ out = parent_prefix.dup
66
+ unless @parent_segments.empty?
67
+ out << '(?:'
68
+ out << @parent_segments.join('/(?:')
69
+ out << '/'
70
+ end
71
+ out << (')?' * segment_joins_count)
72
+ out
73
+ end
74
+
75
+ def build_parent_dir_rule
76
+ # Regexp::IGNORECASE = 1
77
+ ::FastIgnore::Rule.new(::Regexp.new(parent_dir_re, 1), true, anchored_or_file_path, true)
78
+ end
79
+
80
+ def build_child_file_rule
81
+ # Regexp::IGNORECASE = 1
82
+ ::FastIgnore::Rule.new(@re.append_dir.to_regexp, @negation, anchored_or_file_path, false)
83
+ end
84
+
85
+ def build_rule
86
+ joined_re = ::FastIgnore::GitignoreRuleRegexpBuilder.new
87
+ joined_re.append(@parent_segments.join('/'))
88
+ joined_re.append_dir unless @parent_segments.empty?
89
+ joined_re.append(@re)
90
+ @re = joined_re
91
+
92
+ rules = [super, build_parent_dir_rule]
93
+ (rules << build_child_file_rule) if @dir_only
94
+ rules
95
+ end
96
+
97
+ def process_rule
98
+ expand_rule_path if @expand_path_from
99
+ super
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class GitignoreRuleBuilder # rubocop:disable Metrics/ClassLength
5
+ def initialize(rule, file_path)
6
+ @re = ::FastIgnore::GitignoreRuleRegexpBuilder.new
7
+ @s = ::FastIgnore::GitignoreRuleScanner.new(rule)
8
+
9
+ @file_path = file_path
10
+ @negation = false
11
+ @anchored = false
12
+ @dir_only = false
13
+ end
14
+
15
+ def break!
16
+ throw :break
17
+ end
18
+
19
+ def blank!
20
+ throw :abort_build, []
21
+ end
22
+
23
+ def unmatchable_rule!
24
+ throw :abort_build, []
25
+ end
26
+
27
+ def negated!
28
+ @negation = true
29
+ end
30
+
31
+ def anchored!
32
+ @anchored ||= true
33
+ end
34
+
35
+ def never_anchored!
36
+ @anchored = :never
37
+ end
38
+
39
+ def dir_only!
40
+ @dir_only = true
41
+ end
42
+
43
+ def nothing_emitted?
44
+ @re.empty?
45
+ end
46
+
47
+ def emit_dir
48
+ anchored!
49
+ @re.append_dir
50
+ end
51
+
52
+ def emit_end
53
+ @re.append_end_anchor
54
+ break!
55
+ end
56
+
57
+ def process_backslash
58
+ return unless @s.backslash?
59
+
60
+ @re.append_escaped(@s.next_character) || unmatchable_rule!
61
+ end
62
+
63
+ def process_star_end_after_slash
64
+ return true unless @s.star_end?
65
+
66
+ @re.append_many_non_dir
67
+ emit_end
68
+ end
69
+
70
+ def process_slash
71
+ return unless @s.slash?
72
+ return dir_only! if @s.end?
73
+ return unmatchable_rule! if @s.slash?
74
+
75
+ emit_dir
76
+ process_star_end_after_slash
77
+ end
78
+
79
+ def process_two_stars # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
80
+ return unless @s.two_stars?
81
+ return break! if @s.end?
82
+
83
+ if @s.slash?
84
+ if @s.end?
85
+ @re.append_any_non_dir
86
+ dir_only!
87
+ elsif @s.slash?
88
+ unmatchable_rule!
89
+ else
90
+ if nothing_emitted?
91
+ never_anchored!
92
+ else
93
+ @re.append_any_dir
94
+ anchored!
95
+ end
96
+ process_star_end_after_slash
97
+ end
98
+ else
99
+ @re.append_any_non_dir
100
+ end
101
+ end
102
+
103
+ def process_character_class # rubocop:disable Metrics/MethodLength
104
+ return unless @s.character_class_start?
105
+
106
+ @re.append_character_class_open
107
+ @re.append_character_class_negation if @s.character_class_negation?
108
+ unmatchable_rule! if @s.character_class_end?
109
+
110
+ until @s.character_class_end?
111
+ next if process_backslash
112
+ next @re.append_character_class_dash if @s.dash?
113
+ next if @re.append_escaped(@s.character_class_literal)
114
+
115
+ unmatchable_rule!
116
+ end
117
+
118
+ @re.append_character_class_close
119
+ end
120
+
121
+ def process_end
122
+ blank! if nothing_emitted?
123
+
124
+ emit_end
125
+ end
126
+
127
+ def process_rule # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
128
+ anchored! if @s.slash?
129
+
130
+ catch :break do
131
+ loop do
132
+ next if process_backslash
133
+ next if process_slash
134
+ next if process_two_stars
135
+ next @re.append_any_non_dir if @s.star?
136
+ next @re.append_one_non_dir if @s.question_mark?
137
+ next if process_character_class
138
+ next if @re.append_escaped(@s.literal)
139
+ next if @re.append_escaped(@s.significant_whitespace)
140
+
141
+ process_end
142
+ end
143
+ end
144
+ end
145
+
146
+ def prefix # rubocop:disable Metrics/MethodLength
147
+ out = ::FastIgnore::GitignoreRuleRegexpBuilder.new
148
+ if @file_path
149
+ out.append_start_anchor.append(@file_path.escaped)
150
+ out.append_any_dir unless @anchored
151
+ else
152
+ if @anchored
153
+ out.append_start_anchor
154
+ else
155
+ out.append_start_dir_or_anchor
156
+ end
157
+ end
158
+ out
159
+ end
160
+
161
+ def build_rule
162
+ @re.prepend(prefix)
163
+ ::FastIgnore::Rule.new(@re.to_regexp, @negation, anchored_or_file_path, @dir_only)
164
+ end
165
+
166
+ def anchored_or_file_path
167
+ @anchored || @file_path
168
+ end
169
+
170
+ def build
171
+ catch :abort_build do
172
+ blank! if @s.hash?
173
+ negated! if @s.exclamation_mark?
174
+ process_rule
175
+
176
+ @anchored = false if @anchored == :never
177
+
178
+ build_rule
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class GitignoreRuleRegexpBuilder < String
5
+ def to_regexp
6
+ # Regexp::IGNORECASE = 1
7
+ ::Regexp.new(self, 1)
8
+ end
9
+
10
+ def append(value)
11
+ self.<<(value)
12
+
13
+ self
14
+ end
15
+
16
+ def append_escaped(value)
17
+ return unless value
18
+
19
+ append(::Regexp.escape(value))
20
+ end
21
+
22
+ def append_dir
23
+ append('/')
24
+ end
25
+
26
+ def append_any_dir
27
+ append('(?:.*/)?')
28
+ end
29
+
30
+ def append_one_non_dir
31
+ append('[^/]')
32
+ end
33
+
34
+ def append_any_non_dir
35
+ append_one_non_dir
36
+ append('*')
37
+ end
38
+
39
+ def append_many_non_dir
40
+ append_one_non_dir
41
+ append('+')
42
+ end
43
+
44
+ def append_end_anchor
45
+ append('\\z')
46
+ end
47
+
48
+ def append_start_anchor
49
+ append('\\A')
50
+ end
51
+
52
+ def append_start_dir_or_anchor
53
+ append('(?:\\A|/)')
54
+ end
55
+
56
+ def append_end_dir_or_anchor
57
+ append('(?:/|\\z)')
58
+ end
59
+
60
+ def append_character_class_open
61
+ append('(?!/)[')
62
+ end
63
+
64
+ def append_character_class_negation
65
+ append('^')
66
+ end
67
+
68
+ def append_character_class_dash
69
+ append('-')
70
+ end
71
+
72
+ def append_character_class_close
73
+ append(']')
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FastIgnore
4
+ class GitignoreRuleScanner < StringScanner
5
+ def character_class_end?
6
+ skip(/\]/)
7
+ end
8
+
9
+ def character_class_start?
10
+ skip(/\[/)
11
+ end
12
+
13
+ def character_class_negation?
14
+ skip(/\^|!/)
15
+ end
16
+
17
+ def end?
18
+ skip(/\s*\z/)
19
+ end
20
+
21
+ def slash?
22
+ skip(%r{/})
23
+ end
24
+
25
+ def backslash?
26
+ skip(/\\/)
27
+ end
28
+
29
+ def two_stars?
30
+ skip(/\*{2,}/)
31
+ end
32
+
33
+ def star?
34
+ skip(/\*/)
35
+ end
36
+
37
+ def next_character
38
+ matched if scan(/./)
39
+ end
40
+
41
+ def star_end?
42
+ skip(/\*\s*\z/)
43
+ end
44
+
45
+ def question_mark?
46
+ skip(/\?/)
47
+ end
48
+
49
+ def dash?
50
+ skip(/-/)
51
+ end
52
+
53
+ def character_class_literal
54
+ matched if scan(/[^\]\-\\]+/)
55
+ end
56
+
57
+ def literal
58
+ matched if scan(%r{[^*/?\[\\\s]+})
59
+ end
60
+
61
+ def significant_whitespace
62
+ matched if scan(/\s+(?!\s|\z)/)
63
+ end
64
+
65
+ def exclamation_mark?
66
+ skip(/!/)
67
+ end
68
+
69
+ def hash?
70
+ skip(/#/)
71
+ end
72
+ end
73
+ end