Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d108e2e
initial
iisaduan Aug 11, 2025
e450fbf
fourslash completion filtertext
iisaduan Aug 11, 2025
2159797
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Aug 11, 2025
fd072cf
fuzzymatch
iisaduan Aug 11, 2025
f28767b
fix bugs, add tests, remove extra preferences
iisaduan Aug 14, 2025
1f582ed
cleanup
iisaduan Aug 14, 2025
9eac6f1
refactor importFix struct
iisaduan Aug 15, 2025
98404fb
refactor trackeredit struct
iisaduan Aug 15, 2025
8b8adb4
reallow check filtertext
iisaduan Aug 15, 2025
b13ee93
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Aug 15, 2025
357432a
merge changes that didnt go with
iisaduan Aug 15, 2025
f0f88dc
fix broken test and use textpos methods
iisaduan Aug 15, 2025
7a44815
formatting bugs/function clean up
iisaduan Aug 16, 2025
e84c1fd
completions comments, remove deprecated userpreferences, add parents …
iisaduan Aug 18, 2025
6f57d2c
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Aug 18, 2025
82f1032
update to use `debug`
iisaduan Aug 19, 2025
ddc3f88
clean up autpimport types + use stringer
iisaduan Aug 19, 2025
e799762
refactor checker, program, and ctx parameters
iisaduan Aug 21, 2025
48be67b
cancellationtoken
iisaduan Aug 25, 2025
01281ba
refactor `resolvingmodulespecifiers`
iisaduan Aug 25, 2025
89f6550
remove resolvingmoduleSpecifiers
iisaduan Aug 25, 2025
e05bddf
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Aug 25, 2025
07262e4
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Aug 27, 2025
e3f7e0f
refactor export import map generation
iisaduan Aug 27, 2025
ac9da51
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Aug 27, 2025
8e1b836
move test
iisaduan Aug 27, 2025
dd04d96
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Aug 28, 2025
9524b68
fix panic recovery in server
gabritto Aug 28, 2025
82ad946
make updater script throw if untested
gabritto Aug 28, 2025
e256867
Merge commit 'refs/pull/1644/head' of https://github.com/microsoft/ty…
iisaduan Aug 28, 2025
f1e9198
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Aug 28, 2025
8b5e4bc
remove needsfullresolution
iisaduan Aug 29, 2025
f6cb023
Merge branch 'main' of https://github.com/microsoft/typescript-go int…
iisaduan Aug 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -992,10 +992,22 @@ func (n *Node) ModuleSpecifier() *Expression {
return n.AsImportDeclaration().ModuleSpecifier
case KindExportDeclaration:
return n.AsExportDeclaration().ModuleSpecifier
case KindJSDocImportTag:
return n.AsJSDocImportTag().ModuleSpecifier
}
panic("Unhandled case in Node.ModuleSpecifier: " + n.Kind.String())
}

func (n *Node) ImportClause() *Node {
switch n.Kind {
case KindImportDeclaration:
return n.AsImportDeclaration().ImportClause
case KindJSDocImportTag:
return n.AsJSDocImportTag().ImportClause
}
panic("Unhandled case in Node.ImportClause: " + n.Kind.String())
}

