@mrclrchtr/supi-code-intelligence 6.2.0 → 6.4.0
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.
- package/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/README.md +52 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +785 -129
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +660 -98
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +10 -4
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +16 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +4 -32
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sources.ts +108 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +106 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +4 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +513 -113
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +43 -15
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +40 -33
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +36 -6
- package/node_modules/@mrclrchtr/supi-lsp/src/summary.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/workspace-path-policy.ts +18 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/README.md +29 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs +99 -19
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js +94 -18
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +14 -14
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +13 -8
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs +97 -19
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts +28 -8
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts.map +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts +28 -8
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js +92 -18
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +17 -17
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +7 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +2 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm.json +2 -2
- package/package.json +5 -5
- package/src/session/health-refresh.ts +19 -12
- package/src/session/health-types.ts +8 -3
- package/src/session/health-workflow.ts +7 -3
- package/src/session/session.ts +12 -5
- package/src/substrate/lsp/maintenance.ts +53 -47
- package/src/substrate/lsp/settings.ts +2 -1
- package/src/substrate/lsp/source-tracking.ts +247 -0
- package/src/tool/code_find/guidance.ts +3 -3
- package/src/tool/code_graph/guidance.ts +2 -2
- package/src/tool/code_health/guidance.ts +3 -3
- package/src/tool/code_health/markdown.ts +21 -5
- package/src/tool/code_health/refresh-outcome.ts +48 -7
- package/src/tool/code_health/refresh-status.ts +83 -16
- package/src/tool/code_health/tui.ts +4 -0
- package/src/tool/code_inspect/guidance.ts +5 -3
- package/src/tool/code_orientation/guidance.ts +3 -4
- package/src/tool/code_refactor_apply/guidance.ts +1 -1
- package/src/tool/code_refactor_plan/guidance.ts +3 -5
- package/src/tool/code_resolve/guidance.ts +3 -3
- package/src/tool/guidance.ts +11 -9
|
@@ -22,7 +22,14 @@ var UNDEFINED = undefined;
|
|
|
22
22
|
var EMPTY = '';
|
|
23
23
|
var SPACE = ' ';
|
|
24
24
|
var ESCAPE = '\\';
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
// The characters that carry a meaning of their own inside a regular
|
|
27
|
+
// expression, so a literal one has to be escaped before it is emitted.
|
|
28
|
+
var REGEX_LITERAL_SPECIAL = /[.*+?()[\]{}^$|\\/]/;
|
|
29
|
+
// A line of only spaces is blank -- the trailing-space trimming empties it --
|
|
30
|
+
// but a line holding a tab is a pattern for a tab-named path, since git
|
|
31
|
+
// never trims a tab.
|
|
32
|
+
var REGEX_TEST_BLANK_LINE = /^ +$/;
|
|
26
33
|
var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
|
|
27
34
|
var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
|
|
28
35
|
var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
|
|
@@ -36,8 +43,9 @@ var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
|
36
43
|
// - ..
|
|
37
44
|
// Valid:
|
|
38
45
|
// - .foo
|
|
39
|
-
var
|
|
40
|
-
var
|
|
46
|
+
var DOUBLE_SLASH = '//';
|
|
47
|
+
var SLASH_CODE = 47;
|
|
48
|
+
var DOT_CODE = 46;
|
|
41
49
|
var SLASH = '/';
|
|
42
50
|
|
|
43
51
|
// Do not use ternary expression here, since "istanbul ignore next" is buggy
|
|
@@ -53,37 +61,266 @@ var define = function define(object, key, value) {
|
|
|
53
61
|
});
|
|
54
62
|
return value;
|
|
55
63
|
};
|
|
56
|
-
var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
|
|
57
64
|
var RETURN_FALSE = function RETURN_FALSE() {
|
|
58
65
|
return false;
|
|
59
66
|
};
|
|
60
67
|
|
|
61
|
-
// Sanitize the range of a regular expression
|
|
62
|
-
// The cases are complicated, see test cases for details
|
|
63
|
-
var sanitizeRange = function sanitizeRange(range) {
|
|
64
|
-
return range.replace(REGEX_REGEXP_RANGE, function (match, from, to) {
|
|
65
|
-
return from.charCodeAt(0) <= to.charCodeAt(0) ? match
|
|
66
|
-
// Invalid range (out of order) which is ok for gitignore rules but
|
|
67
|
-
// fatal for JavaScript regular expression, so eliminate it.
|
|
68
|
-
: EMPTY;
|
|
69
|
-
});
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// > An optional `!` or `^` at the start of a class negates it, so that it
|
|
73
|
-
// > matches any character not in the set. (gitignore(5), fnmatch(3))
|
|
74
|
-
// The leading `^` has already been escaped to `\^` by the metacharacter
|
|
75
|
-
// escaper, so we strip the literal `!` or escaped `^` and emit a single
|
|
76
|
-
// regex `^` which is the JavaScript negation token.
|
|
77
|
-
var negateRange = function negateRange(range) {
|
|
78
|
-
return range.startsWith('!') || range.startsWith('\\^') ? "^".concat(range.slice(range[0] === '!' ? 1 : 2)) : range;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
68
|
// See fixtures #59
|
|
82
69
|
var cleanRangeBackSlash = function cleanRangeBackSlash(slashes) {
|
|
83
70
|
var length = slashes.length;
|
|
84
71
|
return slashes.slice(0, length - length % 2);
|
|
85
72
|
};
|
|
86
73
|
|
|
74
|
+
// > The range notation, e.g. [a-zA-Z],
|
|
75
|
+
// > can be used to match one of the characters in a range.
|
|
76
|
+
//
|
|
77
|
+
// gitignore(5) defers to fnmatch(3) for this, and git implements it in
|
|
78
|
+
// `wildmatch.c`. A bracket expression has a sub-grammar of its own, which
|
|
79
|
+
// is neither the surrounding pattern grammar nor the JavaScript one:
|
|
80
|
+
//
|
|
81
|
+
// - a `]` right after `[` or `[!` is a literal member, not the terminator
|
|
82
|
+
// - `[:alpha:]` names one of twelve POSIX classes
|
|
83
|
+
// - `\` escapes the next member, `]` included
|
|
84
|
+
// - `*`, `?` and `.` are plain literal members
|
|
85
|
+
// - an unterminated expression makes the whole pattern match nothing
|
|
86
|
+
//
|
|
87
|
+
// which means the expression can not be located -- let alone translated -- by
|
|
88
|
+
// a regular expression. It is scanned out of the pattern before the
|
|
89
|
+
// replacers below run, and put back once they are done, so that neither the
|
|
90
|
+
// metacharacter escaper nor the `?` / `*` replacers ever see its body.
|
|
91
|
+
|
|
92
|
+
// git classifies with its own ASCII-only ctype macros (`wildmatch.c`), never
|
|
93
|
+
// with the C library ones, so these must not be mapped onto `\d` / `\w` /
|
|
94
|
+
// `\s`, which are wider. `/` is left out of every expansion, because a
|
|
95
|
+
// bracket expression never matches a path separator.
|
|
96
|
+
var POSIX_CLASSES = {
|
|
97
|
+
alnum: '0-9A-Za-z',
|
|
98
|
+
alpha: 'A-Za-z',
|
|
99
|
+
blank: ' \\t',
|
|
100
|
+
cntrl: '\\x00-\\x1f\\x7f',
|
|
101
|
+
digit: '0-9',
|
|
102
|
+
graph: '!-.0-~',
|
|
103
|
+
lower: 'a-z',
|
|
104
|
+
print: ' -.0-~',
|
|
105
|
+
punct: '!-.:-@\\[-`{-~',
|
|
106
|
+
// git's `sane-ctype.h` classifies \v and \f as control, not space,
|
|
107
|
+
// unlike C's `isspace`
|
|
108
|
+
space: ' \\t\\n\\r',
|
|
109
|
+
upper: 'A-Z',
|
|
110
|
+
xdigit: '0-9A-Fa-f'
|
|
111
|
+
};
|
|
112
|
+
var CLASS_MEMBERS_TO_ESCAPE = '\\]^-[';
|
|
113
|
+
var escapeMember = function escapeMember(_char) {
|
|
114
|
+
return CLASS_MEMBERS_TO_ESCAPE.indexOf(_char) < 0 ? _char : ESCAPE + _char;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// > if (matched == negated || ((flags & WM_PATHNAME) && text_ch == '/'))
|
|
118
|
+
// > return WM_ABORT_TO_STARSTAR; (git, `wildmatch.c`)
|
|
119
|
+
// A bracket expression never matches a path separator, whatever its members
|
|
120
|
+
// say, so a negated class gets `/` as one more excluded character, while a
|
|
121
|
+
// plain one -- where a literal member or a range could still let `/` in --
|
|
122
|
+
// is guarded with a lookahead, `/` being impossible to subtract from a
|
|
123
|
+
// JavaScript character class.
|
|
124
|
+
var NON_SLASH = '(?!\\/)';
|
|
125
|
+
var classSource = function classSource(negated, body) {
|
|
126
|
+
if (negated) {
|
|
127
|
+
return "[^\\/".concat(body, "]");
|
|
128
|
+
}
|
|
129
|
+
var source = "[".concat(body, "]");
|
|
130
|
+
return new RegExp(source).test('/') ? NON_SLASH + source : source;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Scan the bracket expression that starts at `pattern[start] === '['`,
|
|
134
|
+
// mirroring the member loop of git's `wildmatch.c`.
|
|
135
|
+
// @returns {{end: number, source: string} | null} `null` if the expression is
|
|
136
|
+
// never terminated, which makes the whole pattern match nothing.
|
|
137
|
+
var scanBracket = function scanBracket(pattern, start) {
|
|
138
|
+
var length = pattern.length;
|
|
139
|
+
var index = start + 1;
|
|
140
|
+
var negated = EMPTY;
|
|
141
|
+
var lead = pattern[index];
|
|
142
|
+
if (lead === '!' || lead === '^') {
|
|
143
|
+
negated = '^';
|
|
144
|
+
index++;
|
|
145
|
+
}
|
|
146
|
+
var body = EMPTY;
|
|
147
|
+
|
|
148
|
+
// The member a `-` could start a range from, or EMPTY when the previous
|
|
149
|
+
// member can not open one (start of the body, or a range / POSIX class
|
|
150
|
+
// that has just closed)
|
|
151
|
+
var prev = EMPTY;
|
|
152
|
+
|
|
153
|
+
// git scans the members with a do-while, so the first one is consumed
|
|
154
|
+
// unconditionally. That is the whole reason a leading `]` is a member
|
|
155
|
+
// and not the terminator.
|
|
156
|
+
for (;;) {
|
|
157
|
+
var _char2 = pattern[index];
|
|
158
|
+
if (_char2 === UNDEFINED) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
if (_char2 === ESCAPE) {
|
|
162
|
+
var escaped = pattern[index + 1];
|
|
163
|
+
if (escaped === UNDEFINED) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
body += escapeMember(escaped);
|
|
167
|
+
prev = escaped;
|
|
168
|
+
index++;
|
|
169
|
+
} else if (_char2 === '-' && prev && index + 1 < length && pattern[index + 1] !== ']') {
|
|
170
|
+
index++;
|
|
171
|
+
var to = pattern[index];
|
|
172
|
+
if (to === ESCAPE) {
|
|
173
|
+
// A pattern can not end on a lone backslash -- `checkPattern` has
|
|
174
|
+
// already thrown it away -- so there is an upper bound to read.
|
|
175
|
+
to = pattern[index += 1];
|
|
176
|
+
}
|
|
177
|
+
// An out-of-order range matches nothing in git but is a syntax error in
|
|
178
|
+
// JavaScript, so it is dropped. Its lower bound stays: git tests it
|
|
179
|
+
// as a plain member before it ever looks at the `-`, so `[c-a]` does
|
|
180
|
+
// match `c`.
|
|
181
|
+
if (prev <= to) {
|
|
182
|
+
body += "-".concat(escapeMember(to));
|
|
183
|
+
}
|
|
184
|
+
prev = EMPTY;
|
|
185
|
+
} else if (_char2 === '[' && pattern[index + 1] === ':') {
|
|
186
|
+
var nameStart = index + 2;
|
|
187
|
+
var end = nameStart;
|
|
188
|
+
while (end < length && pattern[end] !== ']') {
|
|
189
|
+
end++;
|
|
190
|
+
}
|
|
191
|
+
if (end === length) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (end > nameStart && pattern[end - 1] === ':') {
|
|
195
|
+
var expanded = POSIX_CLASSES[pattern.slice(nameStart, end - 1)];
|
|
196
|
+
|
|
197
|
+
// An unknown class name makes the whole pattern match nothing
|
|
198
|
+
if (expanded === UNDEFINED) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
body += expanded;
|
|
202
|
+
prev = EMPTY;
|
|
203
|
+
index = end;
|
|
204
|
+
} else {
|
|
205
|
+
// No `:]` to close it, so the `[` is a plain member and scanning
|
|
206
|
+
// resumes right after it.
|
|
207
|
+
body += escapeMember('[');
|
|
208
|
+
prev = '[';
|
|
209
|
+
index = nameStart - 2;
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
body += escapeMember(_char2);
|
|
213
|
+
prev = _char2;
|
|
214
|
+
}
|
|
215
|
+
index++;
|
|
216
|
+
if (pattern[index] === ']') {
|
|
217
|
+
return {
|
|
218
|
+
end: index,
|
|
219
|
+
source: classSource(negated, body)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// An empty JavaScript class can never match, which is how a pattern that git
|
|
226
|
+
// gives up on (`WM_ABORT_ALL`) is expressed here.
|
|
227
|
+
var NEVER_MATCH = '[]';
|
|
228
|
+
|
|
229
|
+
// A NUL can appear in neither a `.gitignore` line nor a path, which makes it
|
|
230
|
+
// the one safe placeholder character. A literal one in the pattern is
|
|
231
|
+
// held aside all the same, so a collision is impossible by construction.
|
|
232
|
+
var PLACEHOLDER = "\0";
|
|
233
|
+
var REGEX_RESTORE_PLACEHOLDER = new RegExp("".concat(PLACEHOLDER, "(\\d+)").concat(PLACEHOLDER), 'g');
|
|
234
|
+
|
|
235
|
+
// The one wildcard the chain does not expand for itself is a trailing `*`. How
|
|
236
|
+
// it expands depends on the mode (`regex` vs `checkRegex`), so `_make` is
|
|
237
|
+
// left to do it, and until then the pending wildcard travels as this marker.
|
|
238
|
+
// That is what keeps it apart from a user-escaped literal `\*`: the unescape
|
|
239
|
+
// steps collapse the escaped one to the exact `\*` a wildcard would leave
|
|
240
|
+
// behind, so by the time `_make` runs the two are otherwise the same string
|
|
241
|
+
// and a literal star gets wrongly rewritten into a wildcard. This marker is a
|
|
242
|
+
// private-use character no compiled pattern carries, and it never survives
|
|
243
|
+
// into a `RegExp` -- `_make` always turns it back into a real wildcard first.
|
|
244
|
+
var TRAILING_WILDCARD = "\uE000";
|
|
245
|
+
|
|
246
|
+
// Replace every bracket expression with a placeholder the replacers below
|
|
247
|
+
// leave alone, and translate it separately.
|
|
248
|
+
var extractBrackets = function extractBrackets(pattern) {
|
|
249
|
+
var sources = [];
|
|
250
|
+
var hold = function hold(source) {
|
|
251
|
+
return "".concat(PLACEHOLDER).concat(sources.push(source) - 1).concat(PLACEHOLDER);
|
|
252
|
+
};
|
|
253
|
+
var length = pattern.length;
|
|
254
|
+
var out = EMPTY;
|
|
255
|
+
var index = 0;
|
|
256
|
+
while (index < length) {
|
|
257
|
+
var _char3 = pattern[index];
|
|
258
|
+
if (_char3 === ESCAPE) {
|
|
259
|
+
// > Put a backslash ("\") in front of ... a character to make it literal.
|
|
260
|
+
// (gitignore(5) -> fnmatch(3))
|
|
261
|
+
// A backslash quotes the next character, whatever it is, so `\d` is a
|
|
262
|
+
// literal `d`, not the regex digit class, and `\?` is a literal `?`,
|
|
263
|
+
// not a wildcard. Held aside as its literal here, the escape never
|
|
264
|
+
// reaches the replacers below, which would otherwise let `\d`, `\b`,
|
|
265
|
+
// `\1`, `\/` keep their regular-expression meaning, and would turn a
|
|
266
|
+
// `\?` into `[^\/]`.
|
|
267
|
+
//
|
|
268
|
+
// Four escapes are left for the chain, each with dedicated handling it
|
|
269
|
+
// would be wrong to bypass: `\*` (a literal star, told apart from a
|
|
270
|
+
// wildcard by `TRAILING_WILDCARD` and the wildcard replacers), `\[`
|
|
271
|
+
// (a literal bracket, the one `[` the bracket replacer still expects),
|
|
272
|
+
// `\ ` (a quoted trailing space), and `\\` (a literal backslash). A
|
|
273
|
+
// lone trailing backslash never reaches here -- `checkPattern` throws
|
|
274
|
+
// it out first.
|
|
275
|
+
var escaped = pattern[index + 1];
|
|
276
|
+
if (escaped === '*' || escaped === '[' || escaped === SPACE || escaped === ESCAPE) {
|
|
277
|
+
out += pattern.slice(index, index + 2);
|
|
278
|
+
} else {
|
|
279
|
+
out += hold(REGEX_LITERAL_SPECIAL.test(escaped) ? ESCAPE + escaped : escaped);
|
|
280
|
+
}
|
|
281
|
+
index += 2;
|
|
282
|
+
} else if (_char3 === PLACEHOLDER) {
|
|
283
|
+
// Hold a literal placeholder character aside as well, so that pattern
|
|
284
|
+
// text can never be mistaken for a placeholder we emitted.
|
|
285
|
+
out += hold("[".concat(PLACEHOLDER, "]"));
|
|
286
|
+
index++;
|
|
287
|
+
} else if (_char3 === '[') {
|
|
288
|
+
var scanned = scanBracket(pattern, index);
|
|
289
|
+
if (scanned === null) {
|
|
290
|
+
// git gives up on the whole pattern (`WM_ABORT_ALL`), so whatever
|
|
291
|
+
// follows can not make it match either.
|
|
292
|
+
out += hold(NEVER_MATCH);
|
|
293
|
+
index = length;
|
|
294
|
+
} else {
|
|
295
|
+
out += hold(scanned.source);
|
|
296
|
+
index = scanned.end + 1;
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
out += _char3;
|
|
300
|
+
index++;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
source: out,
|
|
305
|
+
sources: sources
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// A step of the chain below is normally a `[matcher, replacer]` pair handed
|
|
310
|
+
// to `String.replace`. `DIRECT` marks the ones that are plain string work
|
|
311
|
+
// instead, and take `(source, pattern)`.
|
|
312
|
+
//
|
|
313
|
+
// Anchoring the two ends is exactly that -- a test of one character and a
|
|
314
|
+
// concatenation -- and putting it through the regular expression engine
|
|
315
|
+
// cost a third of this chain for nothing: 167ns where 10ns does the same
|
|
316
|
+
// job. The two are still steps in the same list, in the same places,
|
|
317
|
+
// because their position in the order is part of what they mean.
|
|
318
|
+
var DIRECT = null;
|
|
319
|
+
|
|
320
|
+
// A separator at the beginning or in the middle of a pattern, as opposed to
|
|
321
|
+
// one at the very end.
|
|
322
|
+
var REGEX_INNER_SLASH = /\/(?!$)/;
|
|
323
|
+
|
|
87
324
|
// > If the pattern ends with a slash,
|
|
88
325
|
// > it is removed for the purpose of the following description,
|
|
89
326
|
// > but it would only find a match with a directory.
|
|
@@ -95,27 +332,56 @@ var cleanRangeBackSlash = function cleanRangeBackSlash(slashes) {
|
|
|
95
332
|
// you could use option `mark: true` with `glob`
|
|
96
333
|
|
|
97
334
|
// '`foo/`' should not continue with the '`..`'
|
|
335
|
+
// The chain that turns one gitignore pattern into a regular expression
|
|
336
|
+
// source, in order. A step is either
|
|
337
|
+
//
|
|
338
|
+
// [matcher, replacer] handed to `String.replace`
|
|
339
|
+
// [matcher, replacer, required] the same, but skipped outright unless
|
|
340
|
+
// `required` appears in the string, which
|
|
341
|
+
// the matcher cannot match without
|
|
342
|
+
// [DIRECT, transform] plain string work, taking (source, pattern)
|
|
343
|
+
//
|
|
344
|
+
// The `required` character is only ever a shortcut: finding it does not mean
|
|
345
|
+
// the matcher will match, and not finding it means it cannot. It is there
|
|
346
|
+
// because scanning for one character costs a fraction of running a matcher
|
|
347
|
+
// that then finds nothing -- for the wildcard step, whose `[^\\]+`
|
|
348
|
+
// backtracks its way through the whole string before giving up, 11ns
|
|
349
|
+
// against 400ns.
|
|
98
350
|
var REPLACERS = [[
|
|
99
351
|
// Remove BOM
|
|
100
352
|
// TODO:
|
|
101
353
|
// Other similar zero-width characters?
|
|
102
354
|
/^\uFEFF/, function () {
|
|
103
355
|
return EMPTY;
|
|
356
|
+
}, "\uFEFF"], [
|
|
357
|
+
// A trailing line terminator, left on when a whole file's contents are
|
|
358
|
+
// added as one pattern rather than split into lines. git never sees one
|
|
359
|
+
// -- it reads a `.gitignore` line by line -- so it is not part of the
|
|
360
|
+
// pattern and is dropped here, apart from the trailing-space trimming,
|
|
361
|
+
// which follows git in touching spaces and nothing else.
|
|
362
|
+
/[\r\n]+$/, function () {
|
|
363
|
+
return EMPTY;
|
|
104
364
|
}],
|
|
105
365
|
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
|
106
366
|
[
|
|
367
|
+
// Only spaces, never tabs or other whitespace: git trims a trailing run
|
|
368
|
+
// of `' '` and nothing else (dir.c, `trim_trailing_spaces`, a single
|
|
369
|
+
// `case ' '`), so a pattern ending in a tab keeps it as a literal.
|
|
107
370
|
// (a\ ) -> (a )
|
|
108
371
|
// (a ) -> (a)
|
|
109
372
|
// (a ) -> (a)
|
|
110
373
|
// (a \ ) -> (a )
|
|
111
|
-
/((?:\\\\)*?)(
|
|
374
|
+
/((?:\\\\)*?)(\\? +)$/, function (_, m1, m2) {
|
|
112
375
|
return m1 + (m2.indexOf('\\') === 0 ? SPACE : EMPTY);
|
|
113
376
|
}],
|
|
114
377
|
// Replace (\ ) with ' '
|
|
378
|
+
// Only a space: an escaped tab or other whitespace is already a literal by
|
|
379
|
+
// the time it reaches here, and a bare tab must be left as one, not turned
|
|
380
|
+
// into a space.
|
|
115
381
|
// (\ ) -> ' '
|
|
116
382
|
// (\\ ) -> '\\ '
|
|
117
383
|
// (\\\ ) -> '\\ '
|
|
118
|
-
[/(\\+?)
|
|
384
|
+
[/(\\+?) /g, function (_, m1) {
|
|
119
385
|
var length = m1.length;
|
|
120
386
|
return m1.slice(0, length - length % 2) + SPACE;
|
|
121
387
|
}],
|
|
@@ -142,7 +408,7 @@ var REPLACERS = [[
|
|
|
142
408
|
// > a question mark (?) matches a single character
|
|
143
409
|
/(?!\\)\?/g, function () {
|
|
144
410
|
return '[^/]';
|
|
145
|
-
}],
|
|
411
|
+
}, '?'],
|
|
146
412
|
// leading slash
|
|
147
413
|
[
|
|
148
414
|
// > A leading slash matches the beginning of the pathname.
|
|
@@ -150,11 +416,11 @@ var REPLACERS = [[
|
|
|
150
416
|
// A leading slash matches the beginning of the pathname
|
|
151
417
|
/^\//, function () {
|
|
152
418
|
return '^';
|
|
153
|
-
}],
|
|
419
|
+
}, SLASH],
|
|
154
420
|
// replace special metacharacter slash after the leading slash
|
|
155
421
|
[/\//g, function () {
|
|
156
422
|
return '\\/';
|
|
157
|
-
}], [
|
|
423
|
+
}, SLASH], [
|
|
158
424
|
// > A leading "**" followed by a slash means match in all directories.
|
|
159
425
|
// > For example, "**/foo" matches file or directory "foo" anywhere,
|
|
160
426
|
// > the same as pattern "foo".
|
|
@@ -165,15 +431,20 @@ var REPLACERS = [[
|
|
|
165
431
|
// '**/foo' <-> 'foo'
|
|
166
432
|
function () {
|
|
167
433
|
return '^(?:.*\\/)?';
|
|
168
|
-
}],
|
|
434
|
+
}, '*'],
|
|
169
435
|
// starting
|
|
170
436
|
[
|
|
171
437
|
// there will be no leading '/'
|
|
172
438
|
// (which has been replaced by section "leading slash")
|
|
173
439
|
// If starts with '**', adding a '^' to the regular expression also works
|
|
174
|
-
|
|
440
|
+
DIRECT, function (source, pattern) {
|
|
441
|
+
// Nothing to anchor to, or already anchored
|
|
442
|
+
if (!source || source[0] === '^') {
|
|
443
|
+
return source;
|
|
444
|
+
}
|
|
445
|
+
|
|
175
446
|
// If has a slash `/` at the beginning or middle
|
|
176
|
-
|
|
447
|
+
var anchor = !REGEX_INNER_SLASH.test(pattern)
|
|
177
448
|
// > Prior to 2.22.1
|
|
178
449
|
// > If the pattern does not contain a slash /,
|
|
179
450
|
// > Git treats it as a shell glob pattern
|
|
@@ -191,6 +462,7 @@ function () {
|
|
|
191
462
|
// > Otherwise, Git treats the pattern as a shell glob suitable for
|
|
192
463
|
// > consumption by fnmatch(3)
|
|
193
464
|
: '^';
|
|
465
|
+
return anchor + source;
|
|
194
466
|
}],
|
|
195
467
|
// two globstars
|
|
196
468
|
[
|
|
@@ -203,19 +475,26 @@ function () {
|
|
|
203
475
|
function (_, index, str) {
|
|
204
476
|
return index + 6 < str.length
|
|
205
477
|
|
|
478
|
+
// case: /**/ at the end of the pattern, i.e. a trailing `'/**/'`
|
|
479
|
+
// > A trailing `"/**/"` (a trailing `"/**"` restricted to directories)
|
|
480
|
+
// > matches everything inside, but it should not match the current
|
|
481
|
+
// > folder itself, so it requires at least one directory segment.
|
|
482
|
+
// 'a/**/' matches 'a/b/', 'a/x/y/' but not 'a/'
|
|
483
|
+
? str.slice(index + 6) === '\\/' ? '(?:\\/[^\\/]+)+'
|
|
484
|
+
|
|
206
485
|
// case: /**/
|
|
207
486
|
// > A slash followed by two consecutive asterisks then a slash matches
|
|
208
487
|
// > zero or more directories.
|
|
209
488
|
// > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
|
|
210
489
|
// '/**/'
|
|
211
|
-
|
|
490
|
+
: '(?:\\/[^\\/]+)*'
|
|
212
491
|
|
|
213
492
|
// case: /**
|
|
214
493
|
// > A trailing `"/**"` matches everything inside.
|
|
215
494
|
|
|
216
495
|
// #21: everything inside but it should not include the current folder
|
|
217
496
|
: '\\/.+';
|
|
218
|
-
}],
|
|
497
|
+
}, '*'],
|
|
219
498
|
// normal intermediate wildcards
|
|
220
499
|
[
|
|
221
500
|
// Never replace escaped '*'
|
|
@@ -235,37 +514,55 @@ function (_, p1, p2) {
|
|
|
235
514
|
// > and will match according to the previous rules.
|
|
236
515
|
var unescaped = p2.replace(/\\\*/g, '[^\\/]*');
|
|
237
516
|
return p1 + unescaped;
|
|
238
|
-
}],
|
|
517
|
+
}, '*'],
|
|
518
|
+
// trailing wildcard, held apart from a literal star
|
|
519
|
+
[
|
|
520
|
+
// The step above leaves a trailing `*` alone, so a single `\*` is all that
|
|
521
|
+
// can be left at the end here. Whether it is a wildcard or a literal
|
|
522
|
+
// turns on the backslashes the user put in front of it: the escaper has
|
|
523
|
+
// since doubled every one, so what stands here is those `2N` doubled
|
|
524
|
+
// backslashes and then the star's own escape. An even number of the
|
|
525
|
+
// original `N` leaves the star unescaped -- a wildcard -- and an odd
|
|
526
|
+
// number escapes it -- a literal. This runs while the two are still
|
|
527
|
+
// distinct, before the unescape steps below collapse the literal onto
|
|
528
|
+
// the very `\*` a wildcard leaves behind.
|
|
529
|
+
/(^|[^\\])((?:\\\\)*)\\\*$/, function (match, p1, p2) {
|
|
530
|
+
return (
|
|
531
|
+
// `p2` holds the doubled user backslashes; half of them is `N`.
|
|
532
|
+
p2.length / 2 % 2 === 0
|
|
533
|
+
// A real wildcard: carry it to `_make` as the marker, so the unescape
|
|
534
|
+
// steps and the trailing-wildcard rewrite can never mistake it for a
|
|
535
|
+
// literal `\*` (nor the reverse).
|
|
536
|
+
? p1 + p2 + TRAILING_WILDCARD
|
|
537
|
+
// A literal star: leave it exactly as it stands for the unescape steps.
|
|
538
|
+
: match
|
|
539
|
+
);
|
|
540
|
+
}, '*'], [
|
|
239
541
|
// unescape, revert step 3 except for back slash
|
|
240
542
|
// For example, if a user escape a '\\*',
|
|
241
543
|
// after step 3, the result will be '\\\\\\*'
|
|
242
544
|
/\\\\\\(?=[$.|*+(){^])/g, function () {
|
|
243
545
|
return ESCAPE;
|
|
244
|
-
}], [
|
|
546
|
+
}, ESCAPE + ESCAPE], [
|
|
245
547
|
// '\\\\' -> '\\'
|
|
246
548
|
/\\\\/g, function () {
|
|
247
549
|
return ESCAPE;
|
|
248
|
-
}], [
|
|
249
|
-
//
|
|
250
|
-
//
|
|
550
|
+
}, ESCAPE + ESCAPE], [
|
|
551
|
+
// Every real bracket expression -- POSIX classes included -- has already
|
|
552
|
+
// been held aside by `extractBrackets`, so the only `[` left in the
|
|
553
|
+
// pattern is an escaped, literal one.
|
|
251
554
|
|
|
252
555
|
// `\` is escaped by step 3
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// '[bar]'
|
|
259
|
-
// '[bar\\\\]'
|
|
260
|
-
? "[".concat(negateRange(sanitizeRange(range))).concat(endEscape, "]") // Invalid range notaton
|
|
261
|
-
// '[bar\\]' -> '[bar\\\\]'
|
|
262
|
-
: '[]' : '[]';
|
|
263
|
-
}],
|
|
556
|
+
/\\\[([^\]/]*?)(\\*)($|\])/g,
|
|
557
|
+
// '\\[bar]' -> '\\\\[bar\\]'
|
|
558
|
+
function (match, range, endEscape, close) {
|
|
559
|
+
return "\\[".concat(range).concat(cleanRangeBackSlash(endEscape)).concat(close);
|
|
560
|
+
}, '['],
|
|
264
561
|
// ending
|
|
265
562
|
[
|
|
266
563
|
// 'js' will not match 'js.'
|
|
267
564
|
// 'ab' will not match 'abc'
|
|
268
|
-
|
|
565
|
+
DIRECT,
|
|
269
566
|
// WTF!
|
|
270
567
|
// https://git-scm.com/docs/gitignore
|
|
271
568
|
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
|
@@ -278,13 +575,21 @@ function (_, p1, p2) {
|
|
|
278
575
|
// 'js*' will not match 'a.js'
|
|
279
576
|
// 'js/' will not match 'a.js'
|
|
280
577
|
// 'js' will match 'a.js' and 'a.js/'
|
|
281
|
-
function (
|
|
282
|
-
|
|
578
|
+
function (source) {
|
|
579
|
+
var last = source[source.length - 1];
|
|
580
|
+
|
|
581
|
+
// The pattern is empty, or ends in the pending trailing wildcard the next
|
|
582
|
+
// step owns. A trailing `*` that is not the marker is a literal star,
|
|
583
|
+
// which anchors like any other final character.
|
|
584
|
+
if (!last || last === TRAILING_WILDCARD) {
|
|
585
|
+
return source;
|
|
586
|
+
}
|
|
587
|
+
return last === SLASH
|
|
283
588
|
// foo/ will not match 'foo'
|
|
284
|
-
? "".concat(
|
|
285
|
-
: "".concat(
|
|
589
|
+
? "".concat(source, "$") // foo matches 'foo' and 'foo/'
|
|
590
|
+
: "".concat(source, "(?=$|\\/$)");
|
|
286
591
|
}]];
|
|
287
|
-
var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)
|
|
592
|
+
var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\uE000$/;
|
|
288
593
|
var MODE_IGNORE = 'regex';
|
|
289
594
|
var MODE_CHECK_IGNORE = 'checkRegex';
|
|
290
595
|
var UNDERSCORE = '_';
|
|
@@ -310,15 +615,208 @@ var TRAILING_WILD_CARD_REPLACERS = (_TRAILING_WILD_CARD_R = {}, _defineProperty(
|
|
|
310
615
|
: '[^/]*';
|
|
311
616
|
return "".concat(prefix, "(?=$|\\/$)");
|
|
312
617
|
}), _TRAILING_WILD_CARD_R);
|
|
618
|
+
var WILDCARD = '[^\\/]*';
|
|
619
|
+
|
|
620
|
+
// Where a run of non-slash wildcards is split by single fixed characters --
|
|
621
|
+
// `[^/]*x[^/]*y...` -- the engine has many equivalent ways to lay the
|
|
622
|
+
// input across the wildcards, and on input that does not match it works
|
|
623
|
+
// through all of them, so the time grows with the number of wildcards
|
|
624
|
+
// rather than the length of the path. Every wildcard but the last in such a
|
|
625
|
+
// run can be pinned to stop at the character that follows it, which leaves
|
|
626
|
+
// one way to lay out the input and no rewinding, without changing which
|
|
627
|
+
// paths match: the fixed characters still bound the count, and the last
|
|
628
|
+
// wildcard still absorbs the rest.
|
|
629
|
+
//
|
|
630
|
+
// The source is read one token at a time -- a wildcard, a single-character
|
|
631
|
+
// piece (a literal, an escape, a class), or a parenthesised group or anchor
|
|
632
|
+
// that ends the run -- so only a genuine wildcard is touched.
|
|
633
|
+
var pinWildcards = function pinWildcards(source) {
|
|
634
|
+
if (source.indexOf(WILDCARD) < 0) {
|
|
635
|
+
return source;
|
|
636
|
+
}
|
|
637
|
+
var tokens = [];
|
|
638
|
+
var length = source.length;
|
|
639
|
+
var index = 0;
|
|
640
|
+
while (index < length) {
|
|
641
|
+
var _char4 = source[index];
|
|
642
|
+
if (source.startsWith(WILDCARD, index)) {
|
|
643
|
+
tokens.push({
|
|
644
|
+
wildcard: true
|
|
645
|
+
});
|
|
646
|
+
index += WILDCARD.length;
|
|
647
|
+
} else if (_char4 === '[') {
|
|
648
|
+
var end = index + 1;
|
|
649
|
+
if (source[end] === '^') {
|
|
650
|
+
end++;
|
|
651
|
+
}
|
|
652
|
+
if (source[end] === ']') {
|
|
653
|
+
end++;
|
|
654
|
+
}
|
|
655
|
+
while (end < length && source[end] !== ']') {
|
|
656
|
+
end += source[end] === ESCAPE ? 2 : 1;
|
|
657
|
+
}
|
|
658
|
+
end++;
|
|
659
|
+
tokens.push({
|
|
660
|
+
single: source.slice(index, end)
|
|
661
|
+
});
|
|
662
|
+
index = end;
|
|
663
|
+
} else if (_char4 === ESCAPE) {
|
|
664
|
+
tokens.push({
|
|
665
|
+
single: source.slice(index, index + 2)
|
|
666
|
+
});
|
|
667
|
+
index += 2;
|
|
668
|
+
} else if (_char4 === '(') {
|
|
669
|
+
var depth = 0;
|
|
670
|
+
var _end = index;
|
|
671
|
+
do {
|
|
672
|
+
if (source[_end] === ESCAPE) {
|
|
673
|
+
_end++;
|
|
674
|
+
} else if (source[_end] === '(') {
|
|
675
|
+
depth++;
|
|
676
|
+
} else if (source[_end] === ')') {
|
|
677
|
+
depth--;
|
|
678
|
+
}
|
|
679
|
+
_end++;
|
|
680
|
+
} while (_end < length && depth > 0);
|
|
681
|
+
if ('*+?'.indexOf(source[_end]) >= 0) {
|
|
682
|
+
_end++;
|
|
683
|
+
}
|
|
684
|
+
tokens.push({
|
|
685
|
+
boundary: source.slice(index, _end)
|
|
686
|
+
});
|
|
687
|
+
index = _end;
|
|
688
|
+
} else if (_char4 === '^' || _char4 === '$') {
|
|
689
|
+
tokens.push({
|
|
690
|
+
boundary: _char4
|
|
691
|
+
});
|
|
692
|
+
index++;
|
|
693
|
+
} else {
|
|
694
|
+
tokens.push({
|
|
695
|
+
single: _char4
|
|
696
|
+
});
|
|
697
|
+
index++;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
var out = EMPTY;
|
|
701
|
+
var run = [];
|
|
702
|
+
var flush = function flush() {
|
|
703
|
+
var lastWildcard;
|
|
704
|
+
run.forEach(function (token, at) {
|
|
705
|
+
if (token.wildcard) {
|
|
706
|
+
lastWildcard = at;
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
run.forEach(function (token, at) {
|
|
710
|
+
if (!token.wildcard) {
|
|
711
|
+
out += token.single;
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// A wildcard that is not the last in the run is always immediately
|
|
716
|
+
// followed by the single character that separates it from the next
|
|
717
|
+
// one, because a run never holds two wildcards in a row, so it can be
|
|
718
|
+
// pinned to stop there. The last wildcard stays as it is and takes up
|
|
719
|
+
// the rest.
|
|
720
|
+
out += at === lastWildcard ? WILDCARD : "(?:(?!".concat(run[at + 1].single, ")[^\\/])*");
|
|
721
|
+
});
|
|
722
|
+
run = [];
|
|
723
|
+
};
|
|
724
|
+
tokens.forEach(function (token) {
|
|
725
|
+
if (token.boundary === undefined) {
|
|
726
|
+
run.push(token);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
flush();
|
|
730
|
+
out += token.boundary;
|
|
731
|
+
});
|
|
732
|
+
flush();
|
|
733
|
+
return out;
|
|
734
|
+
};
|
|
313
735
|
|
|
314
736
|
// @param {pattern}
|
|
315
737
|
var makeRegexPrefix = function makeRegexPrefix(pattern) {
|
|
316
|
-
|
|
317
|
-
|
|
738
|
+
var _extractBrackets = extractBrackets(pattern),
|
|
739
|
+
source = _extractBrackets.source,
|
|
740
|
+
sources = _extractBrackets.sources;
|
|
741
|
+
var replaced = REPLACERS.reduce(
|
|
742
|
+
// A pass whose matcher finds nothing hands back the very string it was
|
|
743
|
+
// given, so asking first costs a search and saves a rewrite. Ten of the
|
|
744
|
+
// fifteen passes never fire for a typical .gitignore line, and between
|
|
745
|
+
// them they were 45% of this chain.
|
|
746
|
+
function (prev, _ref) {
|
|
747
|
+
var _ref2 = _slicedToArray(_ref, 3),
|
|
318
748
|
matcher = _ref2[0],
|
|
319
|
-
replacer = _ref2[1]
|
|
320
|
-
|
|
321
|
-
|
|
749
|
+
replacer = _ref2[1],
|
|
750
|
+
required = _ref2[2];
|
|
751
|
+
if (matcher === DIRECT) {
|
|
752
|
+
return replacer(prev, pattern);
|
|
753
|
+
}
|
|
754
|
+
if (required !== UNDEFINED && prev.indexOf(required) < 0) {
|
|
755
|
+
return prev;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// A pass whose matcher finds nothing hands back the very string it was
|
|
759
|
+
// given, so asking first costs a search and saves a rewrite.
|
|
760
|
+
return matcher.test(prev) ? prev.replace(matcher, replacer.bind(pattern)) : prev;
|
|
761
|
+
}, source);
|
|
762
|
+
|
|
763
|
+
// Most patterns hold no bracket expression at all, and then there is
|
|
764
|
+
// nothing to put back.
|
|
765
|
+
return sources.length ? replaced.replace(REGEX_RESTORE_PLACEHOLDER, function (match, index) {
|
|
766
|
+
return sources[index];
|
|
767
|
+
}) : replaced;
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
// A trailing slash does not stop a pattern being basename-only: it restricts
|
|
771
|
+
// the match to a directory, it does not let the pattern reach across one.
|
|
772
|
+
// Everything else a pattern can hold -- a wildcard, a character class, an
|
|
773
|
+
// escape -- stays inside a single path segment too, so a pattern with no
|
|
774
|
+
// separator in it can only ever describe the last one.
|
|
775
|
+
var matchesBasename = function matchesBasename(body) {
|
|
776
|
+
var index = body.indexOf(SLASH);
|
|
777
|
+
return index < 0 || index === body.length - 1;
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
// The last segment of a path, keeping a trailing slash, because a pattern that
|
|
781
|
+
// ends in one matches only a directory.
|
|
782
|
+
// 'a/b/c.js' -> 'c.js'; 'a/b/' -> 'b/'; 'c.js' -> 'c.js' (no copy made)
|
|
783
|
+
var basenameOf = function basenameOf(path) {
|
|
784
|
+
var end = path.length - 1;
|
|
785
|
+
var index = path.lastIndexOf(SLASH, path[end] === SLASH ? end - 1 : end);
|
|
786
|
+
return index < 0 ? path : path.slice(index + 1);
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
// The parent directory of a path, with its trailing separator, or EMPTY when
|
|
790
|
+
// the path has none.
|
|
791
|
+
// 'a/b/c' -> 'a/b/'; 'a/b/' -> 'a/'; 'a' -> EMPTY; 'a/' -> EMPTY
|
|
792
|
+
//
|
|
793
|
+
// A path holding an empty segment has to be taken apart, because its
|
|
794
|
+
// ancestors are not prefixes of it: the parent of 'a//b' is 'a/', not
|
|
795
|
+
// 'a//', and the parent of '/a/' is nothing at all. Both shapes reach here
|
|
796
|
+
// -- 'a//b' is accepted outright, and `checkIgnore` does not put the path
|
|
797
|
+
// it is given through the relative-path check.
|
|
798
|
+
//
|
|
799
|
+
// Every other path is a prefix of itself, and cutting one costs a fraction of
|
|
800
|
+
// splitting it into an array and joining that back at every level: 73ns
|
|
801
|
+
// against 207ns.
|
|
802
|
+
var parentOf = function parentOf(path) {
|
|
803
|
+
if (path.charCodeAt(0) === SLASH_CODE || path.indexOf(DOUBLE_SLASH) >= 0) {
|
|
804
|
+
var slices = path.split(SLASH).filter(Boolean);
|
|
805
|
+
slices.pop();
|
|
806
|
+
return slices.length ? slices.join(SLASH) + SLASH : EMPTY;
|
|
807
|
+
}
|
|
808
|
+
var end = path.length - 1;
|
|
809
|
+
|
|
810
|
+
// Look back from before a trailing separator, since that one belongs to the
|
|
811
|
+
// path itself.
|
|
812
|
+
//
|
|
813
|
+
// A negative place to start would be a trap here: `lastIndexOf` clamps one
|
|
814
|
+
// to 0 and searches there rather than reporting no match, so '/' would
|
|
815
|
+
// come back as its own parent and the walk would never end. It cannot
|
|
816
|
+
// happen -- that is the only path short enough to produce one, and the
|
|
817
|
+
// test above has already sent it the other way.
|
|
818
|
+
var cut = path.lastIndexOf(SLASH, path.charCodeAt(end) === SLASH_CODE ? end - 1 : end);
|
|
819
|
+
return cut < 0 ? EMPTY : path.slice(0, cut + 1);
|
|
322
820
|
};
|
|
323
821
|
var isString = function isString(subject) {
|
|
324
822
|
return typeof subject === 'string';
|
|
@@ -344,7 +842,18 @@ var IgnoreRule = /*#__PURE__*/function () {
|
|
|
344
842
|
define(this, 'ignoreCase', ignoreCase);
|
|
345
843
|
define(this, 'regexPrefix', prefix);
|
|
346
844
|
}
|
|
845
|
+
|
|
846
|
+
// Worked out on first use and kept behind an own property, the way `regex`
|
|
847
|
+
// caches itself in `_regex`. Deciding it in the constructor instead would
|
|
848
|
+
// add a fourth `defineProperty` to every rule ever built, which cost 4% of
|
|
849
|
+
// every compile -- including the compiles of rules that are never matched
|
|
850
|
+
// against anything.
|
|
347
851
|
_createClass(IgnoreRule, [{
|
|
852
|
+
key: "_basenameOnly",
|
|
853
|
+
get: function get() {
|
|
854
|
+
return define(this, '_basenameOnly', matchesBasename(this.body));
|
|
855
|
+
}
|
|
856
|
+
}, {
|
|
348
857
|
key: "regex",
|
|
349
858
|
get: function get() {
|
|
350
859
|
var key = UNDERSCORE + MODE_IGNORE;
|
|
@@ -365,9 +874,9 @@ var IgnoreRule = /*#__PURE__*/function () {
|
|
|
365
874
|
}, {
|
|
366
875
|
key: "_make",
|
|
367
876
|
value: function _make(mode, key) {
|
|
368
|
-
var str = this.regexPrefix.replace(REGEX_REPLACE_TRAILING_WILDCARD,
|
|
877
|
+
var str = pinWildcards(this.regexPrefix.replace(REGEX_REPLACE_TRAILING_WILDCARD,
|
|
369
878
|
// It does not need to bind pattern
|
|
370
|
-
TRAILING_WILD_CARD_REPLACERS[mode]);
|
|
879
|
+
TRAILING_WILD_CARD_REPLACERS[mode]));
|
|
371
880
|
var regex = this.ignoreCase ? new RegExp(str, 'i') : new RegExp(str);
|
|
372
881
|
return define(this, key, regex);
|
|
373
882
|
}
|
|
@@ -400,6 +909,21 @@ var RuleManager = /*#__PURE__*/function () {
|
|
|
400
909
|
_classCallCheck(this, RuleManager);
|
|
401
910
|
this._ignoreCase = ignoreCase;
|
|
402
911
|
this._rules = [];
|
|
912
|
+
|
|
913
|
+
// How many of the rules git would tag `EXC_FLAG_NODIR`.
|
|
914
|
+
//
|
|
915
|
+
// The scan uses it to decide, once for the whole set, whether handing
|
|
916
|
+
// those rules the basename is worth what it costs the others. The
|
|
917
|
+
// shortcut saves a full-path scan on every rule it applies to and costs
|
|
918
|
+
// a check on every rule it does not, so a set where almost nothing is
|
|
919
|
+
// basename-only comes out behind -- a 955 pattern set with 44 of them
|
|
920
|
+
// measured 19% slower with the shortcut always on.
|
|
921
|
+
//
|
|
922
|
+
// Deciding this by measurement rather than by meaning is safe: a
|
|
923
|
+
// basename-only pattern gives the very same answer against the whole
|
|
924
|
+
// path, it just takes longer to say so. The choice can only change how
|
|
925
|
+
// fast the scan runs, never what it returns.
|
|
926
|
+
this._basenameCount = 0;
|
|
403
927
|
}
|
|
404
928
|
_createClass(RuleManager, [{
|
|
405
929
|
key: "_add",
|
|
@@ -407,6 +931,7 @@ var RuleManager = /*#__PURE__*/function () {
|
|
|
407
931
|
// #32
|
|
408
932
|
if (pattern && pattern[KEY_IGNORE]) {
|
|
409
933
|
this._rules = this._rules.concat(pattern._rules._rules);
|
|
934
|
+
this._basenameCount += pattern._rules._basenameCount;
|
|
410
935
|
this._added = true;
|
|
411
936
|
return;
|
|
412
937
|
}
|
|
@@ -419,6 +944,13 @@ var RuleManager = /*#__PURE__*/function () {
|
|
|
419
944
|
var rule = createRule(pattern, this._ignoreCase);
|
|
420
945
|
this._added = true;
|
|
421
946
|
this._rules.push(rule);
|
|
947
|
+
|
|
948
|
+
// Deliberately not `rule._basenameOnly`: reading that would materialise
|
|
949
|
+
// the rule's own copy, and the whole point of leaving it lazy is that
|
|
950
|
+
// a rule which is compiled and never matched never pays for it.
|
|
951
|
+
if (matchesBasename(rule.body)) {
|
|
952
|
+
this._basenameCount++;
|
|
953
|
+
}
|
|
422
954
|
}
|
|
423
955
|
}
|
|
424
956
|
|
|
@@ -445,7 +977,23 @@ var RuleManager = /*#__PURE__*/function () {
|
|
|
445
977
|
var ignored = false;
|
|
446
978
|
var unignored = false;
|
|
447
979
|
var matchedRule;
|
|
448
|
-
|
|
980
|
+
|
|
981
|
+
// Most of a .gitignore is patterns with no slash in them, and running
|
|
982
|
+
// those against the whole path makes the regular expression engine walk
|
|
983
|
+
// every directory name on the way to the only segment that could match.
|
|
984
|
+
// Handing them the basename instead is what git does, and it is where
|
|
985
|
+
// the time in a directory walk goes: the rule scan was two thirds of it.
|
|
986
|
+
var rules = this._rules;
|
|
987
|
+
var length = rules.length;
|
|
988
|
+
var shortcut = this._basenameCount * 2 >= length;
|
|
989
|
+
var basename = shortcut ? basenameOf(path) : path;
|
|
990
|
+
|
|
991
|
+
// A plain loop rather than `forEach`, so that `path`, `basename` and
|
|
992
|
+
// `shortcut` are locals. As a callback they became closure variables,
|
|
993
|
+
// and reaching for one of those once per rule cost 10% of a scan over a
|
|
994
|
+
// large rule set -- more than the shortcut they were there to serve.
|
|
995
|
+
for (var index = 0; index < length; index++) {
|
|
996
|
+
var rule = rules[index];
|
|
449
997
|
var negative = rule.negative;
|
|
450
998
|
|
|
451
999
|
// | ignored : unignored
|
|
@@ -459,17 +1007,13 @@ var RuleManager = /*#__PURE__*/function () {
|
|
|
459
1007
|
// - TEST: always test
|
|
460
1008
|
// - TESTIF: only test if checkUnignored
|
|
461
1009
|
// - X: that never happen
|
|
462
|
-
|
|
463
|
-
|
|
1010
|
+
var skip = unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored;
|
|
1011
|
+
if (!skip && rule[mode].test(shortcut && rule._basenameOnly ? basename : path)) {
|
|
1012
|
+
ignored = !negative;
|
|
1013
|
+
unignored = negative;
|
|
1014
|
+
matchedRule = negative ? UNDEFINED : rule;
|
|
464
1015
|
}
|
|
465
|
-
|
|
466
|
-
if (!matched) {
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
ignored = !negative;
|
|
470
|
-
unignored = negative;
|
|
471
|
-
matchedRule = negative ? UNDEFINED : rule;
|
|
472
|
-
});
|
|
1016
|
+
}
|
|
473
1017
|
var ret = {
|
|
474
1018
|
ignored: ignored,
|
|
475
1019
|
unignored: unignored
|
|
@@ -502,8 +1046,39 @@ var checkPath = function checkPath(path, originalPath, doThrow) {
|
|
|
502
1046
|
}
|
|
503
1047
|
return true;
|
|
504
1048
|
};
|
|
1049
|
+
|
|
1050
|
+
// > pathname should be a `path.relative()`d one
|
|
1051
|
+
//
|
|
1052
|
+
// The same thing `REGEX_TEST_INVALID_PATH` says, spelled out: a path is not
|
|
1053
|
+
// relative if it begins with a separator, or with `./` or `../`, or is
|
|
1054
|
+
// nothing but `.` or `..`. Every match is decided by the first three
|
|
1055
|
+
// characters, and this runs on every path handed to the library -- where it
|
|
1056
|
+
// was 55% of a cached lookup, more than the cache lookup itself.
|
|
505
1057
|
var isNotRelative = function isNotRelative(path) {
|
|
506
|
-
|
|
1058
|
+
var first = path.charCodeAt(0);
|
|
1059
|
+
if (first === SLASH_CODE) {
|
|
1060
|
+
return true;
|
|
1061
|
+
}
|
|
1062
|
+
if (first !== DOT_CODE) {
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// '.'
|
|
1067
|
+
if (path.length === 1) {
|
|
1068
|
+
return true;
|
|
1069
|
+
}
|
|
1070
|
+
var second = path.charCodeAt(1);
|
|
1071
|
+
|
|
1072
|
+
// './'
|
|
1073
|
+
if (second === SLASH_CODE) {
|
|
1074
|
+
return true;
|
|
1075
|
+
}
|
|
1076
|
+
if (second !== DOT_CODE) {
|
|
1077
|
+
return false;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// '..' or '../'
|
|
1081
|
+
return path.length === 2 || path.charCodeAt(2) === SLASH_CODE;
|
|
507
1082
|
};
|
|
508
1083
|
checkPath.isNotRelative = isNotRelative;
|
|
509
1084
|
|
|
@@ -558,25 +1133,24 @@ var Ignore = /*#__PURE__*/function () {
|
|
|
558
1133
|
// @returns {TestResult}
|
|
559
1134
|
}, {
|
|
560
1135
|
key: "_test",
|
|
561
|
-
value: function _test(originalPath, cache, checkUnignored
|
|
1136
|
+
value: function _test(originalPath, cache, checkUnignored) {
|
|
562
1137
|
var path = originalPath
|
|
563
1138
|
// Supports nullable path
|
|
564
1139
|
&& checkPath.convert(originalPath);
|
|
565
1140
|
checkPath(path, originalPath, this._strictPathCheck ? throwError : RETURN_FALSE);
|
|
566
|
-
return this._t(path, cache, checkUnignored
|
|
1141
|
+
return this._t(path, cache, checkUnignored);
|
|
567
1142
|
}
|
|
568
1143
|
}, {
|
|
569
1144
|
key: "checkIgnore",
|
|
570
1145
|
value: function checkIgnore(path) {
|
|
571
1146
|
// If the path doest not end with a slash, `.ignores()` is much equivalent
|
|
572
1147
|
// to `git check-ignore`
|
|
573
|
-
if (
|
|
1148
|
+
if (path.charCodeAt(path.length - 1) !== SLASH_CODE) {
|
|
574
1149
|
return this.test(path);
|
|
575
1150
|
}
|
|
576
|
-
var
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
var parent = this._t(slices.join(SLASH) + SLASH, this._testCache, true, slices);
|
|
1151
|
+
var parentPath = parentOf(path);
|
|
1152
|
+
if (parentPath) {
|
|
1153
|
+
var parent = this._t(parentPath, this._testCache, true);
|
|
580
1154
|
if (parent.ignored) {
|
|
581
1155
|
return parent;
|
|
582
1156
|
}
|
|
@@ -591,27 +1165,15 @@ var Ignore = /*#__PURE__*/function () {
|
|
|
591
1165
|
// The cache for the result of a certain checking
|
|
592
1166
|
cache,
|
|
593
1167
|
// Whether should check if the path is unignored
|
|
594
|
-
checkUnignored
|
|
595
|
-
// The path slices
|
|
596
|
-
slices) {
|
|
1168
|
+
checkUnignored) {
|
|
597
1169
|
if (path in cache) {
|
|
598
1170
|
return cache[path];
|
|
599
1171
|
}
|
|
600
|
-
|
|
601
|
-
// path/to/a.js
|
|
602
|
-
// ['path', 'to', 'a.js']
|
|
603
|
-
slices = path.split(SLASH).filter(Boolean);
|
|
604
|
-
}
|
|
605
|
-
slices.pop();
|
|
606
|
-
|
|
607
|
-
// If the path has no parent directory, just test it
|
|
608
|
-
if (!slices.length) {
|
|
609
|
-
return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE);
|
|
610
|
-
}
|
|
611
|
-
var parent = this._t(slices.join(SLASH) + SLASH, cache, checkUnignored, slices);
|
|
1172
|
+
var parentPath = parentOf(path);
|
|
612
1173
|
|
|
613
1174
|
// If the path contains a parent directory, check the parent first
|
|
614
|
-
|
|
1175
|
+
var parent = parentPath ? this._t(parentPath, cache, checkUnignored) : UNDEFINED;
|
|
1176
|
+
return cache[path] = parent && parent.ignored
|
|
615
1177
|
// > It is not possible to re-include a file if a parent directory of
|
|
616
1178
|
// > that file is excluded.
|
|
617
1179
|
? parent : this._rules.test(path, checkUnignored, MODE_IGNORE);
|