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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +157 -80
- data/lib/fast_ignore.rb +27 -26
- data/lib/fast_ignore/backports.rb +2 -2
- data/lib/fast_ignore/file_root.rb +39 -0
- data/lib/fast_ignore/gitignore_include_rule_builder.rb +102 -0
- data/lib/fast_ignore/gitignore_rule_builder.rb +182 -0
- data/lib/fast_ignore/gitignore_rule_regexp_builder.rb +76 -0
- data/lib/fast_ignore/gitignore_rule_scanner.rb +73 -0
- data/lib/fast_ignore/global_gitignore.rb +50 -0
- data/lib/fast_ignore/rule.rb +28 -12
- data/lib/fast_ignore/rule_builder.rb +18 -80
- data/lib/fast_ignore/rule_set.rb +23 -16
- data/lib/fast_ignore/rule_sets.rb +113 -0
- data/lib/fast_ignore/shebang_rule.rb +34 -14
- data/lib/fast_ignore/unmatchable_rule.rb +41 -0
- data/lib/fast_ignore/version.rb +1 -1
- metadata +19 -31
- data/.gitignore +0 -6
- data/.leftovers.yml +0 -5
- data/.rspec +0 -3
- data/.rubocop.yml +0 -216
- data/.simplecov +0 -10
- data/.spellr.yml +0 -5
- data/.spellr_wordlists/english.txt +0 -62
- data/.spellr_wordlists/ruby.txt +0 -1
- data/.spellr_wordlists/shell.txt +0 -3
- data/.travis.yml +0 -11
- data/Gemfile +0 -9
- data/Rakefile +0 -18
- data/bin/console +0 -8
- data/bin/ls +0 -6
- data/bin/ls_seconds +0 -21
- data/bin/setup +0 -8
- data/bin/time +0 -21
- data/fast_ignore.gemspec +0 -41
- data/lib/fast_ignore/fn_match_to_re.rb +0 -96
- data/lib/fast_ignore/rule_set_builder.rb +0 -126
@@ -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
|