fast_ignore 0.17.0 → 0.17.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +0 -13
- data/lib/fast_ignore/builders/gitignore.rb +15 -0
- data/lib/fast_ignore/builders/shebang.rb +17 -0
- data/lib/fast_ignore/builders/shebang_or_gitignore.rb +15 -0
- data/lib/fast_ignore/candidate.rb +85 -0
- data/lib/fast_ignore/gitconfig_parser.rb +3 -4
- data/lib/fast_ignore/gitignore_include_rule_builder.rb +40 -67
- data/lib/fast_ignore/gitignore_rule_builder.rb +66 -27
- data/lib/fast_ignore/gitignore_rule_group.rb +31 -0
- data/lib/fast_ignore/gitignore_rule_scanner.rb +24 -4
- data/lib/fast_ignore/matchers/allow_any_dir.rb +39 -0
- data/lib/fast_ignore/matchers/allow_path_regexp.rb +45 -0
- data/lib/fast_ignore/matchers/ignore_path_regexp.rb +45 -0
- data/lib/fast_ignore/matchers/shebang_regexp.rb +46 -0
- data/lib/fast_ignore/matchers/unmatchable.rb +31 -0
- data/lib/fast_ignore/matchers/within_dir.rb +50 -0
- data/lib/fast_ignore/path_expander.rb +11 -0
- data/lib/fast_ignore/path_regexp_builder.rb +130 -0
- data/lib/fast_ignore/patterns.rb +33 -0
- data/lib/fast_ignore/relative_candidate.rb +20 -0
- data/lib/fast_ignore/rule_group.rb +42 -0
- data/lib/fast_ignore/rule_groups.rb +55 -0
- data/lib/fast_ignore/version.rb +1 -1
- data/lib/fast_ignore/walkers/base.rb +26 -0
- data/lib/fast_ignore/walkers/file_system.rb +46 -0
- data/lib/fast_ignore/walkers/gitignore_collecting_file_system.rb +48 -0
- data/lib/fast_ignore.rb +54 -91
- metadata +36 -11
- data/lib/fast_ignore/backports.rb +0 -66
- data/lib/fast_ignore/file_root.rb +0 -39
- data/lib/fast_ignore/gitignore_rule_regexp_builder.rb +0 -76
- data/lib/fast_ignore/rule.rb +0 -65
- data/lib/fast_ignore/rule_builder.rb +0 -41
- data/lib/fast_ignore/rule_set.rb +0 -76
- data/lib/fast_ignore/rule_sets.rb +0 -113
- data/lib/fast_ignore/shebang_rule.rb +0 -80
- data/lib/fast_ignore/unmatchable_rule.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c81120885ae42b4893295ecd66b501ed6962d2742adc2bd59433796ca0e41995
|
4
|
+
data.tar.gz: 0a15d4651cc7dfc7eeed32b919da373ebee92195385ff0cf22acd92751258b68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c38a391c2a05c7439899144c2af1db29281538c855a18be47725b3e92f8fc40ecca2f69f15a26508d5f63d7f45f998cbba73ac41feaa1541356daf20067b4ce9
|
7
|
+
data.tar.gz: 0e355b08fbb00c9cca020ac06b0062f73477a59f673f6b465bf71d11c0228889dbeeb487e8e519eeccd09afa84290681f65f75300695332b0a4e44ecbec1b445
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
# v0.17.4
|
2
|
+
- Deprecated `follow_symlinks:`, it's inaccurately named and awkward.
|
3
|
+
|
4
|
+
- Plus lots of refactoring that _should_ have no effect on behaviour
|
5
|
+
- Some performance regression due to the deprecation logic. we'll improve more than we lost after the deprecations are gone entirely.
|
6
|
+
|
7
|
+
# v0.17.3
|
8
|
+
- Add fuzz tests, fix a couple more edge cases it revealed:
|
9
|
+
- `~not_a_user` will be considered literal rather than raising an error, `~a_user` will continue to be expanded to the home directory of `a_user` when used in an `argv_rules:` or `allowed?`
|
10
|
+
- an `include_rule:` with a trailing `/` was raising a FrozenError in some circumstances.
|
11
|
+
|
12
|
+
# v0.17.2
|
13
|
+
- Remove unnecessary backport code that was leftover when support for 2.4 was dropped
|
14
|
+
- Tiny performance improvements from rubocop-performance's suggestions
|
15
|
+
|
16
|
+
# v0.17.1
|
17
|
+
- fix handling of backward character classes `[z-a]`
|
18
|
+
previously this raised a RegexpError, but git just considered it to be identical to `[z]`, now we match the git behaviour (but why would you ever do this?, i only found it because of the fuzz spec in the `leftovers` gem)
|
19
|
+
|
1
20
|
# v0.17.0
|
2
21
|
- allow overriding `exists:` in `allowed?`
|
3
22
|
- allow setting `include_directories: true` in `allowed?`
|
data/README.md
CHANGED
@@ -166,19 +166,6 @@ When `relative: true`: FastIgnore#each will yield paths relative to the [`root:`
|
|
166
166
|
FastIgnore.new(relative: true).to_a
|
167
167
|
```
|
168
168
|
|
169
|
-
### `follow_symlinks: true`
|
170
|
-
|
171
|
-
**Default: false**
|
172
|
-
|
173
|
-
When `follow_symlinks: false`: FastIgnore#each will match git's behaviour and not follow symbolic links.
|
174
|
-
When `follow_symlinks: true`: FastIgnore#each will check if a symlink points to a directory, and files in linked directories must also match rules using the symlink path as the directory location, not the real directory location.
|
175
|
-
|
176
|
-
**This doesn't use the real path for matching or yield or return it.**
|
177
|
-
|
178
|
-
```ruby
|
179
|
-
FastIgnore.new(follow_symlinks: true).to_a
|
180
|
-
```
|
181
|
-
|
182
169
|
### `root:`
|
183
170
|
|
184
171
|
**Default: Dir.pwd ($PWD, the current working directory)**
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FastIgnore
|
4
|
+
module Builders
|
5
|
+
module Gitignore
|
6
|
+
def self.build(rule, allow, expand_path_with: nil)
|
7
|
+
if allow
|
8
|
+
::FastIgnore::GitignoreIncludeRuleBuilder.new(rule, expand_path_with: expand_path_with).build
|
9
|
+
else
|
10
|
+
::FastIgnore::GitignoreRuleBuilder.new(rule, expand_path_with: expand_path_with).build
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FastIgnore
|
4
|
+
module Builders
|
5
|
+
module Shebang
|
6
|
+
def self.build(shebang, allow)
|
7
|
+
shebang.strip!
|
8
|
+
pattern = /\A#!.*\b#{::Regexp.escape(shebang)}\b/i
|
9
|
+
rule = ::FastIgnore::Matchers::ShebangRegexp.new(pattern, allow)
|
10
|
+
return rule unless allow
|
11
|
+
|
12
|
+
# also allow all directories in case they include a file with the matching shebang file
|
13
|
+
[::FastIgnore::Matchers::AllowAnyDir, rule]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FastIgnore
|
4
|
+
module Builders
|
5
|
+
module ShebangOrGitignore
|
6
|
+
def self.build(rule, allow, expand_path_with: nil)
|
7
|
+
if rule.delete_prefix!('#!:')
|
8
|
+
::FastIgnore::Builders::Shebang.build(rule, allow)
|
9
|
+
else
|
10
|
+
::FastIgnore::Builders::Gitignore.build(rule, allow, expand_path_with: expand_path_with)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
class FastIgnore
|
4
|
+
class Candidate
|
5
|
+
class << self
|
6
|
+
def root
|
7
|
+
@root ||= new('/', nil, true, true, nil)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(full_path, filename, directory, exists, content)
|
12
|
+
@full_path = full_path
|
13
|
+
@filename = filename
|
14
|
+
(@directory = directory) unless directory.nil?
|
15
|
+
(@exists = exists) unless exists.nil?
|
16
|
+
(@first_line = content.slice(/.*/)) if content # we only care about the first line
|
17
|
+
end
|
18
|
+
|
19
|
+
def parent
|
20
|
+
@parent ||= ::FastIgnore::Candidate.new(::File.dirname(@full_path), nil, true, true, nil)
|
21
|
+
end
|
22
|
+
|
23
|
+
# use \0 because it can't be in paths
|
24
|
+
def key
|
25
|
+
@key ||= :"#{
|
26
|
+
"\0" if defined?(@directory) && @directory
|
27
|
+
}#{
|
28
|
+
@full_path
|
29
|
+
}\0#{
|
30
|
+
@first_line if defined?(@first_line)
|
31
|
+
}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def relative_to(dir)
|
35
|
+
return unless @full_path.start_with?(dir)
|
36
|
+
|
37
|
+
::FastIgnore::RelativeCandidate.new(@full_path.delete_prefix(dir), self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def directory?
|
41
|
+
return @directory if defined?(@directory)
|
42
|
+
|
43
|
+
@directory = ::File.lstat(@full_path).directory?
|
44
|
+
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENAMETOOLONG
|
45
|
+
@exists ||= false
|
46
|
+
@directory = false
|
47
|
+
end
|
48
|
+
|
49
|
+
def exists?
|
50
|
+
return @exists if defined?(@exists)
|
51
|
+
|
52
|
+
@exists = ::File.exist?(@full_path)
|
53
|
+
rescue ::Errno::EACCES, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
54
|
+
# :nocov: can't quite get this set up in a test
|
55
|
+
@exists = false
|
56
|
+
# :nocov:
|
57
|
+
end
|
58
|
+
|
59
|
+
def filename
|
60
|
+
@filename ||= ::File.basename(@full_path)
|
61
|
+
end
|
62
|
+
|
63
|
+
# how long can a shebang be?
|
64
|
+
# https://www.in-ulm.de/~mascheck/various/shebang/
|
65
|
+
def first_line # rubocop:disable Metrics/MethodLength
|
66
|
+
@first_line ||= begin
|
67
|
+
file = ::File.new(@full_path)
|
68
|
+
first_line = file.sysread(64)
|
69
|
+
if first_line.start_with?('#!')
|
70
|
+
first_line += file.readline unless first_line.include?("\n")
|
71
|
+
file.close
|
72
|
+
first_line
|
73
|
+
else
|
74
|
+
file.close
|
75
|
+
''
|
76
|
+
end
|
77
|
+
rescue ::EOFError, ::SystemCallError
|
78
|
+
# :nocov:
|
79
|
+
file&.close
|
80
|
+
# :nocov:
|
81
|
+
''
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -55,7 +55,7 @@ class FastIgnore
|
|
55
55
|
include_path = scan_value(file)
|
56
56
|
|
57
57
|
value = ::FastIgnore::GitconfigParser.parse(
|
58
|
-
|
58
|
+
PathExpander.expand_path(include_path, ::File.dirname(path)),
|
59
59
|
root: root,
|
60
60
|
nesting: nesting + 1
|
61
61
|
)
|
@@ -100,9 +100,8 @@ class FastIgnore
|
|
100
100
|
|
101
101
|
def on_branch?(branch_pattern)
|
102
102
|
branch_pattern += '**' if branch_pattern.end_with?('/')
|
103
|
-
current_branch = ::File.readable?("#{root}/.git/HEAD") &&
|
104
|
-
|
105
|
-
)
|
103
|
+
current_branch = ::File.readable?("#{root}/.git/HEAD") &&
|
104
|
+
::File.read("#{root}/.git/HEAD").delete_prefix('ref: refs/heads/')
|
106
105
|
return unless current_branch
|
107
106
|
|
108
107
|
# goddamit git what does 'a pattern with standard globbing wildcards' mean
|
@@ -2,26 +2,10 @@
|
|
2
2
|
|
3
3
|
class FastIgnore
|
4
4
|
class GitignoreIncludeRuleBuilder < GitignoreRuleBuilder
|
5
|
-
|
6
|
-
|
7
|
-
# :nocov:
|
8
|
-
|
9
|
-
def initialize(rule, file_path, expand_path_from = nil)
|
10
|
-
super(rule, file_path)
|
5
|
+
def initialize(rule, expand_path_with: nil)
|
6
|
+
super
|
11
7
|
|
12
|
-
@parent_segments = []
|
13
8
|
@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
9
|
end
|
26
10
|
|
27
11
|
def negated!
|
@@ -29,74 +13,63 @@ class FastIgnore
|
|
29
13
|
end
|
30
14
|
|
31
15
|
def unmatchable_rule!
|
32
|
-
throw :abort_build, ::FastIgnore::
|
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
|
16
|
+
throw :abort_build, ::FastIgnore::Matchers::Unmatchable
|
44
17
|
end
|
45
18
|
|
46
19
|
def emit_end
|
47
|
-
@dir_only
|
20
|
+
if @dir_only
|
21
|
+
@child_re = @re.dup
|
22
|
+
@re.append_end_anchor
|
23
|
+
else
|
24
|
+
@re.append_dir_or_end_anchor
|
25
|
+
end
|
26
|
+
|
48
27
|
break!
|
49
28
|
end
|
50
29
|
|
51
|
-
def
|
52
|
-
|
53
|
-
parent_prefix = if @file_path
|
54
|
-
segment_joins_count += @file_path.escaped_segments_length
|
30
|
+
def build_parent_dir_rules
|
31
|
+
return unless @negation
|
55
32
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
33
|
+
if @anchored
|
34
|
+
parent_pattern = @s.string.dup
|
35
|
+
if parent_pattern.sub!(%r{/[^/]+/?\s*\z}, '/')
|
36
|
+
::FastIgnore::GitignoreIncludeRuleBuilder.new(parent_pattern).build_as_parent
|
60
37
|
end
|
61
38
|
else
|
62
|
-
|
39
|
+
[::FastIgnore::Matchers::AllowAnyDir]
|
63
40
|
end
|
41
|
+
end
|
64
42
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
43
|
+
def build_child_file_rule # rubocop:disable Metrics/MethodLength
|
44
|
+
if @child_re.end_with?('/')
|
45
|
+
@child_re.append_many_non_dir.append_dir if @dir_only
|
46
|
+
else
|
47
|
+
@child_re.append_dir
|
70
48
|
end
|
71
|
-
out << (')?' * segment_joins_count)
|
72
|
-
out
|
73
|
-
end
|
74
49
|
|
75
|
-
|
76
|
-
# Regexp::IGNORECASE = 1
|
77
|
-
::FastIgnore::Rule.new(::Regexp.new(parent_dir_re, 1), true, anchored_or_file_path, true)
|
78
|
-
end
|
50
|
+
@child_re.prepend(prefix)
|
79
51
|
|
80
|
-
|
81
|
-
|
82
|
-
|
52
|
+
if @negation
|
53
|
+
::FastIgnore::Matchers::AllowPathRegexp.new(@child_re.to_regexp, @anchored, false)
|
54
|
+
else
|
55
|
+
::FastIgnore::Matchers::IgnorePathRegexp.new(@child_re.to_regexp, @anchored, false)
|
56
|
+
end
|
83
57
|
end
|
84
58
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
joined_re.append_dir unless @parent_segments.empty?
|
89
|
-
joined_re.append(@re)
|
90
|
-
@re = joined_re
|
59
|
+
def build_as_parent
|
60
|
+
anchored!
|
61
|
+
dir_only!
|
91
62
|
|
92
|
-
|
93
|
-
|
94
|
-
|
63
|
+
catch :abort_build do
|
64
|
+
process_rule
|
65
|
+
build_rule(child_file_rule: false)
|
66
|
+
end
|
95
67
|
end
|
96
68
|
|
97
|
-
def
|
98
|
-
|
99
|
-
|
69
|
+
def build_rule(child_file_rule: true)
|
70
|
+
@child_re ||= @re.dup # in case emit_end wasn't called
|
71
|
+
|
72
|
+
[super(), *build_parent_dir_rules, (build_child_file_rule if child_file_rule)].compact
|
100
73
|
end
|
101
74
|
end
|
102
75
|
end
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
class FastIgnore
|
4
4
|
class GitignoreRuleBuilder # rubocop:disable Metrics/ClassLength
|
5
|
-
def initialize(rule,
|
6
|
-
@re = ::FastIgnore::
|
5
|
+
def initialize(rule, expand_path_with: nil)
|
6
|
+
@re = ::FastIgnore::PathRegexpBuilder.new
|
7
7
|
@s = ::FastIgnore::GitignoreRuleScanner.new(rule)
|
8
8
|
|
9
|
-
@
|
9
|
+
@expand_path_with = expand_path_with
|
10
10
|
@negation = false
|
11
11
|
@anchored = false
|
12
12
|
@dir_only = false
|
@@ -49,6 +49,11 @@ class FastIgnore
|
|
49
49
|
@re.append_dir
|
50
50
|
end
|
51
51
|
|
52
|
+
def emit_any_dir
|
53
|
+
anchored!
|
54
|
+
@re.append_any_dir
|
55
|
+
end
|
56
|
+
|
52
57
|
def emit_end
|
53
58
|
@re.append_end_anchor
|
54
59
|
break!
|
@@ -60,11 +65,22 @@ class FastIgnore
|
|
60
65
|
@re.append_escaped(@s.next_character) || unmatchable_rule!
|
61
66
|
end
|
62
67
|
|
63
|
-
def process_star_end_after_slash
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
def process_star_end_after_slash # rubocop:disable Metrics/MethodLength
|
69
|
+
if @s.star_end?
|
70
|
+
@re.append_many_non_dir
|
71
|
+
emit_end
|
72
|
+
elsif @s.two_star_end?
|
73
|
+
break!
|
74
|
+
elsif @s.star_slash_end?
|
75
|
+
@re.append_many_non_dir
|
76
|
+
dir_only!
|
77
|
+
emit_end
|
78
|
+
elsif @s.two_star_slash_end?
|
79
|
+
dir_only!
|
80
|
+
break!
|
81
|
+
else
|
82
|
+
true
|
83
|
+
end
|
68
84
|
end
|
69
85
|
|
70
86
|
def process_slash
|
@@ -76,7 +92,7 @@ class FastIgnore
|
|
76
92
|
process_star_end_after_slash
|
77
93
|
end
|
78
94
|
|
79
|
-
def process_two_stars # rubocop:disable Metrics/
|
95
|
+
def process_two_stars # rubocop:disable Metrics/MethodLength
|
80
96
|
return unless @s.two_stars?
|
81
97
|
return break! if @s.end?
|
82
98
|
|
@@ -90,8 +106,7 @@ class FastIgnore
|
|
90
106
|
if nothing_emitted?
|
91
107
|
never_anchored!
|
92
108
|
else
|
93
|
-
|
94
|
-
anchored!
|
109
|
+
emit_any_dir
|
95
110
|
end
|
96
111
|
process_star_end_after_slash
|
97
112
|
end
|
@@ -108,8 +123,8 @@ class FastIgnore
|
|
108
123
|
unmatchable_rule! if @s.character_class_end?
|
109
124
|
|
110
125
|
until @s.character_class_end?
|
126
|
+
next if process_character_class_range
|
111
127
|
next if process_backslash
|
112
|
-
next @re.append_character_class_dash if @s.dash?
|
113
128
|
next if @re.append_escaped(@s.character_class_literal)
|
114
129
|
|
115
130
|
unmatchable_rule!
|
@@ -118,6 +133,22 @@ class FastIgnore
|
|
118
133
|
@re.append_character_class_close
|
119
134
|
end
|
120
135
|
|
136
|
+
def process_character_class_range
|
137
|
+
start = @s.character_class_range_start
|
138
|
+
return unless start
|
139
|
+
|
140
|
+
start = start.delete_prefix('\\')
|
141
|
+
|
142
|
+
@re.append_escaped(start)
|
143
|
+
|
144
|
+
finish = @s.character_class_range_end.delete_prefix('\\')
|
145
|
+
|
146
|
+
return true unless start < finish
|
147
|
+
|
148
|
+
@re.append_character_class_dash
|
149
|
+
@re.append_escaped(finish)
|
150
|
+
end
|
151
|
+
|
121
152
|
def process_end
|
122
153
|
blank! if nothing_emitted?
|
123
154
|
|
@@ -125,6 +156,7 @@ class FastIgnore
|
|
125
156
|
end
|
126
157
|
|
127
158
|
def process_rule # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
159
|
+
expand_rule_path! if @expand_path_with
|
128
160
|
anchored! if @s.slash?
|
129
161
|
|
130
162
|
catch :break do
|
@@ -143,28 +175,24 @@ class FastIgnore
|
|
143
175
|
end
|
144
176
|
end
|
145
177
|
|
146
|
-
def prefix
|
147
|
-
out = ::FastIgnore::
|
148
|
-
|
149
|
-
|
150
|
-
out.
|
178
|
+
def prefix
|
179
|
+
out = ::FastIgnore::PathRegexpBuilder.new
|
180
|
+
|
181
|
+
if @anchored
|
182
|
+
out.append_start_anchor
|
151
183
|
else
|
152
|
-
|
153
|
-
out.append_start_anchor
|
154
|
-
else
|
155
|
-
out.append_start_dir_or_anchor
|
156
|
-
end
|
184
|
+
out.append_dir_or_start_anchor
|
157
185
|
end
|
158
186
|
out
|
159
187
|
end
|
160
188
|
|
161
189
|
def build_rule
|
162
190
|
@re.prepend(prefix)
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
191
|
+
if @negation
|
192
|
+
::FastIgnore::Matchers::AllowPathRegexp.new(@re.to_regexp, @anchored, @dir_only)
|
193
|
+
else
|
194
|
+
::FastIgnore::Matchers::IgnorePathRegexp.new(@re.to_regexp, @anchored, @dir_only)
|
195
|
+
end
|
168
196
|
end
|
169
197
|
|
170
198
|
def build
|
@@ -178,5 +206,16 @@ class FastIgnore
|
|
178
206
|
build_rule
|
179
207
|
end
|
180
208
|
end
|
209
|
+
|
210
|
+
def expand_rule_path!
|
211
|
+
anchored! unless @s.match?(/\*/) # rubocop:disable Performance/StringInclude # it's StringScanner#match?
|
212
|
+
return unless @s.match?(%r{(?:[~/]|\.{1,2}/|.*/\.\./)})
|
213
|
+
|
214
|
+
dir_only! if @s.match?(%r{.*/\s*\z})
|
215
|
+
|
216
|
+
@s.string.replace(PathExpander.expand_path(@s.rest, @expand_path_with))
|
217
|
+
@s.string.delete_prefix!(@expand_path_with)
|
218
|
+
@s.pos = 0
|
219
|
+
end
|
181
220
|
end
|
182
221
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
class FastIgnore
|
6
|
+
class GitignoreRuleGroup < ::FastIgnore::RuleGroup
|
7
|
+
def initialize(root)
|
8
|
+
@root = root
|
9
|
+
@loaded_paths = Set[root]
|
10
|
+
|
11
|
+
super([
|
12
|
+
::FastIgnore::Patterns.new('.git', root: '/'),
|
13
|
+
::FastIgnore::Patterns.new(from_file: ::FastIgnore::GlobalGitignore.path(root: root), root: root),
|
14
|
+
::FastIgnore::Patterns.new(from_file: "#{root}.git/info/exclude", root: root),
|
15
|
+
::FastIgnore::Patterns.new(from_file: "#{root}.gitignore", root: root)
|
16
|
+
], false)
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_gitignore(dir)
|
20
|
+
return if @loaded_paths.include?(dir)
|
21
|
+
|
22
|
+
@loaded_paths << dir
|
23
|
+
matcher = ::FastIgnore::Patterns.new(from_file: "#{dir}.gitignore").build_matchers(allow: false)
|
24
|
+
@matchers += matcher unless !matcher || matcher.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_gitignore_to_root(path)
|
28
|
+
add_gitignore(path) until @loaded_paths.include?(path = "#{::File.dirname(path)}/")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -42,16 +42,36 @@ class FastIgnore
|
|
42
42
|
skip(/\*\s*\z/)
|
43
43
|
end
|
44
44
|
|
45
|
+
def two_star_end?
|
46
|
+
skip(/\*{2,}\s*\z/)
|
47
|
+
end
|
48
|
+
|
49
|
+
def star_slash_end?
|
50
|
+
skip(%r{\*/\s*\z})
|
51
|
+
end
|
52
|
+
|
53
|
+
def two_star_slash_end?
|
54
|
+
skip(%r{\*{2,}/\s*\z})
|
55
|
+
end
|
56
|
+
|
45
57
|
def question_mark?
|
46
58
|
skip(/\?/)
|
47
59
|
end
|
48
60
|
|
49
|
-
def
|
50
|
-
|
61
|
+
def character_class_literal
|
62
|
+
matched if scan(/[^\]\\][^\]\\-]*(?!-)/)
|
51
63
|
end
|
52
64
|
|
53
|
-
def
|
54
|
-
matched if scan(/[
|
65
|
+
def character_class_range_start
|
66
|
+
matched if scan(/(\\.|[^\\\]])(?=-(\\.|[^\\\]]))/)
|
67
|
+
end
|
68
|
+
|
69
|
+
def character_class_range_end
|
70
|
+
# we already confirmed this was going to match
|
71
|
+
# with the lookahead in character_class_range_start
|
72
|
+
skip(/-/)
|
73
|
+
scan(/(\\.|[^\\\]])/)
|
74
|
+
matched
|
55
75
|
end
|
56
76
|
|
57
77
|
def literal
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FastIgnore
|
4
|
+
module Matchers
|
5
|
+
module AllowAnyDir
|
6
|
+
class << self
|
7
|
+
def squash_id
|
8
|
+
:allow_any_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def dir_only?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def file_only?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def squash(_)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def weight
|
24
|
+
0
|
25
|
+
end
|
26
|
+
|
27
|
+
# :nocov:
|
28
|
+
def inspect
|
29
|
+
'#<AllowAnyDir>'
|
30
|
+
end
|
31
|
+
# :nocov:
|
32
|
+
|
33
|
+
def match?(_)
|
34
|
+
:allow
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FastIgnore
|
4
|
+
module Matchers
|
5
|
+
class AllowPathRegexp
|
6
|
+
attr_reader :dir_only
|
7
|
+
alias_method :dir_only?, :dir_only
|
8
|
+
undef :dir_only
|
9
|
+
|
10
|
+
attr_reader :squash_id
|
11
|
+
attr_reader :rule
|
12
|
+
|
13
|
+
def initialize(rule, squashable, dir_only)
|
14
|
+
@rule = rule
|
15
|
+
@dir_only = dir_only
|
16
|
+
@squashable = squashable
|
17
|
+
@squash_id = squashable ? :allow : object_id
|
18
|
+
|
19
|
+
freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
def squash(list)
|
23
|
+
self.class.new(::Regexp.union(list.map(&:rule)), @squashable, @dir_only)
|
24
|
+
end
|
25
|
+
|
26
|
+
def file_only?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def weight
|
31
|
+
1
|
32
|
+
end
|
33
|
+
|
34
|
+
# :nocov:
|
35
|
+
def inspect
|
36
|
+
"#<AllowPathRegexp #{'dir_only ' if @dir_only}#{@rule.inspect}>"
|
37
|
+
end
|
38
|
+
# :nocov:
|
39
|
+
|
40
|
+
def match?(candidate)
|
41
|
+
:allow if @rule.match?(candidate.relative_path)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|