path_list 0.16

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.
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PathList
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 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
+
57
+ def question_mark?
58
+ skip(/\?/)
59
+ end
60
+
61
+ def dash?
62
+ skip(/-/)
63
+ end
64
+
65
+ def character_class_literal
66
+ matched if scan(/[^\]\-\\]+/)
67
+ end
68
+
69
+ def literal
70
+ matched if scan(%r{[^*/?\[\\\s]+})
71
+ end
72
+
73
+ def significant_whitespace
74
+ matched if scan(/\s+(?!\s|\z)/)
75
+ end
76
+
77
+ def exclamation_mark?
78
+ skip(/!/)
79
+ end
80
+
81
+ def hash?
82
+ skip(/#/)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PathList
4
+ module GlobalGitignore
5
+ class << self
6
+ def path(root:)
7
+ gitconfig_gitignore_path(::File.expand_path('.git/config', root)) ||
8
+ gitconfig_gitignore_path(::File.expand_path('~/.gitconfig')) ||
9
+ gitconfig_gitignore_path(xdg_config_path) ||
10
+ gitconfig_gitignore_path('/etc/gitconfig') ||
11
+ default_global_gitignore_path
12
+ end
13
+
14
+ private
15
+
16
+ def gitconfig_gitignore_path(config_path)
17
+ return unless config_path
18
+ return unless ::File.exist?(config_path)
19
+
20
+ ignore_path = ::File.readlines(config_path).find { |l| l.sub!(/\A\s*excludesfile\s*=/, '') }
21
+ return unless ignore_path
22
+
23
+ ignore_path.strip!
24
+ return ignore_path if ignore_path.empty? # don't expand path in this case
25
+
26
+ ::File.expand_path(ignore_path)
27
+ end
28
+
29
+ def xdg_config_path
30
+ xdg_config_home? && ::File.expand_path('git/config', xdg_config_home)
31
+ end
32
+
33
+ def default_global_gitignore_path
34
+ if xdg_config_home?
35
+ ::File.expand_path('git/ignore', xdg_config_home)
36
+ else
37
+ ::File.expand_path('~/.config/git/ignore')
38
+ end
39
+ end
40
+
41
+ def xdg_config_home
42
+ ::ENV['XDG_CONFIG_HOME']
43
+ end
44
+
45
+ def xdg_config_home?
46
+ xdg_config_home && (not xdg_config_home.empty?)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PathList
4
+ module Matchers
5
+ module AllowAnyDir
6
+ class << self
7
+ def squash_id
8
+ :allow
9
+ end
10
+
11
+ def dir_only?
12
+ true
13
+ end
14
+
15
+ def file_only?
16
+ false
17
+ end
18
+
19
+ def shebang?
20
+ false
21
+ end
22
+
23
+ # :nocov:
24
+ def inspect
25
+ '#<AllowAnyDir>'
26
+ end
27
+ # :nocov:
28
+
29
+ def match?(_)
30
+ :allow
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PathList
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, anchored, dir_only)
14
+ @rule = rule
15
+ @dir_only = dir_only
16
+ @anchored = anchored
17
+ @squash_id = anchored ? :allow : object_id
18
+
19
+ freeze
20
+ end
21
+
22
+ def squash(list)
23
+ self.class.new(::Regexp.union(list.map(&:rule)), @anchored, @dir_only)
24
+ end
25
+
26
+ def file_only?
27
+ false
28
+ end
29
+
30
+ def shebang?
31
+ false
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
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PathList
4
+ module Matchers
5
+ class IgnorePathRegexp
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, anchored, dir_only)
14
+ @rule = rule
15
+ @dir_only = dir_only
16
+ @anchored = anchored
17
+ @squash_id = anchored ? :ignore : object_id
18
+
19
+ freeze
20
+ end
21
+
22
+ def squash(list)
23
+ self.class.new(::Regexp.union(list.map(&:rule)), @anchored, @dir_only)
24
+ end
25
+
26
+ def file_only?
27
+ false
28
+ end
29
+
30
+ def shebang?
31
+ false
32
+ end
33
+
34
+ # :nocov:
35
+ def inspect
36
+ "#<IgnorePathRegexp #{'dir_only ' if @dir_only}#{@rule.inspect}>"
37
+ end
38
+ # :nocov:
39
+
40
+ def match?(candidate)
41
+ :ignore if @rule.match?(candidate.relative_path)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PathList
4
+ module Matchers
5
+ class ShebangRegexp
6
+ attr_reader :squash_id
7
+ attr_reader :rule
8
+
9
+ def initialize(rule, negation)
10
+ @rule = rule
11
+ @return_value = negation ? :allow : :ignore
12
+ @squash_id = negation ? :allow_shebang : :ignore_shebang
13
+
14
+ freeze
15
+ end
16
+
17
+ def squash(list)
18
+ self.class.new(::Regexp.union(list.map(&:rule)), @return_value == :allow)
19
+ end
20
+
21
+ def file_only?
22
+ true
23
+ end
24
+
25
+ def dir_only?
26
+ false
27
+ end
28
+
29
+ # :nocov:
30
+ def inspect
31
+ "#<ShebangRegexp #{@return_value} /#{@rule.to_s[26..-4]}/>"
32
+ end
33
+ # :nocov:
34
+
35
+ def match?(candidate)
36
+ return false if candidate.filename.include?('.')
37
+
38
+ @return_value if candidate.first_line&.match?(@rule)
39
+ end
40
+
41
+ def shebang?
42
+ true
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PathList
4
+ module Matchers
5
+ module Unmatchable
6
+ class << self
7
+ def dir_only?
8
+ false
9
+ end
10
+
11
+ def file_only?
12
+ false
13
+ end
14
+
15
+ def shebang?
16
+ false
17
+ end
18
+
19
+ # :nocov:
20
+ def inspect
21
+ '#<Unmatchable>'
22
+ end
23
+ # :nocov:
24
+
25
+ def match?(_)
26
+ false
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PathList
4
+ module Matchers
5
+ class WithinDir
6
+ def initialize(matchers, root)
7
+ @dir_matchers = squash_matchers(matchers.reject(&:file_only?))
8
+ @file_matchers = squash_matchers(matchers.reject(&:dir_only?))
9
+ @has_shebang_matchers = matchers.any?(&:shebang?)
10
+ @root = root
11
+
12
+ freeze
13
+ end
14
+
15
+ def match?(root_candidate)
16
+ relative_candidate = root_candidate.relative_to(@root)
17
+ return false unless relative_candidate
18
+
19
+ (root_candidate.directory? ? @dir_matchers : @file_matchers).reverse_each do |rule|
20
+ val = rule.match?(relative_candidate)
21
+ return val if val
22
+ end
23
+
24
+ false
25
+ end
26
+
27
+ def empty?
28
+ @dir_matchers.empty? && @file_matchers.empty?
29
+ end
30
+
31
+ def weight
32
+ @dir_matchers.length + (@has_shebang_matchers ? 10 : 0)
33
+ end
34
+
35
+ private
36
+
37
+ def squash_matchers(matchers)
38
+ return matchers if matchers.empty?
39
+
40
+ matchers -= [::PathList::Matchers::Unmatchable]
41
+ return [::PathList::Matchers::Unmatchable] if matchers.empty?
42
+
43
+ matchers.chunk_while { |a, b| a.squash_id == b.squash_id }.map do |chunk|
44
+ next ::PathList::Matchers::AllowAnyDir if chunk.include?(::PathList::Matchers::AllowAnyDir)
45
+
46
+ chunk.uniq!(&:rule)
47
+ next chunk.first if chunk.length == 1
48
+
49
+ chunk.first.squash(chunk)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PathList
4
+ class PathRegexpBuilder < String
5
+ def to_regexp
6
+ # Regexp::IGNORECASE = 1
7
+ ::Regexp.new(self, 1)
8
+ end
9
+
10
+ def append_escaped(value)
11
+ return unless value
12
+
13
+ append_unescaped(::Regexp.escape(value))
14
+ end
15
+
16
+ def append_dir
17
+ append_unescaped('/')
18
+ end
19
+
20
+ def append_any_dir
21
+ append_unescaped('(?:.*/)?')
22
+ end
23
+
24
+ def append_one_non_dir
25
+ append_unescaped('[^/]')
26
+ end
27
+
28
+ def append_any_non_dir
29
+ append_one_non_dir
30
+ append_unescaped('*')
31
+ end
32
+
33
+ def append_many_non_dir
34
+ append_one_non_dir
35
+ append_unescaped('+')
36
+ end
37
+
38
+ def append_end_anchor
39
+ append_unescaped('\\z')
40
+ end
41
+
42
+ def append_start_anchor
43
+ append_unescaped('\\A')
44
+ end
45
+
46
+ def append_dir_or_start_anchor
47
+ append_unescaped('(?:\\A|/)')
48
+ end
49
+
50
+ def append_dir_or_end_anchor
51
+ append_unescaped('(?:/|\\z)')
52
+ end
53
+
54
+ def append_character_class_open
55
+ append_unescaped('(?!/)[')
56
+ end
57
+
58
+ def append_character_class_negation
59
+ append_unescaped('^')
60
+ end
61
+
62
+ def append_character_class_dash
63
+ append_unescaped('-')
64
+ end
65
+
66
+ def append_character_class_close
67
+ append_unescaped(']')
68
+ end
69
+
70
+ private
71
+
72
+ def append_unescaped(value)
73
+ self.<<(value)
74
+
75
+ self
76
+ end
77
+ end
78
+ end