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.
- 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 +29 -13
- 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 +21 -33
- data/.gitignore +0 -6
- data/.leftovers.yml +0 -5
- data/.rspec +0 -3
- data/.rubocop.yml +0 -216
- data/.simplecov +0 -12
- 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 -141
@@ -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
|