Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
154 changes: 82 additions & 72 deletions patches/typescript-go/0004-api-surface.patch

Large diffs are not rendered by default.

36 changes: 34 additions & 2 deletions patches/typescript-go/overlay/internal/api/arena.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"unsafe"

"github.com/microsoft/typescript-go/internal/checker"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
)

const (
Expand Down Expand Up @@ -818,10 +819,11 @@ func (a *arena) encodeExpandedParameters(v [][]SymbolID) {

const (
completionInfoRecordSize = 32
completionEntryRecordSize = 88
completionEntryRecordSize = 96
autoImportFixRecordSize = 36
)

// encodeCompletionEntry writes a CompletionEntryResponse record (88 bytes fixed).
// encodeCompletionEntry writes a CompletionEntryResponse record (96 bytes fixed).
func (a *arena) encodeCompletionEntry(off int, r *CompletionEntryResponse) {
a.u32(off+0, a.str(r.Name))
a.u32(off+4, a.str(r.ElementKind))
Expand Down Expand Up @@ -872,11 +874,15 @@ func (a *arena) encodeCompletionEntry(off int, r *CompletionEntryResponse) {
flags |= 16
}
var dataExportName, dataFileName, dataModuleSpecifier string
var autoImportFix *lsproto.AutoImportFix
if r.Data != nil {
flags |= 32
dataExportName = r.Data.ExportName
dataFileName = r.Data.FileName
dataModuleSpecifier = r.Data.ModuleSpecifier
if r.Data.TnbCompletionData != nil {
autoImportFix = r.Data.TnbCompletionData.AutoImport
}
}
if r.IsPackageJsonImport != nil && *r.IsPackageJsonImport {
flags |= 64
Expand All @@ -889,13 +895,39 @@ func (a *arena) encodeCompletionEntry(off int, r *CompletionEntryResponse) {
a.u32(off+72, a.str(dataExportName))
a.u32(off+76, a.str(dataFileName))
a.displayParts(off+80, r.SourceDisplay)
if autoImportFix != nil {
flags |= 128
a.b(off+64, flags)
fixOff := a.pack(autoImportFixRecordSize)
a.u32(off+88, uint32(fixOff))
a.u32(fixOff+0, uint32(autoImportFix.Kind))
a.u32(fixOff+4, a.str(autoImportFix.Name))
a.u32(fixOff+8, uint32(autoImportFix.ImportKind))
a.u32(fixOff+12, uint32(autoImportFix.AddAsTypeOnly))
a.u32(fixOff+16, uint32(autoImportFix.ImportIndex))
a.u32(fixOff+20, a.str(autoImportFix.NamespacePrefix))
var fixFlags uint32
if autoImportFix.UseRequire {
fixFlags |= 1
}
if autoImportFix.UsagePosition != nil {
fixFlags |= 2
a.u32(fixOff+24, autoImportFix.UsagePosition.Line)
a.u32(fixOff+28, autoImportFix.UsagePosition.Character)
}
a.u32(fixOff+32, fixFlags)
}
// offset map (u32 unless noted):
// 0 name / 4 elementKind / 8 kindModifiers / 12 sortText / 16 insertText
// 20 filterText / 24 source / 28 detail / 32 labelDetail.detail
// 36 labelDetail.description / 40 replacementStart / 44 replacementLength
// 48 commitCharacters (ptr,count) / 56 symbolPtr / 60 kindU32 / 64 flags u8
// 65-67 pad / 68 dataModuleSpecifier / 72 dataExportName / 76 dataFileName
// 80 sourceDisplay (ptr,count of {text,kind} records)
// 88 autoImportFix pointer / 92 pad. The packed fix record is:
// 0 kind / 4 name / 8 importKind / 12 addAsTypeOnly / 16 importIndex
// 20 namespacePrefix / 24 usageLine / 28 usageChar / 32 flags
// (useRequire, hasUsagePosition).
}

// encodeCompletionsResponse writes a CompletionInfoResponse record (32 bytes).
Expand Down
62 changes: 62 additions & 0 deletions patches/typescript-go/overlay/internal/api/completionresolve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package api

import (
"context"
"fmt"

"github.com/microsoft/typescript-go/internal/ls"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
)

type ResolveCompletionItemParams struct {
Snapshot SnapshotID `json:"snapshot"`
Project ProjectID `json:"project"`
Data lsproto.CompletionItemData `json:"data"`
Preferences lsutil.UserPreferences `json:"preferences"`
}

type ResolvedCompletionItemResponse struct {
Detail string `json:"detail"`
TextChanges []TextChangeResponse `json:"textChanges"`
}

type TextChangeResponse struct {
Start uint32 `json:"start"`
Length uint32 `json:"length"`
NewText string `json:"newText"`
}

func (s *Session) handleResolveCompletionItem(ctx context.Context, params *ResolveCompletionItemParams) (*ResolvedCompletionItemResponse, error) {
sd, err := s.getSnapshotData(params.Snapshot)
if err != nil {
return nil, err
}
program, err := sd.getProgram(params.Project)
if err != nil {
return nil, err
}
projectName := parseProjectHandle(params.Project)
proj := sd.snapshot.ProjectCollection.GetProjectByPath(projectName)
if proj == nil {
return nil, fmt.Errorf("%w: project %s not found", ErrClientError, projectName)
}
langSvc := ls.NewLanguageService(proj.ID(), program, &completionsPrefsHost{Host: sd.snapshot, prefs: params.Preferences}, "")
item, err := langSvc.ResolveCompletionItem(ctx, &lsproto.CompletionItem{Label: params.Data.Name}, &params.Data)
if err != nil {
return nil, err
}
response := &ResolvedCompletionItemResponse{TextChanges: []TextChangeResponse{}}
if item.Detail != nil {
response.Detail = *item.Detail
}
if item.AdditionalTextEdits != nil {
sourceFile := program.GetSourceFile(params.Data.FileName)
response.TextChanges = make([]TextChangeResponse, len(*item.AdditionalTextEdits))
for i, edit := range *item.AdditionalTextEdits {
start, length := lspRangeToSpan(sourceFile, edit.Range)
response.TextChanges[i] = TextChangeResponse{Start: start, Length: length, NewText: edit.NewText}
}
}
return response, nil
}
112 changes: 82 additions & 30 deletions patches/typescript/0001-tsgo-hooks.patch
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ index 6034c9dfc3..0c9a6178db 100644
let computedWithoutCacheCount = 0;
const fixes = flatMap(exportInfo, (exportInfo, i) => {
diff --git a/src/services/completions.ts b/src/services/completions.ts
index 28d29136da..e267aa7847 100644
index 28d29136da..d5ba59f622 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -175,6 +175,7 @@ import {
Expand Down Expand Up @@ -802,7 +802,27 @@ index 28d29136da..e267aa7847 100644
// expressions are value space (which includes the value namespaces)
return !!(allFlags & SymbolFlags.Value);
}
@@ -3588,6 +3638,11 @@ function getCompletionData(
@@ -3147,6 +3197,19 @@ function getCompletionEntryCodeActionsAndSourceDisplay(
return { codeActions: undefined, sourceDisplay: undefined };
}

+ if (data?.tnbCompletionData && (program as any).isTsgoBackedProgram === true) {
+ const result = (program as any).resolveCompletionItemTsgo(
+ sourceFile.fileName,
+ position,
+ name,
+ source,
+ data.tnbCompletionData,
+ formatContext.options,
+ preferences,
+ );
+ return { sourceDisplay: [textPart(data.moduleSpecifier!)], codeActions: [result] };
+ }
+
const checker = origin.isFromPackageJson ? host.getPackageJsonAutoImportProvider!()!.getTypeChecker() : program.getTypeChecker();
const { moduleSymbol } = origin;
const targetSymbol = checker.getMergedSymbol(skipAlias(symbol.exportSymbol || symbol, checker));
@@ -3588,6 +3651,11 @@ function getCompletionData(
let importSpecifierResolver: codefix.ImportSpecifierResolver | undefined;
const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
const symbolToSortTextMap: SymbolSortTextMap = [];
Expand All @@ -814,7 +834,7 @@ index 28d29136da..e267aa7847 100644
const seenPropertySymbols = new Set<SymbolId>();
const isTypeOnlyLocation = isTypeOnlyCompletion();
const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => {
@@ -4028,13 +4083,29 @@ function getCompletionData(
@@ -4028,13 +4096,29 @@ function getCompletionData(

symbols = concatenate(symbols, typeChecker.getSymbolsInScope(scopeNode, symbolMeanings));
Debug.assertEachIsDefined(symbols, "getSymbolsInScope() should all be defined");
Expand Down Expand Up @@ -845,7 +865,7 @@ index 28d29136da..e267aa7847 100644
}
if (typeOnlyAliasNeedsPromotion && !(symbol.flags & SymbolFlags.Value)) {
const typeOnlyAliasDeclaration = symbol.declarations && find(symbol.declarations, isTypeOnlyImportDeclaration);
@@ -4161,11 +4232,74 @@ function getCompletionData(
@@ -4161,11 +4245,74 @@ function getCompletionData(
const exportInfo = getExportInfoMap(sourceFile, host, program, preferences, cancellationToken);
const packageJsonAutoImportProvider = host.getPackageJsonAutoImportProvider?.();
const packageJsonFilter = detailsEntryId ? undefined : createPackageJsonImportFilter(sourceFile, preferences, host);
Expand Down Expand Up @@ -921,15 +941,15 @@ index 28d29136da..e267aa7847 100644
position,
preferences,
!!importStatementCompletion,
@@ -4232,6 +4366,7 @@ function getCompletionData(
@@ -4232,6 +4379,7 @@ function getCompletionData(
isDefaultExport,
moduleSymbol: exportInfo.moduleSymbol,
isFromPackageJson: exportInfo.isFromPackageJson,
+ targetFlags: exportInfo.targetFlags,
});
},
);
@@ -4240,20 +4375,7 @@ function getCompletionData(
@@ -4240,20 +4388,7 @@ function getCompletionData(
flags |= context.resolvedAny() ? CompletionInfoFlags.ResolvedModuleSpecifiers : 0;
flags |= context.resolvedBeyondLimit() ? CompletionInfoFlags.ResolvedModuleSpecifiersBeyondLimit : 0;
},
Expand All @@ -951,7 +971,7 @@ index 28d29136da..e267aa7847 100644
}

function pushAutoImportSymbol(symbol: Symbol, origin: SymbolOriginInfoResolvedExport | SymbolOriginInfoExport) {
@@ -4262,6 +4384,12 @@ function getCompletionData(
@@ -4262,6 +4397,12 @@ function getCompletionData(
// If an auto-importable symbol is available as a global, don't add the auto import
return;
}
Expand All @@ -964,33 +984,40 @@ index 28d29136da..e267aa7847 100644
symbolToOriginInfoMap[symbols.length] = origin;
symbolToSortTextMap[symbolId] = importStatementCompletion ? SortText.LocationPriority : SortText.AutoImportSuggestions;
symbols.push(symbol);
@@ -5383,7 +5511,7 @@ function getAutoImportSymbolFromCompletionEntryData(name: string, data: Completi
@@ -5382,14 +5523,25 @@ function getRelevantTokens(position: number, sourceFile: SourceFile): { contextT
function getAutoImportSymbolFromCompletionEntryData(name: string, data: CompletionEntryData, program: Program, host: LanguageServiceHost): { symbol: Symbol; origin: SymbolOriginInfoExport | SymbolOriginInfoResolvedExport; } | undefined {
const containingProgram = data.isPackageJsonImport ? host.getPackageJsonAutoImportProvider!()! : program;
const checker = containingProgram.getTypeChecker();
const moduleSymbol = data.ambientModuleName ? checker.tryFindAmbientModule(data.ambientModuleName) :
- const moduleSymbol = data.ambientModuleName ? checker.tryFindAmbientModule(data.ambientModuleName) :
- data.fileName ? checker.getMergedSymbol(Debug.checkDefined(containingProgram.getSourceFile(data.fileName)).symbol) :
+ data.fileName ? getModuleSymbolForCompletionEntryData(containingProgram, checker, data.fileName) :
undefined;

- undefined;
-
+ const tsgoProgram = containingProgram as Program & {
+ readonly isTsgoBackedProgram: true;
+ getCompletionEntryDataSymbols(fileName: string | undefined, ambientModuleName: string | undefined, exportName: string): { moduleSymbol: Symbol; symbol: Symbol; } | undefined;
+ };
+ let moduleSymbol: Symbol | undefined;
+ let symbol: Symbol | undefined;
+ if (tsgoProgram.isTsgoBackedProgram === true) {
+ const resolved = tsgoProgram.getCompletionEntryDataSymbols(data.fileName, data.ambientModuleName, data.exportName);
+ if (resolved) ({ moduleSymbol, symbol } = resolved);
+ }
+ else {
+ moduleSymbol = data.ambientModuleName ? checker.tryFindAmbientModule(data.ambientModuleName) :
+ data.fileName ? checker.getMergedSymbol(Debug.checkDefined(containingProgram.getSourceFile(data.fileName)).symbol) :
+ undefined;
+ symbol = moduleSymbol && (data.exportName === InternalSymbolName.ExportEquals
+ ? checker.resolveExternalModuleSymbol(moduleSymbol)
+ : checker.tryGetMemberInModuleExportsAndProperties(data.exportName, moduleSymbol));
+ }
if (!moduleSymbol) return undefined;
@@ -5396,6 +5524,16 @@ function getAutoImportSymbolFromCompletionEntryData(name: string, data: Completi
return { symbol, origin: completionEntryDataToSymbolOriginInfo(data, name, moduleSymbol) };
}

+function getModuleSymbolForCompletionEntryData(program: Program, checker: TypeChecker, fileName: string): Symbol {
+ const sourceFile = Debug.checkDefined(program.getSourceFile(fileName));
+ // tsgo-backed programs hand out a host-binder facade for sf.symbol whose
+ // object identity does not match the registry-proxied symbols inside
+ // export info maps; resolve through the bridged checker when it can answer
+ // (absent on stock checkers — falls back to stock behavior).
+ const forSourceFile = (checker as unknown as { getModuleSymbolForSourceFile?: (sourceFile: SourceFile) => Symbol | undefined; }).getModuleSymbolForSourceFile;
+ return checker.getMergedSymbol(forSourceFile?.(sourceFile) ?? Debug.checkDefined(sourceFile.symbol));
+}
+
interface CompletionEntryDisplayNameForSymbol {
readonly name: string;
readonly needsConvertPropertyAccess: boolean;
@@ -6089,6 +6227,23 @@ function isDeprecated(symbol: Symbol, checker: TypeChecker) {
- let symbol = data.exportName === InternalSymbolName.ExportEquals
- ? checker.resolveExternalModuleSymbol(moduleSymbol)
- : checker.tryGetMemberInModuleExportsAndProperties(data.exportName, moduleSymbol);
if (!symbol) return undefined;
const isDefaultExport = data.exportName === InternalSymbolName.Default;
symbol = isDefaultExport && getLocalSymbolForExportDefault(symbol) || symbol;
@@ -6089,6 +6241,23 @@ function isDeprecated(symbol: Symbol, checker: TypeChecker) {
return !!length(declarations) && every(declarations, isDeprecatedDeclaration);
}

Expand Down Expand Up @@ -2478,6 +2505,31 @@ index 9885093ed6..b0a6e5b278 100644
synchronizeHostData();
return Rename.getRenameInfo(program, getValidSourceFile(fileName), position, preferences || {});
}
diff --git a/src/services/types.ts b/src/services/types.ts
index 5329fe901b..e3f5dde954 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -1465,6 +1465,20 @@ export interface CompletionEntryDataAutoImport {
ambientModuleName?: string;
/** True if the export was found in the package.json AutoImportProvider */
isPackageJsonImport?: true;
+ /** @internal */
+ tnbCompletionData?: {
+ autoImport: {
+ kind?: number;
+ name?: string;
+ importKind: number;
+ useRequire?: boolean;
+ addAsTypeOnly: number;
+ moduleSpecifier?: string;
+ importIndex: number;
+ usagePosition?: { line: number; character: number; };
+ namespacePrefix?: string;
+ };
+ };
}

export interface CompletionEntryDataUnresolved extends CompletionEntryDataAutoImport {
diff --git a/src/services/utilities.ts b/src/services/utilities.ts
index bec7d4b266..927d0cff0e 100644
--- a/src/services/utilities.ts
Expand Down
Loading
Loading