func (n *Node) Statement() *Statement {
switch n.Kind {
case KindDoStatement:
Expand Down
19 changes: 19 additions & 0 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1408,6 +1408,16 @@ func GetNameOfDeclaration(declaration *Node) *Node {
return nil
}

func GetImportClauseOfDeclaration(declaration *Declaration) *ImportClause {
switch declaration.Kind {
case KindImportDeclaration:
return declaration.AsImportDeclaration().ImportClause.AsImportClause()
case KindJSDocImportTag:
return declaration.AsJSDocImportTag().ImportClause.AsImportClause()
}
return nil
}

func GetNonAssignedNameOfDeclaration(declaration *Node) *Node {
// !!!
switch declaration.Kind {
Expand Down Expand Up @@ -2722,6 +2732,15 @@ func IsRequireCall(node *Node, requireStringLiteralLikeArgument bool) bool {
return !requireStringLiteralLikeArgument || IsStringLiteralLike(call.Arguments.Nodes[0])
}

func IsRequireVariableStatement(node *Node) bool {
if IsVariableStatement(node) {
if declarations := node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes; len(declarations) > 0 {
return core.Every(declarations, IsVariableDeclarationInitializedToRequire)
}
}
return false
}

func GetJSXImplicitImportBase(compilerOptions *core.CompilerOptions, file *SourceFile) string {
jsxImportSourcePragma := GetPragmaFromSourceFile(file, "jsximportsource")
jsxRuntimePragma := GetPragmaFromSourceFile(file, "jsxruntime")
Expand Down
20 changes: 20 additions & 0 deletions internal/checker/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ func (c *Checker) GetMergedSymbol(symbol *ast.Symbol) *ast.Symbol {
return c.getMergedSymbol(symbol)
}

func (c *Checker) TryFindAmbientModule(moduleName string) *ast.Symbol {
return c.tryFindAmbientModule(moduleName, true /* withAugmentations */)
}

func (c *Checker) GetImmediateAliasedSymbol(symbol *ast.Symbol) *ast.Symbol {
return c.getImmediateAliasedSymbol(symbol)
}

func (c *Checker) GetTypeOnlyAliasDeclaration(symbol *ast.Symbol) *ast.Node {
return c.getTypeOnlyAliasDeclaration(symbol)
}

func (c *Checker) ResolveExternalModuleName(moduleSpecifier *ast.Node) *ast.Symbol {
return c.resolveExternalModuleName(moduleSpecifier, moduleSpecifier, true /*ignoreErrors*/)
}

func (c *Checker) ResolveExternalModuleSymbol(moduleSymbol *ast.Symbol) *ast.Symbol {
return c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/)
}

func (c *Checker) GetTypeFromTypeNode(node *ast.Node) *Type {
return c.getTypeFromTypeNode(node)
}
Expand Down
5 changes: 3 additions & 2 deletions internal/checker/nodebuilderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,7 @@ func canHaveModuleSpecifier(node *ast.Node) bool {
return false
}

func tryGetModuleSpecifierFromDeclaration(node *ast.Node) *ast.Node {
func TryGetModuleSpecifierFromDeclaration(node *ast.Node) *ast.Node {
res := tryGetModuleSpecifierFromDeclarationWorker(node)
if res == nil || !ast.IsStringLiteral(res) {
return nil
Expand Down Expand Up @@ -1165,7 +1165,7 @@ func (b *nodeBuilderImpl) getSpecifierForModuleSymbol(symbol *ast.Symbol, overri
enclosingDeclaration := b.e.MostOriginal(b.ctx.enclosingDeclaration)
var originalModuleSpecifier *ast.Node
if canHaveModuleSpecifier(enclosingDeclaration) {
originalModuleSpecifier = tryGetModuleSpecifierFromDeclaration(enclosingDeclaration)
originalModuleSpecifier = TryGetModuleSpecifierFromDeclaration(enclosingDeclaration)
}
contextFile := b.ctx.enclosingFile
resolutionMode := overrideImportMode
Expand Down Expand Up @@ -1216,6 +1216,7 @@ func (b *nodeBuilderImpl) getSpecifierForModuleSymbol(symbol *ast.Symbol, overri
modulespecifiers.ModuleSpecifierOptions{
OverrideImportMode: overrideImportMode,
},
false, /*forAutoImports*/
)
specifier := allSpecifiers[0]
links.specifierCache[cacheKey] = specifier
Expand Down
36 changes: 36 additions & 0 deletions internal/checker/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,35 @@ func (c *Checker) GetExportsOfModule(symbol *ast.Symbol) []*ast.Symbol {
return symbolsToArray(c.getExportsOfModule(symbol))
}

func (c *Checker) ForEachExportAndPropertyOfModule(moduleSymbol *ast.Symbol, cb func(*ast.Symbol, string)) {
for key, exportedSymbol := range c.getExportsOfModule(moduleSymbol) {
if !isReservedMemberName(key) {
cb(exportedSymbol, key)
}
}

exportEquals := c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/)
if exportEquals == moduleSymbol {
return
}

typeOfSymbol := c.getTypeOfSymbol(exportEquals)
if !c.shouldTreatPropertiesOfExternalModuleAsExports(typeOfSymbol) {
return
}

// forEachPropertyOfType
reducedType := c.getReducedApparentType(typeOfSymbol)
if reducedType.flags&TypeFlagsStructuredType == 0 {
return
}
for name, symbol := range c.resolveStructuredTypeMembers(reducedType).members {
if c.isNamedMember(symbol, name) {
cb(symbol, name)
}
}
}

func (c *Checker) IsValidPropertyAccess(node *ast.Node, propertyName string) bool {
return c.isValidPropertyAccess(node, propertyName)
}
Expand Down Expand Up @@ -345,6 +374,13 @@ func runWithoutResolvedSignatureCaching[T any](c *Checker, node *ast.Node, fn fu
return fn()
}

func (c *Checker) SkipAlias(symbol *ast.Symbol) *ast.Symbol {
if symbol.Flags&ast.SymbolFlagsAlias != 0 {
return c.GetAliasedSymbol(symbol)
}
return symbol
}

func (c *Checker) GetRootSymbols(symbol *ast.Symbol) []*ast.Symbol {
roots := c.getImmediateRootSymbols(symbol)
if roots != nil {
Expand Down
4 changes: 4 additions & 0 deletions internal/compiler/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ func (p *Program) UseCaseSensitiveFileNames() bool {
return p.Host().FS().UseCaseSensitiveFileNames()
}

func (p *Program) UsesUriStyleNodeCoreModules() bool {
return p.usesUriStyleNodeCoreModules.IsTrue()
}

var _ checker.Program = (*Program)(nil)

/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
Expand Down
11 changes: 11 additions & 0 deletions internal/core/compileroptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,17 @@ const (
NewLineKindLF NewLineKind = 2
)

func GetNewLineKind(s string) NewLineKind {
switch s {
case "\r\n":
return NewLineKindCRLF
case "\n":
return NewLineKindLF
default:
return NewLineKindNone
}
}

func (newLine NewLineKind) GetNewLineCharacter() string {
switch newLine {
case NewLineKindCRLF:
Expand Down
11 changes: 11 additions & 0 deletions internal/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ func Every[T any](slice []T, f func(T) bool) bool {
return true
}

func Or[T any](funcs ...func(T) bool) func(T) bool {
return func(input T) bool {
for _, f := range funcs {
if f(input) {
return true
}
}
return false
}
}

func Find[T any](slice []T, f func(T) bool) T {
for _, value := range slice {
if f(value) {
Expand Down
4 changes: 2 additions & 2 deletions internal/core/nodemodules.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ var ExclusivelyPrefixedNodeCoreModules = map[string]bool{
"node:test/reporters": true,
}

var nodeCoreModules = sync.OnceValue(func() map[string]bool {
var NodeCoreModules = sync.OnceValue(func() map[string]bool {
nodeCoreModules := make(map[string]bool, len(UnprefixedNodeCoreModules)*2+len(ExclusivelyPrefixedNodeCoreModules))
for unprefixed := range UnprefixedNodeCoreModules {
nodeCoreModules[unprefixed] = true
Expand All @@ -81,7 +81,7 @@ var nodeCoreModules = sync.OnceValue(func() map[string]bool {
})

func NonRelativeModuleNameForTypingCache(moduleName string) string {
if nodeCoreModules()[moduleName] {
if NodeCoreModules()[moduleName] {
return "node"
}
return moduleName
Expand Down
20 changes: 20 additions & 0 deletions internal/core/textchange.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package core

import "strings"

type TextChange struct {
TextRange
NewText string
Expand All @@ -8,3 +10,21 @@ type TextChange struct {
func (t TextChange) ApplyTo(text string) string {
return text[:t.Pos()] + t.NewText + text[t.End():]
}

func ApplyBulkEdits(text string, edits []TextChange) string {
b := strings.Builder{}
b.Grow(len(text))
lastEnd := 0
for _, e := range edits {
start := e.TextRange.Pos()
if start != lastEnd {
b.WriteString(text[lastEnd:e.TextRange.Pos()])
}
b.WriteString(e.NewText)

lastEnd = e.TextRange.End()
}
b.WriteString(text[lastEnd:])

return b.String()
}
20 changes: 1 addition & 19 deletions internal/execute/tsc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,6 @@ import (
"github.com/microsoft/typescript-go/internal/tspath"
)

func applyBulkEdits(text string, edits []core.TextChange) string {
b := strings.Builder{}
b.Grow(len(text))
lastEnd := 0
for _, e := range edits {
start := e.TextRange.Pos()
if start != lastEnd {
b.WriteString(text[lastEnd:e.TextRange.Pos()])
}
b.WriteString(e.NewText)

lastEnd = e.TextRange.End()
}
b.WriteString(text[lastEnd:])

return b.String()
}

type CommandLineResult struct {
Status ExitStatus
IncrementalProgram *incremental.Program
Expand Down Expand Up @@ -85,7 +67,7 @@ func fmtMain(sys System, input, output string) ExitStatus {
JSDocParsingMode: ast.JSDocParsingModeParseAll,
}, text, core.GetScriptKindFromFileName(string(pathified)))
edits := format.FormatDocument(ctx, sourceFile)
newText := applyBulkEdits(text, edits)
newText := core.ApplyBulkEdits(text, edits)

if err := sys.FS().WriteFile(output, newText, false); err != nil {
fmt.Fprintln(sys.Writer(), err.Error())
Expand Down
19 changes: 19 additions & 0 deletions internal/format/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,25 @@ func FormatSpan(ctx context.Context, span core.TextRange, file *ast.SourceFile,
)
}

func FormatNodeGivenIndentation(ctx context.Context, node *ast.Node, file *ast.SourceFile, languageVariant core.LanguageVariant, initialIndentation int, delta int) []core.TextChange {
textRange := core.NewTextRange(node.Pos(), node.End())
return newFormattingScanner(
file.Text(),
languageVariant,
textRange.Pos(),
textRange.End(),
newFormatSpanWorker(
ctx,
textRange,
node,
initialIndentation,
delta,
FormatRequestKindFormatSelection,
func(core.TextRange) bool { return false }, // assume that node does not have any errors
file,
))
}

func formatNodeLines(ctx context.Context, sourceFile *ast.SourceFile, node *ast.Node, requestKind FormatRequestKind) []core.TextChange {
if node == nil {
return nil
Expand Down
12 changes: 6 additions & 6 deletions internal/format/indent.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func getIndentationForNodeWorker(
if useActualIndentation {
// check if current node is a list item - if yes, take indentation from it
var firstListChild *ast.Node
containerList := getContainingList(current, sourceFile)
containerList := GetContainingList(current, sourceFile)
if containerList != nil {
firstListChild = core.FirstOrNil(containerList.Nodes)
}
Expand Down Expand Up @@ -139,7 +139,7 @@ func getActualIndentationForListItem(node *ast.Node, sourceFile *ast.SourceFile,
// VariableDeclarationList has no wrapping tokens
return -1
}
containingList := getContainingList(node, sourceFile)
containingList := GetContainingList(node, sourceFile)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know this func existed; I wonder if it's general enough to use for sig help. (No action needed, just opining)

if containingList != nil {
index := core.FindIndex(containingList.Nodes, func(e *ast.Node) bool { return e == node })
if index != -1 {
Expand Down Expand Up @@ -196,10 +196,10 @@ func deriveActualIndentationFromList(list *ast.NodeList, index int, sourceFile *

func findColumnForFirstNonWhitespaceCharacterInLine(line int, char int, sourceFile *ast.SourceFile, options *FormatCodeSettings) int {
lineStart := scanner.GetPositionOfLineAndCharacter(sourceFile, line, 0)
return findFirstNonWhitespaceColumn(lineStart, lineStart+char, sourceFile, options)
return FindFirstNonWhitespaceColumn(lineStart, lineStart+char, sourceFile, options)
}

func findFirstNonWhitespaceColumn(startPos int, endPos int, sourceFile *ast.SourceFile, options *FormatCodeSettings) int {
func FindFirstNonWhitespaceColumn(startPos int, endPos int, sourceFile *ast.SourceFile, options *FormatCodeSettings) int {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am suspicious of this function, given it appears to return the column in terms of runes? I'm not sure it walks the chars correctly either, hmm...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I was writing when I first went "wait, do we work in runes or in byte offsets for columns? Looks inconsistent right now". Both ways look wrong to some part of the stack around here because of that inconsistency.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we need everything to be UTF-8 byte offsets, until they hit the LS and get converted based on the client preferences.

The only exception might be symbol baselines (UTF-16 compat?), source maps, and maybe the API, and maybe printed diagnostics? (That's a lot of exceptions...)

Related, #1578

_, col := findFirstNonWhitespaceCharacterAndColumn(startPos, endPos, sourceFile, options)
return col
}
Expand Down Expand Up @@ -249,7 +249,7 @@ func getStartLineAndCharacterForNode(n *ast.Node, sourceFile *ast.SourceFile) (l
return scanner.GetLineAndCharacterOfPosition(sourceFile, scanner.GetTokenPosOfNode(n, sourceFile, false))
}

func getContainingList(node *ast.Node, sourceFile *ast.SourceFile) *ast.NodeList {
func GetContainingList(node *ast.Node, sourceFile *ast.SourceFile) *ast.NodeList {
if node.Parent == nil {
return nil
}
Expand Down Expand Up @@ -348,7 +348,7 @@ func getVisualListRange(node *ast.Node, list core.TextRange, sourceFile *ast.Sou
}

func getContainingListOrParentStart(parent *ast.Node, child *ast.Node, sourceFile *ast.SourceFile) (line int, character int) {
containingList := getContainingList(child, sourceFile)
containingList := GetContainingList(child, sourceFile)
var startPos int
if containingList != nil {
startPos = containingList.Loc.Pos()
Expand Down
4 changes: 2 additions & 2 deletions internal/format/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ func (w *formatSpanWorker) processChildNodes(
indentationOnListStartToken = w.indentationOnLastIndentedLine
} else {
startLinePosition := GetLineStartPositionForPosition(tokenInfo.token.Loc.Pos(), w.sourceFile)
indentationOnListStartToken = findFirstNonWhitespaceColumn(startLinePosition, tokenInfo.token.Loc.Pos(), w.sourceFile, w.formattingContext.Options)
indentationOnListStartToken = FindFirstNonWhitespaceColumn(startLinePosition, tokenInfo.token.Loc.Pos(), w.sourceFile, w.formattingContext.Options)
}

listDynamicIndentation = w.getDynamicIndentation(parent, parentStartLine, indentationOnListStartToken, w.formattingContext.Options.IndentSize)
Expand Down Expand Up @@ -578,7 +578,7 @@ func (w *formatSpanWorker) tryComputeIndentationForListItem(startPos int, endPos
} else {
startLine, _ := scanner.GetLineAndCharacterOfPosition(w.sourceFile, startPos)
startLinePosition := GetLineStartPositionForPosition(startPos, w.sourceFile)
column := findFirstNonWhitespaceColumn(startLinePosition, startPos, w.sourceFile, w.formattingContext.Options)
column := FindFirstNonWhitespaceColumn(startLinePosition, startPos, w.sourceFile, w.formattingContext.Options)
if startLine != parentStartLine || startPos == column {
// Use the base indent size if it is greater than
// the indentation of the inherited predecessor.
Expand Down
Loading
Loading