Skip to content

Commit 896feac

Browse files
committed
Merge branch 'main' into jabaile/symbol-tweaks
2 parents df0e6e9 + 4764672 commit 896feac

File tree

20,804 files changed

+196821
-180547
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

20,804 files changed

+196821
-180547
lines changed

.github/workflows/ci.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ jobs:
3131
with:
3232
cache-name: build
3333

34+
# Avoid duplicate PR annotations.
35+
- name: Disable PR annotations
36+
run: |
37+
echo "::remove-matcher owner=eslint-compact::"
38+
echo "::remove-matcher owner=eslint-stylish::"
39+
echo "::remove-matcher owner=tsc::"
40+
echo "::remove-matcher owner=go::"
41+
3442
- run: npm ci
3543

3644
- run: npx hereby build
@@ -53,6 +61,7 @@ jobs:
5361
config:
5462
- os: ubuntu-latest
5563
coverage: true
64+
main: true
5665
- os: windows-latest
5766
coverage: true
5867
skip: ${{ github.event_name == 'merge_group' }}
@@ -106,6 +115,15 @@ jobs:
106115
with:
107116
cache-name: test
108117

118+
# Avoid duplicate PR annotations.
119+
- if: ${{ ! matrix.config.main }}
120+
name: Disable PR annotations
121+
run: |
122+
echo "::remove-matcher owner=eslint-compact::"
123+
echo "::remove-matcher owner=eslint-stylish::"
124+
echo "::remove-matcher owner=tsc::"
125+
echo "::remove-matcher owner=go::"
126+
109127
- run: npm ci
110128

111129
- run: go install gotest.tools/gotestsum@latest
@@ -155,6 +173,7 @@ jobs:
155173
matrix:
156174
config:
157175
- os: ubuntu-latest
176+
main: true
158177
- os: windows-latest
159178
skip: ${{ github.event_name == 'merge_group' }}
160179
- os: macos-latest
@@ -182,6 +201,15 @@ jobs:
182201
with:
183202
cache-name: lint${{ (matrix.config.noembed && '-noembed') || ''}}
184203

204+
# Avoid duplicate PR annotations.
205+
- if: ${{ ! matrix.config.main }}
206+
name: Disable PR annotations
207+
run: |
208+
echo "::remove-matcher owner=eslint-compact::"
209+
echo "::remove-matcher owner=eslint-stylish::"
210+
echo "::remove-matcher owner=tsc::"
211+
echo "::remove-matcher owner=go::"
212+
185213
- run: npm ci
186214

187215
- run: npx hereby lint
@@ -245,6 +273,14 @@ jobs:
245273
with:
246274
cache-name: smoke
247275

276+
# Avoid duplicate PR annotations.
277+
- name: Disable PR annotations
278+
run: |
279+
echo "::remove-matcher owner=eslint-compact::"
280+
echo "::remove-matcher owner=eslint-stylish::"
281+
echo "::remove-matcher owner=tsc::"
282+
echo "::remove-matcher owner=go::"
283+
248284
- run: npm ci
249285

250286
- run: npx hereby build --race

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/dlclark/regexp2 v1.11.5
77
github.com/go-json-experiment/json v0.0.0-20250517221953-25912455fbc8
88
github.com/google/go-cmp v0.7.0
9-
github.com/pkg/diff v0.0.0-20241224192749-4e6772a4315c
9+
github.com/peter-evans/patience v0.3.0
1010
golang.org/x/sync v0.14.0
1111
golang.org/x/sys v0.33.0
1212
gotest.tools/v3 v3.5.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
1212
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
1313
github.com/matryer/moq v0.5.3 h1:4femQCFmBUwFPYs8VfM5ID7AI67/DTEDRBbTtSWy7GU=
1414
github.com/matryer/moq v0.5.3/go.mod h1:8288Qkw7gMZhUP3cIN86GG7g5p9jRuZH8biXLW4RXvQ=
15-
github.com/pkg/diff v0.0.0-20241224192749-4e6772a4315c h1:8TRxBMS/YsupXoOiGKHr9ZOXo+5DezGWPgBAhBHEHto=
16-
github.com/pkg/diff v0.0.0-20241224192749-4e6772a4315c/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
15+
github.com/peter-evans/patience v0.3.0 h1:rX0JdJeepqdQl1Sk9c9uvorjYYzL2TfgLX1adqYm9cA=
16+
github.com/peter-evans/patience v0.3.0/go.mod h1:Kmxu5sY1NmBLFSStvXjX1wS9mIv7wMcP/ubucyMOAu0=
1717
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1818
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1919
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=

