Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | 286x 23x 23x 23x 24x 24x 101x 165x 165x 33x 33x 33x 165x 343x 109x 1x 108x 194x 3x 355x 122x 21x 21x 106x 106x 106x 120x 19x 101x 106x | import picomatch from 'picomatch';
import { toUniqueStringArray } from './array';
/**
* Normalizes a raw glob pattern or relative path: trims, converts backslashes
* to slashes and strips leading `./`/`/` and trailing `/` sequences, so
* patterns match consistently against normalized workspace-relative paths.
*/
export function normalizeGlobPattern(pattern: string): string {
return pattern
.trim()
.replace(/\\/g, '/')
.replace(/^(?:\.\/+|\/+)+/, '')
.replace(/\/+$/, '');
}
/**
* Returns true when the given string contains glob syntax (including negation).
* Never throws on malformed input.
*/
export function isGlobPattern(pattern: string): boolean {
try {
const scanned = picomatch.scan(pattern);
return scanned.isGlob === true || scanned.negated === true;
} catch {
return false;
}
}
/**
* Returns the fixed literal prefix of a glob pattern (`picomatch.scan().base`),
* which degenerates to the whole string for a literal-looking pattern.
* Root-anchored patterns like `**\/*.ttl` have no fixed prefix and yield an
* empty string. Used to follow folder renames and deletions for patterns.
*/
export function getGlobPatternBase(pattern: string): string {
try {
return picomatch.scan(pattern).base ?? '';
} catch {
return '';
}
}
/**
* Compiled matchers per pattern string. Safe to memoize at module level: a
* compiled matcher for a given pattern string is always identical.
*/
const matcherCache = new Map<string, picomatch.Matcher>();
/**
* Tests whether a glob pattern matches an input string, compiling the pattern
* with `{ dot: true }` and caching the compiled matcher. Returns `false` when
* the pattern fails to compile.
*/
export function matchGlob(pattern: string, input: string): boolean {
let matcher = matcherCache.get(pattern);
if (!matcher) {
try {
matcher = picomatch(pattern, { dot: true });
} catch {
return false;
}
matcherCache.set(pattern, matcher);
}
return matcher(input);
}
/**
* Builds the glob suffix that restricts a pattern to the given file
* extensions, e.g. `.{ttl,n3,nt}` (or `.ttl` for a single extension).
* Returns an empty string when no extensions are given.
*/
export function getExtensionGlob(extensions: readonly string[]): string {
const names = toUniqueStringArray(extensions).map(ext => ext.replace(/^\.+/, '').toLowerCase()).filter(Boolean);
if (names.length === 0) {
return '';
}
return names.length === 1 ? `.${names[0]}` : `.{${names.join(',')}}`;
}
/**
* Returns true when a normalized relative path is safe to match against
* workspace-relative paths: no `..` segments and no absolute-path-looking
* syntax (drive letters). Consistent with the path-traversal guarding of
* `WorkspaceUri`.
*/
export function isSafeRelativePath(path: string): boolean {
if (/^[a-zA-Z]:/.test(path)) {
return false;
}
return !path.split('/').some(segment => segment === '..');
}
/**
* Whether a raw pattern is a `!`-prefixed negation (gitignore-style). Leading
* whitespace is ignored so entries edited by hand behave the same as trimmed
* ones.
*/
export function isNegatedPattern(pattern: string): boolean {
return pattern.trimStart().startsWith('!');
}
/**
* Strips the `!` negation prefix (and any leading whitespace) from a raw
* pattern. Positive patterns are returned unchanged apart from the trim.
*/
export function stripNegationPrefix(pattern: string): string {
const trimmed = pattern.trimStart();
return trimmed.startsWith('!') ? trimmed.slice(1) : trimmed;
}
/**
* Splits patterns into positive patterns and `!`-prefixed negations (with the
* `!` stripped), dropping duplicates and empty entries.
*/
export function splitNegatedPatterns(patterns: readonly string[] | undefined): { positives: string[]; negatives: string[] } {
const positives: string[] = [];
const negatives: string[] = [];
for (const entry of toUniqueStringArray(patterns)) {
if (isNegatedPattern(entry)) {
negatives.push(stripNegationPrefix(entry));
} else {
positives.push(entry);
}
}
return { positives, negatives };
}
|