fast_ignore 0.9.0 → 0.10.0
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/.leftovers.yml +5 -0
- data/.rubocop.yml +3 -0
- data/.spellr_wordlists/english.txt +9 -0
- data/CHANGELOG.md +10 -0
- data/README.md +76 -27
- data/Rakefile +3 -1
- data/fast_ignore.gemspec +1 -0
- data/lib/fast_ignore.rb +25 -61
- data/lib/fast_ignore/backports/delete_prefix_suffix.rb +33 -5
- data/lib/fast_ignore/rule.rb +38 -2
- data/lib/fast_ignore/rule_parser.rb +33 -22
- data/lib/fast_ignore/rule_set.rb +11 -13
- data/lib/fast_ignore/rule_set_builder.rb +1 -1
- data/lib/fast_ignore/version.rb +1 -1
- metadata +17 -3
- data/bin/ls-shebangs +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69021259195d30c9d3eb2e4461a779ef70b67d664370c5dff06d79f16ff009cb
|
4
|
+
data.tar.gz: f3e60391822c26b461316389277f3901801c89e1dfcea59e819976d2b4c4b761
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c99e8a9d3be9addeca4d18d327e450b9d6c345b6db354beb612e7f7e999af743d4ee4f62df9b1be0dec4a03820f948c52f8cb53d202ff8d3242499bbc453da87
|
7
|
+
data.tar.gz: 2d17fe903303a913df5d763789517897e7c444c89069b3f81ce28df0136c1c5c6a94fd1c6f8080e17291713a75da4246da67c1c86a90c89a9084137276ba0b4d
|
data/.leftovers.yml
ADDED
data/.rubocop.yml
CHANGED
@@ -8,15 +8,20 @@ cov
|
|
8
8
|
cyclomatic
|
9
9
|
enoent
|
10
10
|
enumerables
|
11
|
+
env
|
11
12
|
errno
|
12
13
|
extensionless
|
13
14
|
fancyignore
|
15
|
+
frotz
|
14
16
|
gemfile
|
15
17
|
github
|
16
18
|
gitignore
|
17
19
|
gitkeep
|
18
20
|
hashbang
|
21
|
+
includefile
|
22
|
+
janky
|
19
23
|
klass
|
24
|
+
llo
|
20
25
|
nocov
|
21
26
|
noninfringement
|
22
27
|
params
|
@@ -33,9 +38,13 @@ rvm
|
|
33
38
|
sherson
|
34
39
|
simplecov
|
35
40
|
stdin
|
41
|
+
substring
|
36
42
|
sudo
|
43
|
+
symlinks
|
37
44
|
tmp
|
38
45
|
toplevel
|
39
46
|
txt
|
40
47
|
unrecursive
|
48
|
+
upcase
|
49
|
+
usr
|
41
50
|
zsh
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# v0.10.0
|
2
|
+
- patterns with middle slashes are anchored to the root (like the gitignore documentation, now that it more clearly explains)
|
3
|
+
- new shebang pattern (#!:), the previous version was extremely janky.
|
4
|
+
- now you can ignore by shebang pattern
|
5
|
+
- symlinks aren't followed when deciding if a path is a directory or not (this now matches git)
|
6
|
+
- documentation improvements
|
7
|
+
- root can be given as a path relative to PWD
|
8
|
+
- includes with 'a/**/d' now matches a/b/c/d properly
|
9
|
+
-
|
10
|
+
|
1
11
|
# v0.9.0
|
2
12
|
- speed improvements, which may break things (Specifically, only using relative paths internally, is about 30% faster (depending on root depth))
|
3
13
|
- using a `ignore_files:` or `include_files:` that are outside the `root: (default $PWD)` will now raise an error.
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
This started as a way to quickly and natively ruby-ly parse gitignore files and find matching files.
|
6
6
|
It's now gained an equivalent includes file functionality, ARGV awareness, and some shebang matching, while still being extremely fast, to be a one-stop file-list for your linter.
|
7
7
|
|
8
|
-
Filter a directory tree using a .gitignore file. Recognises all of the [gitignore rules](https://www.git-scm.com/docs/gitignore#_pattern_format)
|
8
|
+
Filter a directory tree using a .gitignore file. Recognises all of the [gitignore rules](https://www.git-scm.com/docs/gitignore#_pattern_format)
|
9
9
|
|
10
10
|
```ruby
|
11
11
|
FastIgnore.new(relative: true).sort == `git ls-files`.split("\n").sort
|
@@ -34,61 +34,82 @@ $ gem install fast_ignore
|
|
34
34
|
FastIgnore.new.each { |file| puts "#{file} is not ignored by the .gitignore file" }
|
35
35
|
```
|
36
36
|
|
37
|
-
|
37
|
+
### `#each`, `#map` etc
|
38
|
+
|
39
|
+
The FastIgnore object is an enumerable and responds to all Enumerable methods
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
FastIgnore.new.to_a
|
43
|
+
FastIgnore.new.map { |file| file.upcase }
|
44
|
+
```
|
45
|
+
|
46
|
+
Like other enumerables, `FastIgnore#each` can return an enumerator
|
38
47
|
|
39
48
|
```ruby
|
40
49
|
FastIgnore.new.each.with_index { |file, index| puts "#{file}#{index}" }
|
41
50
|
```
|
42
51
|
|
52
|
+
### `relative: true`
|
43
53
|
By default, FastIgnore will return full paths. To return paths relative to the current working directory, use:
|
44
54
|
|
45
55
|
```ruby
|
46
56
|
FastIgnore.new(relative: true).to_a
|
47
57
|
```
|
48
58
|
|
49
|
-
|
50
|
-
To use a different directory:
|
51
|
-
```ruby
|
52
|
-
FastIgnore.new(root: '/absolute/path/to/root').to_a
|
53
|
-
```
|
59
|
+
### `root:`
|
54
60
|
|
55
|
-
|
61
|
+
By default, root is PWD (the current working directory)
|
62
|
+
This directory is used for:
|
63
|
+
- looking for .gitignore files
|
64
|
+
- as the root directory for array rules starting with `/` or ending with `/**`
|
65
|
+
- and the path that relative is relative to
|
66
|
+
- which files get checked
|
56
67
|
|
68
|
+
To use a different directory:
|
57
69
|
```ruby
|
58
|
-
FastIgnore.new(
|
59
|
-
FastIgnore.new(
|
70
|
+
FastIgnore.new(root: '/absolute/path/to/root').to_a
|
71
|
+
FastIgnore.new(root: '../relative/path/to/root').to_a
|
60
72
|
```
|
61
73
|
|
62
|
-
|
74
|
+
### `gitignore:`
|
63
75
|
|
76
|
+
By default, the .gitignore file in root directory is loaded.
|
77
|
+
To not do this use
|
64
78
|
```ruby
|
65
|
-
FastIgnore.new(
|
66
|
-
FastIgnore.new(ignore_rules: ['.git', '.gitkeep']).to_a
|
79
|
+
FastIgnore.new(gitignore: false).to_a
|
67
80
|
```
|
68
81
|
|
69
|
-
To
|
82
|
+
To raise an `Errno::ENOENT` error if the .gitignore file is not found use:
|
70
83
|
```ruby
|
71
|
-
FastIgnore.new(
|
72
|
-
FastIgnore.new(ignore_rules: %w{my*rule /and/another !rule}, gitignore: false).to_a
|
84
|
+
FastIgnore.new(gitignore: true).to_a
|
73
85
|
```
|
74
86
|
|
75
|
-
|
87
|
+
If the gitignore file is somewhere else
|
76
88
|
```ruby
|
77
89
|
FastIgnore.new(ignore_file: '/absolute/path/to/.gitignore', gitignore: false).to_a
|
78
90
|
```
|
79
91
|
Note that the location of the .gitignore file will affect rules beginning with `/` or ending in `/**`
|
80
92
|
|
81
|
-
|
93
|
+
### `ignore_files:`
|
94
|
+
You can specify other gitignore-style files to ignore as well.
|
95
|
+
Missing files will raise an `Errno::ENOENT` error.
|
96
|
+
|
82
97
|
```ruby
|
83
|
-
FastIgnore.new(
|
98
|
+
FastIgnore.new(ignore_files: '/absolute/path/to/my/ignore/file').to_a
|
99
|
+
FastIgnore.new(ignore_files: ['/absolute/path/to/my/ignore/file', '/and/another']).to_a
|
84
100
|
```
|
85
101
|
|
86
|
-
|
102
|
+
### `ignore_rules:`
|
103
|
+
You can also supply an array of rule strings.
|
104
|
+
|
87
105
|
```ruby
|
88
|
-
FastIgnore.new(
|
89
|
-
FastIgnore.new(
|
106
|
+
FastIgnore.new(ignore_rules: '.DS_Store').to_a
|
107
|
+
FastIgnore.new(ignore_rules: ['.git', '.gitkeep']).to_a
|
108
|
+
FastIgnore.new(ignore_rules: ".git\n.gitkeep").to_a
|
90
109
|
```
|
91
110
|
|
111
|
+
# `#allowed?`
|
112
|
+
|
92
113
|
To check if a single file is allowed, use
|
93
114
|
```ruby
|
94
115
|
FastIgnore.new.allowed?('relative/path')
|
@@ -97,7 +118,7 @@ FastIgnore.new.allowed?('/absolute/path')
|
|
97
118
|
FastIgnore.new.allowed?('~/home/path')
|
98
119
|
```
|
99
120
|
|
100
|
-
###
|
121
|
+
### `include_files:` and `include_rules:`
|
101
122
|
|
102
123
|
Building on the gitignore format, FastIgnore also accepts a list of allowed or included files.
|
103
124
|
|
@@ -132,22 +153,50 @@ It assumes all rules are anchored unless they begin with `*` or `!*`.
|
|
132
153
|
|
133
154
|
Note: it will *not* resolve e.g. `/../` in the middle of a rule that doesn't begin with any of `~`,`../`,`./`,`/`.
|
134
155
|
|
156
|
+
### shebang rules
|
157
|
+
|
158
|
+
Sometimes you need to match files by their shebang rather than their path or filename
|
159
|
+
|
160
|
+
To match extensionless files by shebang/hashbang/etc:
|
161
|
+
|
162
|
+
Lines beginning with `#!:` will match whole words in the shebang line of extensionless files.
|
163
|
+
e.g.
|
164
|
+
```gitignore
|
165
|
+
#!:ruby
|
166
|
+
```
|
167
|
+
will match shebang lines: `#!/usr/bin/env ruby` or `#!/usr/bin/ruby` or `#!/usr/bin/ruby -w`
|
168
|
+
e.g.
|
169
|
+
```gitignore
|
170
|
+
#!:bin/ruby
|
171
|
+
```
|
172
|
+
will match `#!/bin/ruby` or `#!/usr/bin/ruby` or `#!/usr/bin/ruby -w`
|
173
|
+
Currently only exact substring matches are available, There's no special handling of * or / or etc.
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
FastIgnore.new(include_rules: ['*.rb', '#!:ruby']).to_a
|
177
|
+
FastIgnore.new(ignore_rules: ['*.sh', '#!:sh', '#!:bash', '#!:zsh']).to_a
|
178
|
+
```
|
179
|
+
|
135
180
|
## Combinations
|
136
181
|
|
137
182
|
In the simplest case a file must be allowed by each ignore file, each include file, and each array of rules. That is, they are combined using AND.
|
138
183
|
|
139
|
-
To combine files using `OR`, that is, a file may be
|
140
|
-
|
184
|
+
To combine files using `OR`, that is, a file may be matched by either file it doesn't have to be referred to in both:
|
185
|
+
provide the files as strings to `include_rules:` or `ignore_rules:`
|
141
186
|
```ruby
|
142
|
-
FastIgnore.new(
|
187
|
+
FastIgnore.new(include_rules: [File.read('/my/path'), File.read('/another/path')])).to_a
|
143
188
|
```
|
189
|
+
This does unfortunately lose the file path as the root for `/` and `/**` rules.
|
190
|
+
If that's important, combine the files in the file system and use `include_files:` or `ignore_files:` as normal.
|
144
191
|
|
145
|
-
To use the additional ARGV handling rules mentioned above for files
|
192
|
+
To use the additional ARGV handling rules mentioned above for files, read the file into the array as a string.
|
146
193
|
|
147
194
|
```ruby
|
148
195
|
FastIgnore.new(argv_rules: ["my/rule", File.read('/my/path')]).to_a
|
149
196
|
```
|
150
197
|
|
198
|
+
This does unfortunately lose the file path as the root for `/` and `/**` rules.
|
199
|
+
|
151
200
|
## Known issues
|
152
201
|
- Doesn't take into account project excludes in `.git/info/exclude`
|
153
202
|
- Doesn't take into account globally ignored files in `git config core.excludesFile`.
|
data/Rakefile
CHANGED
@@ -4,9 +4,11 @@ require 'bundler/gem_tasks'
|
|
4
4
|
require 'rspec/core/rake_task'
|
5
5
|
require 'rubocop/rake_task'
|
6
6
|
require 'spellr/rake_task'
|
7
|
+
require 'leftovers/rake_task'
|
7
8
|
|
8
9
|
RuboCop::RakeTask.new
|
9
10
|
RSpec::Core::RakeTask.new(:spec)
|
10
11
|
Spellr::RakeTask.generate_task
|
12
|
+
Leftovers::RakeTask.generate_task
|
11
13
|
|
12
|
-
task default: [:spec, :rubocop, :spellr]
|
14
|
+
task default: [:spec, :rubocop, :spellr, :leftovers]
|
data/fast_ignore.gemspec
CHANGED
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.require_paths = ['lib']
|
31
31
|
|
32
32
|
spec.add_development_dependency 'bundler', '>= 1.17'
|
33
|
+
spec.add_development_dependency 'leftovers', '>= 0.2.1'
|
33
34
|
spec.add_development_dependency 'pry', '> 0'
|
34
35
|
spec.add_development_dependency 'rake', '>= 12.3.3'
|
35
36
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
data/lib/fast_ignore.rb
CHANGED
@@ -22,22 +22,27 @@ class FastIgnore
|
|
22
22
|
end
|
23
23
|
# :nocov:
|
24
24
|
|
25
|
-
def initialize(
|
25
|
+
def initialize( # rubocop:disable Metrics/MethodLength
|
26
26
|
relative: false,
|
27
|
-
root:
|
27
|
+
root: nil,
|
28
28
|
include_shebangs: nil,
|
29
29
|
**rule_set_builder_args
|
30
30
|
)
|
31
|
-
|
32
|
-
|
31
|
+
# :nocov:
|
32
|
+
if include_shebangs && !Array(include_shebangs).empty?
|
33
|
+
|
34
|
+
warn <<~WARNING
|
35
|
+
Removed FastIgnore `include_shebangs:` argument.
|
36
|
+
It will be ignored. Please replace with the include_rules: in the shebang format
|
37
|
+
https://github.com/robotdana/fast_ignore#shebang-rules
|
38
|
+
WARNING
|
39
|
+
end
|
40
|
+
# :nocov:
|
33
41
|
|
34
|
-
|
35
|
-
|
36
|
-
**rule_set_builder_args
|
37
|
-
)
|
42
|
+
root = root ? File.expand_path(root, ::Dir.pwd) : ::Dir.pwd
|
43
|
+
@root = "#{root}/"
|
38
44
|
|
39
|
-
@
|
40
|
-
@has_include_rule_sets = !@include_rule_sets.empty?
|
45
|
+
@rule_sets = ::FastIgnore::RuleSetBuilder.from_args(root: @root, **rule_set_builder_args)
|
41
46
|
@relative = relative
|
42
47
|
|
43
48
|
freeze
|
@@ -51,52 +56,35 @@ class FastIgnore
|
|
51
56
|
end
|
52
57
|
end
|
53
58
|
|
54
|
-
def allowed?(path)
|
59
|
+
def allowed?(path)
|
55
60
|
full_path = ::File.expand_path(path, @root)
|
56
61
|
return false unless full_path.start_with?(@root)
|
57
62
|
|
58
|
-
dir = ::File.
|
59
|
-
|
63
|
+
dir = ::File.lstat(full_path).directory?
|
60
64
|
return false if dir
|
61
65
|
|
62
66
|
relative_path = full_path.delete_prefix(@root)
|
67
|
+
filename = ::File.basename(relative_path)
|
63
68
|
|
64
|
-
|
65
|
-
return @include_rule_sets.all? { |r| r.allowed_recursive?(relative_path, dir) } unless @shebang_pattern
|
66
|
-
|
67
|
-
(@has_include_rule_sets &&
|
68
|
-
@include_rule_sets.all? { |r| r.allowed_unrecursive?(relative_path, false) }) ||
|
69
|
-
match_shebang?(full_path, ::File.basename(relative_path))
|
69
|
+
@rule_sets.all? { |r| r.allowed_recursive?(relative_path, dir, filename) }
|
70
70
|
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
71
71
|
false
|
72
72
|
end
|
73
73
|
|
74
74
|
private
|
75
75
|
|
76
|
-
def each_allowed(full_path = @root, relative_path = '', &block) # rubocop:disable Metrics/MethodLength
|
77
|
-
::Dir.each_child(full_path) do |
|
76
|
+
def each_allowed(full_path = @root, relative_path = '', &block) # rubocop:disable Metrics/MethodLength
|
77
|
+
::Dir.each_child(full_path) do |filename|
|
78
78
|
begin
|
79
|
-
full_child = full_path +
|
80
|
-
relative_child = relative_path +
|
81
|
-
dir = ::File.
|
79
|
+
full_child = full_path + filename
|
80
|
+
relative_child = relative_path + filename
|
81
|
+
dir = ::File.lstat(full_child).directory?
|
82
82
|
|
83
|
-
next unless @
|
83
|
+
next unless @rule_sets.all? { |r| r.allowed_unrecursive?(relative_child, dir, filename) }
|
84
84
|
|
85
85
|
if dir
|
86
|
-
next unless @shebang_pattern || @include_rule_sets.all? { |r| r.allowed_unrecursive?(relative_child, dir) }
|
87
|
-
|
88
86
|
each_allowed("#{full_child}/", "#{relative_child}/", &block)
|
89
87
|
else
|
90
|
-
if @shebang_pattern
|
91
|
-
unless (@has_include_rule_sets &&
|
92
|
-
@include_rule_sets.all? { |r| r.allowed_unrecursive?(relative_child, dir) }) ||
|
93
|
-
match_shebang?(full_child, basename)
|
94
|
-
next
|
95
|
-
end
|
96
|
-
else
|
97
|
-
next unless @include_rule_sets.all? { |r| r.allowed_unrecursive?(relative_child, dir) }
|
98
|
-
end
|
99
|
-
|
100
88
|
yield(@relative ? relative_child : full_child)
|
101
89
|
end
|
102
90
|
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
@@ -104,28 +92,4 @@ class FastIgnore
|
|
104
92
|
end
|
105
93
|
end
|
106
94
|
end
|
107
|
-
|
108
|
-
def match_shebang?(path, basename)
|
109
|
-
return false if basename.include?('.')
|
110
|
-
|
111
|
-
begin
|
112
|
-
f = ::File.new(path)
|
113
|
-
# i can't imagine a shebang being longer than 20 characters, lets multiply that by 10 just in case.
|
114
|
-
fragment = f.sysread(256)
|
115
|
-
f.close
|
116
|
-
rescue ::SystemCallError, ::EOFError
|
117
|
-
return false
|
118
|
-
end
|
119
|
-
|
120
|
-
@shebang_pattern.match?(fragment)
|
121
|
-
end
|
122
|
-
|
123
|
-
def prepare_shebang_pattern(rules)
|
124
|
-
return if !rules || (rules = Array(rules)).empty?
|
125
|
-
|
126
|
-
rules = rules.flat_map { |s| s.to_s.split("\n") }
|
127
|
-
rules_re = rules.map { |s| Regexp.escape(s.to_s) }.join('|')
|
128
|
-
|
129
|
-
/\A#!.*\b(?:#{rules_re})\b/.freeze
|
130
|
-
end
|
131
95
|
end
|
@@ -5,22 +5,50 @@ class FastIgnore
|
|
5
5
|
module Backports
|
6
6
|
module DeletePrefixSuffix
|
7
7
|
refine ::String do
|
8
|
+
# delete_prefix!(prefix) -> self or nil
|
9
|
+
# Deletes leading prefix from str, returning nil if no change was made.
|
10
|
+
#
|
11
|
+
# "hello".delete_prefix!("hel") #=> "lo"
|
12
|
+
# "hello".delete_prefix!("llo") #=> nil
|
8
13
|
def delete_prefix!(str)
|
9
|
-
|
14
|
+
return unless start_with?(str)
|
15
|
+
|
16
|
+
slice!(0..(str.length - 1))
|
10
17
|
self
|
11
18
|
end
|
12
19
|
|
20
|
+
# delete_suffix!(suffix) -> self or nil
|
21
|
+
# Deletes trailing suffix from str, returning nil if no change was made.
|
22
|
+
#
|
23
|
+
# "hello".delete_suffix!("llo") #=> "he"
|
24
|
+
# "hello".delete_suffix!("hel") #=> nil
|
13
25
|
def delete_suffix!(str)
|
14
|
-
|
26
|
+
return unless end_with?(str)
|
27
|
+
|
28
|
+
slice!(-str.length..-1)
|
15
29
|
self
|
16
30
|
end
|
17
31
|
|
32
|
+
# delete_prefix(prefix) -> new_str click to toggle source
|
33
|
+
# Returns a copy of str with leading prefix deleted.
|
34
|
+
#
|
35
|
+
# "hello".delete_prefix("hel") #=> "lo"
|
36
|
+
# "hello".delete_prefix("llo") #=> "hello"
|
18
37
|
def delete_prefix(str)
|
19
|
-
dup
|
38
|
+
s = dup
|
39
|
+
s.delete_prefix!(str)
|
40
|
+
s
|
20
41
|
end
|
21
42
|
|
22
|
-
|
23
|
-
|
43
|
+
# delete_suffix(suffix) -> new_str
|
44
|
+
# Returns a copy of str with trailing suffix deleted.
|
45
|
+
#
|
46
|
+
# "hello".delete_suffix("llo") #=> "he"
|
47
|
+
# "hello".delete_suffix("hel") #=> "hello"
|
48
|
+
def delete_suffix(str) # leftovers:allowed
|
49
|
+
s = dup
|
50
|
+
s.delete_suffix!(str)
|
51
|
+
s
|
24
52
|
end
|
25
53
|
end
|
26
54
|
end
|
data/lib/fast_ignore/rule.rb
CHANGED
@@ -12,21 +12,57 @@ class FastIgnore
|
|
12
12
|
alias_method :negation?, :negation
|
13
13
|
attr_reader :dir_only
|
14
14
|
alias_method :dir_only?, :dir_only
|
15
|
+
attr_reader :file_only
|
16
|
+
alias_method :file_only?, :file_only
|
15
17
|
|
18
|
+
attr_reader :shebang
|
16
19
|
attr_reader :rule
|
17
20
|
|
18
|
-
def initialize(rule, dir_only, negation)
|
21
|
+
def initialize(rule, dir_only, file_only, negation, shebang = nil)
|
19
22
|
@rule = rule
|
20
23
|
@dir_only = dir_only
|
24
|
+
@file_only = file_only
|
21
25
|
@negation = negation
|
26
|
+
@shebang = shebang
|
22
27
|
|
23
28
|
freeze
|
24
29
|
end
|
25
30
|
|
26
31
|
# :nocov:
|
27
32
|
def inspect
|
28
|
-
|
33
|
+
if shebang
|
34
|
+
"#<Rule #{'allow ' if negation?}#!:#{shebang.to_s[15..-4]}>"
|
35
|
+
else
|
36
|
+
"#<Rule #{'!' if negation?}#{rule}#{'/' if dir_only?}>"
|
37
|
+
end
|
29
38
|
end
|
30
39
|
# :nocov:
|
40
|
+
|
41
|
+
def match?(path, filename)
|
42
|
+
if @shebang
|
43
|
+
match_shebang?(path, filename)
|
44
|
+
else
|
45
|
+
::File.fnmatch?(@rule, path, 14)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def match_shebang?(path, filename)
|
50
|
+
return false if filename.include?('.')
|
51
|
+
|
52
|
+
first_line(path)&.match?(@shebang)
|
53
|
+
end
|
54
|
+
|
55
|
+
def first_line(path)
|
56
|
+
file = ::File.new(path)
|
57
|
+
first_line = file.sysread(25)
|
58
|
+
first_line += file.sysread(50) until first_line.include?("\n")
|
59
|
+
file.close
|
60
|
+
first_line
|
61
|
+
rescue ::EOFError, ::SystemCallError
|
62
|
+
# :nocov:
|
63
|
+
file&.close
|
64
|
+
# :nocov:
|
65
|
+
first_line
|
66
|
+
end
|
31
67
|
end
|
32
68
|
end
|
@@ -11,13 +11,15 @@ class FastIgnore
|
|
11
11
|
|
12
12
|
# rule or nil
|
13
13
|
class << self
|
14
|
-
def new_rule(rule, rule_set:, allow: false, expand_path: false, file_root: nil) # rubocop:disable Metrics/MethodLength
|
14
|
+
def new_rule(rule, rule_set:, allow: false, expand_path: false, file_root: nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
15
15
|
rule = rule.dup
|
16
16
|
strip(rule)
|
17
|
-
|
17
|
+
|
18
|
+
return rule_set.append_rules(false, shebang_rules(rule, allow)) if extract_shebang(rule)
|
18
19
|
|
19
20
|
return if skip?(rule)
|
20
21
|
|
22
|
+
dir_only = extract_dir_only(rule)
|
21
23
|
negation = extract_negation(rule, allow)
|
22
24
|
|
23
25
|
expand_rule_path(rule, expand_path) if expand_path
|
@@ -36,34 +38,41 @@ class FastIgnore
|
|
36
38
|
|
37
39
|
private
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
rules = [::FastIgnore::Rule.new(rule, dir_only, negation)]
|
41
|
+
def shebang_rules(rule, allow)
|
42
|
+
rules = [::FastIgnore::Rule.new(nil, false, true, allow, /\A#!.*\b#{Regexp.escape(rule)}\b/)]
|
42
43
|
return rules unless allow
|
43
44
|
|
44
|
-
rules << ::FastIgnore::Rule.new(
|
45
|
-
parent = File.dirname(rule)
|
46
|
-
while parent != DOT
|
47
|
-
rules << ::FastIgnore::Rule.new(parent.freeze, true, true)
|
48
|
-
parent = File.dirname(parent)
|
49
|
-
end
|
45
|
+
rules << ::FastIgnore::Rule.new('**/*', true, false, true)
|
50
46
|
rules
|
51
47
|
end
|
52
48
|
|
53
|
-
def
|
54
|
-
|
49
|
+
def rules(rule, allow, dir_only, negation)
|
50
|
+
rules = [::FastIgnore::Rule.new(rule, dir_only, false, negation)]
|
51
|
+
return rules unless allow
|
52
|
+
|
53
|
+
rules << ::FastIgnore::Rule.new("#{rule}/**/*", false, false, negation)
|
54
|
+
rules + ancestor_rules(rule)
|
55
|
+
end
|
56
|
+
|
57
|
+
def ancestor_rules(parent)
|
58
|
+
ancestor_rules = []
|
55
59
|
|
56
|
-
|
60
|
+
while (parent = ::File.dirname(parent)) != '.'
|
61
|
+
rule = ::File.basename(parent) == '**' ? "#{parent}/*" : parent.freeze
|
62
|
+
ancestor_rules << ::FastIgnore::Rule.new(rule, true, false, true)
|
63
|
+
end
|
57
64
|
|
58
|
-
|
65
|
+
ancestor_rules
|
59
66
|
end
|
60
67
|
|
61
|
-
def
|
62
|
-
return
|
68
|
+
def extract_negation(rule, allow)
|
69
|
+
return allow unless rule.delete_prefix!('!')
|
63
70
|
|
64
|
-
|
71
|
+
not allow
|
72
|
+
end
|
65
73
|
|
66
|
-
|
74
|
+
def extract_dir_only(rule)
|
75
|
+
rule.delete_suffix!('/')
|
67
76
|
end
|
68
77
|
|
69
78
|
def strip(rule)
|
@@ -72,9 +81,7 @@ class FastIgnore
|
|
72
81
|
end
|
73
82
|
|
74
83
|
def anchored?(rule)
|
75
|
-
rule.
|
76
|
-
rule.end_with?('/**') ||
|
77
|
-
rule.include?('/**/')
|
84
|
+
rule.include?('/') # we've already removed the trailing '/' with extract_dir_only
|
78
85
|
end
|
79
86
|
|
80
87
|
EXPAND_PATH_RE = %r{^(?:[~/]|\.{1,2}/)}.freeze
|
@@ -84,6 +91,10 @@ class FastIgnore
|
|
84
91
|
rule.prepend('/') unless rule.start_with?('*') || rule.start_with?('/')
|
85
92
|
end
|
86
93
|
|
94
|
+
def extract_shebang(rule)
|
95
|
+
rule.delete_prefix!('#!:') && (rule.strip! || true)
|
96
|
+
end
|
97
|
+
|
87
98
|
def skip?(rule)
|
88
99
|
rule.empty? || rule.start_with?('#')
|
89
100
|
end
|
data/lib/fast_ignore/rule_set.rb
CHANGED
@@ -2,10 +2,6 @@
|
|
2
2
|
|
3
3
|
class FastIgnore
|
4
4
|
class RuleSet
|
5
|
-
attr_reader :rules
|
6
|
-
attr_reader :allow
|
7
|
-
alias_method :allow?, :allow
|
8
|
-
|
9
5
|
def initialize(allow: false)
|
10
6
|
@dir_rules = []
|
11
7
|
@file_rules = []
|
@@ -21,17 +17,18 @@ class FastIgnore
|
|
21
17
|
super
|
22
18
|
end
|
23
19
|
|
24
|
-
def allowed_recursive?(path, dir)
|
20
|
+
def allowed_recursive?(path, dir, filename)
|
25
21
|
@allowed_recursive.fetch(path) do
|
26
22
|
@allowed_recursive[path] =
|
27
|
-
allowed_recursive?(::File.dirname(path), true) && allowed_unrecursive?(path, dir)
|
23
|
+
allowed_recursive?(::File.dirname(path), true, nil) && allowed_unrecursive?(path, dir, filename)
|
28
24
|
end
|
29
25
|
end
|
30
26
|
|
31
|
-
def allowed_unrecursive?(path, dir)
|
27
|
+
def allowed_unrecursive?(path, dir, filename)
|
32
28
|
(dir ? @dir_rules : @file_rules).reverse_each do |rule|
|
33
29
|
# 14 = Rule::FNMATCH_OPTIONS
|
34
|
-
|
30
|
+
|
31
|
+
return rule.negation? if rule.match?(path, filename)
|
35
32
|
end
|
36
33
|
|
37
34
|
(not @allow) || (@any_not_anchored if dir)
|
@@ -39,18 +36,19 @@ class FastIgnore
|
|
39
36
|
|
40
37
|
def append_rules(anchored, rules)
|
41
38
|
rules.each do |rule|
|
42
|
-
@dir_rules << rule
|
43
|
-
@file_rules << rule unless rule.dir_only?
|
39
|
+
(@dir_rules << rule) unless rule.file_only?
|
40
|
+
(@file_rules << rule) unless rule.dir_only?
|
44
41
|
@any_not_anchored ||= !anchored
|
42
|
+
@has_shebang_rules ||= rule.shebang
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
48
|
-
def
|
49
|
-
@dir_rules.length
|
46
|
+
def weight
|
47
|
+
@dir_rules.length + (@has_shebang_rules ? 10 : 0)
|
50
48
|
end
|
51
49
|
|
52
50
|
def empty?
|
53
|
-
@dir_rules.empty?
|
51
|
+
@dir_rules.empty? && @file_rules.empty?
|
54
52
|
end
|
55
53
|
end
|
56
54
|
end
|
data/lib/fast_ignore/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_ignore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dana Sherson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.17'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: leftovers
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.1
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.2.1
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: pry
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,6 +144,7 @@ extensions: []
|
|
130
144
|
extra_rdoc_files: []
|
131
145
|
files:
|
132
146
|
- ".gitignore"
|
147
|
+
- ".leftovers.yml"
|
133
148
|
- ".rspec"
|
134
149
|
- ".rubocop.yml"
|
135
150
|
- ".simplecov"
|
@@ -146,7 +161,6 @@ files:
|
|
146
161
|
- bin/console
|
147
162
|
- bin/ls
|
148
163
|
- bin/ls-disable-gems
|
149
|
-
- bin/ls-shebangs
|
150
164
|
- bin/setup
|
151
165
|
- fast_ignore.gemspec
|
152
166
|
- lib/fast_ignore.rb
|