fast_ignore 0.10.2 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.spellr_wordlists/english.txt +4 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +4 -0
- data/README.md +27 -3
- data/bin/time +11 -11
- data/lib/fast_ignore.rb +15 -19
- data/lib/fast_ignore/backports.rb +60 -6
- data/lib/fast_ignore/fn_match_to_re.rb +96 -0
- data/lib/fast_ignore/rule.rb +9 -11
- data/lib/fast_ignore/rule_builder.rb +10 -10
- data/lib/fast_ignore/rule_set.rb +18 -14
- data/lib/fast_ignore/rule_set_builder.rb +1 -4
- data/lib/fast_ignore/shebang_rule.rb +12 -6
- data/lib/fast_ignore/version.rb +1 -1
- metadata +3 -4
- data/lib/fast_ignore/backports/delete_prefix_suffix.rb +0 -56
- data/lib/fast_ignore/backports/dir_each_child.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f467113b0ee03faf028758ac176c59b3e51465684f8ffe144d432ba75133c10f
|
4
|
+
data.tar.gz: 2f6f0ce0babec00977a55595baa7ec806d7a9a3f1fec86df46f29475b41238cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a7698e54ab0897b6b50c8638d41f3243bd3ba4ced559a5c7e01f6d365b6fb875b906a1924e96641572815748479eb793dfabc91da6f3bec62115f98ff39a392
|
7
|
+
data.tar.gz: 7e8b85ff838d04d1c16cd3eca5a480d6948342a07667760782840b0e36a706c02db2bb4e09e3370b3ccc1cdc2f38f9e893707c9f2128652a8a4aae4cdb8dd3d0
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# v0.11.0
|
2
|
+
- major performance improvement (use regexp rather than fnmatch)
|
3
|
+
- optionally pass directory: and content: into allowed? if these are already loaded.
|
4
|
+
|
1
5
|
# v0.10.2
|
2
6
|
- add FastIgnore#=== as an alias for FastIgnore#allowed? so that FastIgnore objects can be used for case statements.
|
3
7
|
- Fix shebangs in non-pwd-root situations
|
data/README.md
CHANGED
@@ -11,6 +11,16 @@ Filter a directory tree using a .gitignore file. Recognises all of the [gitignor
|
|
11
11
|
FastIgnore.new(relative: true).sort == `git ls-files`.split("\n").sort
|
12
12
|
```
|
13
13
|
|
14
|
+
## Features
|
15
|
+
|
16
|
+
- Fast (faster than using `` `git ls-files`.split("\n") `` for small repos (because it avoids the overhead of ``` `` ```))
|
17
|
+
- Supports ruby 2.4 - 2.7
|
18
|
+
- supports all gitignore rule patterns
|
19
|
+
- doesn't require git to be installed
|
20
|
+
- supports a gitignore-esque "include" patterns. (`include_rules:`/`include_files:`)
|
21
|
+
- supports an expansion of include patterns, matching expanded paths (`argv_rules:`)
|
22
|
+
- supports matching by shebang rather than filename for extensionless files: `#!:`
|
23
|
+
|
14
24
|
## Installation
|
15
25
|
|
16
26
|
Add this line to your application's Gemfile:
|
@@ -62,7 +72,8 @@ FastIgnore.new.allowed?('~/home/path')
|
|
62
72
|
This is aliased as `===` so you can use the FastIgnore object in case statements.
|
63
73
|
```ruby
|
64
74
|
case my_path
|
65
|
-
when FastIgnore.new
|
75
|
+
when FastIgnore.new
|
76
|
+
puts(my_path)
|
66
77
|
end
|
67
78
|
```
|
68
79
|
|
@@ -214,6 +225,15 @@ FastIgnore.new(argv_rules: ["my/rule", File.read('/my/path')]).to_a
|
|
214
225
|
|
215
226
|
This does unfortunately lose the file path as the root for `/` and `/**` rules.
|
216
227
|
|
228
|
+
### optimising #allowed?
|
229
|
+
|
230
|
+
To avoid unnecessary calls to the filesystem, if your code already knows whether or not it's a directory, or if you're checking shebangs and you have already read the content of the file: use
|
231
|
+
```ruby
|
232
|
+
FastIgnore.new.allowed?('relative/path', directory: false, content: "#!/usr/bin/ruby\n\nputs 'ok'\n")
|
233
|
+
```
|
234
|
+
This is not required, and if FastIgnore does have to go to the filesystem for this information it's well optimised to only read what is necessary.
|
235
|
+
|
236
|
+
|
217
237
|
## Known issues
|
218
238
|
- Doesn't take into account project excludes in `.git/info/exclude`
|
219
239
|
- Doesn't take into account globally ignored files in `git config core.excludesFile`.
|
@@ -224,9 +244,13 @@ This does unfortunately lose the file path as the root for `/` and `/**` rules.
|
|
224
244
|
|
225
245
|
## Development
|
226
246
|
|
227
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake
|
247
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests and linters.
|
248
|
+
|
249
|
+
You can run `bin/console` for an interactive prompt that will allow you to experiment.
|
250
|
+
`bin/ls [argv_rules]` will return something equivalent to `git ls-files` and `bin/time [argv_rules]` will give you the average time for 30 runs.
|
251
|
+
This repo is too small to stress bin/time more than 0.01s, switch to a large repo and find the average time before and after changes.
|
228
252
|
|
229
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
253
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
230
254
|
|
231
255
|
## Contributing
|
232
256
|
|
data/bin/time
CHANGED
@@ -5,17 +5,17 @@
|
|
5
5
|
# ensure nothing else is watching that dir in the filesystem e.g. webpack
|
6
6
|
|
7
7
|
require 'open3'
|
8
|
+
require 'shellwords'
|
8
9
|
RUNS = 30
|
9
|
-
SCRIPT = "time #{__dir__}/ls"
|
10
|
-
Dir.chdir ARGV[0] do
|
11
|
-
times = Array.new(RUNS).map do
|
12
|
-
run_times = Open3.capture3(SCRIPT)[1]
|
13
|
-
puts run_times.lstrip
|
14
|
-
run_times.scan(/(?:\d+(?:.\d+)?)/)
|
15
|
-
end
|
10
|
+
SCRIPT = "time #{__dir__}/ls #{Shellwords.join(ARGV)}"
|
16
11
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
)
|
12
|
+
times = Array.new(RUNS).map do
|
13
|
+
run_times = Open3.capture3(SCRIPT)[1]
|
14
|
+
puts run_times.lstrip
|
15
|
+
run_times.scan(/(?:\d+(?:.\d+)?)/)
|
21
16
|
end
|
17
|
+
|
18
|
+
puts format(
|
19
|
+
"\e[1mAverage:\n\e[32m%0.2f real %0.2f user %0.2f sys\e[0m", # rubocop:disable Style/FormatStringToken
|
20
|
+
*times.transpose.map { |n| (n.map(&:to_f).sum / RUNS) }
|
21
|
+
)
|
data/lib/fast_ignore.rb
CHANGED
@@ -7,6 +7,7 @@ require_relative './fast_ignore/rule_builder'
|
|
7
7
|
require_relative './fast_ignore/rule_set'
|
8
8
|
require_relative './fast_ignore/rule'
|
9
9
|
require_relative './fast_ignore/shebang_rule'
|
10
|
+
require_relative './fast_ignore/fn_match_to_re'
|
10
11
|
|
11
12
|
class FastIgnore
|
12
13
|
class Error < StandardError; end
|
@@ -14,13 +15,8 @@ class FastIgnore
|
|
14
15
|
include ::Enumerable
|
15
16
|
|
16
17
|
# :nocov:
|
17
|
-
|
18
|
-
|
19
|
-
using ::FastIgnore::Backports::DeletePrefixSuffix
|
20
|
-
|
21
|
-
require_relative 'fast_ignore/backports/dir_each_child'
|
22
|
-
using ::FastIgnore::Backports::DirEachChild
|
23
|
-
end
|
18
|
+
using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
|
19
|
+
using ::FastIgnore::Backports::DirEachChild if defined?(::FastIgnore::Backports::DirEachChild)
|
24
20
|
# :nocov:
|
25
21
|
|
26
22
|
def initialize(relative: false, root: nil, follow_symlinks: false, **rule_set_builder_args)
|
@@ -41,23 +37,15 @@ class FastIgnore
|
|
41
37
|
each_recursive(root_from_pwd, '', &block)
|
42
38
|
end
|
43
39
|
|
44
|
-
def
|
45
|
-
if @follow_symlinks
|
46
|
-
::File.stat(path).directory?
|
47
|
-
else
|
48
|
-
::File.lstat(path).directory?
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def allowed?(path)
|
40
|
+
def allowed?(path, directory: nil, content: nil)
|
53
41
|
full_path = ::File.expand_path(path, @root)
|
54
42
|
return false unless full_path.start_with?(@root)
|
55
|
-
return false if directory?(full_path)
|
43
|
+
return false if directory.nil? ? directory?(full_path) : directory
|
56
44
|
|
57
45
|
relative_path = full_path.delete_prefix(@root)
|
58
46
|
filename = ::File.basename(relative_path)
|
59
47
|
|
60
|
-
@rule_sets.all? { |r| r.allowed_recursive?(relative_path, false, full_path, filename) }
|
48
|
+
@rule_sets.all? { |r| r.allowed_recursive?(relative_path, false, full_path, filename, content) }
|
61
49
|
rescue ::Errno::ENOENT, ::Errno::EACCES, ::Errno::ENOTDIR, ::Errno::ELOOP, ::Errno::ENAMETOOLONG
|
62
50
|
false
|
63
51
|
end
|
@@ -65,6 +53,14 @@ class FastIgnore
|
|
65
53
|
|
66
54
|
private
|
67
55
|
|
56
|
+
def directory?(path)
|
57
|
+
if @follow_symlinks
|
58
|
+
::File.stat(path).directory?
|
59
|
+
else
|
60
|
+
::File.lstat(path).directory?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
68
64
|
def each_recursive(parent_full_path, parent_relative_path, &block) # rubocop:disable Metrics/MethodLength
|
69
65
|
::Dir.each_child(parent_full_path) do |filename|
|
70
66
|
begin
|
@@ -72,7 +68,7 @@ class FastIgnore
|
|
72
68
|
relative_path = parent_relative_path + filename
|
73
69
|
dir = directory?(full_path)
|
74
70
|
|
75
|
-
next unless @rule_sets.all? { |r| r.allowed_unrecursive?(relative_path, dir, full_path, filename) }
|
71
|
+
next unless @rule_sets.all? { |r| r.allowed_unrecursive?(relative_path, dir, full_path, filename, nil) }
|
76
72
|
|
77
73
|
if dir
|
78
74
|
each_recursive(full_path + '/', relative_path + '/', &block)
|
@@ -2,15 +2,69 @@
|
|
2
2
|
|
3
3
|
class FastIgnore
|
4
4
|
module Backports
|
5
|
-
|
5
|
+
ruby_major, ruby_minor = RUBY_VERSION.split('.', 2)
|
6
|
+
unless ruby_major.to_i > 2 || ruby_major.to_i == 2 && ruby_minor.to_i > 5
|
7
|
+
module DirEachChild
|
8
|
+
refine ::Dir.singleton_class do
|
9
|
+
def each_child(path, &block)
|
10
|
+
Dir.entries(path).each do |entry|
|
11
|
+
next if entry == '.' || entry == '..'
|
6
12
|
|
7
|
-
|
8
|
-
|
13
|
+
block.call entry
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
9
18
|
|
10
|
-
|
11
|
-
|
19
|
+
module DeletePrefixSuffix
|
20
|
+
refine ::String do
|
21
|
+
# delete_prefix!(prefix) -> self or nil
|
22
|
+
# Deletes leading prefix from str, returning nil if no change was made.
|
23
|
+
#
|
24
|
+
# "hello".delete_prefix!("hel") #=> "lo"
|
25
|
+
# "hello".delete_prefix!("llo") #=> nil
|
26
|
+
def delete_prefix!(str)
|
27
|
+
return unless start_with?(str)
|
12
28
|
|
13
|
-
|
29
|
+
slice!(0..(str.length - 1))
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# delete_suffix!(suffix) -> self or nil
|
34
|
+
# Deletes trailing suffix from str, returning nil if no change was made.
|
35
|
+
#
|
36
|
+
# "hello".delete_suffix!("llo") #=> "he"
|
37
|
+
# "hello".delete_suffix!("hel") #=> nil
|
38
|
+
def delete_suffix!(str)
|
39
|
+
return unless end_with?(str)
|
40
|
+
|
41
|
+
slice!(-str.length..-1)
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# delete_prefix(prefix) -> new_str click to toggle source
|
46
|
+
# Returns a copy of str with leading prefix deleted.
|
47
|
+
#
|
48
|
+
# "hello".delete_prefix("hel") #=> "lo"
|
49
|
+
# "hello".delete_prefix("llo") #=> "hello"
|
50
|
+
def delete_prefix(str)
|
51
|
+
s = dup
|
52
|
+
s.delete_prefix!(str)
|
53
|
+
s
|
54
|
+
end
|
55
|
+
|
56
|
+
# delete_suffix(suffix) -> new_str
|
57
|
+
# Returns a copy of str with trailing suffix deleted.
|
58
|
+
#
|
59
|
+
# "hello".delete_suffix("llo") #=> "he"
|
60
|
+
# "hello".delete_suffix("hel") #=> "hello"
|
61
|
+
def delete_suffix(str) # leftovers:allowed
|
62
|
+
s = dup
|
63
|
+
s.delete_suffix!(str)
|
64
|
+
s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
14
68
|
end
|
15
69
|
end
|
16
70
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FastIgnore
|
4
|
+
module FNMatchToRegex
|
5
|
+
# This doesn't look rubyish because i ported it from rust (the only rust i ever wrote that worked)
|
6
|
+
class << self
|
7
|
+
def call(pattern) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
8
|
+
re = '\\A'.dup
|
9
|
+
|
10
|
+
in_character_group = false
|
11
|
+
has_characters_in_group = false
|
12
|
+
escape_next_character = false
|
13
|
+
last_char_opened_character_group = false
|
14
|
+
negated_character_group = false
|
15
|
+
stars = 0
|
16
|
+
|
17
|
+
pattern.each_char do |char| # rubocop:disable Metrics/BlockLength
|
18
|
+
if escape_next_character
|
19
|
+
re << Regexp.escape(char)
|
20
|
+
escape_next_character = false
|
21
|
+
elsif char == '\\' # single char, just needs to be escaped
|
22
|
+
escape_next_character = true
|
23
|
+
elsif in_character_group
|
24
|
+
if char == '/'
|
25
|
+
if negated_character_group
|
26
|
+
has_characters_in_group = true
|
27
|
+
re << char
|
28
|
+
end
|
29
|
+
elsif char == '^'
|
30
|
+
if last_char_opened_character_group
|
31
|
+
re << char
|
32
|
+
negated_character_group = true
|
33
|
+
else
|
34
|
+
re << '\\^'
|
35
|
+
has_characters_in_group = true
|
36
|
+
end
|
37
|
+
# not characters in group
|
38
|
+
elsif char == ']'
|
39
|
+
break unless has_characters_in_group
|
40
|
+
|
41
|
+
re << ']'
|
42
|
+
in_character_group = false
|
43
|
+
has_characters_in_group = false
|
44
|
+
negated_character_group = false
|
45
|
+
last_char_opened_character_group = false
|
46
|
+
elsif char == '-'
|
47
|
+
has_characters_in_group = true
|
48
|
+
re << char
|
49
|
+
else
|
50
|
+
has_characters_in_group = true
|
51
|
+
re << Regexp.escape(char)
|
52
|
+
end
|
53
|
+
last_char_opened_character_group = false
|
54
|
+
elsif char == '*'
|
55
|
+
stars += 1
|
56
|
+
elsif char == '/'
|
57
|
+
re << if stars >= 2
|
58
|
+
'(?:.*/)?'
|
59
|
+
elsif stars.positive?
|
60
|
+
'[^/]*/'
|
61
|
+
else
|
62
|
+
char
|
63
|
+
end
|
64
|
+
stars = 0
|
65
|
+
else
|
66
|
+
if stars.positive?
|
67
|
+
re << '[^/]*'
|
68
|
+
stars = 0
|
69
|
+
end
|
70
|
+
if char == '?'
|
71
|
+
re << '[^/]'
|
72
|
+
elsif char == '['
|
73
|
+
re << '['
|
74
|
+
in_character_group = true
|
75
|
+
last_char_opened_character_group = true
|
76
|
+
else
|
77
|
+
re << Regexp.escape(char)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if in_character_group
|
83
|
+
return /(?!)/ # impossible to match anything
|
84
|
+
end
|
85
|
+
|
86
|
+
if stars >= 2
|
87
|
+
re << '.*'
|
88
|
+
elsif stars.positive?
|
89
|
+
re << '[^/]*'
|
90
|
+
end
|
91
|
+
re << '\\z'
|
92
|
+
Regexp.new(re, Regexp::IGNORECASE)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/fast_ignore/rule.rb
CHANGED
@@ -2,12 +2,6 @@
|
|
2
2
|
|
3
3
|
class FastIgnore
|
4
4
|
class Rule
|
5
|
-
# FNMATCH_OPTIONS = (
|
6
|
-
# ::File::FNM_DOTMATCH |
|
7
|
-
# ::File::FNM_PATHNAME |
|
8
|
-
# ::File::FNM_CASEFOLD
|
9
|
-
# ).freeze # = 14
|
10
|
-
|
11
5
|
attr_reader :negation
|
12
6
|
alias_method :negation?, :negation
|
13
7
|
undef :negation
|
@@ -20,12 +14,16 @@ class FastIgnore
|
|
20
14
|
alias_method :unanchored?, :unanchored
|
21
15
|
undef :unanchored
|
22
16
|
|
23
|
-
|
24
|
-
|
17
|
+
attr_reader :type
|
18
|
+
attr_reader :rule
|
19
|
+
|
20
|
+
def initialize(rule, negation, unanchored = nil, dir_only = nil)
|
21
|
+
@rule = rule.is_a?(Regexp) ? rule : ::FastIgnore::FNMatchToRegex.call(rule)
|
25
22
|
@unanchored = unanchored
|
26
23
|
@dir_only = dir_only
|
27
24
|
@negation = negation
|
28
|
-
|
25
|
+
|
26
|
+
@type = negation ? 1 : 0
|
29
27
|
|
30
28
|
freeze
|
31
29
|
end
|
@@ -44,8 +42,8 @@ class FastIgnore
|
|
44
42
|
end
|
45
43
|
# :nocov:
|
46
44
|
|
47
|
-
def match?(relative_path, _, _)
|
48
|
-
|
45
|
+
def match?(relative_path, _, _, _)
|
46
|
+
@rule.match?(relative_path)
|
49
47
|
end
|
50
48
|
end
|
51
49
|
end
|
@@ -4,10 +4,7 @@ class FastIgnore
|
|
4
4
|
module RuleBuilder
|
5
5
|
class << self
|
6
6
|
# :nocov:
|
7
|
-
|
8
|
-
require_relative 'backports/delete_prefix_suffix'
|
9
|
-
using ::FastIgnore::Backports::DeletePrefixSuffix
|
10
|
-
end
|
7
|
+
using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
|
11
8
|
# :nocov:
|
12
9
|
|
13
10
|
def build(rule, allow, expand_path, file_root)
|
@@ -50,8 +47,11 @@ class FastIgnore
|
|
50
47
|
dir_only = extract_dir_only(rule)
|
51
48
|
negation = extract_negation(rule, allow)
|
52
49
|
|
53
|
-
|
54
|
-
|
50
|
+
unanchored = if expand_path
|
51
|
+
expand_rule_path(rule, expand_path)
|
52
|
+
else
|
53
|
+
unanchored?(rule)
|
54
|
+
end
|
55
55
|
rule.delete_prefix!('/')
|
56
56
|
|
57
57
|
rule.prepend("#{file_root}#{'**/' if unanchored}") if file_root || unanchored
|
@@ -73,7 +73,7 @@ class FastIgnore
|
|
73
73
|
def expand_rule_path(rule, root)
|
74
74
|
rule.replace(::File.expand_path(rule)) if rule.match?(EXPAND_PATH_RE)
|
75
75
|
rule.delete_prefix!(root)
|
76
|
-
rule.
|
76
|
+
rule.start_with?('*')
|
77
77
|
end
|
78
78
|
|
79
79
|
def unanchored?(rule)
|
@@ -81,10 +81,10 @@ class FastIgnore
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def build_gitignore_rules(rule, unanchored, allow, dir_only, negation)
|
84
|
-
rules = [::FastIgnore::Rule.new(rule
|
84
|
+
rules = [::FastIgnore::Rule.new(rule, negation, unanchored, dir_only)]
|
85
85
|
return rules unless allow
|
86
86
|
|
87
|
-
rules << ::FastIgnore::Rule.new("#{rule}/**/*", unanchored, false
|
87
|
+
rules << ::FastIgnore::Rule.new("#{rule}/**/*", negation, unanchored, false)
|
88
88
|
rules + ancestor_rules(rule, unanchored)
|
89
89
|
end
|
90
90
|
|
@@ -93,7 +93,7 @@ class FastIgnore
|
|
93
93
|
|
94
94
|
while (parent = ::File.dirname(parent)) != '.'
|
95
95
|
rule = ::File.basename(parent) == '**' ? "#{parent}/*" : parent.freeze
|
96
|
-
ancestor_rules << ::FastIgnore::Rule.new(rule,
|
96
|
+
ancestor_rules << ::FastIgnore::Rule.new(rule, true, unanchored, true)
|
97
97
|
end
|
98
98
|
|
99
99
|
ancestor_rules
|
data/lib/fast_ignore/rule_set.rb
CHANGED
@@ -3,39 +3,43 @@
|
|
3
3
|
class FastIgnore
|
4
4
|
class RuleSet
|
5
5
|
def initialize(rules, allow)
|
6
|
-
@dir_rules = rules.reject(&:file_only?).freeze
|
7
|
-
@file_rules = rules.reject(&:dir_only?).freeze
|
6
|
+
@dir_rules = squash_rules(rules.reject(&:file_only?)).freeze
|
7
|
+
@file_rules = squash_rules(rules.reject(&:dir_only?)).freeze
|
8
8
|
@any_not_anchored = rules.any?(&:unanchored?)
|
9
9
|
@has_shebang_rules = rules.any?(&:shebang)
|
10
|
+
|
10
11
|
@allowed_recursive = { '.' => true }
|
11
12
|
@allow = allow
|
12
13
|
|
13
14
|
freeze
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
@dir_rules.freeze
|
18
|
-
@file_rules.freeze
|
19
|
-
|
20
|
-
super
|
21
|
-
end
|
22
|
-
|
23
|
-
def allowed_recursive?(relative_path, dir, full_path, filename)
|
17
|
+
def allowed_recursive?(relative_path, dir, full_path, filename, content = nil)
|
24
18
|
@allowed_recursive.fetch(relative_path) do
|
25
19
|
@allowed_recursive[relative_path] =
|
26
|
-
allowed_recursive?(::File.dirname(relative_path), true, nil, nil) &&
|
27
|
-
allowed_unrecursive?(relative_path, dir, full_path, filename)
|
20
|
+
allowed_recursive?(::File.dirname(relative_path), true, nil, nil, nil) &&
|
21
|
+
allowed_unrecursive?(relative_path, dir, full_path, filename, content)
|
28
22
|
end
|
29
23
|
end
|
30
24
|
|
31
|
-
def allowed_unrecursive?(relative_path, dir, full_path, filename)
|
25
|
+
def allowed_unrecursive?(relative_path, dir, full_path, filename, content)
|
32
26
|
(dir ? @dir_rules : @file_rules).reverse_each do |rule|
|
33
|
-
return rule.negation? if rule.match?(relative_path, full_path, filename)
|
27
|
+
return rule.negation? if rule.match?(relative_path, full_path, filename, content)
|
34
28
|
end
|
35
29
|
|
36
30
|
(not @allow) || (dir && @any_not_anchored)
|
37
31
|
end
|
38
32
|
|
33
|
+
def squash_rules(rules)
|
34
|
+
out = rules.chunk_while { |a, b| a.type == b.type }.map do |chunk|
|
35
|
+
next chunk.first if chunk.length == 1
|
36
|
+
|
37
|
+
chunk.first.class.new(Regexp.union(chunk.map(&:rule)), chunk.first.negation?)
|
38
|
+
end
|
39
|
+
|
40
|
+
out
|
41
|
+
end
|
42
|
+
|
39
43
|
def weight
|
40
44
|
@dir_rules.length + (@has_shebang_rules ? 10 : 0)
|
41
45
|
end
|
@@ -4,10 +4,7 @@ class FastIgnore
|
|
4
4
|
module RuleSetBuilder
|
5
5
|
class << self
|
6
6
|
# :nocov:
|
7
|
-
|
8
|
-
require_relative 'backports/delete_prefix_suffix'
|
9
|
-
using ::FastIgnore::Backports::DeletePrefixSuffix
|
10
|
-
end
|
7
|
+
using ::FastIgnore::Backports::DeletePrefixSuffix if defined?(::FastIgnore::Backports::DeletePrefixSuffix)
|
11
8
|
# :nocov:
|
12
9
|
|
13
10
|
def build( # rubocop:disable Metrics/ParameterLists
|
@@ -6,12 +6,18 @@ class FastIgnore
|
|
6
6
|
alias_method :negation?, :negation
|
7
7
|
undef :negation
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :rule
|
10
|
+
alias_method :shebang, :rule
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
attr_reader :type
|
13
|
+
|
14
|
+
def initialize(rule, negation)
|
15
|
+
@rule = rule
|
13
16
|
@negation = negation
|
14
17
|
|
18
|
+
@type = 2
|
19
|
+
@type += 1 if negation
|
20
|
+
|
15
21
|
freeze
|
16
22
|
end
|
17
23
|
|
@@ -29,14 +35,14 @@ class FastIgnore
|
|
29
35
|
|
30
36
|
# :nocov:
|
31
37
|
def inspect
|
32
|
-
"#<ShebangRule #{'allow ' if @negation}#!:#{@
|
38
|
+
"#<ShebangRule #{'allow ' if @negation}#!:#{@rule.to_s[15..-4]}>"
|
33
39
|
end
|
34
40
|
# :nocov:
|
35
41
|
|
36
|
-
def match?(_, full_path, filename)
|
42
|
+
def match?(_, full_path, filename, content)
|
37
43
|
return false if filename.include?('.')
|
38
44
|
|
39
|
-
first_line(full_path)&.match?(@
|
45
|
+
(content || first_line(full_path))&.match?(@rule)
|
40
46
|
end
|
41
47
|
|
42
48
|
private
|
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.11.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-
|
11
|
+
date: 2020-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -165,8 +165,7 @@ files:
|
|
165
165
|
- fast_ignore.gemspec
|
166
166
|
- lib/fast_ignore.rb
|
167
167
|
- lib/fast_ignore/backports.rb
|
168
|
-
- lib/fast_ignore/
|
169
|
-
- lib/fast_ignore/backports/dir_each_child.rb
|
168
|
+
- lib/fast_ignore/fn_match_to_re.rb
|
170
169
|
- lib/fast_ignore/rule.rb
|
171
170
|
- lib/fast_ignore/rule_builder.rb
|
172
171
|
- lib/fast_ignore/rule_set.rb
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# This is a backport of ruby 2.5's delete_prefix/delete_suffix methods
|
4
|
-
class FastIgnore
|
5
|
-
module Backports
|
6
|
-
module DeletePrefixSuffix
|
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
|
13
|
-
def delete_prefix!(str)
|
14
|
-
return unless start_with?(str)
|
15
|
-
|
16
|
-
slice!(0..(str.length - 1))
|
17
|
-
self
|
18
|
-
end
|
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
|
25
|
-
def delete_suffix!(str)
|
26
|
-
return unless end_with?(str)
|
27
|
-
|
28
|
-
slice!(-str.length..-1)
|
29
|
-
self
|
30
|
-
end
|
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"
|
37
|
-
def delete_prefix(str)
|
38
|
-
s = dup
|
39
|
-
s.delete_prefix!(str)
|
40
|
-
s
|
41
|
-
end
|
42
|
-
|
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
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# This is a backport of ruby 2.5's each_child method
|
4
|
-
class FastIgnore
|
5
|
-
module Backports
|
6
|
-
module DirEachChild
|
7
|
-
refine ::Dir.singleton_class do
|
8
|
-
def each_child(path, &block)
|
9
|
-
Dir.entries(path).each do |entry|
|
10
|
-
next if entry == '.' || entry == '..'
|
11
|
-
|
12
|
-
block.call entry
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|