(x);
+ }|]
+}|]`
+ f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
+ f.VerifyOutliningSpans(t)
+}
diff --git a/pkg/fourslash/tests/manual/incrementalParsingWithJsDoc_test.go b/pkg/fourslash/tests/manual/incrementalParsingWithJsDoc_test.go
new file mode 100644
index 000000000..4010160b9
--- /dev/null
+++ b/pkg/fourslash/tests/manual/incrementalParsingWithJsDoc_test.go
@@ -0,0 +1,24 @@
+package fourslash_test
+
+import (
+ "testing"
+
+ "github.com/buke/typescript-go-internal/pkg/fourslash"
+ "github.com/buke/typescript-go-internal/pkg/testutil"
+)
+
+func TestIncrementalParsingWithJsDoc(t *testing.T) {
+ t.Parallel()
+
+ defer testutil.RecoverAndFail(t, "Panic on fourslash test")
+ const content = `[|import a from 'a/aaaaaaa/aaaaaaa/aaaaaa/aaaaaaa';
+/**/import b from 'b';
+import c from 'c';|]
+[|/** @internal */|]
+export class LanguageIdentifier[| { }|]`
+ f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
+ f.VerifyOutliningSpans(t)
+ f.GoToMarker(t, "")
+ f.Backspace(t, 1)
+ f.VerifyOutliningSpans(t)
+}
diff --git a/pkg/fourslash/tests/manual/outliningForNonCompleteInterfaceDeclaration_test.go b/pkg/fourslash/tests/manual/outliningForNonCompleteInterfaceDeclaration_test.go
new file mode 100644
index 000000000..b675c3cfc
--- /dev/null
+++ b/pkg/fourslash/tests/manual/outliningForNonCompleteInterfaceDeclaration_test.go
@@ -0,0 +1,17 @@
+package fourslash_test
+
+import (
+ "testing"
+
+ "github.com/buke/typescript-go-internal/pkg/fourslash"
+ "github.com/buke/typescript-go-internal/pkg/testutil"
+)
+
+func TestOutliningForNonCompleteInterfaceDeclaration(t *testing.T) {
+ t.Parallel()
+
+ defer testutil.RecoverAndFail(t, "Panic on fourslash test")
+ const content = `interface I`
+ f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
+ f.VerifyOutliningSpans(t)
+}
diff --git a/pkg/fourslash/tests/manual/outliningHintSpansForFunction_test.go b/pkg/fourslash/tests/manual/outliningHintSpansForFunction_test.go
new file mode 100644
index 000000000..0b14ecb22
--- /dev/null
+++ b/pkg/fourslash/tests/manual/outliningHintSpansForFunction_test.go
@@ -0,0 +1,28 @@
+package fourslash_test
+
+import (
+ "testing"
+
+ "github.com/buke/typescript-go-internal/pkg/fourslash"
+ "github.com/buke/typescript-go-internal/pkg/testutil"
+)
+
+func TestOutliningHintSpansForFunction(t *testing.T) {
+ t.Parallel()
+
+ defer testutil.RecoverAndFail(t, "Panic on fourslash test")
+ const content = `namespace NS[| {
+ function f(x: number, y: number)[| {
+ return x + y;
+ }|]
+
+ function g[|(
+ x: number,
+ y: number,
+ ): number {
+ return x + y;
+ }|]
+}|]`
+ f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
+ f.VerifyOutliningSpans(t)
+}
diff --git a/pkg/ls/completions.go b/pkg/ls/completions.go
index d00fe3823..4dd05a71d 100644
--- a/pkg/ls/completions.go
+++ b/pkg/ls/completions.go
@@ -682,7 +682,7 @@ func (l *LanguageService) getCompletionData(
// Note for `` we don't want to treat this as a jsx inializer, instead it's the attribute name.
if parent != previousToken.Parent &&
parent.Initializer() == nil &&
- findChildOfKind(parent, ast.KindEqualsToken, file) != nil {
+ astnav.FindChildOfKind(parent, ast.KindEqualsToken, file) != nil {
jsxInitializer.initializer = previousToken
}
}
@@ -2080,9 +2080,9 @@ func (l *LanguageService) createCompletionItem(
insertText = "?." + insertText
}
- dot := findChildOfKind(data.propertyAccessToConvert, ast.KindDotToken, file)
+ dot := astnav.FindChildOfKind(data.propertyAccessToConvert, ast.KindDotToken, file)
if dot == nil {
- dot = findChildOfKind(data.propertyAccessToConvert, ast.KindQuestionDotToken, file)
+ dot = astnav.FindChildOfKind(data.propertyAccessToConvert, ast.KindQuestionDotToken, file)
}
if dot == nil {
@@ -4072,7 +4072,7 @@ func tryGetObjectTypeDeclarationCompletionContainer(
stmtList := location.Parent.StatementList()
if stmtList != nil && len(stmtList.Nodes) > 0 && ast.IsObjectTypeDeclaration(stmtList.Nodes[len(stmtList.Nodes)-1]) {
cls := stmtList.Nodes[len(stmtList.Nodes)-1]
- if findChildOfKind(cls, ast.KindCloseBraceToken, file) == nil {
+ if astnav.FindChildOfKind(cls, ast.KindCloseBraceToken, file) == nil {
return cls
}
}
@@ -4424,7 +4424,7 @@ func (l *LanguageService) getJsxClosingTagCompletion(
// var x = MainComponent /*1*/ >
// var y = /*2*/ MainComponent >
// the completion list at "1" and "2" will contain "MainComponent.Child" with a replacement span of closing tag name
- hasClosingAngleBracket := findChildOfKind(jsxClosingElement, ast.KindGreaterThanToken, file) != nil
+ hasClosingAngleBracket := astnav.FindChildOfKind(jsxClosingElement, ast.KindGreaterThanToken, file) != nil
tagName := jsxClosingElement.Parent.AsJsxElement().OpeningElement.TagName()
closingTag := scanner.GetTextOfNode(tagName)
fullClosingTag := closingTag + core.IfElse(hasClosingAngleBracket, "", ">")
diff --git a/pkg/ls/documenthighlights.go b/pkg/ls/documenthighlights.go
index 1d5567aa7..b45dd1357 100644
--- a/pkg/ls/documenthighlights.go
+++ b/pkg/ls/documenthighlights.go
@@ -276,7 +276,7 @@ func getReturnOccurrences(node *ast.Node, sourceFile *ast.SourceFile) []*ast.Nod
body := funcNode.Body()
if body != nil {
ast.ForEachReturnStatement(body, func(ret *ast.Node) bool {
- keyword := findChildOfKind(ret, ast.KindReturnKeyword, sourceFile)
+ keyword := astnav.FindChildOfKind(ret, ast.KindReturnKeyword, sourceFile)
if keyword != nil {
keywords = append(keywords, keyword)
}
@@ -286,7 +286,7 @@ func getReturnOccurrences(node *ast.Node, sourceFile *ast.SourceFile) []*ast.Nod
// Get all throw statements not in a try block
throwStatements := aggregateOwnedThrowStatements(body, sourceFile)
for _, throw := range throwStatements {
- keyword := findChildOfKind(throw, ast.KindThrowKeyword, sourceFile)
+ keyword := astnav.FindChildOfKind(throw, ast.KindThrowKeyword, sourceFile)
if keyword != nil {
keywords = append(keywords, keyword)
}
@@ -348,7 +348,7 @@ func getThrowOccurrences(node *ast.Node, sourceFile *ast.SourceFile) []*ast.Node
// Aggregate all throw statements "owned" by this owner.
throwStatements := aggregateOwnedThrowStatements(owner, sourceFile)
for _, throw := range throwStatements {
- keyword := findChildOfKind(throw, ast.KindThrowKeyword, sourceFile)
+ keyword := astnav.FindChildOfKind(throw, ast.KindThrowKeyword, sourceFile)
if keyword != nil {
keywords = append(keywords, keyword)
}
@@ -358,7 +358,7 @@ func getThrowOccurrences(node *ast.Node, sourceFile *ast.SourceFile) []*ast.Node
// ability to "jump out" of the function, and include occurrences for both
if ast.IsFunctionBlock(owner) {
ast.ForEachReturnStatement(owner, func(ret *ast.Node) bool {
- keyword := findChildOfKind(ret, ast.KindReturnKeyword, sourceFile)
+ keyword := astnav.FindChildOfKind(ret, ast.KindReturnKeyword, sourceFile)
if keyword != nil {
keywords = append(keywords, keyword)
}
@@ -412,7 +412,7 @@ func getTryCatchFinallyOccurrences(node *ast.Node, sourceFile *ast.SourceFile) [
}
if tryStatement.FinallyBlock != nil {
- finallyKeyword := findChildOfKind(node, ast.KindFinallyKeyword, sourceFile)
+ finallyKeyword := astnav.FindChildOfKind(node, ast.KindFinallyKeyword, sourceFile)
if finallyKeyword.Kind == ast.KindFinallyKeyword {
keywords = append(keywords, finallyKeyword)
}
diff --git a/pkg/ls/findallreferences.go b/pkg/ls/findallreferences.go
index 5055cbda4..0bc52f9d0 100644
--- a/pkg/ls/findallreferences.go
+++ b/pkg/ls/findallreferences.go
@@ -1357,7 +1357,7 @@ func (l *LanguageService) getReferencedSymbolsForModule(ctx context.Context, pro
node = decl.AsBinaryExpression().Left.Expression()
} else if ast.IsExportAssignment(decl) {
// Find the export keyword
- node = findChildOfKind(decl, ast.KindExportKeyword, sourceFile)
+ node = astnav.FindChildOfKind(decl, ast.KindExportKeyword, sourceFile)
debug.Assert(node != nil, "Expected to find export keyword")
} else {
node = ast.GetNameOfDeclaration(decl)
@@ -1645,7 +1645,7 @@ func findOwnConstructorReferences(classSymbol *ast.Symbol, sourceFile *ast.Sourc
if constructorSymbol != nil && len(constructorSymbol.Declarations) > 0 {
for _, decl := range constructorSymbol.Declarations {
if decl.Kind == ast.KindConstructor {
- if ctrKeyword := findChildOfKind(decl, ast.KindConstructorKeyword, sourceFile); ctrKeyword != nil {
+ if ctrKeyword := astnav.FindChildOfKind(decl, ast.KindConstructorKeyword, sourceFile); ctrKeyword != nil {
addNode(ctrKeyword)
}
}
diff --git a/pkg/ls/folding.go b/pkg/ls/folding.go
new file mode 100644
index 000000000..1ea81f908
--- /dev/null
+++ b/pkg/ls/folding.go
@@ -0,0 +1,533 @@
+package ls
+
+import (
+ "cmp"
+ "context"
+ "slices"
+ "strings"
+ "unicode"
+
+ "github.com/buke/typescript-go-internal/pkg/ast"
+ "github.com/buke/typescript-go-internal/pkg/astnav"
+ "github.com/buke/typescript-go-internal/pkg/debug"
+ "github.com/buke/typescript-go-internal/pkg/lsp/lsproto"
+ "github.com/buke/typescript-go-internal/pkg/printer"
+ "github.com/buke/typescript-go-internal/pkg/scanner"
+)
+
+func (l *LanguageService) ProvideFoldingRange(ctx context.Context, documentURI lsproto.DocumentUri) (lsproto.FoldingRangeResponse, error) {
+ _, sourceFile := l.getProgramAndFile(documentURI)
+ res := l.addNodeOutliningSpans(ctx, sourceFile)
+ res = append(res, l.addRegionOutliningSpans(sourceFile)...)
+ slices.SortFunc(res, func(a, b *lsproto.FoldingRange) int {
+ if c := cmp.Compare(a.StartLine, b.StartLine); c != 0 {
+ return c
+ }
+ return cmp.Compare(*a.StartCharacter, *b.StartCharacter)
+ })
+ return lsproto.FoldingRangesOrNull{FoldingRanges: &res}, nil
+}
+
+func (l *LanguageService) addNodeOutliningSpans(ctx context.Context, sourceFile *ast.SourceFile) []*lsproto.FoldingRange {
+ depthRemaining := 40
+ current := 0
+
+ statements := sourceFile.Statements
+ n := len(statements.Nodes)
+ foldingRange := make([]*lsproto.FoldingRange, 0, 40)
+ for current < n {
+ for current < n && !ast.IsAnyImportSyntax(statements.Nodes[current]) {
+ foldingRange = append(foldingRange, visitNode(ctx, statements.Nodes[current], depthRemaining, sourceFile, l)...)
+ current++
+ }
+ if current == n {
+ break
+ }
+ firstImport := current
+ for current < n && ast.IsAnyImportSyntax(statements.Nodes[current]) {
+ foldingRange = append(foldingRange, visitNode(ctx, statements.Nodes[current], depthRemaining, sourceFile, l)...)
+ current++
+ }
+ lastImport := current - 1
+ if lastImport != firstImport {
+ foldingRangeKind := lsproto.FoldingRangeKindImports
+ foldingRange = append(foldingRange, createFoldingRangeFromBounds(
+ astnav.GetStartOfNode(astnav.FindChildOfKind(statements.Nodes[firstImport],
+ ast.KindImportKeyword, sourceFile), sourceFile, false /*includeJSDoc*/),
+ statements.Nodes[lastImport].End(),
+ foldingRangeKind,
+ sourceFile,
+ l))
+ }
+ }
+
+ // Visit the EOF Token so that comments which aren't attached to statements are included.
+ foldingRange = append(foldingRange, visitNode(ctx, sourceFile.EndOfFileToken, depthRemaining, sourceFile, l)...)
+ return foldingRange
+}
+
+func (l *LanguageService) addRegionOutliningSpans(sourceFile *ast.SourceFile) []*lsproto.FoldingRange {
+ regions := make([]*lsproto.FoldingRange, 0, 40)
+ out := make([]*lsproto.FoldingRange, 0, 40)
+ lineStarts := scanner.GetECMALineStarts(sourceFile)
+ for _, currentLineStart := range lineStarts {
+ lineEnd := getLineEndOfPosition(sourceFile, int(currentLineStart))
+ lineText := sourceFile.Text()[currentLineStart:lineEnd]
+ result := parseRegionDelimiter(lineText)
+ if result == nil || isInComment(sourceFile, int(currentLineStart), astnav.GetTokenAtPosition(sourceFile, int(currentLineStart))) != nil {
+ continue
+ }
+
+ if result.isStart {
+ commentStart := l.createLspPosition(strings.Index(sourceFile.Text()[currentLineStart:lineEnd], "//")+int(currentLineStart), sourceFile)
+ foldingRangeKindRegion := lsproto.FoldingRangeKindRegion
+ collapsedText := "#region"
+ if result.name != "" {
+ collapsedText = result.name
+ }
+ // Our spans start out with some initial data.
+ // On every `#endregion`, we'll come back to these `FoldingRange`s
+ // and fill in their EndLine/EndCharacter.
+ regions = append(regions, &lsproto.FoldingRange{
+ StartLine: commentStart.Line,
+ StartCharacter: &commentStart.Character,
+ Kind: &foldingRangeKindRegion,
+ CollapsedText: &collapsedText,
+ })
+ } else {
+ if len(regions) > 0 {
+ region := regions[len(regions)-1]
+ regions = regions[:len(regions)-1]
+ endingPosition := l.createLspPosition(lineEnd, sourceFile)
+ region.EndLine = endingPosition.Line
+ region.EndCharacter = &endingPosition.Character
+ out = append(out, region)
+ }
+ }
+ }
+ return out
+}
+
+func visitNode(ctx context.Context, n *ast.Node, depthRemaining int, sourceFile *ast.SourceFile, l *LanguageService) []*lsproto.FoldingRange {
+ if depthRemaining == 0 {
+ return nil
+ }
+ if ctx.Err() != nil {
+ return nil
+ }
+ foldingRange := make([]*lsproto.FoldingRange, 0, 40)
+ if (!ast.IsBinaryExpression(n) && ast.IsDeclaration(n)) || ast.IsVariableStatement(n) || ast.IsReturnStatement(n) || ast.IsCallOrNewExpression(n) || n.Kind == ast.KindEndOfFile {
+ foldingRange = append(foldingRange, addOutliningForLeadingCommentsForNode(ctx, n, sourceFile, l)...)
+ }
+ if ast.IsFunctionLike(n) && n.Parent != nil && ast.IsBinaryExpression(n.Parent) && n.Parent.AsBinaryExpression().Left != nil && ast.IsPropertyAccessExpression(n.Parent.AsBinaryExpression().Left) {
+ foldingRange = append(foldingRange, addOutliningForLeadingCommentsForNode(ctx, n.Parent.AsBinaryExpression().Left, sourceFile, l)...)
+ }
+ if ast.IsBlock(n) {
+ statements := n.AsBlock().Statements
+ if statements != nil {
+ foldingRange = append(foldingRange, addOutliningForLeadingCommentsForPos(ctx, statements.End(), sourceFile, l)...)
+ }
+ }
+ if ast.IsModuleBlock(n) {
+ statements := n.AsModuleBlock().Statements
+ if statements != nil {
+ foldingRange = append(foldingRange, addOutliningForLeadingCommentsForPos(ctx, statements.End(), sourceFile, l)...)
+ }
+ }
+ if ast.IsClassLike(n) || ast.IsInterfaceDeclaration(n) {
+ var members *ast.NodeList
+ if ast.IsClassDeclaration(n) {
+ members = n.AsClassDeclaration().Members
+ } else if ast.IsClassExpression(n) {
+ members = n.AsClassExpression().Members
+ } else {
+ members = n.AsInterfaceDeclaration().Members
+ }
+ if members != nil {
+ foldingRange = append(foldingRange, addOutliningForLeadingCommentsForPos(ctx, members.End(), sourceFile, l)...)
+ }
+ }
+
+ span := getOutliningSpanForNode(n, sourceFile, l)
+ if span != nil {
+ foldingRange = append(foldingRange, span)
+ }
+
+ depthRemaining--
+ if ast.IsCallExpression(n) {
+ depthRemaining++
+ expressionNodes := visitNode(ctx, n.Expression(), depthRemaining, sourceFile, l)
+ if expressionNodes != nil {
+ foldingRange = append(foldingRange, expressionNodes...)
+ }
+ depthRemaining--
+ for _, arg := range n.Arguments() {
+ if arg != nil {
+ foldingRange = append(foldingRange, visitNode(ctx, arg, depthRemaining, sourceFile, l)...)
+ }
+ }
+ typeArguments := n.TypeArguments()
+ for _, typeArg := range typeArguments {
+ if typeArg != nil {
+ foldingRange = append(foldingRange, visitNode(ctx, typeArg, depthRemaining, sourceFile, l)...)
+ }
+ }
+ } else if ast.IsIfStatement(n) && n.AsIfStatement().ElseStatement != nil && ast.IsIfStatement(n.AsIfStatement().ElseStatement) {
+ // Consider an 'else if' to be on the same depth as the 'if'.
+ ifStatement := n.AsIfStatement()
+ expressionNodes := visitNode(ctx, n.Expression(), depthRemaining, sourceFile, l)
+ if expressionNodes != nil {
+ foldingRange = append(foldingRange, expressionNodes...)
+ }
+ thenNode := visitNode(ctx, ifStatement.ThenStatement, depthRemaining, sourceFile, l)
+ if thenNode != nil {
+ foldingRange = append(foldingRange, thenNode...)
+ }
+ depthRemaining++
+ elseNode := visitNode(ctx, ifStatement.ElseStatement, depthRemaining, sourceFile, l)
+ if elseNode != nil {
+ foldingRange = append(foldingRange, elseNode...)
+ }
+ depthRemaining--
+ } else {
+ visit := func(node *ast.Node) bool {
+ childNode := visitNode(ctx, node, depthRemaining, sourceFile, l)
+ if childNode != nil {
+ foldingRange = append(foldingRange, childNode...)
+ }
+ return false
+ }
+ n.ForEachChild(visit)
+ }
+ depthRemaining++
+ return foldingRange
+}
+
+func addOutliningForLeadingCommentsForNode(ctx context.Context, n *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) []*lsproto.FoldingRange {
+ if ast.IsJsxText(n) {
+ return nil
+ }
+ return addOutliningForLeadingCommentsForPos(ctx, n.Pos(), sourceFile, l)
+}
+
+func addOutliningForLeadingCommentsForPos(ctx context.Context, pos int, sourceFile *ast.SourceFile, l *LanguageService) []*lsproto.FoldingRange {
+ p := &printer.EmitContext{}
+ foldingRange := make([]*lsproto.FoldingRange, 0, 40)
+ firstSingleLineCommentStart := -1
+ lastSingleLineCommentEnd := -1
+ singleLineCommentCount := 0
+ foldingRangeKindComment := lsproto.FoldingRangeKindComment
+
+ combineAndAddMultipleSingleLineComments := func() *lsproto.FoldingRange {
+ // Only outline spans of two or more consecutive single line comments
+ if singleLineCommentCount > 1 {
+ return createFoldingRangeFromBounds(firstSingleLineCommentStart, lastSingleLineCommentEnd, foldingRangeKindComment, sourceFile, l)
+ }
+ return nil
+ }
+
+ sourceText := sourceFile.Text()
+ for comment := range scanner.GetLeadingCommentRanges(&printer.NewNodeFactory(p).NodeFactory, sourceText, pos) {
+ commentPos := comment.Pos()
+ commentEnd := comment.End()
+
+ if ctx.Err() != nil {
+ return nil
+ }
+ switch comment.Kind {
+ case ast.KindSingleLineCommentTrivia:
+ // never fold region delimiters into single-line comment regions
+ commentText := sourceText[commentPos:commentEnd]
+ if parseRegionDelimiter(commentText) != nil {
+ comments := combineAndAddMultipleSingleLineComments()
+ if comments != nil {
+ foldingRange = append(foldingRange, comments)
+ }
+ singleLineCommentCount = 0
+ break
+ }
+
+ // For single line comments, combine consecutive ones (2 or more) into
+ // a single span from the start of the first till the end of the last
+ if singleLineCommentCount == 0 {
+ firstSingleLineCommentStart = commentPos
+ }
+ lastSingleLineCommentEnd = commentEnd
+ singleLineCommentCount++
+ break
+ case ast.KindMultiLineCommentTrivia:
+ comments := combineAndAddMultipleSingleLineComments()
+ if comments != nil {
+ foldingRange = append(foldingRange, comments)
+ }
+ foldingRange = append(foldingRange, createFoldingRangeFromBounds(commentPos, commentEnd, foldingRangeKindComment, sourceFile, l))
+ singleLineCommentCount = 0
+ break
+ default:
+ debug.AssertNever(comment.Kind)
+ }
+ }
+ addedComments := combineAndAddMultipleSingleLineComments()
+ if addedComments != nil {
+ foldingRange = append(foldingRange, addedComments)
+ }
+ return foldingRange
+}
+
+type regionDelimiterResult struct {
+ isStart bool
+ name string
+}
+
+func parseRegionDelimiter(lineText string) *regionDelimiterResult {
+ // We trim the leading whitespace and // without the regex since the
+ // multiple potential whitespace matches can make for some gnarly backtracking behavior
+ lineText = strings.TrimLeftFunc(lineText, unicode.IsSpace)
+ if !strings.HasPrefix(lineText, "//") {
+ return nil
+ }
+ lineText = strings.TrimSpace(lineText[2:])
+ lineText = strings.TrimSuffix(lineText, "\r")
+ if !strings.HasPrefix(lineText, "#") {
+ return nil
+ }
+ lineText = lineText[1:]
+ isStart := true
+ if strings.HasPrefix(lineText, "end") {
+ isStart = false
+ lineText = lineText[3:]
+ }
+ if !strings.HasPrefix(lineText, "region") {
+ return nil
+ }
+ lineText = lineText[6:]
+ return ®ionDelimiterResult{
+ isStart: isStart,
+ name: strings.TrimSpace(lineText),
+ }
+}
+
+func getOutliningSpanForNode(n *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ switch n.Kind {
+ case ast.KindBlock:
+ if ast.IsFunctionLike(n.Parent) {
+ return functionSpan(n.Parent, n, sourceFile, l)
+ }
+ // Check if the block is standalone, or 'attached' to some parent statement.
+ // If the latter, we want to collapse the block, but consider its hint span
+ // to be the entire span of the parent.
+ switch n.Parent.Kind {
+ case ast.KindDoStatement, ast.KindForInStatement, ast.KindForOfStatement, ast.KindForStatement, ast.KindIfStatement, ast.KindWhileStatement, ast.KindWithStatement, ast.KindCatchClause:
+ return spanForNode(n, ast.KindOpenBraceToken, true /*useFullStart*/, sourceFile, l)
+ case ast.KindTryStatement:
+ // Could be the try-block, or the finally-block.
+ tryStatement := n.Parent.AsTryStatement()
+ if tryStatement.TryBlock == n {
+ return spanForNode(n, ast.KindOpenBraceToken, true /*useFullStart*/, sourceFile, l)
+ } else if tryStatement.FinallyBlock == n {
+ if span := spanForNode(n, ast.KindOpenBraceToken, true /*useFullStart*/, sourceFile, l); span != nil {
+ return span
+ }
+ }
+ fallthrough
+ default:
+ // Block was a standalone block. In this case we want to only collapse
+ // the span of the block, independent of any parent span.
+ return createFoldingRange(l.createLspRangeFromNode(n, sourceFile), "", "")
+ }
+ case ast.KindModuleBlock:
+ return spanForNode(n, ast.KindOpenBraceToken, true /*useFullStart*/, sourceFile, l)
+ case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration, ast.KindEnumDeclaration, ast.KindCaseBlock, ast.KindTypeLiteral, ast.KindObjectBindingPattern:
+ return spanForNode(n, ast.KindOpenBraceToken, true /*useFullStart*/, sourceFile, l)
+ case ast.KindTupleType:
+ return spanForNode(n, ast.KindOpenBracketToken, !ast.IsTupleTypeNode(n.Parent) /*useFullStart*/, sourceFile, l)
+ case ast.KindCaseClause, ast.KindDefaultClause:
+ return spanForNodeArray(n.AsCaseOrDefaultClause().Statements, sourceFile, l)
+ case ast.KindObjectLiteralExpression:
+ return spanForNode(n, ast.KindOpenBraceToken, !ast.IsArrayLiteralExpression(n.Parent) && !ast.IsCallExpression(n.Parent) /*useFullStart*/, sourceFile, l)
+ case ast.KindArrayLiteralExpression:
+ return spanForNode(n, ast.KindOpenBracketToken, !ast.IsArrayLiteralExpression(n.Parent) && !ast.IsCallExpression(n.Parent) /*useFullStart*/, sourceFile, l)
+ case ast.KindJsxElement, ast.KindJsxFragment:
+ return spanForJSXElement(n, sourceFile, l)
+ case ast.KindJsxSelfClosingElement, ast.KindJsxOpeningElement:
+ return spanForJSXAttributes(n, sourceFile, l)
+ case ast.KindTemplateExpression, ast.KindNoSubstitutionTemplateLiteral:
+ return spanForTemplateLiteral(n, sourceFile, l)
+ case ast.KindArrayBindingPattern:
+ return spanForNode(n, ast.KindOpenBracketToken, !ast.IsBindingElement(n.Parent) /*useFullStart*/, sourceFile, l)
+ case ast.KindArrowFunction:
+ return spanForArrowFunction(n, sourceFile, l)
+ case ast.KindCallExpression:
+ return spanForCallExpression(n, sourceFile, l)
+ case ast.KindParenthesizedExpression:
+ return spanForParenthesizedExpression(n, sourceFile, l)
+ case ast.KindNamedImports, ast.KindNamedExports, ast.KindImportAttributes:
+ return spanForImportExportElements(n, sourceFile, l)
+ }
+ return nil
+}
+
+func spanForImportExportElements(node *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ var elements *ast.NodeList
+ switch node.Kind {
+ case ast.KindNamedImports:
+ elements = node.AsNamedImports().Elements
+ case ast.KindNamedExports:
+ elements = node.AsNamedExports().Elements
+ case ast.KindImportAttributes:
+ elements = node.AsImportAttributes().Attributes
+ }
+ if elements == nil || len(elements.Nodes) == 0 {
+ return nil
+ }
+ openToken := astnav.FindChildOfKind(node, ast.KindOpenBraceToken, sourceFile)
+ closeToken := astnav.FindChildOfKind(node, ast.KindCloseBraceToken, sourceFile)
+ if openToken == nil || closeToken == nil || printer.PositionsAreOnSameLine(openToken.Pos(), closeToken.Pos(), sourceFile) {
+ return nil
+ }
+ return rangeBetweenTokens(openToken, closeToken, sourceFile, false /*useFullStart*/, l)
+}
+
+func spanForParenthesizedExpression(node *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ start := astnav.GetStartOfNode(node, sourceFile, false /*includeJSDoc*/)
+ if printer.PositionsAreOnSameLine(start, node.End(), sourceFile) {
+ return nil
+ }
+ textRange := l.createLspRangeFromBounds(start, node.End(), sourceFile)
+ return createFoldingRange(textRange, "", "")
+}
+
+func spanForCallExpression(node *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ if node.AsCallExpression().Arguments == nil || len(node.AsCallExpression().Arguments.Nodes) == 0 {
+ return nil
+ }
+ openToken := astnav.FindChildOfKind(node, ast.KindOpenParenToken, sourceFile)
+ closeToken := astnav.FindChildOfKind(node, ast.KindCloseParenToken, sourceFile)
+ if openToken == nil || closeToken == nil || printer.PositionsAreOnSameLine(openToken.Pos(), closeToken.Pos(), sourceFile) {
+ return nil
+ }
+
+ return rangeBetweenTokens(openToken, closeToken, sourceFile, true /*useFullStart*/, l)
+}
+
+func spanForArrowFunction(node *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ arrowFunctionNode := node.AsArrowFunction()
+ if ast.IsBlock(arrowFunctionNode.Body) || ast.IsParenthesizedExpression(arrowFunctionNode.Body) || printer.PositionsAreOnSameLine(arrowFunctionNode.Body.Pos(), arrowFunctionNode.Body.End(), sourceFile) {
+ return nil
+ }
+ textRange := l.createLspRangeFromBounds(arrowFunctionNode.Body.Pos(), arrowFunctionNode.Body.End(), sourceFile)
+ return createFoldingRange(textRange, "", "")
+}
+
+func spanForTemplateLiteral(node *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ if node.Kind == ast.KindNoSubstitutionTemplateLiteral && len(node.Text()) == 0 {
+ return nil
+ }
+ return createFoldingRangeFromBounds(astnav.GetStartOfNode(node, sourceFile, false /*includeJSDoc*/), node.End(), "", sourceFile, l)
+}
+
+func spanForJSXElement(node *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ if node.Kind == ast.KindJsxElement {
+ jsxElement := node.AsJsxElement()
+ textRange := l.createLspRangeFromBounds(astnav.GetStartOfNode(jsxElement.OpeningElement, sourceFile, false /*includeJSDoc*/), jsxElement.ClosingElement.End(), sourceFile)
+ tagName := jsxElement.OpeningElement.TagName().Text()
+ bannerText := "<" + tagName + ">..." + tagName + ">"
+ return createFoldingRange(textRange, "", bannerText)
+ }
+ // JsxFragment
+ jsxFragment := node.AsJsxFragment()
+ textRange := l.createLspRangeFromBounds(astnav.GetStartOfNode(jsxFragment.OpeningFragment, sourceFile, false /*includeJSDoc*/), jsxFragment.ClosingFragment.End(), sourceFile)
+ return createFoldingRange(textRange, "", "<>...>")
+}
+
+func spanForJSXAttributes(node *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ var attributes *ast.JsxAttributesNode
+ if node.Kind == ast.KindJsxSelfClosingElement {
+ attributes = node.AsJsxSelfClosingElement().Attributes
+ } else {
+ attributes = node.AsJsxOpeningElement().Attributes
+ }
+ if len(attributes.Properties()) == 0 {
+ return nil
+ }
+ return createFoldingRangeFromBounds(astnav.GetStartOfNode(node, sourceFile, false /*includeJSDoc*/), node.End(), "", sourceFile, l)
+}
+
+func spanForNodeArray(statements *ast.NodeList, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ if statements != nil && len(statements.Nodes) != 0 {
+ return createFoldingRange(l.createLspRangeFromBounds(statements.Pos(), statements.End(), sourceFile), "", "")
+ }
+ return nil
+}
+
+func spanForNode(node *ast.Node, open ast.Kind, useFullStart bool, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ closeBrace := ast.KindCloseBraceToken
+ if open != ast.KindOpenBraceToken {
+ closeBrace = ast.KindCloseBracketToken
+ }
+ openToken := astnav.FindChildOfKind(node, open, sourceFile)
+ closeToken := astnav.FindChildOfKind(node, closeBrace, sourceFile)
+ if openToken != nil && closeToken != nil {
+ return rangeBetweenTokens(openToken, closeToken, sourceFile, useFullStart, l)
+ }
+ return nil
+}
+
+func rangeBetweenTokens(openToken *ast.Node, closeToken *ast.Node, sourceFile *ast.SourceFile, useFullStart bool, l *LanguageService) *lsproto.FoldingRange {
+ var textRange *lsproto.Range
+ if useFullStart {
+ textRange = l.createLspRangeFromBounds(openToken.Pos(), closeToken.End(), sourceFile)
+ } else {
+ textRange = l.createLspRangeFromBounds(astnav.GetStartOfNode(openToken, sourceFile, false /*includeJSDoc*/), closeToken.End(), sourceFile)
+ }
+ return createFoldingRange(textRange, "", "")
+}
+
+func createFoldingRange(textRange *lsproto.Range, foldingRangeKind lsproto.FoldingRangeKind, collapsedText string) *lsproto.FoldingRange {
+ if collapsedText == "" {
+ defaultText := "..."
+ collapsedText = defaultText
+ }
+ var kind *lsproto.FoldingRangeKind
+ if foldingRangeKind != "" {
+ kind = &foldingRangeKind
+ }
+ return &lsproto.FoldingRange{
+ StartLine: textRange.Start.Line,
+ StartCharacter: &textRange.Start.Character,
+ EndLine: textRange.End.Line,
+ EndCharacter: &textRange.End.Character,
+ Kind: kind,
+ CollapsedText: &collapsedText,
+ }
+}
+
+func createFoldingRangeFromBounds(pos int, end int, foldingRangeKind lsproto.FoldingRangeKind, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ return createFoldingRange(l.createLspRangeFromBounds(pos, end, sourceFile), foldingRangeKind, "")
+}
+
+func functionSpan(node *ast.Node, body *ast.Node, sourceFile *ast.SourceFile, l *LanguageService) *lsproto.FoldingRange {
+ openToken := tryGetFunctionOpenToken(node, body, sourceFile)
+ closeToken := astnav.FindChildOfKind(body, ast.KindCloseBraceToken, sourceFile)
+ if openToken != nil && closeToken != nil {
+ return rangeBetweenTokens(openToken, closeToken, sourceFile, true /*useFullStart*/, l)
+ }
+ return nil
+}
+
+func tryGetFunctionOpenToken(node *ast.SignatureDeclaration, body *ast.Node, sourceFile *ast.SourceFile) *ast.Node {
+ if isNodeArrayMultiLine(node.Parameters(), sourceFile) {
+ openParenToken := astnav.FindChildOfKind(node, ast.KindOpenParenToken, sourceFile)
+ if openParenToken != nil {
+ return openParenToken
+ }
+ }
+ return astnav.FindChildOfKind(body, ast.KindOpenBraceToken, sourceFile)
+}
+
+func isNodeArrayMultiLine(list []*ast.Node, sourceFile *ast.SourceFile) bool {
+ if len(list) == 0 {
+ return false
+ }
+ return !printer.PositionsAreOnSameLine(list[0].Pos(), list[len(list)-1].End(), sourceFile)
+}
diff --git a/pkg/ls/inlay_hints.go b/pkg/ls/inlay_hints.go
index db9db059b..8b68d07f4 100644
--- a/pkg/ls/inlay_hints.go
+++ b/pkg/ls/inlay_hints.go
@@ -104,7 +104,7 @@ func (s *inlayHintState) visit(node *ast.Node) bool {
// FunctionDeclaration | MethodDeclaration | GetAccessorDeclaration | FunctionExpression | ArrowFunction
func (s *inlayHintState) visitFunctionDeclarationLikeForReturnType(decl *ast.FunctionLikeDeclaration) {
if ast.IsArrowFunction(decl) {
- if findChildOfKind(decl, ast.KindOpenParenToken, s.file) == nil {
+ if astnav.FindChildOfKind(decl, ast.KindOpenParenToken, s.file) == nil {
return
}
}
@@ -889,7 +889,7 @@ func (s *inlayHintState) leadingCommentsContainsParameterName(node *ast.Node, na
}
func (s *inlayHintState) getTypeAnnotationPosition(decl *ast.FunctionLikeDeclaration) int {
- closeParenToken := findChildOfKind(decl, ast.KindCloseParenToken, s.file)
+ closeParenToken := astnav.FindChildOfKind(decl, ast.KindCloseParenToken, s.file)
if closeParenToken != nil {
return closeParenToken.End()
}
diff --git a/pkg/ls/utilities.go b/pkg/ls/utilities.go
index 72dbcdab3..d490d2bf0 100644
--- a/pkg/ls/utilities.go
+++ b/pkg/ls/utilities.go
@@ -166,62 +166,7 @@ func isInComment(file *ast.SourceFile, position int, tokenAtPosition *ast.Node)
}
func hasChildOfKind(containingNode *ast.Node, kind ast.Kind, sourceFile *ast.SourceFile) bool {
- return findChildOfKind(containingNode, kind, sourceFile) != nil
-}
-
-func findChildOfKind(containingNode *ast.Node, kind ast.Kind, sourceFile *ast.SourceFile) *ast.Node {
- lastNodePos := containingNode.Pos()
- scanner := scanner.GetScannerForSourceFile(sourceFile, lastNodePos)
-
- var foundChild *ast.Node
- visitNode := func(node *ast.Node) bool {
- if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 {
- return false
- }
- // Look for child in preceding tokens.
- startPos := lastNodePos
- for startPos < node.Pos() {
- tokenKind := scanner.Token()
- tokenFullStart := scanner.TokenFullStart()
- tokenEnd := scanner.TokenEnd()
- token := sourceFile.GetOrCreateToken(tokenKind, tokenFullStart, tokenEnd, containingNode)
- if tokenKind == kind {
- foundChild = token
- return true
- }
- startPos = tokenEnd
- scanner.Scan()
- }
- if node.Kind == kind {
- foundChild = node
- return true
- }
-
- lastNodePos = node.End()
- scanner.ResetPos(lastNodePos)
- return false
- }
-
- ast.ForEachChildAndJSDoc(containingNode, sourceFile, visitNode)
-
- if foundChild != nil {
- return foundChild
- }
-
- // Look for child in trailing tokens.
- startPos := lastNodePos
- for startPos < containingNode.End() {
- tokenKind := scanner.Token()
- tokenFullStart := scanner.TokenFullStart()
- tokenEnd := scanner.TokenEnd()
- token := sourceFile.GetOrCreateToken(tokenKind, tokenFullStart, tokenEnd, containingNode)
- if tokenKind == kind {
- return token
- }
- startPos = tokenEnd
- scanner.Scan()
- }
- return nil
+ return astnav.FindChildOfKind(containingNode, kind, sourceFile) != nil
}
type PossibleTypeArgumentInfo struct {
@@ -1095,10 +1040,10 @@ func getAdjustedLocationForDeclaration(node *ast.Node, forRename bool, sourceFil
return core.Find(node.ModifierNodes(), func(*ast.Node) bool { return node.Kind == ast.KindDefaultKeyword })
case ast.KindClassExpression:
// for class expressions, use the `class` keyword when the class is unnamed
- return findChildOfKind(node, ast.KindClassKeyword, sourceFile)
+ return astnav.FindChildOfKind(node, ast.KindClassKeyword, sourceFile)
case ast.KindFunctionExpression:
// for function expressions, use the `function` keyword when the function is unnamed
- return findChildOfKind(node, ast.KindFunctionKeyword, sourceFile)
+ return astnav.FindChildOfKind(node, ast.KindFunctionKeyword, sourceFile)
case ast.KindConstructor:
return node
}
diff --git a/pkg/lsp/server.go b/pkg/lsp/server.go
index ff71fadc5..9e0ec3bd2 100644
--- a/pkg/lsp/server.go
+++ b/pkg/lsp/server.go
@@ -519,6 +519,7 @@ var handlers = sync.OnceValue(func() handlerMap {
registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentCodeLensInfo, (*Server).handleCodeLens)
registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentCodeActionInfo, (*Server).handleCodeAction)
registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentPrepareCallHierarchyInfo, (*Server).handlePrepareCallHierarchy)
+ registerLanguageServiceDocumentRequestHandler(handlers, lsproto.TextDocumentFoldingRangeInfo, (*Server).handleFoldingRange)
registerMultiProjectReferenceRequestHandler(handlers, lsproto.TextDocumentReferencesInfo, (*Server).handleReferences, combineReferences)
registerMultiProjectReferenceRequestHandler(handlers, lsproto.TextDocumentRenameInfo, (*Server).handleRename, combineRenameResponse)
@@ -526,6 +527,9 @@ var handlers = sync.OnceValue(func() handlerMap {
registerRequestHandler(handlers, lsproto.CallHierarchyIncomingCallsInfo, (*Server).handleCallHierarchyIncomingCalls)
registerRequestHandler(handlers, lsproto.CallHierarchyOutgoingCallsInfo, (*Server).handleCallHierarchyOutgoingCalls)
+ registerRequestHandler(handlers, lsproto.CallHierarchyIncomingCallsInfo, (*Server).handleCallHierarchyIncomingCalls)
+ registerRequestHandler(handlers, lsproto.CallHierarchyOutgoingCallsInfo, (*Server).handleCallHierarchyOutgoingCalls)
+
registerRequestHandler(handlers, lsproto.WorkspaceSymbolInfo, (*Server).handleWorkspaceSymbol)
registerRequestHandler(handlers, lsproto.CompletionItemResolveInfo, (*Server).handleCompletionItemResolve)
registerRequestHandler(handlers, lsproto.CodeLensResolveInfo, (*Server).handleCodeLensResolve)
@@ -939,6 +943,9 @@ func (s *Server) handleInitialize(ctx context.Context, params *lsproto.Initializ
DocumentSymbolProvider: &lsproto.BooleanOrDocumentSymbolOptions{
Boolean: ptrTo(true),
},
+ FoldingRangeProvider: &lsproto.BooleanOrFoldingRangeOptionsOrFoldingRangeRegistrationOptions{
+ Boolean: ptrTo(true),
+ },
RenameProvider: &lsproto.BooleanOrRenameOptions{
Boolean: ptrTo(true),
},
@@ -1133,6 +1140,10 @@ func (s *Server) handleSignatureHelp(ctx context.Context, languageService *ls.La
)
}
+func (s *Server) handleFoldingRange(ctx context.Context, ls *ls.LanguageService, params *lsproto.FoldingRangeParams) (lsproto.FoldingRangeResponse, error) {
+ return ls.ProvideFoldingRange(ctx, params.TextDocument.Uri)
+}
+
func (s *Server) handleDefinition(ctx context.Context, ls *ls.LanguageService, params *lsproto.DefinitionParams) (lsproto.DefinitionResponse, error) {
return ls.ProvideDefinition(ctx, params.TextDocument.Uri, params.Position)
}
diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go
index 35b8fcd30..cd9977647 100644
--- a/pkg/printer/printer.go
+++ b/pkg/printer/printer.go
@@ -5059,7 +5059,7 @@ func (p *Printer) emitCommentsBeforeToken(token ast.Kind, pos int, contextNode *
if contextNode.Pos() != startPos {
indentLeading := flags&tefIndentLeadingComments != 0
- needsIndent := indentLeading && p.currentSourceFile != nil && !positionsAreOnSameLine(startPos, pos, p.currentSourceFile)
+ needsIndent := indentLeading && p.currentSourceFile != nil && !PositionsAreOnSameLine(startPos, pos, p.currentSourceFile)
p.increaseIndentIf(needsIndent)
p.emitLeadingComments(startPos, false /*elided*/)
p.decreaseIndentIf(needsIndent)
diff --git a/pkg/printer/utilities.go b/pkg/printer/utilities.go
index 9a5334bbf..14631107e 100644
--- a/pkg/printer/utilities.go
+++ b/pkg/printer/utilities.go
@@ -335,7 +335,7 @@ func rangeIsOnSingleLine(r core.TextRange, sourceFile *ast.SourceFile) bool {
}
func rangeStartPositionsAreOnSameLine(range1 core.TextRange, range2 core.TextRange, sourceFile *ast.SourceFile) bool {
- return positionsAreOnSameLine(
+ return PositionsAreOnSameLine(
getStartPositionOfRange(range1, sourceFile, false /*includeComments*/),
getStartPositionOfRange(range2, sourceFile, false /*includeComments*/),
sourceFile,
@@ -343,15 +343,15 @@ func rangeStartPositionsAreOnSameLine(range1 core.TextRange, range2 core.TextRan
}
func rangeEndPositionsAreOnSameLine(range1 core.TextRange, range2 core.TextRange, sourceFile *ast.SourceFile) bool {
- return positionsAreOnSameLine(range1.End(), range2.End(), sourceFile)
+ return PositionsAreOnSameLine(range1.End(), range2.End(), sourceFile)
}
func rangeStartIsOnSameLineAsRangeEnd(range1 core.TextRange, range2 core.TextRange, sourceFile *ast.SourceFile) bool {
- return positionsAreOnSameLine(getStartPositionOfRange(range1, sourceFile, false /*includeComments*/), range2.End(), sourceFile)
+ return PositionsAreOnSameLine(getStartPositionOfRange(range1, sourceFile, false /*includeComments*/), range2.End(), sourceFile)
}
func rangeEndIsOnSameLineAsRangeStart(range1 core.TextRange, range2 core.TextRange, sourceFile *ast.SourceFile) bool {
- return positionsAreOnSameLine(range1.End(), getStartPositionOfRange(range2, sourceFile, false /*includeComments*/), sourceFile)
+ return PositionsAreOnSameLine(range1.End(), getStartPositionOfRange(range2, sourceFile, false /*includeComments*/), sourceFile)
}
func getStartPositionOfRange(r core.TextRange, sourceFile *ast.SourceFile, includeComments bool) int {
@@ -361,7 +361,7 @@ func getStartPositionOfRange(r core.TextRange, sourceFile *ast.SourceFile, inclu
return scanner.SkipTriviaEx(sourceFile.Text(), r.Pos(), &scanner.SkipTriviaOptions{StopAtComments: includeComments})
}
-func positionsAreOnSameLine(pos1 int, pos2 int, sourceFile *ast.SourceFile) bool {
+func PositionsAreOnSameLine(pos1 int, pos2 int, sourceFile *ast.SourceFile) bool {
return GetLinesBetweenPositions(sourceFile, pos1, pos2) == 0
}
diff --git a/pkg/testrunner/compiler_runner.go b/pkg/testrunner/compiler_runner.go
index 4601a4fa3..061454824 100644
--- a/pkg/testrunner/compiler_runner.go
+++ b/pkg/testrunner/compiler_runner.go
@@ -350,19 +350,8 @@ func newCompilerTest(
}
}
-var concurrentSkippedErrorBaselines = map[string]string{
- "typeOnlyMerge2.ts": "Type-only merging is not detected when files are checked on different checkers.",
- "typeOnlyMerge3.ts": "Type-only merging is not detected when files are checked on different checkers.",
-}
-
func (c *compilerTest) verifyDiagnostics(t *testing.T, suiteName string, isSubmodule bool) {
t.Run("error", func(t *testing.T) {
- if !testutil.TestProgramIsSingleThreaded() {
- if msg, ok := concurrentSkippedErrorBaselines[c.basename]; ok {
- t.Skipf("Skipping in concurrent mode: %s", msg)
- }
- }
-
defer testutil.RecoverAndFail(t, "Panic on creating error baseline for test "+c.filename)
files := core.Concatenate(c.tsConfigFiles, core.Concatenate(c.toBeCompiled, c.otherFiles))
tsbaseline.DoErrorBaseline(t, c.configuredName, files, c.result.Diagnostics, c.result.Options.Pretty.IsTrue(), baseline.Options{
diff --git a/pkg/tspath/path.go b/pkg/tspath/path.go
index 4c95d73b9..55869b2b5 100644
--- a/pkg/tspath/path.go
+++ b/pkg/tspath/path.go
@@ -334,6 +334,12 @@ func GetNormalizedPathComponents(path string, currentDirectory string) []string
return reducePathComponents(GetPathComponents(path, currentDirectory))
}
+func GetNormalizedAbsolutePathWithoutRoot(fileName string, currentDirectory string) string {
+ absolutePath := GetNormalizedAbsolutePath(fileName, currentDirectory)
+ rootLength := GetRootLength(absolutePath)
+ return absolutePath[rootLength:]
+}
+
func GetNormalizedAbsolutePath(fileName string, currentDirectory string) string {
rootLength := GetRootLength(fileName)
if rootLength == 0 && currentDirectory != "" {
diff --git a/pkg/tspath/path_test.go b/pkg/tspath/path_test.go
index 85d7829de..711030a1c 100644
--- a/pkg/tspath/path_test.go
+++ b/pkg/tspath/path_test.go
@@ -417,6 +417,14 @@ func TestGetNormalizedAbsolutePath(t *testing.T) {
assert.Equal(t, GetNormalizedAbsolutePath("\\\\a\\b\\\\c", ""), "//a/b/c")
}
+func TestGetNormalizedAbsolutePathWithoutRoot(t *testing.T) {
+ t.Parallel()
+
+ assert.Equal(t, GetNormalizedAbsolutePathWithoutRoot("/a/b/c.txt", "/a/b"), "a/b/c.txt")
+ assert.Equal(t, GetNormalizedAbsolutePathWithoutRoot("c:/work/hello.txt", "c:/work"), "work/hello.txt")
+ assert.Equal(t, GetNormalizedAbsolutePathWithoutRoot("c:/work/hello.txt", "d:/worspaces"), "work/hello.txt")
+}
+
var getNormalizedAbsolutePathTests = map[string][][]string{
"non-normalized inputs": {
{"/.", ""},
diff --git a/testdata/baselines/reference/submodule/compiler/incrementalInvalid.errors.txt b/testdata/baselines/reference/submodule/compiler/incrementalInvalid.errors.txt
new file mode 100644
index 000000000..29b9039f3
--- /dev/null
+++ b/testdata/baselines/reference/submodule/compiler/incrementalInvalid.errors.txt
@@ -0,0 +1,8 @@
+error TS5074: Option '--incremental' is only valid with a known configuration file (like 'tsconfig.json') or when '--tsBuildInfoFile' is explicitly provided.
+
+
+!!! error TS5074: Option '--incremental' is only valid with a known configuration file (like 'tsconfig.json') or when '--tsBuildInfoFile' is explicitly provided.
+==== incrementalInvalid.ts (0 errors) ====
+ const x = 10;
+
+
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/incrementalInvalid.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/incrementalInvalid.errors.txt.diff
index 0e9d82e83..ff26767ed 100644
--- a/testdata/baselines/reference/submodule/compiler/incrementalInvalid.errors.txt.diff
+++ b/testdata/baselines/reference/submodule/compiler/incrementalInvalid.errors.txt.diff
@@ -5,8 +5,10 @@
-
-
-!!! error TS5074: Option '--incremental' can only be specified using tsconfig, emitting to single file or when option '--tsBuildInfoFile' is specified.
--==== incrementalInvalid.ts (0 errors) ====
-- const x = 10;
--
--
-+
\ No newline at end of file
++error TS5074: Option '--incremental' is only valid with a known configuration file (like 'tsconfig.json') or when '--tsBuildInfoFile' is explicitly provided.
++
++
++!!! error TS5074: Option '--incremental' is only valid with a known configuration file (like 'tsconfig.json') or when '--tsBuildInfoFile' is explicitly provided.
+ ==== incrementalInvalid.ts (0 errors) ====
+ const x = 10;
+
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/conformance/importEquals3.errors.txt b/testdata/baselines/reference/submodule/conformance/importEquals3.errors.txt
index 1d66b070d..5a5de9539 100644
--- a/testdata/baselines/reference/submodule/conformance/importEquals3.errors.txt
+++ b/testdata/baselines/reference/submodule/conformance/importEquals3.errors.txt
@@ -1,7 +1,6 @@
b.ts(2,12): error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
b.ts(3,13): error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
c.ts(2,12): error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
-c.ts(3,13): error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
==== a.ts (0 errors) ====
@@ -21,16 +20,13 @@ c.ts(3,13): error TS1380: An import alias cannot reference a declaration that wa
const x = 0;
export { a, A, x };
-==== c.ts (2 errors) ====
+==== c.ts (1 errors) ====
import * as b from './b';
import A = b.a.A; // Error
~~~~~
!!! error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
!!! related TS1376 b.ts:1:18: 'a' was imported here.
import AA = b.A; // Error
- ~~~
-!!! error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
-!!! related TS1376 b.ts:1:18: 'a' was imported here.
import x = b.x;
console.log(x);
diff --git a/testdata/baselines/reference/submodule/conformance/importEquals3.errors.txt.diff b/testdata/baselines/reference/submodule/conformance/importEquals3.errors.txt.diff
new file mode 100644
index 000000000..12ebbe21c
--- /dev/null
+++ b/testdata/baselines/reference/submodule/conformance/importEquals3.errors.txt.diff
@@ -0,0 +1,28 @@
+--- old.importEquals3.errors.txt
++++ new.importEquals3.errors.txt
+@@= skipped -0, +0 lines =@@
+ b.ts(2,12): error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
+ b.ts(3,13): error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
+ c.ts(2,12): error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
+-c.ts(3,13): error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
+
+
+ ==== a.ts (0 errors) ====
+@@= skipped -20, +19 lines =@@
+ const x = 0;
+ export { a, A, x };
+
+-==== c.ts (2 errors) ====
++==== c.ts (1 errors) ====
+ import * as b from './b';
+ import A = b.a.A; // Error
+ ~~~~~
+ !!! error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
+ !!! related TS1376 b.ts:1:18: 'a' was imported here.
+ import AA = b.A; // Error
+- ~~~
+-!!! error TS1380: An import alias cannot reference a declaration that was imported using 'import type'.
+-!!! related TS1376 b.ts:1:18: 'a' was imported here.
+
+ import x = b.x;
+ console.log(x);
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/conformance/typeOnlyMerge3.errors.txt b/testdata/baselines/reference/submodule/conformance/typeOnlyMerge3.errors.txt
index 6b50da4e6..0ad257fcf 100644
--- a/testdata/baselines/reference/submodule/conformance/typeOnlyMerge3.errors.txt
+++ b/testdata/baselines/reference/submodule/conformance/typeOnlyMerge3.errors.txt
@@ -1,7 +1,4 @@
b.ts(1,10): error TS2440: Import declaration conflicts with local declaration of 'A'.
-c.ts(2,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
-c.ts(3,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
-c.ts(4,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
c.ts(4,1): error TS2349: This expression is not callable.
Type 'typeof A' has no call signatures.
@@ -19,21 +16,12 @@ c.ts(4,1): error TS2349: This expression is not callable.
}
export { A };
-==== c.ts (4 errors) ====
+==== c.ts (1 errors) ====
import { A } from "./b";
A;
- ~
-!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
-!!! related TS1377 a.ts:2:15: 'A' was exported here.
A.displayName;
- ~
-!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
-!!! related TS1377 a.ts:2:15: 'A' was exported here.
A();
~
-!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
-!!! related TS1377 a.ts:2:15: 'A' was exported here.
- ~
!!! error TS2349: This expression is not callable.
!!! error TS2349: Type 'typeof A' has no call signatures.
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/conformance/typeOnlyMerge3.errors.txt.diff b/testdata/baselines/reference/submodule/conformance/typeOnlyMerge3.errors.txt.diff
new file mode 100644
index 000000000..cce54988e
--- /dev/null
+++ b/testdata/baselines/reference/submodule/conformance/typeOnlyMerge3.errors.txt.diff
@@ -0,0 +1,32 @@
+--- old.typeOnlyMerge3.errors.txt
++++ new.typeOnlyMerge3.errors.txt
+@@= skipped -0, +0 lines =@@
+ b.ts(1,10): error TS2440: Import declaration conflicts with local declaration of 'A'.
+-c.ts(2,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
+-c.ts(3,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
+-c.ts(4,1): error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
+ c.ts(4,1): error TS2349: This expression is not callable.
+ Type 'typeof A' has no call signatures.
+
+@@= skipped -18, +15 lines =@@
+ }
+ export { A };
+
+-==== c.ts (4 errors) ====
++==== c.ts (1 errors) ====
+ import { A } from "./b";
+ A;
+- ~
+-!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
+-!!! related TS1377 a.ts:2:15: 'A' was exported here.
+ A.displayName;
+- ~
+-!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
+-!!! related TS1377 a.ts:2:15: 'A' was exported here.
+ A();
+- ~
+-!!! error TS1362: 'A' cannot be used as a value because it was exported using 'export type'.
+-!!! related TS1377 a.ts:2:15: 'A' was exported here.
+ ~
+ !!! error TS2349: This expression is not callable.
+ !!! error TS2349: Type 'typeof A' has no call signatures.
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/conformance/umd-augmentation-3.types b/testdata/baselines/reference/submodule/conformance/umd-augmentation-3.types
index 73c994f78..95571b602 100644
--- a/testdata/baselines/reference/submodule/conformance/umd-augmentation-3.types
+++ b/testdata/baselines/reference/submodule/conformance/umd-augmentation-3.types
@@ -48,7 +48,7 @@ var t = p.x;
=== node_modules/math2d/index.d.ts ===
export as namespace Math2d;
->Math2d : typeof import("node_modules/math2d/index.d.ts")
+>Math2d : typeof import("node_modules/math2d/index")
export = M2D;
>M2D : typeof import("node_modules/math2d/index.d.ts")
diff --git a/testdata/baselines/reference/submodule/conformance/umd-augmentation-3.types.diff b/testdata/baselines/reference/submodule/conformance/umd-augmentation-3.types.diff
index e3de594a2..c2336b4c0 100644
--- a/testdata/baselines/reference/submodule/conformance/umd-augmentation-3.types.diff
+++ b/testdata/baselines/reference/submodule/conformance/umd-augmentation-3.types.diff
@@ -12,7 +12,16 @@
var t = p.x;
>t : number
-@@= skipped -65, +65 lines =@@
+@@= skipped -12, +12 lines =@@
+
+ === node_modules/math2d/index.d.ts ===
+ export as namespace Math2d;
+->Math2d : typeof import("node_modules/math2d/index.d.ts")
++>Math2d : typeof import("node_modules/math2d/index")
+
+ export = M2D;
+ >M2D : typeof import("node_modules/math2d/index.d.ts")
+@@= skipped -53, +53 lines =@@
// Add a method to the class
interface Vector {
reverse(): Math2d.Point;
diff --git a/testdata/baselines/reference/submodule/conformance/umd-augmentation-4.types b/testdata/baselines/reference/submodule/conformance/umd-augmentation-4.types
index c49c82177..b500a26ac 100644
--- a/testdata/baselines/reference/submodule/conformance/umd-augmentation-4.types
+++ b/testdata/baselines/reference/submodule/conformance/umd-augmentation-4.types
@@ -46,7 +46,7 @@ var t = p.x;
=== node_modules/math2d/index.d.ts ===
export as namespace Math2d;
->Math2d : typeof import("node_modules/math2d/index.d.ts")
+>Math2d : typeof import("node_modules/math2d/index")
export = M2D;
>M2D : typeof import("node_modules/math2d/index.d.ts")
diff --git a/testdata/baselines/reference/submodule/conformance/umd-augmentation-4.types.diff b/testdata/baselines/reference/submodule/conformance/umd-augmentation-4.types.diff
index ee8caf02b..7d963e8c0 100644
--- a/testdata/baselines/reference/submodule/conformance/umd-augmentation-4.types.diff
+++ b/testdata/baselines/reference/submodule/conformance/umd-augmentation-4.types.diff
@@ -1,6 +1,15 @@
--- old.umd-augmentation-4.types
+++ new.umd-augmentation-4.types
-@@= skipped -98, +98 lines =@@
+@@= skipped -45, +45 lines =@@
+
+ === node_modules/math2d/index.d.ts ===
+ export as namespace Math2d;
+->Math2d : typeof import("node_modules/math2d/index.d.ts")
++>Math2d : typeof import("node_modules/math2d/index")
+
+ export = M2D;
+ >M2D : typeof import("node_modules/math2d/index.d.ts")
+@@= skipped -53, +53 lines =@@
// Add a method to the class
interface Vector {
reverse(): Math2d.Point;
diff --git a/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/two-files-exist-on-disk-that-differs-only-in-casing.js b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/two-files-exist-on-disk-that-differs-only-in-casing.js
new file mode 100644
index 000000000..cf19f856c
--- /dev/null
+++ b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/two-files-exist-on-disk-that-differs-only-in-casing.js
@@ -0,0 +1,72 @@
+currentDirectory::/home/src/workspaces/project
+useCaseSensitiveFileNames::true
+Input::
+//// [/home/src/workspaces/project/D.ts] *new*
+export const x = 10;
+//// [/home/src/workspaces/project/c.ts] *new*
+import {x} from "./D"
+//// [/home/src/workspaces/project/d.ts] *new*
+export const y = 20;
+//// [/home/src/workspaces/project/tsconfig.json] *new*
+{
+ "files": ["c.ts", "d.ts"]
+}
+
+tsgo
+ExitStatus:: DiagnosticsPresent_OutputsGenerated
+Output::
+[96mc.ts[0m:[93m1[0m:[93m17[0m - [91merror[0m[90m TS1261: [0mAlready included file name '/home/src/workspaces/project/D.ts' differs from file name '/home/src/workspaces/project/d.ts' only in casing.
+ The file is in the program because:
+ Imported via "./D" from file '/home/src/workspaces/project/c.ts'
+ Part of 'files' list in tsconfig.json
+
+[7m1[0m import {x} from "./D"
+[7m [0m [91m ~~~~~[0m
+
+ [96mtsconfig.json[0m:[93m2[0m:[93m23[0m - File is matched by 'files' list specified here.
+ [7m2[0m "files": ["c.ts", "d.ts"]
+ [7m [0m [96m ~~~~~~[0m
+
+
+Found 1 error in c.ts[90m:1[0m
+
+//// [/home/src/tslibs/TS/Lib/lib.d.ts] *Lib*
+///
+interface Boolean {}
+interface Function {}
+interface CallableFunction {}
+interface NewableFunction {}
+interface IArguments {}
+interface Number { toExponential: any; }
+interface Object {}
+interface RegExp {}
+interface String { charAt: any; }
+interface Array { length: number; [n: number]: T; }
+interface ReadonlyArray {}
+interface SymbolConstructor {
+ (desc?: string | number): symbol;
+ for(name: string): symbol;
+ readonly toStringTag: symbol;
+}
+declare var Symbol: SymbolConstructor;
+interface Symbol {
+ readonly [Symbol.toStringTag]: string;
+}
+declare const console: { log(msg: any): void; };
+//// [/home/src/workspaces/project/D.js] *new*
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.x = void 0;
+exports.x = 10;
+
+//// [/home/src/workspaces/project/c.js] *new*
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+
+//// [/home/src/workspaces/project/d.js] *new*
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.y = void 0;
+exports.y = 20;
+
+
diff --git a/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/when-file-is-included-from-multiple-places-with-different-casing.js b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/when-file-is-included-from-multiple-places-with-different-casing.js
new file mode 100644
index 000000000..2c5f0bfa4
--- /dev/null
+++ b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/when-file-is-included-from-multiple-places-with-different-casing.js
@@ -0,0 +1,315 @@
+currentDirectory::/home/src/projects/project
+useCaseSensitiveFileNames::false
+Input::
+//// [/home/src/projects/project/node_modules/fp-ts/lib/struct.d.ts] *new*
+export function foo(): void
+//// [/home/src/projects/project/src/anotherFile.ts] *new*
+import * as xs1 from "fp-ts/lib/Struct";
+import * as xs2 from "fp-ts/lib/struct";
+import * as xs3 from "./Struct";
+import * as xs4 from "./struct";
+//// [/home/src/projects/project/src/oneMore.ts] *new*
+import * as xs1 from "fp-ts/lib/Struct";
+import * as xs2 from "fp-ts/lib/struct";
+import * as xs3 from "./Struct";
+import * as xs4 from "./struct";
+//// [/home/src/projects/project/src/struct.d.ts] *new*
+import * as xs1 from "fp-ts/lib/Struct";
+import * as xs2 from "fp-ts/lib/struct";
+import * as xs3 from "./Struct";
+import * as xs4 from "./struct";
+//// [/home/src/projects/project/tsconfig.json] *new*
+{}
+
+tsgo --explainFiles
+ExitStatus:: DiagnosticsPresent_OutputsGenerated
+Output::
+[96msrc/Struct.d.ts[0m:[93m2[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/home/src/projects/project/node_modules/fp-ts/lib/struct.d.ts' differs from already included file name '/home/src/projects/project/node_modules/fp-ts/lib/Struct.d.ts' only in casing.
+ The file is in the program because:
+ Imported via "fp-ts/lib/Struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "fp-ts/lib/struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "fp-ts/lib/Struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "fp-ts/lib/struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "fp-ts/lib/Struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Imported via "fp-ts/lib/struct" from file '/home/src/projects/project/src/oneMore.ts'
+
+[7m2[0m import * as xs2 from "fp-ts/lib/struct";
+[7m [0m [91m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m2[0m:[93m22[0m - File is included via import here.
+ [7m2[0m import * as xs2 from "fp-ts/lib/struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m2[0m:[93m22[0m - File is included via import here.
+ [7m2[0m import * as xs2 from "fp-ts/lib/struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+[96msrc/Struct.d.ts[0m:[93m4[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/home/src/projects/project/src/struct.d.ts' differs from already included file name '/home/src/projects/project/src/Struct.d.ts' only in casing.
+ The file is in the program because:
+ Imported via "./Struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "./Struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "./Struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Matched by default include pattern '**/*'
+
+[7m4[0m import * as xs4 from "./struct";
+[7m [0m [91m ~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+[96msrc/anotherFile.ts[0m:[93m2[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/home/src/projects/project/node_modules/fp-ts/lib/struct.d.ts' differs from already included file name '/home/src/projects/project/node_modules/fp-ts/lib/Struct.d.ts' only in casing.
+ The file is in the program because:
+ Imported via "fp-ts/lib/Struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "fp-ts/lib/struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "fp-ts/lib/Struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "fp-ts/lib/struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "fp-ts/lib/Struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Imported via "fp-ts/lib/struct" from file '/home/src/projects/project/src/oneMore.ts'
+
+[7m2[0m import * as xs2 from "fp-ts/lib/struct";
+[7m [0m [91m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m2[0m:[93m22[0m - File is included via import here.
+ [7m2[0m import * as xs2 from "fp-ts/lib/struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m2[0m:[93m22[0m - File is included via import here.
+ [7m2[0m import * as xs2 from "fp-ts/lib/struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+[96msrc/anotherFile.ts[0m:[93m3[0m:[93m22[0m - [91merror[0m[90m TS1261: [0mAlready included file name '/home/src/projects/project/src/Struct.d.ts' differs from file name '/home/src/projects/project/src/struct.d.ts' only in casing.
+ The file is in the program because:
+ Imported via "./Struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "./Struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "./Struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Matched by default include pattern '**/*'
+
+[7m3[0m import * as xs3 from "./Struct";
+[7m [0m [91m ~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+[96msrc/anotherFile.ts[0m:[93m4[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/home/src/projects/project/src/struct.d.ts' differs from already included file name '/home/src/projects/project/src/Struct.d.ts' only in casing.
+ The file is in the program because:
+ Imported via "./Struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "./Struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "./Struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Matched by default include pattern '**/*'
+
+[7m4[0m import * as xs4 from "./struct";
+[7m [0m [91m ~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+[96msrc/oneMore.ts[0m:[93m2[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/home/src/projects/project/node_modules/fp-ts/lib/struct.d.ts' differs from already included file name '/home/src/projects/project/node_modules/fp-ts/lib/Struct.d.ts' only in casing.
+ The file is in the program because:
+ Imported via "fp-ts/lib/Struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "fp-ts/lib/struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "fp-ts/lib/Struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "fp-ts/lib/struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "fp-ts/lib/Struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Imported via "fp-ts/lib/struct" from file '/home/src/projects/project/src/oneMore.ts'
+
+[7m2[0m import * as xs2 from "fp-ts/lib/struct";
+[7m [0m [91m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m2[0m:[93m22[0m - File is included via import here.
+ [7m2[0m import * as xs2 from "fp-ts/lib/struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m2[0m:[93m22[0m - File is included via import here.
+ [7m2[0m import * as xs2 from "fp-ts/lib/struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+[96msrc/oneMore.ts[0m:[93m4[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/home/src/projects/project/src/struct.d.ts' differs from already included file name '/home/src/projects/project/src/Struct.d.ts' only in casing.
+ The file is in the program because:
+ Imported via "./Struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "./Struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/Struct.d.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/anotherFile.ts'
+ Imported via "./Struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Imported via "./struct" from file '/home/src/projects/project/src/oneMore.ts'
+ Matched by default include pattern '**/*'
+
+[7m4[0m import * as xs4 from "./struct";
+[7m [0m [91m ~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/Struct.d.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/anotherFile.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+ [96msrc/oneMore.ts[0m:[93m3[0m:[93m22[0m - File is included via import here.
+ [7m3[0m import * as xs3 from "./Struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+../../tslibs/TS/Lib/lib.d.ts
+ Default library for target 'ES5'
+node_modules/fp-ts/lib/Struct.d.ts
+ Imported via "fp-ts/lib/Struct" from file 'src/anotherFile.ts'
+ Imported via "fp-ts/lib/struct" from file 'src/anotherFile.ts'
+ Imported via "fp-ts/lib/Struct" from file 'src/Struct.d.ts'
+ Imported via "fp-ts/lib/struct" from file 'src/Struct.d.ts'
+ Imported via "fp-ts/lib/Struct" from file 'src/oneMore.ts'
+ Imported via "fp-ts/lib/struct" from file 'src/oneMore.ts'
+src/Struct.d.ts
+ Imported via "./Struct" from file 'src/anotherFile.ts'
+ Imported via "./Struct" from file 'src/Struct.d.ts'
+ Imported via "./struct" from file 'src/Struct.d.ts'
+ Imported via "./struct" from file 'src/anotherFile.ts'
+ Imported via "./Struct" from file 'src/oneMore.ts'
+ Imported via "./struct" from file 'src/oneMore.ts'
+ Matched by default include pattern '**/*'
+src/anotherFile.ts
+ Matched by default include pattern '**/*'
+src/oneMore.ts
+ Matched by default include pattern '**/*'
+
+Found 7 errors in 3 files.
+
+Errors Files
+ 2 src/Struct.d.ts[90m:2[0m
+ 3 src/anotherFile.ts[90m:2[0m
+ 2 src/oneMore.ts[90m:2[0m
+
+//// [/home/src/projects/project/src/anotherFile.js] *new*
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+
+//// [/home/src/projects/project/src/oneMore.js] *new*
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+
+//// [/home/src/tslibs/TS/Lib/lib.d.ts] *Lib*
+///
+interface Boolean {}
+interface Function {}
+interface CallableFunction {}
+interface NewableFunction {}
+interface IArguments {}
+interface Number { toExponential: any; }
+interface Object {}
+interface RegExp {}
+interface String { charAt: any; }
+interface Array { length: number; [n: number]: T; }
+interface ReadonlyArray {}
+interface SymbolConstructor {
+ (desc?: string | number): symbol;
+ for(name: string): symbol;
+ readonly toStringTag: symbol;
+}
+declare var Symbol: SymbolConstructor;
+interface Symbol {
+ readonly [Symbol.toStringTag]: string;
+}
+declare const console: { log(msg: any): void; };
+
diff --git a/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/with-relative-and-non-relative-file-resolutions.js b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/with-relative-and-non-relative-file-resolutions.js
new file mode 100644
index 000000000..19ce089f6
--- /dev/null
+++ b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/with-relative-and-non-relative-file-resolutions.js
@@ -0,0 +1,75 @@
+currentDirectory::/user/username/projects/myproject
+useCaseSensitiveFileNames::false
+Input::
+//// [/user/username/projects/myproject/node_modules/fp-ts/lib/struct.d.ts] *new*
+export function foo(): void
+//// [/user/username/projects/myproject/src/struct.d.ts] *new*
+import * as xs1 from "fp-ts/lib/Struct";
+import * as xs2 from "fp-ts/lib/struct";
+import * as xs3 from "./Struct";
+import * as xs4 from "./struct";
+
+tsgo /user/username/projects/myproject/src/struct.d.ts --forceConsistentCasingInFileNames --explainFiles
+ExitStatus:: DiagnosticsPresent_OutputsGenerated
+Output::
+[96msrc/struct.d.ts[0m:[93m2[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/user/username/projects/myproject/node_modules/fp-ts/lib/struct.d.ts' differs from already included file name '/user/username/projects/myproject/node_modules/fp-ts/lib/Struct.d.ts' only in casing.
+ The file is in the program because:
+ Imported via "fp-ts/lib/Struct" from file '/user/username/projects/myproject/src/struct.d.ts'
+ Imported via "fp-ts/lib/struct" from file '/user/username/projects/myproject/src/struct.d.ts'
+
+[7m2[0m import * as xs2 from "fp-ts/lib/struct";
+[7m [0m [91m ~~~~~~~~~~~~~~~~~~[0m
+
+ [96msrc/struct.d.ts[0m:[93m1[0m:[93m22[0m - File is included via import here.
+ [7m1[0m import * as xs1 from "fp-ts/lib/Struct";
+ [7m [0m [96m ~~~~~~~~~~~~~~~~~~[0m
+
+[96msrc/struct.d.ts[0m:[93m3[0m:[93m22[0m - [91merror[0m[90m TS1149: [0mFile name '/user/username/projects/myproject/src/Struct.d.ts' differs from already included file name '/user/username/projects/myproject/src/struct.d.ts' only in casing.
+ The file is in the program because:
+ Root file specified for compilation
+ Imported via "./Struct" from file '/user/username/projects/myproject/src/struct.d.ts'
+ Imported via "./struct" from file '/user/username/projects/myproject/src/struct.d.ts'
+
+[7m3[0m import * as xs3 from "./Struct";
+[7m [0m [91m ~~~~~~~~~~[0m
+
+ [96msrc/struct.d.ts[0m:[93m4[0m:[93m22[0m - File is included via import here.
+ [7m4[0m import * as xs4 from "./struct";
+ [7m [0m [96m ~~~~~~~~~~[0m
+
+../../../../home/src/tslibs/TS/Lib/lib.d.ts
+ Default library for target 'ES5'
+node_modules/fp-ts/lib/Struct.d.ts
+ Imported via "fp-ts/lib/Struct" from file 'src/struct.d.ts'
+ Imported via "fp-ts/lib/struct" from file 'src/struct.d.ts'
+src/struct.d.ts
+ Root file specified for compilation
+ Imported via "./Struct" from file 'src/struct.d.ts'
+ Imported via "./struct" from file 'src/struct.d.ts'
+
+Found 2 errors in the same file, starting at: src/struct.d.ts[90m:2[0m
+
+//// [/home/src/tslibs/TS/Lib/lib.d.ts] *Lib*
+///
+interface Boolean {}
+interface Function {}
+interface CallableFunction {}
+interface NewableFunction {}
+interface IArguments {}
+interface Number { toExponential: any; }
+interface Object {}
+interface RegExp {}
+interface String { charAt: any; }
+interface Array { length: number; [n: number]: T; }
+interface ReadonlyArray {}
+interface SymbolConstructor {
+ (desc?: string | number): symbol;
+ for(name: string): symbol;
+ readonly toStringTag: symbol;
+}
+declare var Symbol: SymbolConstructor;
+interface Symbol {
+ readonly [Symbol.toStringTag]: string;
+}
+declare const console: { log(msg: any): void; };
+
diff --git a/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/with-triple-slash-ref-from-file.js b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/with-triple-slash-ref-from-file.js
new file mode 100644
index 000000000..1d1692756
--- /dev/null
+++ b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/with-triple-slash-ref-from-file.js
@@ -0,0 +1,53 @@
+currentDirectory::/home/src/workspaces/project
+useCaseSensitiveFileNames::false
+Input::
+//// [/home/src/workspaces/project/src/c.ts] *new*
+///
+//// [/home/src/workspaces/project/src/d.ts] *new*
+declare class c { }
+//// [/home/src/workspaces/project/tsconfig.json] *new*
+{ }
+
+tsgo
+ExitStatus:: DiagnosticsPresent_OutputsGenerated
+Output::
+[96msrc/c.ts[0m:[93m1[0m:[93m22[0m - [91merror[0m[90m TS1261: [0mAlready included file name '/home/src/workspaces/project/src/D.ts' differs from file name '/home/src/workspaces/project/src/d.ts' only in casing.
+ The file is in the program because:
+ Referenced via './D.ts' from file '/home/src/workspaces/project/src/c.ts'
+ Matched by default include pattern '**/*'
+
+[7m1[0m ///
+[7m [0m [91m ~~~~~~[0m
+
+
+Found 1 error in src/c.ts[90m:1[0m
+
+//// [/home/src/tslibs/TS/Lib/lib.d.ts] *Lib*
+///
+interface Boolean {}
+interface Function {}
+interface CallableFunction {}
+interface NewableFunction {}
+interface IArguments {}
+interface Number { toExponential: any; }
+interface Object {}
+interface RegExp {}
+interface String { charAt: any; }
+interface Array { length: number; [n: number]: T; }
+interface ReadonlyArray {}
+interface SymbolConstructor {
+ (desc?: string | number): symbol;
+ for(name: string): symbol;
+ readonly toStringTag: symbol;
+}
+declare var Symbol: SymbolConstructor;
+interface Symbol {
+ readonly [Symbol.toStringTag]: string;
+}
+declare const console: { log(msg: any): void; };
+//// [/home/src/workspaces/project/src/D.js] *new*
+
+//// [/home/src/workspaces/project/src/c.js] *new*
+///
+
+
diff --git a/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/with-type-ref-from-file.js b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/with-type-ref-from-file.js
new file mode 100644
index 000000000..2f55514b0
--- /dev/null
+++ b/testdata/baselines/reference/tsc/forceConsistentCasingInFileNames/with-type-ref-from-file.js
@@ -0,0 +1,58 @@
+currentDirectory::/user/username/projects/myproject
+useCaseSensitiveFileNames::false
+Input::
+//// [/user/username/projects/myproject/src/file2.d.ts] *new*
+///
+declare const y: c;
+//// [/user/username/projects/myproject/src/fileOne.d.ts] *new*
+declare class c { }
+//// [/user/username/projects/myproject/tsconfig.json] *new*
+{ }
+
+tsgo -p /user/username/projects/myproject --explainFiles --traceResolution
+ExitStatus:: Success
+Output::
+======== Resolving type reference directive './fileOne.d.ts', containing file '/user/username/projects/myproject/src/file2.d.ts', root directory '/user/username/projects/myproject/node_modules/@types,/user/username/projects/node_modules/@types,/user/username/node_modules/@types,/user/node_modules/@types,/node_modules/@types'. ========
+Resolving with primary search path '/user/username/projects/myproject/node_modules/@types, /user/username/projects/node_modules/@types, /user/username/node_modules/@types, /user/node_modules/@types, /node_modules/@types'.
+Directory '/user/username/projects/myproject/node_modules/@types' does not exist, skipping all lookups in it.
+Directory '/user/username/projects/node_modules/@types' does not exist, skipping all lookups in it.
+Directory '/user/username/node_modules/@types' does not exist, skipping all lookups in it.
+Directory '/user/node_modules/@types' does not exist, skipping all lookups in it.
+Directory '/node_modules/@types' does not exist, skipping all lookups in it.
+Looking up in 'node_modules' folder, initial location '/user/username/projects/myproject/src'.
+Loading module as file / folder, candidate module location '/user/username/projects/myproject/src/fileOne.d.ts', target file types: Declaration.
+File name '/user/username/projects/myproject/src/fileOne.d.ts' has a '.d.ts' extension - stripping it.
+File '/user/username/projects/myproject/src/fileOne.d.ts' exists - use it as a name resolution result.
+Resolving real path for '/user/username/projects/myproject/src/fileOne.d.ts', result '/user/username/projects/myproject/src/fileOne.d.ts'.
+======== Type reference directive './fileOne.d.ts' was successfully resolved to '/user/username/projects/myproject/src/fileOne.d.ts', primary: false. ========
+../../../../home/src/tslibs/TS/Lib/lib.d.ts
+ Default library for target 'ES5'
+src/fileOne.d.ts
+ Type library referenced via './fileOne.d.ts' from file 'src/file2.d.ts'
+ Matched by default include pattern '**/*'
+src/file2.d.ts
+ Matched by default include pattern '**/*'
+//// [/home/src/tslibs/TS/Lib/lib.d.ts] *Lib*
+///
+interface Boolean {}
+interface Function {}
+interface CallableFunction {}
+interface NewableFunction {}
+interface IArguments {}
+interface Number { toExponential: any; }
+interface Object {}
+interface RegExp {}
+interface String { charAt: any; }
+interface Array { length: number; [n: number]: T; }
+interface ReadonlyArray {}
+interface SymbolConstructor {
+ (desc?: string | number): symbol;
+ for(name: string): symbol;
+ readonly toStringTag: symbol;
+}
+declare var Symbol: SymbolConstructor;
+interface Symbol {
+ readonly [Symbol.toStringTag]: string;
+}
+declare const console: { log(msg: any): void; };
+
diff --git a/testdata/baselines/reference/tsc/incremental/Compile-incremental-with-case-insensitive-file-names.js b/testdata/baselines/reference/tsc/incremental/Compile-incremental-with-case-insensitive-file-names.js
new file mode 100644
index 000000000..416b8b892
--- /dev/null
+++ b/testdata/baselines/reference/tsc/incremental/Compile-incremental-with-case-insensitive-file-names.js
@@ -0,0 +1,196 @@
+currentDirectory::/home/project
+useCaseSensitiveFileNames::false
+Input::
+//// [/home/node_modules/lib1/index.d.ts] *new*
+import type { Foo } from 'someLib';
+export type { Foo as Foo1 };
+//// [/home/node_modules/lib1/package.json] *new*
+{
+ "name": "lib1"
+}
+//// [/home/node_modules/lib2/index.d.ts] *new*
+import type { Foo } from 'somelib';
+export type { Foo as Foo2 };
+export declare const foo2: Foo;
+//// [/home/node_modules/lib2/package.json] *new*
+{
+ "name": "lib2"
+}
+//// [/home/node_modules/otherLib/index.d.ts] *new*
+export type Str = string;
+//// [/home/node_modules/otherLib/package.json] *new*
+{
+ "name": "otherlib"
+}
+//// [/home/node_modules/someLib/index.d.ts] *new*
+import type { Str } from 'otherLib';
+export type Foo = { foo: Str; };
+//// [/home/node_modules/someLib/package.json] *new*
+{
+ "name": "somelib"
+}
+//// [/home/project/src/index.ts] *new*
+import type { Foo1 } from 'lib1';
+import type { Foo2 } from 'lib2';
+export const foo1: Foo1 = { foo: "a" };
+export const foo2: Foo2 = { foo: "b" };
+//// [/home/project/tsconfig.json] *new*
+{
+ "compilerOptions": {
+ "incremental": true
+ },
+}
+
+tsgo -p .
+ExitStatus:: DiagnosticsPresent_OutputsGenerated
+Output::
+[96m../node_modules/lib2/index.d.ts[0m:[93m1[0m:[93m26[0m - [91merror[0m[90m TS1149: [0mFile name '/home/node_modules/somelib/index.d.ts' differs from already included file name '/home/node_modules/someLib/index.d.ts' only in casing.
+ The file is in the program because:
+ Imported via 'someLib' from file '/home/node_modules/lib1/index.d.ts'
+ Imported via 'somelib' from file '/home/node_modules/lib2/index.d.ts'
+
+[7m1[0m import type { Foo } from 'somelib';
+[7m [0m [91m ~~~~~~~~~[0m
+
+ [96m../node_modules/lib1/index.d.ts[0m:[93m1[0m:[93m26[0m - File is included via import here.
+ [7m1[0m import type { Foo } from 'someLib';
+ [7m [0m [96m ~~~~~~~~~[0m
+
+
+Found 1 error in ../node_modules/lib2/index.d.ts[90m:1[0m
+
+//// [/home/project/src/index.js] *new*
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.foo2 = exports.foo1 = void 0;
+exports.foo1 = { foo: "a" };
+exports.foo2 = { foo: "b" };
+
+//// [/home/project/tsconfig.tsbuildinfo] *new*
+{"version":"FakeTSVersion","errors":true,"root":[6],"fileNames":["lib.d.ts","../node_modules/otherlib/index.d.ts","../node_modules/somelib/index.d.ts","../node_modules/lib1/index.d.ts","../node_modules/lib2/index.d.ts","./src/index.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},"1fe659ed0634bb57b6dc25e9062f1162-export type Str = string;","12e112ff6e2744bb42d8e0b511e44117-import type { Str } from 'otherLib';\nexport type Foo = { foo: Str; };","b6305455d920a6729c435e6acf45eff6-import type { Foo } from 'someLib';\nexport type { Foo as Foo1 };","a5393e550a9c20a242a120bf6410db48-import type { Foo } from 'somelib';\nexport type { Foo as Foo2 };\nexport declare const foo2: Foo;","42aef197ff5f079223e2c29fb2e77cc5-import type { Foo1 } from 'lib1';\nimport type { Foo2 } from 'lib2';\nexport const foo1: Foo1 = { foo: \"a\" };\nexport const foo2: Foo2 = { foo: \"b\" };"],"fileIdsList":[[3],[2],[4,5]],"referencedMap":[[4,1],[5,1],[3,2],[6,3]]}
+//// [/home/project/tsconfig.tsbuildinfo.readable.baseline.txt] *new*
+{
+ "version": "FakeTSVersion",
+ "errors": true,
+ "root": [
+ {
+ "files": [
+ "./src/index.ts"
+ ],
+ "original": 6
+ }
+ ],
+ "fileNames": [
+ "lib.d.ts",
+ "../node_modules/otherlib/index.d.ts",
+ "../node_modules/somelib/index.d.ts",
+ "../node_modules/lib1/index.d.ts",
+ "../node_modules/lib2/index.d.ts",
+ "./src/index.ts"
+ ],
+ "fileInfos": [
+ {
+ "fileName": "lib.d.ts",
+ "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };",
+ "signature": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };",
+ "affectsGlobalScope": true,
+ "impliedNodeFormat": "CommonJS",
+ "original": {
+ "version": "8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };",
+ "affectsGlobalScope": true,
+ "impliedNodeFormat": 1
+ }
+ },
+ {
+ "fileName": "../node_modules/otherlib/index.d.ts",
+ "version": "1fe659ed0634bb57b6dc25e9062f1162-export type Str = string;",
+ "signature": "1fe659ed0634bb57b6dc25e9062f1162-export type Str = string;",
+ "impliedNodeFormat": "CommonJS"
+ },
+ {
+ "fileName": "../node_modules/somelib/index.d.ts",
+ "version": "12e112ff6e2744bb42d8e0b511e44117-import type { Str } from 'otherLib';\nexport type Foo = { foo: Str; };",
+ "signature": "12e112ff6e2744bb42d8e0b511e44117-import type { Str } from 'otherLib';\nexport type Foo = { foo: Str; };",
+ "impliedNodeFormat": "CommonJS"
+ },
+ {
+ "fileName": "../node_modules/lib1/index.d.ts",
+ "version": "b6305455d920a6729c435e6acf45eff6-import type { Foo } from 'someLib';\nexport type { Foo as Foo1 };",
+ "signature": "b6305455d920a6729c435e6acf45eff6-import type { Foo } from 'someLib';\nexport type { Foo as Foo1 };",
+ "impliedNodeFormat": "CommonJS"
+ },
+ {
+ "fileName": "../node_modules/lib2/index.d.ts",
+ "version": "a5393e550a9c20a242a120bf6410db48-import type { Foo } from 'somelib';\nexport type { Foo as Foo2 };\nexport declare const foo2: Foo;",
+ "signature": "a5393e550a9c20a242a120bf6410db48-import type { Foo } from 'somelib';\nexport type { Foo as Foo2 };\nexport declare const foo2: Foo;",
+ "impliedNodeFormat": "CommonJS"
+ },
+ {
+ "fileName": "./src/index.ts",
+ "version": "42aef197ff5f079223e2c29fb2e77cc5-import type { Foo1 } from 'lib1';\nimport type { Foo2 } from 'lib2';\nexport const foo1: Foo1 = { foo: \"a\" };\nexport const foo2: Foo2 = { foo: \"b\" };",
+ "signature": "42aef197ff5f079223e2c29fb2e77cc5-import type { Foo1 } from 'lib1';\nimport type { Foo2 } from 'lib2';\nexport const foo1: Foo1 = { foo: \"a\" };\nexport const foo2: Foo2 = { foo: \"b\" };",
+ "impliedNodeFormat": "CommonJS"
+ }
+ ],
+ "fileIdsList": [
+ [
+ "../node_modules/somelib/index.d.ts"
+ ],
+ [
+ "../node_modules/otherlib/index.d.ts"
+ ],
+ [
+ "../node_modules/lib1/index.d.ts",
+ "../node_modules/lib2/index.d.ts"
+ ]
+ ],
+ "referencedMap": {
+ "../node_modules/lib1/index.d.ts": [
+ "../node_modules/somelib/index.d.ts"
+ ],
+ "../node_modules/lib2/index.d.ts": [
+ "../node_modules/somelib/index.d.ts"
+ ],
+ "../node_modules/somelib/index.d.ts": [
+ "../node_modules/otherlib/index.d.ts"
+ ],
+ "./src/index.ts": [
+ "../node_modules/lib1/index.d.ts",
+ "../node_modules/lib2/index.d.ts"
+ ]
+ },
+ "size": 1685
+}
+//// [/home/src/tslibs/TS/Lib/lib.d.ts] *Lib*
+///
+interface Boolean {}
+interface Function {}
+interface CallableFunction {}
+interface NewableFunction {}
+interface IArguments {}
+interface Number { toExponential: any; }
+interface Object {}
+interface RegExp {}
+interface String { charAt: any; }
+interface Array { length: number; [n: number]: T; }
+interface ReadonlyArray {}
+interface SymbolConstructor {
+ (desc?: string | number): symbol;
+ for(name: string): symbol;
+ readonly toStringTag: symbol;
+}
+declare var Symbol: SymbolConstructor;
+interface Symbol {
+ readonly [Symbol.toStringTag]: string;
+}
+declare const console: { log(msg: any): void; };
+
+tsconfig.json::
+SemanticDiagnostics::
+*refresh* /home/src/tslibs/TS/Lib/lib.d.ts
+*refresh* /home/node_modules/otherLib/index.d.ts
+*refresh* /home/node_modules/someLib/index.d.ts
+*refresh* /home/node_modules/lib1/index.d.ts
+*refresh* /home/node_modules/lib2/index.d.ts
+*refresh* /home/project/src/index.ts
+Signatures::