fast_ignore 0.12.0 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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