path_list 0.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -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