@mrclrchtr/supi-lsp 8.0.0 → 9.0.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 +1 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +10 -1
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +9 -4
- package/node_modules/@mrclrchtr/supi-core/src/session-capabilities.ts +55 -1
- package/node_modules/@mrclrchtr/supi-core/src/session-capability-profiles.ts +240 -0
- package/node_modules/@mrclrchtr/supi-core/src/session.ts +22 -0
- package/node_modules/ignore/README.md +6 -9
- package/node_modules/ignore/index.js +202 -106
- package/node_modules/ignore/legacy.js +164 -88
- package/node_modules/ignore/package.json +1 -1
- package/package.json +3 -3
- package/src/client/client.ts +14 -3
- package/src/config/defaults.json +6 -0
- package/src/diagnostics/evidence.ts +1 -1
- package/src/manager/manager.ts +1 -1
- package/src/provider/refactor-planning.ts +1 -0
- package/src/provider/semantic-edit-normalizer.ts +1 -1
- package/src/utils.ts +2 -0
|
@@ -13,12 +13,7 @@ const ESCAPE = '\\'
|
|
|
13
13
|
// The characters that carry a meaning of their own inside a regular
|
|
14
14
|
// expression, so a literal one has to be escaped before it is emitted.
|
|
15
15
|
const REGEX_LITERAL_SPECIAL = /[.*+?()[\]{}^$|\\/]/
|
|
16
|
-
|
|
17
|
-
// but a line holding a tab is a pattern for a tab-named path, since git
|
|
18
|
-
// never trims a tab.
|
|
19
|
-
// A leading BOM is removed during compilation, so reject a line that would
|
|
20
|
-
// become empty after removing it and trimming spaces.
|
|
21
|
-
const REGEX_TEST_BLANK_LINE = /^\uFEFF? *$/
|
|
16
|
+
const BOM = '\uFEFF'
|
|
22
17
|
const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/
|
|
23
18
|
const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/
|
|
24
19
|
const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/
|
|
@@ -255,6 +250,11 @@ const REGEX_RESTORE_PLACEHOLDER = new RegExp(
|
|
|
255
250
|
// into a `RegExp` -- `_make` always turns it back into a real wildcard first.
|
|
256
251
|
const TRAILING_WILDCARD = '\uE000'
|
|
257
252
|
|
|
253
|
+
// A trailing "/**" is deferred the same way, because `checkRegex` has to tell
|
|
254
|
+
// it apart from a trailing "/*" (see `checkSourceOf`). For `.ignores()` it
|
|
255
|
+
// still never matches the folder itself (#21).
|
|
256
|
+
const TRAILING_DOUBLESTAR = '\uE001'
|
|
257
|
+
|
|
258
258
|
// Replace every bracket expression with a placeholder the replacers below
|
|
259
259
|
// leave alone, and translate it separately.
|
|
260
260
|
const extractBrackets = pattern => {
|
|
@@ -284,11 +284,17 @@ const extractBrackets = pattern => {
|
|
|
284
284
|
// wildcard by `TRAILING_WILDCARD` and the wildcard replacers), `\[`
|
|
285
285
|
// (a literal bracket, the one `[` the bracket replacer still expects),
|
|
286
286
|
// `\ ` (a quoted trailing space), and `\\` (a literal backslash). A
|
|
287
|
-
// lone trailing backslash
|
|
288
|
-
// it out first.
|
|
287
|
+
// lone trailing backslash can reach here after line-ending cleanup.
|
|
289
288
|
const escaped = pattern[index + 1]
|
|
290
289
|
|
|
291
290
|
if (
|
|
291
|
+
escaped === UNDEFINED
|
|
292
|
+
|| escaped === SLASH && index + 2 === length
|
|
293
|
+
) {
|
|
294
|
+
// Git removes a final directory slash before matching. An escape
|
|
295
|
+
// without a character after that removal makes the pattern invalid.
|
|
296
|
+
out += hold(NEVER_MATCH)
|
|
297
|
+
} else if (
|
|
292
298
|
escaped === '*'
|
|
293
299
|
|| escaped === '['
|
|
294
300
|
|| escaped === SPACE
|
|
@@ -376,42 +382,6 @@ const REGEX_INNER_SLASH = /\/(?!$)/
|
|
|
376
382
|
// against 400ns.
|
|
377
383
|
const REPLACERS = [
|
|
378
384
|
|
|
379
|
-
[
|
|
380
|
-
// Remove BOM
|
|
381
|
-
// TODO:
|
|
382
|
-
// Other similar zero-width characters?
|
|
383
|
-
/^\uFEFF/,
|
|
384
|
-
() => EMPTY,
|
|
385
|
-
'\uFEFF'
|
|
386
|
-
],
|
|
387
|
-
|
|
388
|
-
[
|
|
389
|
-
// A trailing line terminator, left on when a whole file's contents are
|
|
390
|
-
// added as one pattern rather than split into lines. git never sees one
|
|
391
|
-
// -- it reads a `.gitignore` line by line -- so it is not part of the
|
|
392
|
-
// pattern and is dropped here, apart from the trailing-space trimming,
|
|
393
|
-
// which follows git in touching spaces and nothing else.
|
|
394
|
-
/[\r\n]+$/,
|
|
395
|
-
() => EMPTY
|
|
396
|
-
],
|
|
397
|
-
|
|
398
|
-
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
|
399
|
-
[
|
|
400
|
-
// Only spaces, never tabs or other whitespace: git trims a trailing run
|
|
401
|
-
// of `' '` and nothing else (dir.c, `trim_trailing_spaces`, a single
|
|
402
|
-
// `case ' '`), so a pattern ending in a tab keeps it as a literal.
|
|
403
|
-
// (a\ ) -> (a )
|
|
404
|
-
// (a ) -> (a)
|
|
405
|
-
// (a ) -> (a)
|
|
406
|
-
// (a \ ) -> (a )
|
|
407
|
-
/((?:\\\\)*?)(\\? +)$/,
|
|
408
|
-
(_, m1, m2) => m1 + (
|
|
409
|
-
m2.indexOf('\\') === 0
|
|
410
|
-
? SPACE
|
|
411
|
-
: EMPTY
|
|
412
|
-
)
|
|
413
|
-
],
|
|
414
|
-
|
|
415
385
|
// Replace (\ ) with ' '
|
|
416
386
|
// Only a space: an escaped tab or other whitespace is already a literal by
|
|
417
387
|
// the time it reaches here, and a bare tab must be left as one, not turned
|
|
@@ -420,11 +390,14 @@ const REPLACERS = [
|
|
|
420
390
|
// (\\ ) -> '\\ '
|
|
421
391
|
// (\\\ ) -> '\\ '
|
|
422
392
|
[
|
|
423
|
-
|
|
424
|
-
(
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
393
|
+
// A run of backslashes is taken whole, from its first one, so no match
|
|
394
|
+
// ever starts again inside it -- `(\\+?) ` did, which made a long run
|
|
395
|
+
// quadratic.
|
|
396
|
+
/(\\+)( ?)/g,
|
|
397
|
+
(_, run, space) => space
|
|
398
|
+
? run.slice(0, run.length - run.length % 2) + SPACE
|
|
399
|
+
: run,
|
|
400
|
+
ESCAPE + SPACE
|
|
428
401
|
],
|
|
429
402
|
|
|
430
403
|
// Escape metacharacters
|
|
@@ -553,8 +526,9 @@ const REPLACERS = [
|
|
|
553
526
|
// case: /**
|
|
554
527
|
// > A trailing `"/**"` matches everything inside.
|
|
555
528
|
|
|
556
|
-
// #21: everything inside but it should not include the current folder
|
|
557
|
-
|
|
529
|
+
// #21: everything inside but it should not include the current folder,
|
|
530
|
+
// resolved by `_make`
|
|
531
|
+
: `\\/${TRAILING_DOUBLESTAR}`,
|
|
558
532
|
'*'
|
|
559
533
|
],
|
|
560
534
|
|
|
@@ -658,10 +632,11 @@ const REPLACERS = [
|
|
|
658
632
|
source => {
|
|
659
633
|
const last = source[source.length - 1]
|
|
660
634
|
|
|
661
|
-
// The pattern is empty, or ends in
|
|
662
|
-
//
|
|
663
|
-
// which anchors like any other
|
|
664
|
-
|
|
635
|
+
// The pattern is empty, or ends in a pending marker `_make` owns --
|
|
636
|
+
// the trailing wildcard, or a trailing "/**". A trailing `*` that is
|
|
637
|
+
// not the marker is a literal star, which anchors like any other
|
|
638
|
+
// final character.
|
|
639
|
+
if (!last || last === TRAILING_WILDCARD || last === TRAILING_DOUBLESTAR) {
|
|
665
640
|
return source
|
|
666
641
|
}
|
|
667
642
|
|
|
@@ -679,38 +654,27 @@ const MODE_IGNORE = 'regex'
|
|
|
679
654
|
const MODE_CHECK_IGNORE = 'checkRegex'
|
|
680
655
|
const UNDERSCORE = '_'
|
|
681
656
|
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
// '/*' does not match everything
|
|
657
|
+
const replaceTrailingWildcard = (_, p1) => {
|
|
658
|
+
const prefix = p1
|
|
659
|
+
// '\^':
|
|
660
|
+
// '/*' does not match EMPTY
|
|
661
|
+
// '/*' does not match everything
|
|
688
662
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
663
|
+
// '\\\/':
|
|
664
|
+
// 'abc/*' does not match 'abc/'
|
|
665
|
+
? `${p1}[^/]+`
|
|
692
666
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
667
|
+
// 'a*' matches 'a'
|
|
668
|
+
// 'a*' matches 'aa'
|
|
669
|
+
: '[^/]*'
|
|
696
670
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
[MODE_CHECK_IGNORE] (_, p1) {
|
|
701
|
-
// When doing `git check-ignore`
|
|
702
|
-
const prefix = p1
|
|
703
|
-
// '\\\/':
|
|
704
|
-
// 'abc/*' DOES match 'abc/' !
|
|
705
|
-
? `${p1}[^/]*`
|
|
671
|
+
return `${prefix}(?=$|\\/$)`
|
|
672
|
+
}
|
|
706
673
|
|
|
707
|
-
|
|
708
|
-
// 'a*' matches 'aa'
|
|
709
|
-
: '[^/]*'
|
|
674
|
+
const REGEX_REPLACE_TRAILING_DOUBLESTAR = /\uE001$/
|
|
710
675
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
}
|
|
676
|
+
// 'abc/**' matches everything under 'abc', but not 'abc/' itself (#21)
|
|
677
|
+
const replaceTrailingDoublestar = () => '.+(?=$|\\/$)'
|
|
714
678
|
|
|
715
679
|
const WILDCARD = '[^\\/]*'
|
|
716
680
|
|
|
@@ -898,6 +862,28 @@ const makeRegexPrefix = pattern => {
|
|
|
898
862
|
: replaced
|
|
899
863
|
}
|
|
900
864
|
|
|
865
|
+
// `checkRegex` tests a path with a trailing slash, 'abc/', the way
|
|
866
|
+
// `git check-ignore` matches that literal string once the directory itself
|
|
867
|
+
// is known not to be excluded (see `checkIgnore`): a trailing '/' of the
|
|
868
|
+
// pattern is dropped, a basename pattern sees an empty basename, and only a
|
|
869
|
+
// pattern whose last segment is a pending wildcard can match what is left
|
|
870
|
+
// after the final slash -- 'abc/*' and 'abc/**' match 'abc/', and 'abc/**'
|
|
871
|
+
// also matches 'abc/d/' (#77, #169).
|
|
872
|
+
const checkSourceOf = (body, prefix) => {
|
|
873
|
+
if (body[body.length - 1] === SLASH) {
|
|
874
|
+
prefix = makeRegexPrefix(body.slice(0, - 1))
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const head = prefix.slice(0, - 1)
|
|
878
|
+
const last = prefix[prefix.length - 1]
|
|
879
|
+
|
|
880
|
+
return last === TRAILING_WILDCARD
|
|
881
|
+
? `${head}$`
|
|
882
|
+
: last === TRAILING_DOUBLESTAR
|
|
883
|
+
? `${head}.*$`
|
|
884
|
+
: NEVER_MATCH
|
|
885
|
+
}
|
|
886
|
+
|
|
901
887
|
// A trailing slash does not stop a pattern being basename-only: it restricts
|
|
902
888
|
// the match to a directory, it does not let the pattern reach across one.
|
|
903
889
|
// Everything else a pattern can hold -- a wildcard, a character class, an
|
|
@@ -978,15 +964,12 @@ const parentOf = path => {
|
|
|
978
964
|
|
|
979
965
|
const isString = subject => typeof subject === 'string'
|
|
980
966
|
|
|
981
|
-
//
|
|
967
|
+
// Blank lines and comments are told by `createRule`, once the pattern is
|
|
968
|
+
// normalized.
|
|
982
969
|
const checkPattern = pattern => pattern
|
|
983
970
|
&& isString(pattern)
|
|
984
|
-
&& !REGEX_TEST_BLANK_LINE.test(pattern)
|
|
985
971
|
&& !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)
|
|
986
972
|
|
|
987
|
-
// > A line starting with # serves as a comment.
|
|
988
|
-
&& pattern.indexOf('#') !== 0
|
|
989
|
-
|
|
990
973
|
const splitPattern = pattern => pattern
|
|
991
974
|
.split(REGEX_SPLITALL_CRLF)
|
|
992
975
|
.filter(Boolean)
|
|
@@ -1039,12 +1022,13 @@ class IgnoreRule {
|
|
|
1039
1022
|
}
|
|
1040
1023
|
|
|
1041
1024
|
_make (mode, key) {
|
|
1042
|
-
const str = pinWildcards(
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1025
|
+
const str = pinWildcards(
|
|
1026
|
+
mode === MODE_IGNORE
|
|
1027
|
+
? this.regexPrefix
|
|
1028
|
+
.replace(REGEX_REPLACE_TRAILING_WILDCARD, replaceTrailingWildcard)
|
|
1029
|
+
.replace(REGEX_REPLACE_TRAILING_DOUBLESTAR, replaceTrailingDoublestar)
|
|
1030
|
+
: checkSourceOf(this.body, this.regexPrefix)
|
|
1031
|
+
)
|
|
1048
1032
|
|
|
1049
1033
|
const regex = this.ignoreCase
|
|
1050
1034
|
? new RegExp(str, 'i')
|
|
@@ -1054,17 +1038,79 @@ class IgnoreRule {
|
|
|
1054
1038
|
}
|
|
1055
1039
|
}
|
|
1056
1040
|
|
|
1041
|
+
const isLineEnd = char => char === '\r' || char === '\n'
|
|
1042
|
+
|
|
1043
|
+
// Drop a line terminator, which can remain when patterns are passed as an
|
|
1044
|
+
// array, and then the trailing spaces. Git trims spaces only, and a space
|
|
1045
|
+
// escaped by an odd run of backslashes stays, unescaped, as the last one.
|
|
1046
|
+
// Scanned from the end: a regular expression can only try each start in
|
|
1047
|
+
// turn, which is quadratic over a long run of spaces or backslashes.
|
|
1048
|
+
// 'a ' -> 'a'; 'a\ ' -> 'a '; 'a\\ ' -> 'a\\'; 'a\t' -> 'a\t'
|
|
1049
|
+
const trimEnd = body => {
|
|
1050
|
+
let end = body.length
|
|
1051
|
+
|
|
1052
|
+
while (end && isLineEnd(body[end - 1])) {
|
|
1053
|
+
end --
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
const lineEnd = end
|
|
1057
|
+
|
|
1058
|
+
while (end && body[end - 1] === SPACE) {
|
|
1059
|
+
end --
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
if (end === lineEnd) {
|
|
1063
|
+
return body.slice(0, end)
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
let backslashes = 0
|
|
1067
|
+
|
|
1068
|
+
while (backslashes < end && body[end - backslashes - 1] === ESCAPE) {
|
|
1069
|
+
backslashes ++
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
return backslashes % 2
|
|
1073
|
+
? body.slice(0, end - 1) + SPACE
|
|
1074
|
+
: body.slice(0, end)
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1057
1077
|
const createRule = ({
|
|
1058
1078
|
pattern,
|
|
1059
1079
|
mark
|
|
1060
1080
|
}, ignoreCase) => {
|
|
1081
|
+
// Git drops a BOM before it reads the first line, so before anything else.
|
|
1082
|
+
let body = pattern[0] === BOM
|
|
1083
|
+
? pattern.slice(1)
|
|
1084
|
+
: pattern
|
|
1085
|
+
|
|
1086
|
+
// > A line starting with # serves as a comment.
|
|
1087
|
+
if (body[0] === '#') {
|
|
1088
|
+
return
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1061
1091
|
let negative = false
|
|
1062
|
-
let body = pattern
|
|
1063
1092
|
|
|
1064
1093
|
// > An optional prefix "!" which negates the pattern;
|
|
1065
|
-
if (body
|
|
1094
|
+
if (body[0] === '!') {
|
|
1066
1095
|
negative = true
|
|
1067
|
-
body = body.
|
|
1096
|
+
body = body.slice(1)
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// Normalize the body before checking which slashes anchor the pattern.
|
|
1100
|
+
// Otherwise `bar/ ` is mistaken for a pattern containing an inner slash.
|
|
1101
|
+
// Only a body ending in a space or a line terminator has anything to trim.
|
|
1102
|
+
const last = body[body.length - 1]
|
|
1103
|
+
|
|
1104
|
+
if (last === SPACE || last === '\r' || last === '\n') {
|
|
1105
|
+
body = trimEnd(body)
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// > A blank line matches no files, so it can serve as a separator for
|
|
1109
|
+
// > readability.
|
|
1110
|
+
// Nor does a lone "!", or anything else left empty here: an empty pattern
|
|
1111
|
+
// would compile to a regex matching every path.
|
|
1112
|
+
if (!body) {
|
|
1113
|
+
return
|
|
1068
1114
|
}
|
|
1069
1115
|
|
|
1070
1116
|
body = body
|
|
@@ -1123,8 +1169,10 @@ class RuleManager {
|
|
|
1123
1169
|
}
|
|
1124
1170
|
}
|
|
1125
1171
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1172
|
+
const rule = checkPattern(pattern.pattern)
|
|
1173
|
+
&& createRule(pattern, this._ignoreCase)
|
|
1174
|
+
|
|
1175
|
+
if (rule) {
|
|
1128
1176
|
this._added = true
|
|
1129
1177
|
this._rules.push(rule)
|
|
1130
1178
|
|
|
@@ -1147,9 +1195,54 @@ class RuleManager {
|
|
|
1147
1195
|
: pattern
|
|
1148
1196
|
).forEach(this._add, this)
|
|
1149
1197
|
|
|
1198
|
+
if (this._added) {
|
|
1199
|
+
this._literalRules = UNDEFINED
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1150
1202
|
return this._added
|
|
1151
1203
|
}
|
|
1152
1204
|
|
|
1205
|
+
// Match the literal 'abc/' for `checkIgnore`, last rule wins. Only a rule
|
|
1206
|
+
// ending in a wildcard can match it (see `checkSourceOf`), so the rest
|
|
1207
|
+
// are left out once, rather than tested or compiled for every path.
|
|
1208
|
+
testLiteral (path) {
|
|
1209
|
+
const rules = this._literalRules || (
|
|
1210
|
+
this._literalRules = this._rules.filter(
|
|
1211
|
+
({body}) => body[body.length - (
|
|
1212
|
+
body[body.length - 1] === SLASH ? 2 : 1
|
|
1213
|
+
)] === '*'
|
|
1214
|
+
)
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1217
|
+
let ignored = false
|
|
1218
|
+
let unignored = false
|
|
1219
|
+
let matchedRule
|
|
1220
|
+
|
|
1221
|
+
for (let index = rules.length - 1; index >= 0; index --) {
|
|
1222
|
+
const rule = rules[index]
|
|
1223
|
+
|
|
1224
|
+
if (rule.checkRegex.test(path)) {
|
|
1225
|
+
ignored = !rule.negative
|
|
1226
|
+
unignored = rule.negative
|
|
1227
|
+
matchedRule = rule.negative
|
|
1228
|
+
? UNDEFINED
|
|
1229
|
+
: rule
|
|
1230
|
+
break
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
const ret = {
|
|
1235
|
+
ignored,
|
|
1236
|
+
unignored
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
if (matchedRule) {
|
|
1240
|
+
ret.rule = matchedRule
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
return ret
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1153
1246
|
// Test one single path without recursively checking parent directories
|
|
1154
1247
|
//
|
|
1155
1248
|
// - checkUnignored `boolean` whether should check if the path is unignored,
|
|
@@ -1361,17 +1454,20 @@ class Ignore {
|
|
|
1361
1454
|
return this.test(path)
|
|
1362
1455
|
}
|
|
1363
1456
|
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1457
|
+
// Like `git check-ignore`: a directory excluded by itself or by an
|
|
1458
|
+
// ancestor settles it, and only then is the literal 'abc/' matched
|
|
1459
|
+
// against the rules (see `checkSourceOf`).
|
|
1460
|
+
const dir = this._t(path, this._testCache, true)
|
|
1368
1461
|
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
}
|
|
1462
|
+
if (dir.ignored) {
|
|
1463
|
+
return dir
|
|
1372
1464
|
}
|
|
1373
1465
|
|
|
1374
|
-
|
|
1466
|
+
const literal = this._rules.testLiteral(path)
|
|
1467
|
+
|
|
1468
|
+
return literal.ignored || literal.unignored
|
|
1469
|
+
? literal
|
|
1470
|
+
: dir
|
|
1375
1471
|
}
|
|
1376
1472
|
|
|
1377
1473
|
_t (
|