Skip to content

Commit 469b0d0

Browse files
committed
GlobGitignore doesn't preprocess patterns
1 parent c22b18d commit 469b0d0

File tree

17 files changed

+1274
-181
lines changed

17 files changed

+1274
-181
lines changed

.spellr_wordlists/english.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,15 @@ torvolds
136136
tsx
137137
ttributes
138138
txt
139+
umc
139140
unanchorable
140141
unc
141142
unexpandable
142143
unfuck
143144
unnegated
144145
unrecursive
145146
unstaged
147+
unstub
146148
untr
147149
upcase
148150
urrent

lib/path_list/autoloader.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def autoload(klass)
1919
def class_from_path(path)
2020
name = ::File.basename(path).delete_suffix('.rb')
2121

22-
if name == 'version' || name == 'expandable_path'
22+
if name == 'version' || name == 'scanner'
2323
name.upcase
2424
else
2525
name.gsub(/(?:^|_)(\w)/, &:upcase).delete('_')

lib/path_list/candidate.rb

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@ class PathList
44
# @api private
55
# The object that gets passed to all {PathList::Matcher} subclasses #match
66
class Candidate
7+
# https://github.com/jruby/jruby/issues/8018
8+
# ftype follows symlinks on jruby on windows.
9+
if ::RUBY_PLATFORM == 'java' && ::RbConfig::CONFIG['host_os'].match?(/mswin|mingw/)
10+
# :nodoc:
11+
module FileFtypeFix
12+
refine ::File do
13+
# :nodoc:
14+
def ftype(path)
15+
if ::File.symlink?(path)
16+
'link'
17+
else
18+
super
19+
end
20+
end
21+
end
22+
end
23+
using FileFtypeFix
24+
end
25+
# :nocov:
26+
727
attr_reader :full_path
828

929
# @param full_path [String] resolved absolute path
@@ -106,22 +126,6 @@ def shebang
106126
private
107127

108128
# :nocov:
109-
# https://github.com/jruby/jruby/issues/8018
110-
# ftype follows symlinks on jruby on windows.
111-
if ::RUBY_PLATFORM == 'java' && ::RbConfig::CONFIG['host_os'].match?(/mswin|mingw/)
112-
refine ::File do
113-
# :nodoc:
114-
def ftype(path)
115-
if ::File.symlink?(path)
116-
'link'
117-
else
118-
super
119-
end
120-
end
121-
end
122-
end
123-
# :nocov:
124-
125129
def ftype
126130
return @ftype if @ftype
127131

lib/path_list/canonical_path.rb

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,22 @@ class << self
1010
class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
1111
def case_insensitive?
1212
#{
13-
pwd = ::Dir.pwd
14-
pwd_swapcase = pwd.swapcase
15-
# :nocov:
16-
# if the current directory has no casing differences
17-
# (maybe because it's at /)
18-
# then:
19-
if pwd == pwd_swapcase
13+
test_dir = ::Dir.pwd
14+
test_dir_swapcase = test_dir.swapcase
15+
16+
if test_dir == test_dir_swapcase
17+
# :nocov:
18+
# if the current directory has no casing differences
19+
# (maybe because it's at /)
20+
# then:
2021
require 'tmpdir'
21-
pwd = ::File.write(::Dir.mktmpdir + '/case_test', '')
22-
pwd_swapcase = pwd.swapcase
22+
test_file = ::Dir.mktmpdir + '/case_test'
23+
::File.write(test_file, '')
24+
::File.exist?(test_file.swapcase)
25+
# :nocov:
26+
else
27+
::File.identical?(test_dir, test_dir_swapcase)
2328
end
24-
# :nocov:
25-
26-
::File.identical?(pwd, pwd_swapcase)
2729
}
2830
end
2931
RUBY

lib/path_list/pattern_parser/gitignore.rb

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ class PatternParser
1515
class Gitignore
1616
Autoloader.autoload(self)
1717

18+
SCANNER = RuleScanner
19+
1820
# @api private
1921
# @param pattern [String]
2022
# @param polarity [:ignore, :allow]
2123
# @param root [String]
2224
def initialize(pattern, polarity, root)
23-
@s = RuleScanner.new(pattern)
25+
@s = self.class::SCANNER.new(pattern)
2426
@default_polarity = polarity
2527
@rule_polarity = polarity
2628
@root = root
@@ -51,9 +53,7 @@ def implicit_matcher
5153
private
5254

5355
def prepare_regexp_builder
54-
@re = if @root.nil?
55-
TokenRegexp::Path.new([:start_anchor])
56-
elsif @root.end_with?('/')
56+
@re = if @root.end_with?('/')
5757
TokenRegexp::Path.new_from_path(@root, [:any_dir])
5858
else
5959
TokenRegexp::Path.new_from_path(@root, [:dir, :any_dir])
@@ -117,12 +117,13 @@ def append_string(string)
117117
end
118118

119119
def emit_end
120+
@re.remove_trailing_dir
120121
append_part :end_anchor
121122
break!
122123
end
123124

124-
def process_backslash
125-
return unless @s.backslash?
125+
def process_escape
126+
return unless @s.escape?
126127

127128
if @re.append_string(@s.next_character)
128129
emitted!
@@ -142,7 +143,7 @@ def process_character_class
142143

