55
66import { isThenable } from 'vs/base/common/async' ;
77import { CharCode } from 'vs/base/common/charCode' ;
8- import * as extpath from 'vs/base/common/extpath' ;
8+ import { isEqualOrParent } from 'vs/base/common/extpath' ;
99import { LRUCache } from 'vs/base/common/map' ;
10- import * as paths from 'vs/base/common/path' ;
11- import * as strings from 'vs/base/common/strings' ;
10+ import { basename , extname , posix , sep } from 'vs/base/common/path' ;
11+ import { isLinux } from 'vs/base/common/platform' ;
12+ import { escapeRegExpCharacters } from 'vs/base/common/strings' ;
1213
1314export interface IExpression {
1415 [ pattern : string ] : boolean | SiblingClause ;
1516}
1617
1718export interface IRelativePattern {
18- base : string ;
19- pattern : string ;
19+
20+ /**
21+ * A base file path to which this pattern will be matched against relatively.
22+ */
23+ readonly base : string ;
24+
25+ /**
26+ * A file glob pattern like `*.{ts,js}` that will be matched on file paths
27+ * relative to the base path.
28+ *
29+ * Example: Given a base of `/home/work/folder` and a file path of `/home/work/folder/index.js`,
30+ * the file glob pattern will match on `index.js`.
31+ */
32+ readonly pattern : string ;
2033}
2134
2235export function getEmptyExpression ( ) : IExpression {
@@ -29,6 +42,7 @@ export interface SiblingClause {
2942
3043export const GLOBSTAR = '**' ;
3144export const GLOB_SPLIT = '/' ;
45+
3246const PATH_REGEX = '[/\\\\]' ; // any slash or backslash
3347const NO_PATH_REGEX = '[^/\\\\]' ; // any non-slash and non-backslash
3448const ALL_FORWARD_SLASHES = / \/ / g;
@@ -161,7 +175,7 @@ function parseRegExp(pattern: string): string {
161175
162176 // anything else gets escaped
163177 else {
164- res = strings . escapeRegExpCharacters ( char ) ;
178+ res = escapeRegExpCharacters ( char ) ;
165179 }
166180
167181 bracketVal += res ;
@@ -208,7 +222,7 @@ function parseRegExp(pattern: string): string {
208222 continue ;
209223
210224 default :
211- regEx += strings . escapeRegExpCharacters ( char ) ;
225+ regEx += escapeRegExpCharacters ( char ) ;
212226 }
213227 }
214228
@@ -230,21 +244,25 @@ function parseRegExp(pattern: string): string {
230244}
231245
232246// regexes to check for trivial glob patterns that just check for String#endsWith
233- const T1 = / ^ \* \* \/ \* \. [ \w \. - ] + $ / ; // **/*.something
234- const T2 = / ^ \* \* \/ ( [ \w \. - ] + ) \/ ? $ / ; // **/something
235- const T3 = / ^ { \* \* \/ [ \* \. ] ? [ \w \. - ] + \/ ? ( , \* \* \/ [ \* \. ] ? [ \w \. - ] + \/ ? ) * } $ / ; // {**/*.something,**/*.else} or {**/package.json,**/project.json}
247+ const T1 = / ^ \* \* \/ \* \. [ \w \. - ] + $ / ; // **/*.something
248+ const T2 = / ^ \* \* \/ ( [ \w \. - ] + ) \/ ? $ / ; // **/something
249+ const T3 = / ^ { \* \* \/ [ \* \. ] ? [ \w \. - ] + \/ ? ( , \* \* \/ [ \* \. ] ? [ \w \. - ] + \/ ? ) * } $ / ; // {**/*.something,**/*.else} or {**/package.json,**/project.json}
236250const T3_2 = / ^ { \* \* \/ [ \* \. ] ? [ \w \. - ] + ( \/ ( \* \* ) ? ) ? ( , \* \* \/ [ \* \. ] ? [ \w \. - ] + ( \/ ( \* \* ) ? ) ? ) * } $ / ; // Like T3, with optional trailing /**
237- const T4 = / ^ \* \* ( ( \/ [ \w \. - ] + ) + ) \/ ? $ / ; // **/something/else
238- const T5 = / ^ ( [ \w \. - ] + ( \/ [ \w \. - ] + ) * ) \/ ? $ / ; // something/else
251+ const T4 = / ^ \* \* ( ( \/ [ \w \. - ] + ) + ) \/ ? $ / ; // **/something/else
252+ const T5 = / ^ ( [ \w \. - ] + ( \/ [ \w \. - ] + ) * ) \/ ? $ / ; // something/else
239253
240254export type ParsedPattern = ( path : string , basename ?: string ) => boolean ;
241255
242- // The ParsedExpression returns a Promise iff hasSibling returns a Promise.
256+ // The `ParsedExpression` returns a `Promise`
257+ // iff `hasSibling` returns a `Promise`.
243258export type ParsedExpression = ( path : string , basename ?: string , hasSibling ?: ( name : string ) => boolean | Promise < boolean > ) => string | null | Promise < string | null > /* the matching pattern */ ;
244259
245260export interface IGlobOptions {
261+
246262 /**
247- * Simplify patterns for use as exclusion filters during tree traversal to skip entire subtrees. Cannot be used outside of a tree traversal.
263+ * Simplify patterns for use as exclusion filters during
264+ * tree traversal to skip entire subtrees. Cannot be used
265+ * outside of a tree traversal.
248266 */
249267 trimForExclusions ?: boolean ;
250268}
@@ -330,10 +348,15 @@ function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string |
330348 }
331349
332350 return function ( path , basename ) {
333- if ( ! extpath . isEqualOrParent ( path , arg2 . base ) ) {
351+ if ( ! isEqualOrParent ( path , arg2 . base , ! isLinux ) ) {
352+ // skip glob matching if `base` is not a parent of `path`
334353 return null ;
335354 }
336- return parsedPattern ( paths . relative ( arg2 . base , path ) , basename ) ;
355+
356+ // Given we have checked `base` being a parent of `path`,
357+ // we can now remove the `base` portion of the `path`
358+ // and only match on the remaining path components
359+ return parsedPattern ( path . substr ( arg2 . base . length + 1 ) , basename ) ;
337360 } ;
338361}
339362
@@ -394,10 +417,10 @@ function trivia3(pattern: string, options: IGlobOptions): ParsedStringPattern {
394417
395418// common patterns: **/something/else just need endsWith check, something/else just needs and equals check
396419function trivia4and5 ( targetPath : string , pattern : string , matchPathEnds : boolean ) : ParsedStringPattern {
397- const usingPosixSep = paths . sep === paths . posix . sep ;
398- const nativePath = usingPosixSep ? targetPath : targetPath . replace ( ALL_FORWARD_SLASHES , paths . sep ) ;
399- const nativePathEnd = paths . sep + nativePath ;
400- const targetPathEnd = paths . posix . sep + targetPath ;
420+ const usingPosixSep = sep === posix . sep ;
421+ const nativePath = usingPosixSep ? targetPath : targetPath . replace ( ALL_FORWARD_SLASHES , sep ) ;
422+ const nativePathEnd = sep + nativePath ;
423+ const targetPathEnd = posix . sep + targetPath ;
401424
402425 const parsedPattern : ParsedStringPattern = matchPathEnds ? function ( testPath , basename ) {
403426 return typeof testPath === 'string' &&
@@ -578,21 +601,21 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse
578601 return resultExpression ;
579602 }
580603
581- const resultExpression : ParsedStringPattern = function ( path : string , basename ?: string , hasSibling ?: ( name : string ) => boolean | Promise < boolean > ) {
604+ const resultExpression : ParsedStringPattern = function ( path : string , base ?: string , hasSibling ?: ( name : string ) => boolean | Promise < boolean > ) {
582605 let name : string | undefined = undefined ;
583606
584607 for ( let i = 0 , n = parsedPatterns . length ; i < n ; i ++ ) {
585608 // Pattern matches path
586609 const parsedPattern = ( < ParsedExpressionPattern > parsedPatterns [ i ] ) ;
587610 if ( parsedPattern . requiresSiblings && hasSibling ) {
588- if ( ! basename ) {
589- basename = paths . basename ( path ) ;
611+ if ( ! base ) {
612+ base = basename ( path ) ;
590613 }
591614 if ( ! name ) {
592- name = basename . substr ( 0 , basename . length - paths . extname ( path ) . length ) ;
615+ name = base . substr ( 0 , base . length - extname ( path ) . length ) ;
593616 }
594617 }
595- const result = parsedPattern ( path , basename , name , hasSibling ) ;
618+ const result = parsedPattern ( path , base , name , hasSibling ) ;
596619 if ( result ) {
597620 return result ;
598621 }
@@ -698,5 +721,6 @@ function aggregateBasenameMatches(parsedPatterns: Array<ParsedStringPattern | Pa
698721
699722 const aggregatedPatterns = parsedPatterns . filter ( parsedPattern => ! ( < ParsedStringPattern > parsedPattern ) . basenames ) ;
700723 aggregatedPatterns . push ( aggregate ) ;
724+
701725 return aggregatedPatterns ;
702726}
0 commit comments