@mrclrchtr/supi-lsp 11.0.0 → 11.1.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.
@@ -15,6 +15,8 @@ const ESCAPE = '\\'
15
15
  const REGEX_LITERAL_SPECIAL = /[.*+?()[\]{}^$|\\/]/
16
16
  const BOM = '\uFEFF'
17
17
  const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/
18
+ // A run of two or more asterisks is a globstar (#175)
19
+ const REGEX_ANCHORED_GLOBSTAR = /^\/\*{2,}$/
18
20
  const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/
19
21
  const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/
20
22
  const REGEX_SPLITALL_CRLF = /\r?\n/g
@@ -49,12 +51,6 @@ const define = (object, key, value) => {
49
51
 
50
52
  const RETURN_FALSE = () => false
51
53
 
52
- // See fixtures #59
53
- const cleanRangeBackSlash = slashes => {
54
- const {length} = slashes
55
- return slashes.slice(0, length - length % 2)
56
- }
57
-
58
54
  // > The range notation, e.g. [a-zA-Z],
59
55
  // > can be used to match one of the characters in a range.
60
56
  //
@@ -194,14 +190,14 @@ const scanBracket = (pattern, start) => {
194
190
  }
195
191
 
196
192
  if (end > nameStart && pattern[end - 1] === ':') {
197
- const expanded = POSIX_CLASSES[pattern.slice(nameStart, end - 1)]
193
+ const name = pattern.slice(nameStart, end - 1)
198
194
 
199
195
  // An unknown class name makes the whole pattern match nothing
200
- if (expanded === UNDEFINED) {
196
+ if (!Object.prototype.hasOwnProperty.call(POSIX_CLASSES, name)) {
201
197
  return null
202
198
  }
203
199
 
204
- body += expanded
200
+ body += POSIX_CLASSES[name]
205
201
  prev = EMPTY
206
202
  index = end
207
203
  } else {
@@ -245,9 +241,10 @@ const REGEX_RESTORE_PLACEHOLDER = new RegExp(
245
241
  // That is what keeps it apart from a user-escaped literal `\*`: the unescape
246
242
  // steps collapse the escaped one to the exact `\*` a wildcard would leave
247
243
  // behind, so by the time `_make` runs the two are otherwise the same string
248
- // and a literal star gets wrongly rewritten into a wildcard. This marker is a
249
- // private-use character no compiled pattern carries, and it never survives
250
- // into a `RegExp` -- `_make` always turns it back into a real wildcard first.
244
+ // and a literal star gets wrongly rewritten into a wildcard. The marker is a
245
+ // private-use character, and `extractBrackets` keeps one that came out of the
246
+ // pattern apart from it, so the only marker `_make` ever turns back into a
247
+ // real wildcard is the one this chain put there itself.
251
248
  const TRAILING_WILDCARD = '\uE000'
252
249
 
253
250
  // A trailing "/**" is deferred the same way, because `checkRegex` has to tell
@@ -255,6 +252,28 @@ const TRAILING_WILDCARD = '\uE000'
255
252
  // still never matches the folder itself (#21).
256
253
  const TRAILING_DOUBLESTAR = '\uE001'
257
254
 
255
+ // The three characters above mean something to the chain that builds the
256
+ // source rather than to the path, so one that came out of the pattern is
257
+ // held aside like a bracket expression instead of going straight out. Being
258
+ // held is what makes it safe: the restore runs after the step that anchors
259
+ // the source, so the character comes back in the middle of the source and
260
+ // not at its end, which is the one place `_make` and `checkSourceOf` look
261
+ // for a marker. Without it a pattern's own character lands where a pending
262
+ // marker sits, and `_make` turns it into a real trailing wildcard.
263
+ //
264
+ // It is held as a one-member class rather than bare, in both of the branches
265
+ // a reserved character can reach, so that the property can be
266
+ // read off this function alone -- a reserved character never goes out on
267
+ // its own -- rather than depending on where in the chain the restore
268
+ // happens to run. In the expression built here `[x]` matches exactly what
269
+ // `x` matches, none of the three being special to a regular expression or
270
+ // inside a class, so no test can tell the two apart: the class is insurance,
271
+ // and the paragraph above is the reason it is not needed.
272
+ const isReserved = char =>
273
+ char === PLACEHOLDER
274
+ || char === TRAILING_WILDCARD
275
+ || char === TRAILING_DOUBLESTAR
276
+
258
277
  // Replace every bracket expression with a placeholder the replacers below
259
278
  // leave alone, and translate it separately.
260
279
  const extractBrackets = pattern => {
@@ -279,12 +298,19 @@ const extractBrackets = pattern => {
279
298
  // `\1`, `\/` keep their regular-expression meaning, and would turn a
280
299
  // `\?` into `[^\/]`.
281
300
  //
282
- // Four escapes are left for the chain, each with dedicated handling it
283
- // would be wrong to bypass: `\*` (a literal star, told apart from a
284
- // wildcard by `TRAILING_WILDCARD` and the wildcard replacers), `\[`
285
- // (a literal bracket, the one `[` the bracket replacer still expects),
286
- // `\ ` (a quoted trailing space), and `\\` (a literal backslash). A
287
- // lone trailing backslash can reach here after line-ending cleanup.
301
+ // Three escapes are still left for the chain: `\*` (a literal star,
302
+ // told apart from a wildcard by `TRAILING_WILDCARD` and the wildcard
303
+ // replacers), `\[` (a literal bracket, the one `[` the bracket
304
+ // replacer still expects), and `\ ` (a quoted space, which one pass
305
+ // below rewrites in bulk -- holding each one aside instead costs more
306
+ // on a pattern built out of them). A lone trailing backslash can reach
307
+ // here after line-ending cleanup.
308
+ //
309
+ // A quoted backslash is not among them. The chain doubles every
310
+ // backslash, then takes the doubling back out again, and a quoted one
311
+ // standing next to a character the chain also escapes left the two
312
+ // counts sharing a run, so one of the escapes was lost. Held here as
313
+ // the literal it means, it never joins that run.
288
314
  const escaped = pattern[index + 1]
289
315
 
290
316
  if (
@@ -298,22 +324,21 @@ const extractBrackets = pattern => {
298
324
  escaped === '*'
299
325
  || escaped === '['
300
326
  || escaped === SPACE
301
- || escaped === ESCAPE
302
327
  ) {
303
328
  out += pattern.slice(index, index + 2)
304
329
  } else {
305
330
  out += hold(
306
331
  REGEX_LITERAL_SPECIAL.test(escaped)
307
332
  ? ESCAPE + escaped
308
- : escaped
333
+ : isReserved(escaped)
334
+ ? `[${escaped}]`
335
+ : escaped
309
336
  )
310
337
  }
311
338
 
312
339
  index += 2
313
- } else if (char === PLACEHOLDER) {
314
- // Hold a literal placeholder character aside as well, so that pattern
315
- // text can never be mistaken for a placeholder we emitted.
316
- out += hold(`[${PLACEHOLDER}]`)
340
+ } else if (isReserved(char)) {
341
+ out += hold(`[${char}]`)
317
342
  index ++
318
343
  } else if (char === '[') {
319
344
  const scanned = scanBracket(pattern, index)
@@ -400,6 +425,21 @@ const REPLACERS = [
400
425
  ESCAPE + SPACE
401
426
  ],
402
427
 
428
+ // git's wildmatch() reads a run of two or more asterisks as one globstar
429
+ // whenever the run fills a path segment: it starts the pattern or follows
430
+ // a `/`, and it ends the pattern or comes before a `/`. A run of two is
431
+ // already `**`; a longer one is folded into `**` here, so the steps
432
+ // below only ever have to know `**`.
433
+ // `***/foo` -> `**/foo`, `a/***/b` -> `a/**/b`, `a/***` -> `a/**`
434
+ // git also takes a run beside a literal prefix (`a***/b`) or beside an
435
+ // escaped slash (`***\/b`) as a globstar, and this does not; the README
436
+ // lists both, under "A run of asterisks".
437
+ [
438
+ /(^|\/)\*{3,}(?=\/|$)/g,
439
+ (_, start) => `${start}**`,
440
+ '*'
441
+ ],
442
+
403
443
  // Escape metacharacters
404
444
  // which is written down by users but means special for regular expressions.
405
445
 
@@ -559,23 +599,23 @@ const REPLACERS = [
559
599
  // trailing wildcard, held apart from a literal star
560
600
  [
561
601
  // The step above leaves a trailing `*` alone, so a single `\*` is all that
562
- // can be left at the end here. Whether it is a wildcard or a literal
563
- // turns on the backslashes the user put in front of it: the escaper has
564
- // since doubled every one, so what stands here is those `2N` doubled
565
- // backslashes and then the star's own escape. An even number of the
566
- // original `N` leaves the star unescaped -- a wildcard -- and an odd
567
- // number escapes it -- a literal. This runs while the two are still
568
- // distinct, before the unescape steps below collapse the literal onto
569
- // the very `\*` a wildcard leaves behind.
602
+ // can be left at the end here, and what stands in front of it says which
603
+ // kind it is. A wildcard arrives as the escaper wrote it, `\*` with
604
+ // nothing before it; a literal arrives as the user's own escape, which
605
+ // the escaper doubled, and then the star's own -- `\\` and then `\*`.
606
+ // There is no third case: every backslash still reaching the chain is
607
+ // one `\*`, `\[` or `\ ` carries itself, and each is followed by its
608
+ // own character, so a longer run can never form. This runs while the
609
+ // two are still distinct, before the unescape steps below collapse the
610
+ // literal onto the very `\*` a wildcard leaves behind.
570
611
  /(^|[^\\])((?:\\\\)*)\\\*$/,
571
612
 
572
613
  (match, p1, p2) =>
573
- // `p2` holds the doubled user backslashes; half of them is `N`.
574
- (p2.length / 2) % 2 === 0
614
+ p2 === EMPTY
575
615
  // A real wildcard: carry it to `_make` as the marker, so the unescape
576
616
  // steps and the trailing-wildcard rewrite can never mistake it for a
577
617
  // literal `\*` (nor the reverse).
578
- ? p1 + p2 + TRAILING_WILDCARD
618
+ ? p1 + TRAILING_WILDCARD
579
619
  // A literal star: leave it exactly as it stands for the unescape steps.
580
620
  : match,
581
621
  '*'
@@ -602,12 +642,12 @@ const REPLACERS = [
602
642
  // been held aside by `extractBrackets`, so the only `[` left in the
603
643
  // pattern is an escaped, literal one.
604
644
 
605
- // `\` is escaped by step 3
606
- /\\\[([^\]/]*?)(\\*)($|\])/g,
645
+ // A backslash could only stand at the end of the range if it quoted the
646
+ // `]`, and `\]` is held aside long before this.
647
+ /\\\[([^\]/]*?)($|\])/g,
607
648
 
608
- // '\\[bar]' -> '\\\\[bar\\]'
609
- (match, range, endEscape, close) =>
610
- `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`,
649
+ // '\\[bar]' -> '\\\\[bar]'
650
+ (match, range, close) => `\\[${range}${close}`,
611
651
  '['
612
652
  ],
613
653
 
@@ -678,23 +718,10 @@ const replaceTrailingDoublestar = () => '.+(?=$|\\/$)'
678
718
 
679
719
  const WILDCARD = '[^\\/]*'
680
720
 
681
- // Where a run of non-slash wildcards is split by single fixed characters --
682
- // `[^/]*x[^/]*y...` -- the engine has many equivalent ways to lay the
683
- // input across the wildcards, and on input that does not match it works
684
- // through all of them, so the time grows with the number of wildcards
685
- // rather than the length of the path. Every wildcard but the last in such a
686
- // run can be pinned to stop at the character that follows it, which leaves
687
- // one way to lay out the input and no rewinding, without changing which
688
- // paths match: the fixed characters still bound the count, and the last
689
- // wildcard still absorbs the rest.
690
- //
691
- // The source is read one token at a time -- a wildcard, a single-character
692
- // piece (a literal, an escape, a class), or a parenthesised group or anchor
693
- // that ends the run -- so only a genuine wildcard is touched.
694
-
695
- // The fixed pieces between the wildcard at `at` and the next wildcard of the
696
- // run, joined as they appear in the source. A run never holds two wildcards
697
- // in a row, so there is always at least one piece.
721
+ // The fixed piece between the wildcard at `at` and the next wildcard of the
722
+ // run: the tokens that separate them, joined as they appear in the source.
723
+ // A run never holds two wildcards in a row, so there is always at least one
724
+ // token.
698
725
  const separatorAfter = (run, at) => {
699
726
  let separator = EMPTY
700
727
 
@@ -705,6 +732,29 @@ const separatorAfter = (run, at) => {
705
732
  return separator
706
733
  }
707
734
 
735
+ // Where a run of non-slash wildcards is split by fixed pieces --
736
+ // `[^/]*ab[^/]*cd...` -- the engine has many equivalent ways to lay the
737
+ // input across the wildcards, and on input that does not match it works
738
+ // through all of them, so the time grows with the number of wildcards
739
+ // rather than the length of the path. Every wildcard but the last in such a
740
+ // run can be pinned to stop where the piece after it begins, which leaves
741
+ // one way to lay out the input and no rewinding, without changing which
742
+ // paths match: the fixed pieces still bound the count, and the last
743
+ // wildcard still absorbs the rest.
744
+ //
745
+ // A wildcard is pinned to the whole piece, never to the piece's first
746
+ // character. The pin leaves no way back, so a wildcard that stopped at that
747
+ // first character would hand over wherever the character happens to turn
748
+ // up, including the places the rest of the piece cannot follow -- which is
749
+ // how `f*o/*/*` came to miss `foo/b/c`, the first `o` of `foo` not being
750
+ // the one the `o/` after it needs. Every token of a piece matches exactly
751
+ // one character, so the first place the whole piece fits is the leftmost
752
+ // place it can sit, and whatever is left over goes to the wildcards that
753
+ // follow.
754
+ //
755
+ // The source is read one token at a time -- a wildcard, a single-character
756
+ // piece (a literal, an escape, a class), or a parenthesised group or anchor
757
+ // that ends the run -- so only a genuine wildcard is touched.
708
758
  const pinWildcards = source => {
709
759
  if (source.indexOf(WILDCARD) < 0) {
710
760
  return source
@@ -792,16 +842,9 @@ const pinWildcards = source => {
792
842
  return
793
843
  }
794
844
 
795
- // A wildcard that is not the last in the run is pinned to the whole
796
- // fixed piece that separates it from the next wildcard, not just to
797
- // that piece's first character: stopping at the first character alone
798
- // would let the wildcard hand over at a place where the rest of the
799
- // separator cannot follow, and the pin leaves no way back, so
800
- // `f*o/*/*` would miss `foo/b/c` -- the first `o` of `foo` is not the
801
- // one the `o/` after it needs. Every piece matches exactly one
802
- // character, so pinning to the first place the whole separator fits
803
- // is the leftmost place it can sit, and the wildcards that follow
804
- // take up whatever is left.
845
+ // Every wildcard but the last is pinned to the whole piece that
846
+ // follows it, for the reason set out above this function. The last
847
+ // one stays as it is and takes up the rest.
805
848
  out += at === lastWildcard
806
849
  ? WILDCARD
807
850
  : `(?:(?!${separatorAfter(run, at)})[^\\/])*`
@@ -827,6 +870,15 @@ const pinWildcards = source => {
827
870
 
828
871
  // @param {pattern}
829
872
  const makeRegexPrefix = pattern => {
873
+ // `/**` matches every path, as `**` does. The translation below only finds
874
+ // a globstar beside a slash it keeps, and the leading one is turned into
875
+ // an anchor, so `/**` would come out as `/*` and match one level only.
876
+ // Here rather than in `createRule`, so that `checkSourceOf`, which
877
+ // compiles `/**/` again without its trailing slash, gets it too.
878
+ if (REGEX_ANCHORED_GLOBSTAR.test(pattern)) {
879
+ pattern = '**'
880
+ }
881
+
830
882
  const {source, sources} = extractBrackets(pattern)
831
883
 
832
884
  const replaced = REPLACERS.reduce(
@@ -1245,17 +1297,10 @@ class RuleManager {
1245
1297
 
1246
1298
  // Test one single path without recursively checking parent directories
1247
1299
  //
1248
- // - checkUnignored `boolean` whether should check if the path is unignored,
1249
- // setting `checkUnignored` to `false` could reduce additional
1250
- // path matching.
1251
- // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
1252
-
1253
- // @returns {TestResult} true if a file is ignored
1254
- test (path, checkUnignored, mode) {
1255
- let ignored = false
1256
- let unignored = false
1257
- let matchedRule
1300
+ // - mode `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
1258
1301
 
1302
+ // @returns {TestResult}
1303
+ test (path, mode) {
1259
1304
  // Most of a .gitignore is patterns with no slash in them, and running
1260
1305
  // those against the whole path makes the regular expression engine walk
1261
1306
  // every directory name on the way to the only segment that could match.
@@ -1270,52 +1315,40 @@ class RuleManager {
1270
1315
  ? basenameOf(path)
1271
1316
  : path
1272
1317
 
1318
+ // The last rule to match decides, as in git, and it is the rule
1319
+ // `git check-ignore -v` reports, so the scan runs from the end and stops
1320
+ // at the first match.
1273
1321
  // A plain loop rather than `forEach`, so that `path`, `basename` and
1274
1322
  // `shortcut` are locals. As a callback they became closure variables,
1275
1323
  // and reaching for one of those once per rule cost 10% of a scan over a
1276
1324
  // large rule set -- more than the shortcut they were there to serve.
1277
- for (let index = 0; index < length; index ++) {
1325
+ for (let index = length - 1; index >= 0; index --) {
1278
1326
  const rule = rules[index]
1279
- const {negative} = rule
1280
-
1281
- // | ignored : unignored
1282
- // -------- | ---------------------------------------
1283
- // negative | 0:0 | 0:1 | 1:0 | 1:1
1284
- // -------- | ------- | ------- | ------- | --------
1285
- // 0 | TEST | TEST | SKIP | X
1286
- // 1 | TESTIF | SKIP | TEST | X
1287
-
1288
- // - SKIP: always skip
1289
- // - TEST: always test
1290
- // - TESTIF: only test if checkUnignored
1291
- // - X: that never happen
1292
- const skip = unignored === negative && ignored !== unignored
1293
- || negative && !ignored && !unignored && !checkUnignored
1294
-
1295
- if (!skip && rule[mode].test(
1327
+
1328
+ if (rule[mode].test(
1296
1329
  shortcut && rule._basenameOnly
1297
1330
  ? basename
1298
1331
  : path
1299
1332
  )) {
1300
- ignored = !negative
1301
- unignored = negative
1333
+ const {negative} = rule
1302
1334
 
1303
- matchedRule = negative
1304
- ? UNDEFINED
1305
- : rule
1306
- }
1307
- }
1335
+ const ret = {
1336
+ ignored: !negative,
1337
+ unignored: negative
1338
+ }
1308
1339
 
1309
- const ret = {
1310
- ignored,
1311
- unignored
1312
- }
1340
+ if (!negative) {
1341
+ ret.rule = rule
1342
+ }
1313
1343
 
1314
- if (matchedRule) {
1315
- ret.rule = matchedRule
1344
+ return ret
1345
+ }
1316
1346
  }
1317
1347
 
1318
- return ret
1348
+ return {
1349
+ ignored: false,
1350
+ unignored: false
1351
+ }
1319
1352
  }
1320
1353
  }
1321
1354
 
@@ -1431,7 +1464,7 @@ class Ignore {
1431
1464
  }
1432
1465
 
1433
1466
  // @returns {TestResult}
1434
- _test (originalPath, cache, checkUnignored) {
1467
+ _test (originalPath, cache) {
1435
1468
  const path = originalPath
1436
1469
  // Supports nullable path
1437
1470
  && checkPath.convert(originalPath)
@@ -1444,7 +1477,7 @@ class Ignore {
1444
1477
  : RETURN_FALSE
1445
1478
  )
1446
1479
 
1447
- return this._t(path, cache, checkUnignored)
1480
+ return this._t(path, cache)
1448
1481
  }
1449
1482
 
1450
1483
  checkIgnore (path) {
@@ -1457,7 +1490,7 @@ class Ignore {
1457
1490
  // Like `git check-ignore`: a directory excluded by itself or by an
1458
1491
  // ancestor settles it, and only then is the literal 'abc/' matched
1459
1492
  // against the rules (see `checkSourceOf`).
1460
- const dir = this._t(path, this._testCache, true)
1493
+ const dir = this._t(path, this._testCache)
1461
1494
 
1462
1495
  if (dir.ignored) {
1463
1496
  return dir
@@ -1475,10 +1508,7 @@ class Ignore {
1475
1508
  path,
1476
1509
 
1477
1510
  // The cache for the result of a certain checking
1478
- cache,
1479
-
1480
- // Whether should check if the path is unignored
1481
- checkUnignored
1511
+ cache
1482
1512
  ) {
1483
1513
  if (path in cache) {
1484
1514
  return cache[path]
@@ -1488,18 +1518,18 @@ class Ignore {
1488
1518
 
1489
1519
  // If the path contains a parent directory, check the parent first
1490
1520
  const parent = parentPath
1491
- ? this._t(parentPath, cache, checkUnignored)
1521
+ ? this._t(parentPath, cache)
1492
1522
  : UNDEFINED
1493
1523
 
1494
1524
  return cache[path] = parent && parent.ignored
1495
1525
  // > It is not possible to re-include a file if a parent directory of
1496
1526
  // > that file is excluded.
1497
1527
  ? parent
1498
- : this._rules.test(path, checkUnignored, MODE_IGNORE)
1528
+ : this._rules.test(path, MODE_IGNORE)
1499
1529
  }
1500
1530
 
1501
1531
  ignores (path) {
1502
- return this._test(path, this._ignoreCache, false).ignored
1532
+ return this._test(path, this._ignoreCache).ignored
1503
1533
  }
1504
1534
 
1505
1535
  createFilter () {
@@ -1512,7 +1542,7 @@ class Ignore {
1512
1542
 
1513
1543
  // @returns {TestResult}
1514
1544
  test (path) {
1515
- return this._test(path, this._testCache, true)
1545
+ return this._test(path, this._testCache)
1516
1546
  }
1517
1547
  }
1518
1548