143144
until @s.character_class_end?
144145
next if process_character_class_range
145-
next if process_backslash
146+
next if process_escape
146147
next if append_string(@s.character_class_literal)
147148

148149
unmatchable_rule!
@@ -158,11 +159,9 @@ def process_character_class_range
158159
start = @s.character_class_range_start
159160
return unless start
160161

161-
start = start.delete_prefix('\\')
162-
163162
append_string(start)
164163

165-
finish = @s.character_class_range_end.delete_prefix('\\')
164+
finish = @s.character_class_range_end
166165

167166
return true unless start < finish
168167

@@ -184,31 +183,39 @@ def process_rule
184183
catch :abort_build do
185184
blank! if @s.hash?
186185
negated! if @s.exclamation_mark?
187-
prepare_regexp_builder
188-
anchored! if !@anchored && @s.slash?
186+
process_first_characters
189187

190188
catch :break do
191189
loop do
192-
next if process_backslash
193-
next unmatchable_rule! if @s.star_star_slash_slash?
194-
next append_part(:any) && dir_only! if @s.star_star_slash_end?
195-
next append_part(:any_dir) && anchored! if @s.star_star_slash?
196-
next unmatchable_rule! if @s.slash_slash?
197-
next append_part(:dir) && append_part(:any) && anchored! if @s.slash_star_star_end?
198-
next append_part(:any_non_dir) if @s.star?
199-
next dir_only! if @s.slash_end?
200-
next append_part(:dir) && anchored! if @s.slash?
201-
next append_part(:one_non_dir) if @s.question_mark?
202-
next if process_character_class
203-
next if append_string(@s.literal)
204-
next if append_string(@s.significant_whitespace)
205-
206-
process_end
190+
process_next_characters
207191
end
208192
end
209193
end
210194
end
211195

196+
def process_first_characters
197+
prepare_regexp_builder
198+
anchored! if !@anchored && @s.slash?
199+
end
200+
201+
def process_next_characters
202+
return if process_escape
203+
return unmatchable_rule! if @s.star_star_slash_slash?
204+
return append_part(:any) && dir_only! if @s.star_star_slash_end?
205+
return append_part(:any_dir) && anchored! if @s.star_star_slash?
206+
return unmatchable_rule! if @s.slash_slash?
207+
return append_part(:dir) && append_part(:any) && anchored! if @s.slash_star_star_end?
208+
return append_part(:any_non_dir) if @s.star?
209+
return dir_only! if @s.slash_end?
210+
return append_part(:dir) && anchored! if @s.slash?
211+
return append_part(:one_non_dir) if @s.question_mark?
212+
return if process_character_class
213+
return if append_string(@s.literal)
214+
return if append_string(@s.significant_whitespace)
215+
216+
process_end
217+
end
218+
212219
def build_matcher
213220
@main_re ||= @re.dup.compress
214221

lib/path_list/pattern_parser/gitignore/rule_scanner.rb

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,53 @@ def slash?
2727
skip(%r{/})
2828
end
2929

30+
# @return [String, nil]
31+
def root_end
32+
matched if scan(%r{/\s*\z})
33+
end
34+
35+
# @return [String, nil]
36+
def root
37+
matched if scan(%r{/})
38+
end
39+
40+
# @return [String, nil]
41+
def home_slash_end
42+
self[1] if scan(%r{(~[^/]*)/\s*\z})
43+
end
44+
45+
# @return [String, nil]
46+
def home_slash_or_end
47+
self[1] if scan(%r{(~[^/]*)(?:/|\s*\z)})
48+
end
49+
50+
# @return [Boolean]
51+
def dot_slash_or_end?
52+
skip(%r{\.(?:/|\s*\z)})
53+
end
54+
55+
# @return [Boolean]
56+
def dot_slash_end?
57+
skip(%r{\./\s*\z})
58+
end
59+
60+
# @return [Boolean]
61+
def dot_dot_slash_end?
62+
skip(%r{\.\./\s*\z})
63+
end
64+
65+
# @return [Boolean]
66+
def dot_dot_slash_or_end?
67+
skip(%r{\.\.(?:/|\s*\z)})
68+
end
69+
3070
# @return [Boolean]
3171
def slash_end?
3272
skip(%r{/\s*\z})
3373
end
3474

3575
# @return [Boolean]
36-
def backslash?
76+
def escape?
3777
skip(/\\/)
3878
end
3979

@@ -84,7 +124,7 @@ def character_class_literal
84124

85125
# @return [String, nil]
86126
def character_class_range_start
87-
matched if scan(/(\\.|[^\\\]])(?=-(\\.|[^\\\]]))/)
127+
matched.delete_prefix('\\') if scan(/(\\.|[^\\\]])(?=-(\\.|[^\\\]]))/)
88128
end
89129

90130
# @return [String, nil]
@@ -93,7 +133,7 @@ def character_class_range_end
93133
# with the lookahead in character_class_range_start
94134
skip(/-/)
95135
scan(/(\\.|[^\\\]])/)
96-
matched
136+
matched.delete_prefix('\\')
97137
end
98138

99139
# @return [String, nil]

0 commit comments

Comments
 (0)