internal/ast/ast.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,8 @@ type (
17961796
ObjectLiteralExpressionNode = Node
17971797
ConstructorDeclarationNode = Node
17981798
NamedExportsNode = Node
1799+
UnionType = Node
1800+
LiteralType = Node
17991801
)
18001802

18011803
type (

internal/ast/utilities.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2933,3 +2933,15 @@ func GetPropertyNameForPropertyNameNode(name *Node) string {
29332933
}
29342934
panic("Unhandled case in getPropertyNameForPropertyNameNode")
29352935
}
2936+
2937+
func IsStringTextContainingNode(node *Node) bool {
2938+
return node.Kind == KindStringLiteral || IsTemplateLiteralKind(node.Kind)
2939+
}
2940+
2941+
func IsTemplateLiteralKind(kind Kind) bool {
2942+
return KindFirstTemplateToken <= kind && kind <= KindLastTemplateToken
2943+
}
2944+
2945+
func IsTemplateLiteralToken(node *Node) bool {
2946+
return IsTemplateLiteralKind(node.Kind)
2947+
}

internal/checker/exports.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,7 @@ func GetDeclarationModifierFlagsFromSymbol(s *ast.Symbol) ast.ModifierFlags {
5252
func (c *Checker) WasCanceled() bool {
5353
return c.wasCanceled
5454
}
55+
56+
func (c *Checker) GetBaseConstraintOfType(t *Type) *Type {
57+
return c.getBaseConstraintOfType(t)
58+
}

internal/checker/services.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ func (c *Checker) GetRootSymbols(symbol *ast.Symbol) []*ast.Symbol {
341341
for _, root := range roots {
342342
result = append(result, c.GetRootSymbols(root)...)
343343
}
344+
return result
344345
}
345346
return []*ast.Symbol{symbol}
346347
}
@@ -481,3 +482,31 @@ func (c *Checker) GetJsxIntrinsicTagNamesAt(location *ast.Node) []*ast.Symbol {
481482
func (c *Checker) GetContextualTypeForJsxAttribute(attribute *ast.JsxAttributeLike) *Type {
482483
return c.getContextualTypeForJsxAttribute(attribute, ContextFlagsNone)
483484
}
485+
486+
func (c *Checker) GetConstantValue(node *ast.Node) any {
487+
if node.Kind == ast.KindEnumMember {
488+
return c.getEnumMemberValue(node).Value
489+
}
490+
491+
if c.symbolNodeLinks.Get(node).resolvedSymbol == nil {
492+
c.checkExpressionCached(node) // ensure cached resolved symbol is set
493+
}
494+
symbol := c.symbolNodeLinks.Get(node).resolvedSymbol
495+
if symbol == nil && ast.IsEntityNameExpression(node) {
496+
symbol = c.resolveEntityName(
497+
node,
498+
ast.SymbolFlagsValue,
499+
true, /*ignoreErrors*/
500+
false, /*dontResolveAlias*/
501+
nil /*location*/)
502+
}
503+
if symbol != nil && symbol.Flags&ast.SymbolFlagsEnumMember != 0 {
504+
// inline property\index accesses only for const enums
505+
member := symbol.ValueDeclaration
506+
if ast.IsEnumConst(member.Parent) {
507+
return c.getEnumMemberValue(member).Value
508+
}
509+
}
510+
511+
return nil
512+
}

internal/checker/types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,10 @@ func (t *Type) IsClass() bool {
680680
return t.objectFlags&ObjectFlagsClass != 0
681681
}
682682

683+
func (t *Type) IsTypeParameter() bool {
684+
return t.flags&TypeFlagsTypeParameter != 0
685+
}
686+
683687
// TypeData
684688

685689
type TypeData interface {
@@ -1216,3 +1220,6 @@ var LanguageFeatureMinimumTarget = LanguageFeatureMinimumTargetMap{
12161220
ClassAndClassElementDecorators: core.ScriptTargetESNext,
12171221
RegularExpressionFlagsUnicodeSets: core.ScriptTargetESNext,
12181222
}
1223+
1224+
// Aliases for types
1225+
type StringLiteralType = Type

internal/ls/completions.go

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ const (
256256
// true otherwise.
257257
type uniqueNamesMap = map[string]bool
258258

259-
type literalValue any // string | jsnum.Number | PseudoBigInt
259+
// string | jsnum.Number | PseudoBigInt
260+
type literalValue any
260261

261262
type globalsSearch int
262263

@@ -276,7 +277,7 @@ func (l *LanguageService) getCompletionsAtPosition(
276277
clientOptions *lsproto.CompletionClientCapabilities,
277278
) *lsproto.CompletionList {
278279
_, previousToken := getRelevantTokens(position, file)
279-
if context.TriggerCharacter != nil && !isInString(file, position, previousToken) && !isValidTrigger(file, *context.TriggerCharacter, previousToken, position) {
280+
if context.TriggerCharacter != nil && !IsInString(file, position, previousToken) && !isValidTrigger(file, *context.TriggerCharacter, previousToken, position) {
280281
return nil
281282
}
282283

@@ -294,7 +295,19 @@ func (l *LanguageService) getCompletionsAtPosition(
294295

295296
// !!! see if incomplete completion list and continue or clean
296297

297-
// !!! string literal completions
298+
stringCompletions := l.getStringLiteralCompletions(
299+
ctx,
300+
file,
301+
position,
302+
previousToken,
303+
compilerOptions,
304+
program,
305+
preferences,
306+
clientOptions,
307+
)
308+
if stringCompletions != nil {
309+
return stringCompletions
310+
}
298311

299312
if previousToken != nil && ast.IsBreakOrContinueStatement(previousToken.Parent) &&
300313
(previousToken.Kind == ast.KindBreakKeyword ||
@@ -1479,10 +1492,11 @@ func (l *LanguageService) completionInfoFromData(
14791492
clientOptions *lsproto.CompletionClientCapabilities,
14801493
) *lsproto.CompletionList {
14811494
keywordFilters := data.keywordFilters
1482-
symbols := data.symbols
14831495
isNewIdentifierLocation := data.isNewIdentifierLocation
14841496
contextToken := data.contextToken
14851497
literals := data.literals
1498+
typeChecker, done := program.GetTypeCheckerForFile(ctx, file)
1499+
defer done()
14861500

14871501
// Verify if the file is JSX language variant
14881502
if ast.GetLanguageVariant(file.ScriptKind) == core.LanguageVariantJSX {
@@ -1498,17 +1512,31 @@ func (l *LanguageService) completionInfoFromData(
14981512
if caseClause != nil &&
14991513
(contextToken.Kind == ast.KindCaseKeyword ||
15001514
ast.IsNodeDescendantOf(contextToken, caseClause.Expression())) {
1501-
// !!! switch completions
1515+
tracker := newCaseClauseTracker(typeChecker, caseClause.Parent.AsCaseBlock().Clauses.Nodes)
1516+
literals = core.Filter(literals, func(literal literalValue) bool {
1517+
return !tracker.hasValue(literal)
1518+
})
1519+
data.symbols = core.Filter(data.symbols, func(symbol *ast.Symbol) bool {
1520+
if symbol.ValueDeclaration != nil && ast.IsEnumMember(symbol.ValueDeclaration) {
1521+
value := typeChecker.GetConstantValue(symbol.ValueDeclaration)
1522+
if value != nil && tracker.hasValue(value) {
1523+
return false
1524+
}
1525+
}
1526+
return true
1527+
})
15021528
}
15031529

15041530
isChecked := isCheckedFile(file, compilerOptions)
1505-
if isChecked && !isNewIdentifierLocation && len(symbols) == 0 && keywordFilters == KeywordCompletionFiltersNone {
1531+
if isChecked && !isNewIdentifierLocation && len(data.symbols) == 0 && keywordFilters == KeywordCompletionFiltersNone {
15061532
return nil
15071533
}
15081534

1535+
optionalReplacementSpan := l.getOptionalReplacementSpan(data.location, file)
15091536
uniqueNames, sortedEntries := l.getCompletionEntriesFromSymbols(
15101537
ctx,
15111538
data,
1539+
optionalReplacementSpan,
15121540
nil, /*replacementToken*/
15131541
position,
15141542
file,
@@ -1570,6 +1598,7 @@ func (l *LanguageService) completionInfoFromData(
15701598
func (l *LanguageService) getCompletionEntriesFromSymbols(
15711599
ctx context.Context,
15721600
data *completionDataData,
1601+
optionalReplacementSpan *lsproto.Range,
15731602
replacementToken *ast.Node,
15741603
position int,
15751604
file *ast.SourceFile,
@@ -1584,7 +1613,6 @@ func (l *LanguageService) getCompletionEntriesFromSymbols(
15841613
typeChecker, done := program.GetTypeCheckerForFile(ctx, file)
15851614
defer done()
15861615
isMemberCompletion := isMemberCompletionKind(data.completionKind)
1587-
optionalReplacementSpan := getOptionalReplacementSpan(data.location, file)
15881616
// Tracks unique names.
15891617
// Value is set to false for global variables or completions from external module exports, because we can have multiple of those;
15901618
// true otherwise. Based on the order we add things we will always see locals first, then globals, then module exports.
@@ -1708,7 +1736,7 @@ func (l *LanguageService) createCompletionItem(
17081736
preferences *UserPreferences,
17091737
clientOptions *lsproto.CompletionClientCapabilities,
17101738
isMemberCompletion bool,
1711-
optionalReplacementSpan *core.TextRange,
1739+
optionalReplacementSpan *lsproto.Range,
17121740
) *lsproto.CompletionItem {
17131741
contextToken := data.contextToken
17141742
var insertText string
@@ -3110,12 +3138,11 @@ func getJSCompletionEntries(
31103138
return sortedEntries
31113139
}
31123140

3113-
func getOptionalReplacementSpan(location *ast.Node, file *ast.SourceFile) *core.TextRange {
3141+
func (l *LanguageService) getOptionalReplacementSpan(location *ast.Node, file *ast.SourceFile) *lsproto.Range {
31143142
// StringLiteralLike locations are handled separately in stringCompletions.ts
31153143
if location != nil && location.Kind == ast.KindIdentifier {
31163144
start := astnav.GetStartOfNode(location, file, false /*includeJSDoc*/)
3117-
textRange := core.NewTextRange(start, location.End())
3118-
return &textRange
3145+
return l.createLspRangeFromBounds(start, location.End(), file)
31193146
}
31203147
return nil
31213148
}
@@ -3934,7 +3961,7 @@ func (l *LanguageService) getJsxClosingTagCompletion(
39343961
tagName := jsxClosingElement.Parent.AsJsxElement().OpeningElement.TagName()
39353962
closingTag := tagName.Text()
39363963
fullClosingTag := closingTag + core.IfElse(hasClosingAngleBracket, "", ">")
3937-
optionalReplacementSpan := core.NewTextRange(jsxClosingElement.TagName().Pos(), jsxClosingElement.TagName().End())
3964+
optionalReplacementSpan := l.createLspRangeFromNode(jsxClosingElement.TagName(), file)
39383965
defaultCommitCharacters := getDefaultCommitCharacters(false /*isNewIdentifierLocation*/)
39393966

39403967
item := l.createLSPCompletionItem(
@@ -3945,7 +3972,7 @@ func (l *LanguageService) getJsxClosingTagCompletion(
39453972
ScriptElementKindClassElement,
39463973
core.Set[ScriptElementKindModifier]{}, /*kindModifiers*/
39473974
nil, /*replacementSpan*/
3948-
&optionalReplacementSpan,
3975+
optionalReplacementSpan,
39493976
nil, /*commitCharacters*/
39503977
nil, /*labelDetails*/
39513978
file,
@@ -3975,7 +4002,7 @@ func (l *LanguageService) createLSPCompletionItem(
39754002
elementKind ScriptElementKind,
39764003
kindModifiers core.Set[ScriptElementKindModifier],
39774004
replacementSpan *lsproto.Range,
3978-
optionalReplacementSpan *core.TextRange,
4005+
optionalReplacementSpan *lsproto.Range,
39794006
commitCharacters *[]string,
39804007
labelDetails *lsproto.CompletionItemLabelDetails,
39814008
file *ast.SourceFile,
@@ -4001,8 +4028,11 @@ func (l *LanguageService) createLSPCompletionItem(
40014028
} else {
40024029
// Ported from vscode ts extension.
40034030
if optionalReplacementSpan != nil && ptrIsTrue(clientOptions.CompletionItem.InsertReplaceSupport) {
4004-
insertRange := l.createLspRangeFromBounds(optionalReplacementSpan.Pos(), position, file)
4005-
replaceRange := l.createLspRangeFromBounds(optionalReplacementSpan.Pos(), optionalReplacementSpan.End(), file)
4031+
insertRange := &lsproto.Range{
4032+
Start: optionalReplacementSpan.Start,
4033+
End: l.createLspPosition(position, file),
4034+
}
4035+
replaceRange := optionalReplacementSpan
40064036
textEdit = &lsproto.TextEditOrInsertReplaceEdit{
40074037
InsertReplaceEdit: &lsproto.InsertReplaceEdit{
40084038
NewText: core.IfElse(insertText == "", name, insertText),
@@ -4085,11 +4115,10 @@ func (l *LanguageService) createLSPCompletionItem(
40854115
// !!! adjust label like vscode does
40864116
}
40874117

4118+
// Client assumes plain text by default.
40884119
var insertTextFormat *lsproto.InsertTextFormat
40894120
if isSnippet {
40904121
insertTextFormat = ptrTo(lsproto.InsertTextFormatSnippet)
4091-
} else {
4092-
insertTextFormat = ptrTo(lsproto.InsertTextFormatPlainText)
40934122
}
40944123

40954124
return &lsproto.CompletionItem{

0 commit comments

Comments
 (0)