mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into bug/42785
This commit is contained in:
Generated
+11
-11
@@ -345,12 +345,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.0.tgz",
|
||||
"integrity": "sha512-Ofusy7BwHkU7z4TNsVdf7wm5W3KR625KqlQj4AiWPnBvclmZU0Y2bVK8b8Mz8nW7sEX9TJcCdX6KeaincE/cLw==",
|
||||
"version": "4.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.1.tgz",
|
||||
"integrity": "sha512-T9YhQqpbO9Onmg+FYk09uci9pfChg8CZR9GBaPJWj+bDSzictW1xnU0NtCSSKKyrwvpW/opu7CtuDSs/HF1Syg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@octokit/types": "^6.11.0",
|
||||
"@octokit/types": "^6.11.1",
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
@@ -390,15 +390,15 @@
|
||||
}
|
||||
},
|
||||
"@octokit/rest": {
|
||||
"version": "18.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.3.0.tgz",
|
||||
"integrity": "sha512-R45oBVhnq3HAOGVtC6lHY7LX7TGWqbbcD4KvBHoT4QIjgJzfqKag3m/DUJwLnp8xrokz1spZmspTIXiDeQqJSA==",
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.3.1.tgz",
|
||||
"integrity": "sha512-g57ebsk7dtbLjiPBgEYDAiDTsyQM9kvlIt0J5UN6OSjG82K6fQQck6HXPpwcyNIDqbN7lIaWr3nsz56jBfI6qg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@octokit/core": "^3.2.3",
|
||||
"@octokit/plugin-paginate-rest": "^2.6.2",
|
||||
"@octokit/plugin-request-log": "^1.0.2",
|
||||
"@octokit/plugin-rest-endpoint-methods": "4.13.0"
|
||||
"@octokit/plugin-rest-endpoint-methods": "4.13.1"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
@@ -7887,9 +7887,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.12.8",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz",
|
||||
"integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==",
|
||||
"version": "3.13.0",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.0.tgz",
|
||||
"integrity": "sha512-TWYSWa9T2pPN4DIJYbU9oAjQx+5qdV5RUDxwARg8fmJZrD/V27Zj0JngW5xg1DFz42G0uDYl2XhzF6alSzD62w==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
|
||||
@@ -3266,7 +3266,7 @@ namespace ts {
|
||||
if (node.name) {
|
||||
setParent(node.name, node);
|
||||
}
|
||||
file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol)));
|
||||
file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations![0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol)));
|
||||
}
|
||||
symbol.exports!.set(prototypeSymbol.escapedName, prototypeSymbol);
|
||||
prototypeSymbol.parent = symbol;
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace ts {
|
||||
|
||||
// From ambient modules
|
||||
for (const ambientModule of program.getTypeChecker().getAmbientModules()) {
|
||||
if (ambientModule.declarations.length > 1) {
|
||||
if (ambientModule.declarations && ambientModule.declarations.length > 1) {
|
||||
addReferenceFromAmbientModule(ambientModule);
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,9 @@ namespace ts {
|
||||
return referencedFiles;
|
||||
|
||||
function addReferenceFromAmbientModule(symbol: Symbol) {
|
||||
if (!symbol.declarations) {
|
||||
return;
|
||||
}
|
||||
// Add any file other than our own as reference
|
||||
for (const declaration of symbol.declarations) {
|
||||
const declarationSourceFile = getSourceFileOfNode(declaration);
|
||||
|
||||
+293
-249
File diff suppressed because it is too large
Load Diff
@@ -292,7 +292,7 @@ namespace ts {
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function countWhere<T>(array: readonly T[], predicate: (x: T, i: number) => boolean): number {
|
||||
export function countWhere<T>(array: readonly T[] | undefined, predicate: (x: T, i: number) => boolean): number {
|
||||
let count = 0;
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
|
||||
@@ -385,7 +385,7 @@ namespace ts.moduleSpecifiers {
|
||||
}
|
||||
|
||||
function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol, checker: TypeChecker): string | undefined {
|
||||
const decl = find(moduleSymbol.declarations,
|
||||
const decl = moduleSymbol.declarations?.find(
|
||||
d => isNonGlobalAmbientModule(d) && (!isExternalModuleAugmentation(d) || !isExternalModuleNameRelative(getTextOfIdentifierOrLiteral(d.name)))
|
||||
) as (ModuleDeclaration & { name: StringLiteral }) | undefined;
|
||||
if (decl) {
|
||||
|
||||
@@ -206,13 +206,15 @@ namespace ts {
|
||||
}
|
||||
|
||||
function reportNonlocalAugmentation(containingFile: SourceFile, parentSymbol: Symbol, symbol: Symbol) {
|
||||
const primaryDeclaration = find(parentSymbol.declarations, d => getSourceFileOfNode(d) === containingFile)!;
|
||||
const primaryDeclaration = parentSymbol.declarations?.find(d => getSourceFileOfNode(d) === containingFile)!;
|
||||
const augmentingDeclarations = filter(symbol.declarations, d => getSourceFileOfNode(d) !== containingFile);
|
||||
for (const augmentations of augmentingDeclarations) {
|
||||
context.addDiagnostic(addRelatedInfo(
|
||||
createDiagnosticForNode(augmentations, Diagnostics.Declaration_augments_declaration_in_another_file_This_cannot_be_serialized),
|
||||
createDiagnosticForNode(primaryDeclaration, Diagnostics.This_is_the_declaration_being_augmented_Consider_moving_the_augmenting_declaration_into_the_same_file)
|
||||
));
|
||||
if (augmentingDeclarations) {
|
||||
for (const augmentations of augmentingDeclarations) {
|
||||
context.addDiagnostic(addRelatedInfo(
|
||||
createDiagnosticForNode(augmentations, Diagnostics.Declaration_augments_declaration_in_another_file_This_cannot_be_serialized),
|
||||
createDiagnosticForNode(primaryDeclaration, Diagnostics.This_is_the_declaration_being_augmented_Consider_moving_the_augmenting_declaration_into_the_same_file)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4165,6 +4165,7 @@ namespace ts {
|
||||
/* @internal */ createSymbol(flags: SymbolFlags, name: __String): TransientSymbol;
|
||||
/* @internal */ createIndexInfo(type: Type, isReadonly: boolean, declaration?: SignatureDeclaration): IndexInfo;
|
||||
/* @internal */ isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult;
|
||||
/* @internal */ tryFindAmbientModule(moduleName: string): Symbol | undefined;
|
||||
/* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol | undefined;
|
||||
|
||||
/* @internal */ getSymbolWalker(accept?: (symbol: Symbol) => boolean): SymbolWalker;
|
||||
@@ -4713,7 +4714,7 @@ namespace ts {
|
||||
export interface Symbol {
|
||||
flags: SymbolFlags; // Symbol flags
|
||||
escapedName: __String; // Name of symbol
|
||||
declarations: Declaration[]; // Declarations associated with this symbol
|
||||
declarations?: Declaration[]; // Declarations associated with this symbol
|
||||
valueDeclaration: Declaration; // First value declaration of the symbol
|
||||
members?: SymbolTable; // Class, interface or object literal instance members
|
||||
exports?: SymbolTable; // Module exports
|
||||
|
||||
@@ -767,7 +767,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getNonAugmentationDeclaration(symbol: Symbol) {
|
||||
return find(symbol.declarations, d => !isExternalModuleAugmentation(d) && !(isModuleDeclaration(d) && isGlobalScopeAugmentation(d)));
|
||||
return symbol.declarations?.find(d => !isExternalModuleAugmentation(d) && !(isModuleDeclaration(d) && isGlobalScopeAugmentation(d)));
|
||||
}
|
||||
|
||||
export function isEffectiveExternalModule(node: SourceFile, compilerOptions: CompilerOptions) {
|
||||
@@ -4888,7 +4888,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getLocalSymbolForExportDefault(symbol: Symbol) {
|
||||
if (!isExportDefaultSymbol(symbol)) return undefined;
|
||||
if (!isExportDefaultSymbol(symbol) || !symbol.declarations) return undefined;
|
||||
for (const decl of symbol.declarations) {
|
||||
if (decl.localSymbol) return decl.localSymbol;
|
||||
}
|
||||
@@ -4896,7 +4896,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isExportDefaultSymbol(symbol: Symbol): boolean {
|
||||
return symbol && length(symbol.declarations) > 0 && hasSyntacticModifier(symbol.declarations[0], ModifierFlags.Default);
|
||||
return symbol && length(symbol.declarations) > 0 && hasSyntacticModifier(symbol.declarations![0], ModifierFlags.Default);
|
||||
}
|
||||
|
||||
/** Return ".ts", ".d.ts", or ".tsx", if that is the extension. */
|
||||
@@ -5445,7 +5445,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getClassLikeDeclarationOfSymbol(symbol: Symbol): ClassLikeDeclaration | undefined {
|
||||
return find(symbol.declarations, isClassLike);
|
||||
return symbol.declarations?.find(isClassLike);
|
||||
}
|
||||
|
||||
export function getObjectFlags(type: Type): ObjectFlags {
|
||||
|
||||
@@ -205,9 +205,9 @@ namespace ts.server {
|
||||
isNewIdentifierLocation: false,
|
||||
entries: response.body!.map<CompletionEntry>(entry => { // TODO: GH#18217
|
||||
if (entry.replacementSpan !== undefined) {
|
||||
const { name, kind, kindModifiers, sortText, replacementSpan, hasAction, source, isRecommended } = entry;
|
||||
const { name, kind, kindModifiers, sortText, replacementSpan, hasAction, source, data, isRecommended } = entry;
|
||||
// TODO: GH#241
|
||||
const res: CompletionEntry = { name, kind, kindModifiers, sortText, replacementSpan: this.decodeSpan(replacementSpan, fileName), hasAction, source, isRecommended };
|
||||
const res: CompletionEntry = { name, kind, kindModifiers, sortText, replacementSpan: this.decodeSpan(replacementSpan, fileName), hasAction, source, data: data as any, isRecommended };
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -216,14 +216,13 @@ namespace ts.server {
|
||||
};
|
||||
}
|
||||
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, _options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined): CompletionEntryDetails {
|
||||
const args: protocol.CompletionDetailsRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), entryNames: [{ name: entryName, source }] };
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, _options: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, _preferences: UserPreferences | undefined, data: unknown): CompletionEntryDetails {
|
||||
const args: protocol.CompletionDetailsRequestArgs = { ...this.createFileLocationRequestArgs(fileName, position), entryNames: [{ name: entryName, source, data }] };
|
||||
|
||||
const request = this.processRequest<protocol.CompletionDetailsRequest>(CommandNames.CompletionDetails, args);
|
||||
const response = this.processResponse<protocol.CompletionDetailsResponse>(request);
|
||||
Debug.assert(response.body!.length === 1, "Unexpected length of completion details response body.");
|
||||
const convertedCodeActions = map(response.body![0].codeActions, ({ description, changes }) => ({ description, changes: this.convertChanges(changes, fileName) }));
|
||||
return { ...response.body![0], codeActions: convertedCodeActions };
|
||||
const request = this.processRequest<protocol.CompletionDetailsRequest>(CommandNames.CompletionDetailsFull, args);
|
||||
const response = this.processResponse<protocol.Response>(request);
|
||||
Debug.assert(response.body.length === 1, "Unexpected length of completion details response body.");
|
||||
return response.body[0];
|
||||
}
|
||||
|
||||
getCompletionEntrySymbol(_fileName: string, _position: number, _entryName: string): Symbol {
|
||||
|
||||
@@ -399,7 +399,7 @@ namespace FourSlash {
|
||||
}
|
||||
const memo = Utils.memoize(
|
||||
(_version: number, _active: string, _caret: number, _selectEnd: number, _marker: string, ...args: any[]) => (ls[key] as Function)(...args),
|
||||
(...args) => args.join("|,|")
|
||||
(...args) => args.map(a => a && typeof a === "object" ? JSON.stringify(a) : a).join("|,|")
|
||||
);
|
||||
proxy[key] = (...args: any[]) => memo(
|
||||
target.languageServiceAdapterHost.getScriptInfo(target.activeFile.fileName)!.version,
|
||||
@@ -867,7 +867,7 @@ namespace FourSlash {
|
||||
nameToEntries.set(entry.name, [entry]);
|
||||
}
|
||||
else {
|
||||
if (entries.some(e => e.source === entry.source)) {
|
||||
if (entries.some(e => e.source === entry.source && this.deepEqual(e.data, entry.data))) {
|
||||
this.raiseError(`Duplicate completions for ${entry.name}`);
|
||||
}
|
||||
entries.push(entry);
|
||||
@@ -885,8 +885,8 @@ namespace FourSlash {
|
||||
const name = typeof include === "string" ? include : include.name;
|
||||
const found = nameToEntries.get(name);
|
||||
if (!found) throw this.raiseError(`Includes: completion '${name}' not found.`);
|
||||
assert(found.length === 1, `Must use 'exact' for multiple completions with same name: '${name}'`);
|
||||
this.verifyCompletionEntry(ts.first(found), include);
|
||||
if (!found.length) throw this.raiseError(`Includes: no completions with name '${name}' remain unmatched.`);
|
||||
this.verifyCompletionEntry(found.shift()!, include);
|
||||
}
|
||||
}
|
||||
if (options.excludes) {
|
||||
@@ -933,7 +933,7 @@ namespace FourSlash {
|
||||
assert.equal(actual.sortText, expected.sortText || ts.Completions.SortText.LocationPriority, this.messageAtLastKnownMarker(`Actual entry: ${JSON.stringify(actual)}`));
|
||||
|
||||
if (expected.text !== undefined) {
|
||||
const actualDetails = ts.Debug.checkDefined(this.getCompletionEntryDetails(actual.name, actual.source), `No completion details available for name '${actual.name}' and source '${actual.source}'`);
|
||||
const actualDetails = ts.Debug.checkDefined(this.getCompletionEntryDetails(actual.name, actual.source, actual.data), `No completion details available for name '${actual.name}' and source '${actual.source}'`);
|
||||
assert.equal(ts.displayPartsToString(actualDetails.displayParts), expected.text, "Expected 'text' property to match 'displayParts' string");
|
||||
assert.equal(ts.displayPartsToString(actualDetails.documentation), expected.documentation || "", "Expected 'documentation' property to match 'documentation' display parts string");
|
||||
// TODO: GH#23587
|
||||
@@ -1003,8 +1003,8 @@ namespace FourSlash {
|
||||
|
||||
private verifySymbol(symbol: ts.Symbol, declarationRanges: Range[]) {
|
||||
const { declarations } = symbol;
|
||||
if (declarations.length !== declarationRanges.length) {
|
||||
this.raiseError(`Expected to get ${declarationRanges.length} declarations, got ${declarations.length}`);
|
||||
if (declarations?.length !== declarationRanges.length) {
|
||||
this.raiseError(`Expected to get ${declarationRanges.length} declarations, got ${declarations?.length}`);
|
||||
}
|
||||
|
||||
ts.zipWith(declarations, declarationRanges, (decl, range) => {
|
||||
@@ -1254,6 +1254,16 @@ namespace FourSlash {
|
||||
|
||||
}
|
||||
|
||||
private deepEqual(a: unknown, b: unknown) {
|
||||
try {
|
||||
this.assertObjectsEqual(a, b);
|
||||
return true;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) {
|
||||
const referencedSymbols = this.findReferencesAtCaret()!;
|
||||
|
||||
@@ -1281,11 +1291,11 @@ namespace FourSlash {
|
||||
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options);
|
||||
}
|
||||
|
||||
private getCompletionEntryDetails(entryName: string, source?: string, preferences?: ts.UserPreferences): ts.CompletionEntryDetails | undefined {
|
||||
private getCompletionEntryDetails(entryName: string, source: string | undefined, data: ts.CompletionEntryData | undefined, preferences?: ts.UserPreferences): ts.CompletionEntryDetails | undefined {
|
||||
if (preferences) {
|
||||
this.configure(preferences);
|
||||
}
|
||||
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source, preferences);
|
||||
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source, preferences, data);
|
||||
}
|
||||
|
||||
private getReferencesAtCaret() {
|
||||
@@ -2796,14 +2806,14 @@ namespace FourSlash {
|
||||
public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) {
|
||||
this.goToMarker(markerName);
|
||||
|
||||
const details = this.getCompletionEntryDetails(options.name, options.source, options.preferences);
|
||||
const details = this.getCompletionEntryDetails(options.name, options.source, options.data, options.preferences);
|
||||
if (!details) {
|
||||
const completions = this.getCompletionListAtCaret(options.preferences)?.entries;
|
||||
const matchingName = completions?.filter(e => e.name === options.name);
|
||||
const detailMessage = matchingName?.length
|
||||
? `\n Found ${matchingName.length} with name '${options.name}' from source(s) ${matchingName.map(e => `'${e.source}'`).join(", ")}.`
|
||||
: ` (In fact, there were no completions with name '${options.name}' at all.)`;
|
||||
return this.raiseError(`No completions were found for the given name, source, and preferences.` + detailMessage);
|
||||
return this.raiseError(`No completions were found for the given name, source/data, and preferences.` + detailMessage);
|
||||
}
|
||||
const codeActions = details.codeActions;
|
||||
if (codeActions?.length !== 1) {
|
||||
|
||||
@@ -1701,6 +1701,7 @@ namespace FourSlashInterface {
|
||||
export interface VerifyCompletionActionOptions extends NewContentOptions {
|
||||
name: string;
|
||||
source?: string;
|
||||
data?: ts.CompletionEntryData;
|
||||
description: string;
|
||||
preferences?: ts.UserPreferences;
|
||||
}
|
||||
|
||||
@@ -474,8 +474,8 @@ namespace Harness.LanguageService {
|
||||
getCompletionsAtPosition(fileName: string, position: number, preferences: ts.UserPreferences | undefined): ts.CompletionInfo {
|
||||
return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, preferences));
|
||||
}
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: ts.FormatCodeOptions | undefined, source: string | undefined, preferences: ts.UserPreferences | undefined): ts.CompletionEntryDetails {
|
||||
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(formatOptions), source, preferences));
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: ts.FormatCodeOptions | undefined, source: string | undefined, preferences: ts.UserPreferences | undefined, data: ts.CompletionEntryData | undefined): ts.CompletionEntryDetails {
|
||||
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(formatOptions), source, preferences, data));
|
||||
}
|
||||
getCompletionEntrySymbol(): ts.Symbol {
|
||||
throw new Error("getCompletionEntrySymbol not implemented across the shim layer.");
|
||||
|
||||
@@ -2169,6 +2169,7 @@ namespace ts.server.protocol {
|
||||
export interface CompletionEntryIdentifier {
|
||||
name: string;
|
||||
source?: string;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2255,6 +2256,12 @@ namespace ts.server.protocol {
|
||||
* in the project package.json.
|
||||
*/
|
||||
isPackageJsonImport?: true;
|
||||
/**
|
||||
* A property to be sent back to TS Server in the CompletionDetailsRequest, along with `name`,
|
||||
* that allows TS Server to look up the symbol represented by the completion item, disambiguating
|
||||
* items with the same name.
|
||||
*/
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+14
-6
@@ -1808,14 +1808,14 @@ namespace ts.server {
|
||||
if (kind === protocol.CommandTypes.CompletionsFull) return completions;
|
||||
|
||||
const prefix = args.prefix || "";
|
||||
const entries = mapDefined<CompletionEntry, protocol.CompletionEntry>(completions.entries, entry => {
|
||||
const entries = stableSort(mapDefined<CompletionEntry, protocol.CompletionEntry>(completions.entries, entry => {
|
||||
if (completions.isMemberCompletion || startsWith(entry.name.toLowerCase(), prefix.toLowerCase())) {
|
||||
const { name, kind, kindModifiers, sortText, insertText, replacementSpan, hasAction, source, isRecommended, isPackageJsonImport } = entry;
|
||||
const { name, kind, kindModifiers, sortText, insertText, replacementSpan, hasAction, source, isRecommended, isPackageJsonImport, data } = entry;
|
||||
const convertedSpan = replacementSpan ? toProtocolTextSpan(replacementSpan, scriptInfo) : undefined;
|
||||
// Use `hasAction || undefined` to avoid serializing `false`.
|
||||
return { name, kind, kindModifiers, sortText, insertText, replacementSpan: convertedSpan, hasAction: hasAction || undefined, source, isRecommended, isPackageJsonImport };
|
||||
return { name, kind, kindModifiers, sortText, insertText, replacementSpan: convertedSpan, hasAction: hasAction || undefined, source, isRecommended, isPackageJsonImport, data };
|
||||
}
|
||||
}).sort((a, b) => compareStringsCaseSensitiveUI(a.name, b.name));
|
||||
}), (a, b) => compareStringsCaseSensitiveUI(a.name, b.name));
|
||||
|
||||
if (kind === protocol.CommandTypes.Completions) {
|
||||
if (completions.metadata) (entries as WithMetadata<readonly protocol.CompletionEntry[]>).metadata = completions.metadata;
|
||||
@@ -1837,8 +1837,8 @@ namespace ts.server {
|
||||
const formattingOptions = project.projectService.getFormatCodeOptions(file);
|
||||
|
||||
const result = mapDefined(args.entryNames, entryName => {
|
||||
const { name, source } = typeof entryName === "string" ? { name: entryName, source: undefined } : entryName;
|
||||
return project.getLanguageService().getCompletionEntryDetails(file, position, name, formattingOptions, source, this.getPreferences(file));
|
||||
const { name, source, data } = typeof entryName === "string" ? { name: entryName, source: undefined, data: undefined } : entryName;
|
||||
return project.getLanguageService().getCompletionEntryDetails(file, position, name, formattingOptions, source, this.getPreferences(file), data ? cast(data, isCompletionEntryData) : undefined);
|
||||
});
|
||||
return simplifiedResult
|
||||
? result.map(details => ({ ...details, codeActions: map(details.codeActions, action => this.mapCodeAction(action)) }))
|
||||
@@ -3118,4 +3118,12 @@ namespace ts.server {
|
||||
isDefinition
|
||||
};
|
||||
}
|
||||
|
||||
function isCompletionEntryData(data: any): data is CompletionEntryData {
|
||||
return data === undefined || data && typeof data === "object"
|
||||
&& typeof data.exportName === "string"
|
||||
&& (data.fileName === undefined || typeof data.fileName === "string")
|
||||
&& (data.ambientModuleName === undefined || typeof data.ambientModuleName === "string"
|
||||
&& (data.isPackageJsonImport === undefined || typeof data.isPackageJsonImport === "boolean"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ namespace ts.CallHierarchy {
|
||||
const indices = indicesOf(symbol.declarations);
|
||||
const keys = map(symbol.declarations, decl => ({ file: decl.getSourceFile().fileName, pos: decl.pos }));
|
||||
indices.sort((a, b) => compareStringsCaseSensitive(keys[a].file, keys[b].file) || keys[a].pos - keys[b].pos);
|
||||
const sortedDeclarations = map(indices, i => symbol.declarations[i]);
|
||||
const sortedDeclarations = map(indices, i => symbol.declarations![i]);
|
||||
let lastDecl: CallHierarchyDeclaration | undefined;
|
||||
for (const decl of sortedDeclarations) {
|
||||
if (isValidCallHierarchyDeclaration(decl)) {
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace ts.codefix {
|
||||
// all static members are stored in the "exports" array of symbol
|
||||
if (symbol.exports) {
|
||||
symbol.exports.forEach(member => {
|
||||
if (member.name === "prototype") {
|
||||
if (member.name === "prototype" && member.declarations) {
|
||||
const firstDeclaration = member.declarations[0];
|
||||
// only one "x.prototype = { ... }" will pass
|
||||
if (member.declarations.length === 1 &&
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace ts.codefix {
|
||||
const superSymbol = superElement && checker.getSymbolAtLocation(superElement.expression);
|
||||
if (!superSymbol) break;
|
||||
const symbol = superSymbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(superSymbol) : superSymbol;
|
||||
const superDecl = find(symbol.declarations, isClassLike);
|
||||
const superDecl = symbol.declarations && find(symbol.declarations, isClassLike);
|
||||
if (!superDecl) break;
|
||||
res.push(superDecl);
|
||||
decl = superDecl;
|
||||
|
||||
+80
-24
@@ -48,6 +48,8 @@ namespace ts.Completions {
|
||||
moduleSymbol: Symbol;
|
||||
isDefaultExport: boolean;
|
||||
isFromPackageJson?: boolean;
|
||||
exportName: string;
|
||||
fileName?: string;
|
||||
}
|
||||
|
||||
function originIsThisType(origin: SymbolOriginInfo): boolean {
|
||||
@@ -104,7 +106,6 @@ namespace ts.Completions {
|
||||
export interface AutoImportSuggestion {
|
||||
symbol: Symbol;
|
||||
symbolName: string;
|
||||
skipFilter: boolean;
|
||||
origin: SymbolOriginInfoExport;
|
||||
}
|
||||
export interface ImportSuggestionsForFileCache {
|
||||
@@ -422,6 +423,7 @@ namespace ts.Completions {
|
||||
): CompletionEntry | undefined {
|
||||
let insertText: string | undefined;
|
||||
let replacementSpan = getReplacementSpanForContextToken(contextToken);
|
||||
let data: CompletionEntryData | undefined;
|
||||
|
||||
const insertQuestionDot = origin && originIsNullableMember(origin);
|
||||
const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess;
|
||||
@@ -472,6 +474,15 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (originIsExport(origin)) {
|
||||
data = {
|
||||
exportName: origin.exportName,
|
||||
fileName: origin.fileName,
|
||||
ambientModuleName: origin.fileName ? undefined : stripQuotes(origin.moduleSymbol.name),
|
||||
isPackageJsonImport: origin.isFromPackageJson ? true : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(drosen): Right now we just permit *all* semantic meanings when calling
|
||||
// 'getSymbolKind' which is permissible given that it is backwards compatible; but
|
||||
// really we should consider passing the meaning for the node so that we don't report
|
||||
@@ -491,6 +502,7 @@ namespace ts.Completions {
|
||||
insertText,
|
||||
replacementSpan,
|
||||
isPackageJsonImport: originIsPackageJsonImport(origin) || undefined,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -669,6 +681,7 @@ namespace ts.Completions {
|
||||
export interface CompletionEntryIdentifier {
|
||||
name: string;
|
||||
source?: string;
|
||||
data?: CompletionEntryData;
|
||||
}
|
||||
|
||||
export function getCompletionEntryDetails(
|
||||
@@ -877,7 +890,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
function isModuleSymbol(symbol: Symbol): boolean {
|
||||
return symbol.declarations.some(d => d.kind === SyntaxKind.SourceFile);
|
||||
return !!symbol.declarations?.some(d => d.kind === SyntaxKind.SourceFile);
|
||||
}
|
||||
|
||||
function getCompletionData(
|
||||
@@ -1227,7 +1240,7 @@ namespace ts.Completions {
|
||||
const isValidAccess: (symbol: Symbol) => boolean =
|
||||
isNamespaceName
|
||||
// At `namespace N.M/**/`, if this is the only declaration of `M`, don't include `M` as a completion.
|
||||
? symbol => !!(symbol.flags & SymbolFlags.Namespace) && !symbol.declarations.every(d => d.parent === node.parent)
|
||||
? symbol => !!(symbol.flags & SymbolFlags.Namespace) && !symbol.declarations?.every(d => d.parent === node.parent)
|
||||
: isRhsOfImportDeclaration ?
|
||||
// Any kind is allowed when dotting off namespace in internal import equals declaration
|
||||
symbol => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
|
||||
@@ -1485,25 +1498,35 @@ namespace ts.Completions {
|
||||
|
||||
if (shouldOfferImportCompletions()) {
|
||||
const lowerCaseTokenText = previousToken && isIdentifier(previousToken) ? previousToken.text.toLowerCase() : "";
|
||||
const autoImportSuggestions = getSymbolsFromOtherSourceFileExports(program.getCompilerOptions().target!, host);
|
||||
if (!detailsEntryId && importSuggestionsCache) {
|
||||
importSuggestionsCache.set(sourceFile.fileName, autoImportSuggestions, host.getProjectVersion && host.getProjectVersion());
|
||||
if (detailsEntryId?.data) {
|
||||
const autoImport = getAutoImportSymbolFromCompletionEntryData(detailsEntryId.data);
|
||||
if (autoImport) {
|
||||
const symbolId = getSymbolId(autoImport.symbol);
|
||||
symbols.push(autoImport.symbol);
|
||||
symbolToOriginInfoMap[symbolId] = autoImport.origin;
|
||||
}
|
||||
}
|
||||
autoImportSuggestions.forEach(({ symbol, symbolName, skipFilter, origin }) => {
|
||||
if (detailsEntryId) {
|
||||
if (detailsEntryId.source && stripQuotes(origin.moduleSymbol.name) !== detailsEntryId.source) {
|
||||
else {
|
||||
const autoImportSuggestions = getSymbolsFromOtherSourceFileExports(program.getCompilerOptions().target!, host);
|
||||
if (!detailsEntryId && importSuggestionsCache) {
|
||||
importSuggestionsCache.set(sourceFile.fileName, autoImportSuggestions, host.getProjectVersion && host.getProjectVersion());
|
||||
}
|
||||
autoImportSuggestions.forEach(({ symbol, symbolName, origin }) => {
|
||||
if (detailsEntryId) {
|
||||
if (detailsEntryId.source && stripQuotes(origin.moduleSymbol.name) !== detailsEntryId.source) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!stringContainsCharactersInOrder(symbolName.toLowerCase(), lowerCaseTokenText)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!skipFilter && !stringContainsCharactersInOrder(symbolName.toLowerCase(), lowerCaseTokenText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const symbolId = getSymbolId(symbol);
|
||||
symbols.push(symbol);
|
||||
symbolToOriginInfoMap[symbolId] = origin;
|
||||
symbolToSortTextMap[symbolId] = SortText.AutoImportSuggestions;
|
||||
});
|
||||
const symbolId = getSymbolId(symbol);
|
||||
symbols.push(symbol);
|
||||
symbolToOriginInfoMap[symbolId] = origin;
|
||||
symbolToSortTextMap[symbolId] = SortText.AutoImportSuggestions;
|
||||
});
|
||||
}
|
||||
}
|
||||
filterGlobalCompletion(symbols);
|
||||
}
|
||||
@@ -1673,7 +1696,7 @@ namespace ts.Completions {
|
||||
const seenResolvedModules = new Map<SymbolId, true>();
|
||||
const results = createMultiMap<SymbolId, AutoImportSuggestion>();
|
||||
|
||||
codefix.forEachExternalModuleToImportFrom(program, host, sourceFile, !detailsEntryId, /*useAutoImportProvider*/ true, (moduleSymbol, _, program, isFromPackageJson) => {
|
||||
codefix.forEachExternalModuleToImportFrom(program, host, sourceFile, !detailsEntryId, /*useAutoImportProvider*/ true, (moduleSymbol, file, program, isFromPackageJson) => {
|
||||
// Perf -- ignore other modules if this is a request for details
|
||||
if (detailsEntryId && detailsEntryId.source && stripQuotes(moduleSymbol.name) !== detailsEntryId.source) {
|
||||
return;
|
||||
@@ -1689,7 +1712,7 @@ namespace ts.Completions {
|
||||
// Don't add another completion for `export =` of a symbol that's already global.
|
||||
// So in `declare namespace foo {} declare module "foo" { export = foo; }`, there will just be the global completion for `foo`.
|
||||
if (resolvedModuleSymbol !== moduleSymbol && every(resolvedModuleSymbol.declarations, isNonGlobalDeclaration)) {
|
||||
pushSymbol(resolvedModuleSymbol, moduleSymbol, isFromPackageJson, /*skipFilter*/ true);
|
||||
pushSymbol(resolvedModuleSymbol, InternalSymbolName.ExportEquals, moduleSymbol, file, isFromPackageJson);
|
||||
}
|
||||
|
||||
for (const symbol of typeChecker.getExportsAndPropertiesOfModule(moduleSymbol)) {
|
||||
@@ -1698,14 +1721,14 @@ namespace ts.Completions {
|
||||
continue;
|
||||
}
|
||||
|
||||
pushSymbol(symbol, moduleSymbol, isFromPackageJson, /*skipFilter*/ false);
|
||||
pushSymbol(symbol, symbol.name, moduleSymbol, file, isFromPackageJson);
|
||||
}
|
||||
});
|
||||
|
||||
log(`getSymbolsFromOtherSourceFileExports: ${timestamp() - startTime}`);
|
||||
return flatten(arrayFrom(results.values()));
|
||||
|
||||
function pushSymbol(symbol: Symbol, moduleSymbol: Symbol, isFromPackageJson: boolean, skipFilter: boolean) {
|
||||
function pushSymbol(symbol: Symbol, exportName: string, moduleSymbol: Symbol, file: SourceFile | undefined, isFromPackageJson: boolean) {
|
||||
const isDefaultExport = symbol.escapedName === InternalSymbolName.Default;
|
||||
const nonLocalSymbol = symbol;
|
||||
if (isDefaultExport) {
|
||||
@@ -1718,17 +1741,50 @@ namespace ts.Completions {
|
||||
const symbolName = getNameForExportedSymbol(symbol, target);
|
||||
const existingSuggestions = results.get(getSymbolId(original));
|
||||
if (!some(existingSuggestions, s => s.symbolName === symbolName && moduleSymbolsAreDuplicateOrigins(moduleSymbol, s.origin.moduleSymbol))) {
|
||||
const origin: SymbolOriginInfoExport = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport, isFromPackageJson };
|
||||
const origin: SymbolOriginInfoExport = {
|
||||
kind: SymbolOriginInfoKind.Export,
|
||||
moduleSymbol,
|
||||
isDefaultExport,
|
||||
isFromPackageJson,
|
||||
exportName,
|
||||
fileName: file?.fileName
|
||||
};
|
||||
results.add(getSymbolId(original), {
|
||||
symbol,
|
||||
symbolName,
|
||||
origin,
|
||||
skipFilter,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getAutoImportSymbolFromCompletionEntryData(data: CompletionEntryData): { symbol: Symbol, origin: SymbolOriginInfoExport } | undefined {
|
||||
const containingProgram = data.isPackageJsonImport ? host.getPackageJsonAutoImportProvider!()! : program;
|
||||
const checker = containingProgram.getTypeChecker();
|
||||
const moduleSymbol =
|
||||
data.ambientModuleName ? checker.tryFindAmbientModule(data.ambientModuleName) :
|
||||
data.fileName ? checker.getMergedSymbol(Debug.checkDefined(containingProgram.getSourceFile(data.fileName)).symbol) :
|
||||
undefined;
|
||||
|
||||
if (!moduleSymbol) return undefined;
|
||||
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;
|
||||
return {
|
||||
symbol,
|
||||
origin: {
|
||||
kind: SymbolOriginInfoKind.Export,
|
||||
moduleSymbol,
|
||||
isDefaultExport,
|
||||
exportName: data.exportName,
|
||||
fileName: data.fileName,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a module symbol is redundant with another for purposes of offering
|
||||
* auto-import completions for exports of the same symbol. Exports of the same symbol
|
||||
|
||||
@@ -839,7 +839,7 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
const exported = symbol.exports!.get(InternalSymbolName.ExportEquals);
|
||||
if (exported) {
|
||||
if (exported?.declarations) {
|
||||
for (const decl of exported.declarations) {
|
||||
const sourceFile = decl.getSourceFile();
|
||||
if (sourceFilesSet.has(sourceFile.fileName)) {
|
||||
@@ -916,7 +916,7 @@ namespace ts.FindAllReferences {
|
||||
const result: SymbolAndEntries[] = [];
|
||||
const state = new State(sourceFiles, sourceFilesSet, node ? getSpecialSearchKind(node) : SpecialSearchKind.None, checker, cancellationToken, searchMeaning, options, result);
|
||||
|
||||
const exportSpecifier = !isForRenameWithPrefixAndSuffixText(options) ? undefined : find(symbol.declarations, isExportSpecifier);
|
||||
const exportSpecifier = !isForRenameWithPrefixAndSuffixText(options) || !symbol.declarations ? undefined : find(symbol.declarations, isExportSpecifier);
|
||||
if (exportSpecifier) {
|
||||
// When renaming at an export specifier, rename the export and not the thing being exported.
|
||||
getReferencesAtExportSpecifier(exportSpecifier.name, symbol, exportSpecifier, state.createSearch(node, originalSymbol, /*comingFrom*/ undefined), state, /*addReferencesHere*/ true, /*alwaysGetReferences*/ true);
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace ts {
|
||||
importLiteral => {
|
||||
const importedModuleSymbol = program.getTypeChecker().getSymbolAtLocation(importLiteral);
|
||||
// No need to update if it's an ambient module^M
|
||||
if (importedModuleSymbol && importedModuleSymbol.declarations.some(d => isAmbientModule(d))) return undefined;
|
||||
if (importedModuleSymbol?.declarations && importedModuleSymbol.declarations.some(d => isAmbientModule(d))) return undefined;
|
||||
|
||||
const toImport = oldFromNew !== undefined
|
||||
// If we're at the new location (file was already renamed), need to redo module resolution starting from the old location.
|
||||
@@ -185,7 +185,7 @@ namespace ts {
|
||||
): ToImport | undefined {
|
||||
if (importedModuleSymbol) {
|
||||
// `find` should succeed because we checked for ambient modules before calling this function.
|
||||
const oldFileName = find(importedModuleSymbol.declarations, isSourceFile)!.fileName;
|
||||
const oldFileName = find(importedModuleSymbol.declarations!, isSourceFile)!.fileName;
|
||||
const newFileName = oldToNew(oldFileName);
|
||||
return newFileName === undefined ? { newFileName: oldFileName, updated: false } : { newFileName, updated: true };
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace ts.GoToDefinition {
|
||||
// assignment. This case and others are handled by the following code.
|
||||
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration);
|
||||
const definitions = shorthandSymbol ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node)) : emptyArray;
|
||||
const definitions = shorthandSymbol?.declarations ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node)) : emptyArray;
|
||||
return concatenate(definitions, getDefinitionFromObjectLiteralElement(typeChecker, node) || emptyArray);
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace ts.GoToDefinition {
|
||||
// get the aliased symbol instead. This allows for goto def on an import e.g.
|
||||
// import {A, B} from "mod";
|
||||
// to jump to the implementation directly.
|
||||
if (symbol && symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) {
|
||||
if (symbol?.declarations && symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) {
|
||||
const aliased = checker.getAliasedSymbol(symbol);
|
||||
if (aliased.declarations) {
|
||||
return aliased;
|
||||
@@ -254,7 +254,7 @@ namespace ts.GoToDefinition {
|
||||
// Applicable only if we are in a new expression, or we are on a constructor declaration
|
||||
// and in either case the symbol has a construct signature definition, i.e. class
|
||||
if (symbol.flags & SymbolFlags.Class && !(symbol.flags & (SymbolFlags.Function | SymbolFlags.Variable)) && (isNewExpressionTarget(node) || node.kind === SyntaxKind.ConstructorKeyword)) {
|
||||
const cls = find(filteredDeclarations, isClassLike) || Debug.fail("Expected declaration to have at least one class-like declaration");
|
||||
const cls = find(filteredDeclarations!, isClassLike) || Debug.fail("Expected declaration to have at least one class-like declaration");
|
||||
return getSignatureDefinition(cls.members, /*selectConstructors*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,9 +62,11 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
// Module augmentations may use this module's exports without importing it.
|
||||
for (const decl of exportingModuleSymbol.declarations) {
|
||||
if (isExternalModuleAugmentation(decl) && sourceFilesSet.has(decl.getSourceFile().fileName)) {
|
||||
addIndirectUser(decl);
|
||||
if (exportingModuleSymbol.declarations) {
|
||||
for (const decl of exportingModuleSymbol.declarations) {
|
||||
if (isExternalModuleAugmentation(decl) && sourceFilesSet.has(decl.getSourceFile().fileName)) {
|
||||
addIndirectUser(decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +470,7 @@ namespace ts.FindAllReferences {
|
||||
if (parent.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
// When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use.
|
||||
// So check that we are at the declaration.
|
||||
return symbol.declarations.some(d => d === parent) && isBinaryExpression(grandParent)
|
||||
return symbol.declarations?.some(d => d === parent) && isBinaryExpression(grandParent)
|
||||
? getSpecialPropertyExport(grandParent, /*useLhsSymbol*/ false)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@@ -201,10 +201,10 @@ ${newComment.split("\n").map(c => ` * ${c}`).join("\n")}
|
||||
if (!every(decls, d => getSourceFileOfNode(d) === file)) {
|
||||
return;
|
||||
}
|
||||
if (!isConvertableSignatureDeclaration(decls[0])) {
|
||||
if (!isConvertableSignatureDeclaration(decls![0])) {
|
||||
return;
|
||||
}
|
||||
const kindOne = decls[0].kind;
|
||||
const kindOne = decls![0].kind;
|
||||
if (!every(decls, d => d.kind === kindOne)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ namespace ts.refactor.convertParamsToDestructuredObject {
|
||||
if (isObjectLiteralExpression(functionDeclaration.parent)) {
|
||||
const contextualSymbol = getSymbolForContextualType(functionDeclaration.name, checker);
|
||||
// don't offer the refactor when there are multiple signatures since we won't know which ones the user wants to change
|
||||
return contextualSymbol?.declarations.length === 1 && isSingleImplementation(functionDeclaration, checker);
|
||||
return contextualSymbol?.declarations?.length === 1 && isSingleImplementation(functionDeclaration, checker);
|
||||
}
|
||||
return isSingleImplementation(functionDeclaration, checker);
|
||||
case SyntaxKind.Constructor:
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace ts.refactor {
|
||||
if (isTypeReferenceNode(node)) {
|
||||
if (isIdentifier(node.typeName)) {
|
||||
const symbol = checker.resolveName(node.typeName.text, node.typeName, SymbolFlags.TypeParameter, /* excludeGlobals */ true);
|
||||
if (symbol) {
|
||||
if (symbol?.declarations) {
|
||||
const declaration = cast(first(symbol.declarations), isTypeParameterDeclaration);
|
||||
if (rangeContainsSkipTrivia(statement, declaration, file) && !rangeContainsSkipTrivia(selection, declaration, file)) {
|
||||
pushIfUnique(result, declaration);
|
||||
|
||||
@@ -443,6 +443,9 @@ namespace ts.refactor {
|
||||
const oldFileNamedImports: string[] = [];
|
||||
const markSeenTop = nodeSeenTracker(); // Needed because multiple declarations may appear in `const x = 0, y = 1;`.
|
||||
newFileImportsFromOldFile.forEach(symbol => {
|
||||
if (!symbol.declarations) {
|
||||
return;
|
||||
}
|
||||
for (const decl of symbol.declarations) {
|
||||
if (!isTopLevelDeclaration(decl)) continue;
|
||||
const name = nameOfTopLevelDeclaration(decl);
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace ts.Rename {
|
||||
return getRenameInfoError(Diagnostics.You_cannot_rename_a_module_via_a_global_import);
|
||||
}
|
||||
|
||||
const moduleSourceFile = find(moduleSymbol.declarations, isSourceFile);
|
||||
const moduleSourceFile = moduleSymbol.declarations && find(moduleSymbol.declarations, isSourceFile);
|
||||
if (!moduleSourceFile) return undefined;
|
||||
const withoutIndex = endsWith(node.text, "/index") || endsWith(node.text, "/index.js") ? undefined : tryRemoveSuffix(removeFileExtension(moduleSourceFile.fileName), "/index");
|
||||
const name = withoutIndex === undefined ? moduleSourceFile.fileName : withoutIndex;
|
||||
|
||||
@@ -1558,14 +1558,14 @@ namespace ts {
|
||||
options.triggerCharacter);
|
||||
}
|
||||
|
||||
function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = emptyOptions): CompletionEntryDetails | undefined {
|
||||
function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = emptyOptions, data?: CompletionEntryData): CompletionEntryDetails | undefined {
|
||||
synchronizeHostData();
|
||||
return Completions.getCompletionEntryDetails(
|
||||
program,
|
||||
log,
|
||||
getValidSourceFile(fileName),
|
||||
position,
|
||||
{ name, source },
|
||||
{ name, source, data },
|
||||
host,
|
||||
(formattingOptions && formatting.getFormatContext(formattingOptions, host))!, // TODO: GH#18217
|
||||
preferences,
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace ts {
|
||||
getEncodedSemanticClassifications(fileName: string, start: number, length: number, format?: SemanticClassificationFormat): string;
|
||||
|
||||
getCompletionsAtPosition(fileName: string, position: number, preferences: UserPreferences | undefined): string;
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined): string;
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined, data: CompletionEntryData | undefined): string;
|
||||
|
||||
getQuickInfoAtPosition(fileName: string, position: number): string;
|
||||
|
||||
@@ -962,12 +962,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
/** Get a string based representation of a completion list entry details */
|
||||
public getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined) {
|
||||
public getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: string/*Services.FormatCodeOptions*/ | undefined, source: string | undefined, preferences: UserPreferences | undefined, data: CompletionEntryData | undefined) {
|
||||
return this.forwardJSONCall(
|
||||
`getCompletionEntryDetails('${fileName}', ${position}, '${entryName}')`,
|
||||
() => {
|
||||
const localOptions: FormatCodeOptions = formatOptions === undefined ? undefined : JSON.parse(formatOptions);
|
||||
return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions, source, preferences);
|
||||
return this.languageService.getCompletionEntryDetails(fileName, position, entryName, localOptions, source, preferences, data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -401,8 +401,8 @@ namespace ts.SymbolDisplay {
|
||||
if (symbolFlags & SymbolFlags.EnumMember) {
|
||||
symbolKind = ScriptElementKind.enumMemberElement;
|
||||
addPrefixForAnyFunctionOrVar(symbol, "enum member");
|
||||
const declaration = symbol.declarations[0];
|
||||
if (declaration.kind === SyntaxKind.EnumMember) {
|
||||
const declaration = symbol.declarations?.[0];
|
||||
if (declaration?.kind === SyntaxKind.EnumMember) {
|
||||
const constantValue = typeChecker.getConstantValue(<EnumMember>declaration);
|
||||
if (constantValue !== undefined) {
|
||||
displayParts.push(spacePart());
|
||||
@@ -446,22 +446,24 @@ namespace ts.SymbolDisplay {
|
||||
}
|
||||
}
|
||||
|
||||
switch (symbol.declarations[0].kind) {
|
||||
case SyntaxKind.NamespaceExportDeclaration:
|
||||
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword));
|
||||
break;
|
||||
case SyntaxKind.ExportAssignment:
|
||||
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(keywordPart((symbol.declarations[0] as ExportAssignment).isExportEquals ? SyntaxKind.EqualsToken : SyntaxKind.DefaultKeyword));
|
||||
break;
|
||||
case SyntaxKind.ExportSpecifier:
|
||||
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
|
||||
break;
|
||||
default:
|
||||
displayParts.push(keywordPart(SyntaxKind.ImportKeyword));
|
||||
if (symbol.declarations) {
|
||||
switch (symbol.declarations[0].kind) {
|
||||
case SyntaxKind.NamespaceExportDeclaration:
|
||||
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword));
|
||||
break;
|
||||
case SyntaxKind.ExportAssignment:
|
||||
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
|
||||
displayParts.push(spacePart());
|
||||
displayParts.push(keywordPart((symbol.declarations[0] as ExportAssignment).isExportEquals ? SyntaxKind.EqualsToken : SyntaxKind.DefaultKeyword));
|
||||
break;
|
||||
case SyntaxKind.ExportSpecifier:
|
||||
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
|
||||
break;
|
||||
default:
|
||||
displayParts.push(keywordPart(SyntaxKind.ImportKeyword));
|
||||
}
|
||||
}
|
||||
displayParts.push(spacePart());
|
||||
addFullSymbolName(symbol);
|
||||
@@ -556,7 +558,7 @@ namespace ts.SymbolDisplay {
|
||||
// For some special property access expressions like `exports.foo = foo` or `module.exports.foo = foo`
|
||||
// there documentation comments might be attached to the right hand side symbol of their declarations.
|
||||
// The pattern of such special property access is that the parent symbol is the symbol of the file.
|
||||
if (symbol.parent && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) {
|
||||
if (symbol.parent && symbol.declarations && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) {
|
||||
for (const declaration of symbol.declarations) {
|
||||
if (!declaration.parent || declaration.parent.kind !== SyntaxKind.BinaryExpression) {
|
||||
continue;
|
||||
|
||||
+27
-2
@@ -424,10 +424,11 @@ namespace ts {
|
||||
*
|
||||
* @param fileName The path to the file
|
||||
* @param position A zero based index of the character where you want the entries
|
||||
* @param entryName The name from an existing completion which came from `getCompletionsAtPosition`
|
||||
* @param entryName The `name` from an existing completion which came from `getCompletionsAtPosition`
|
||||
* @param formatOptions How should code samples in the completions be formatted, can be undefined for backwards compatibility
|
||||
* @param source Source code for the current file, can be undefined for backwards compatibility
|
||||
* @param source `source` property from the completion entry
|
||||
* @param preferences User settings, can be undefined for backwards compatibility
|
||||
* @param data `data` property from the completion entry
|
||||
*/
|
||||
getCompletionEntryDetails(
|
||||
fileName: string,
|
||||
@@ -436,6 +437,7 @@ namespace ts {
|
||||
formatOptions: FormatCodeOptions | FormatCodeSettings | undefined,
|
||||
source: string | undefined,
|
||||
preferences: UserPreferences | undefined,
|
||||
data: CompletionEntryData | undefined,
|
||||
): CompletionEntryDetails | undefined;
|
||||
|
||||
getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol | undefined;
|
||||
@@ -1136,6 +1138,20 @@ namespace ts {
|
||||
entries: CompletionEntry[];
|
||||
}
|
||||
|
||||
export interface CompletionEntryData {
|
||||
/** The file name declaring the export's module symbol, if it was an external module */
|
||||
fileName?: string;
|
||||
/** The module name (with quotes stripped) of the export's module symbol, if it was an ambient module */
|
||||
ambientModuleName?: string;
|
||||
/** True if the export was found in the package.json AutoImportProvider */
|
||||
isPackageJsonImport?: true;
|
||||
/**
|
||||
* The name of the property or export in the module's symbol table. Differs from the completion name
|
||||
* in the case of InternalSymbolName.ExportEquals and InternalSymbolName.Default.
|
||||
*/
|
||||
exportName: string;
|
||||
}
|
||||
|
||||
// see comments in protocol.ts
|
||||
export interface CompletionEntry {
|
||||
name: string;
|
||||
@@ -1154,6 +1170,15 @@ namespace ts {
|
||||
isRecommended?: true;
|
||||
isFromUncheckedFile?: true;
|
||||
isPackageJsonImport?: true;
|
||||
/**
|
||||
* A property to be sent back to TS Server in the CompletionDetailsRequest, along with `name`,
|
||||
* that allows TS Server to look up the symbol represented by the completion item, disambiguating
|
||||
* items with the same name. Currently only defined for auto-import completions, but the type is
|
||||
* `unknown` in the protocol, so it can be changed as needed to support other kinds of completions.
|
||||
* The presence of this property should generally not be used to assume that this completion entry
|
||||
* is an auto-import.
|
||||
*/
|
||||
data?: CompletionEntryData;
|
||||
}
|
||||
|
||||
export interface CompletionEntryDetails {
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace ts {
|
||||
placeOpenBraceOnNewLineForControlBlocks: false,
|
||||
};
|
||||
verifyOperationCancelledAfter(file, 1, service => // The LS doesn't do any top-level checks on the token for completion entry details, so the first check is within the checker
|
||||
service.getCompletionEntryDetails("file.ts", file.lastIndexOf("f"), "foo", options, /*content*/ undefined, {})!, r => assert.exists(r.displayParts)
|
||||
service.getCompletionEntryDetails("file.ts", file.lastIndexOf("f"), "foo", options, /*source*/ undefined, {}, /*data*/ undefined)!, r => assert.exists(r.displayParts)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace ts.projectSystem {
|
||||
isPackageJsonImport: undefined,
|
||||
sortText: Completions.SortText.AutoImportSuggestions,
|
||||
source: "/a",
|
||||
data: { exportName: "foo", fileName: "/a.ts", ambientModuleName: undefined, isPackageJsonImport: undefined }
|
||||
};
|
||||
assert.deepEqual<protocol.CompletionInfo | undefined>(response, {
|
||||
isGlobalCompletion: true,
|
||||
@@ -50,7 +51,7 @@ namespace ts.projectSystem {
|
||||
|
||||
const detailsRequestArgs: protocol.CompletionDetailsRequestArgs = {
|
||||
...requestLocation,
|
||||
entryNames: [{ name: "foo", source: "/a" }],
|
||||
entryNames: [{ name: "foo", source: "/a", data: { exportName: "foo", fileName: "/a.ts" } }],
|
||||
};
|
||||
|
||||
const detailsResponse = executeSessionRequest<protocol.CompletionDetailsRequest, protocol.CompletionDetailsResponse>(session, protocol.CommandTypes.CompletionDetails, detailsRequestArgs);
|
||||
|
||||
@@ -70,7 +70,8 @@ import { something } from "something";
|
||||
isPackageJsonImport: undefined,
|
||||
isRecommended: undefined,
|
||||
replacementSpan: undefined,
|
||||
source: undefined
|
||||
source: undefined,
|
||||
data: undefined,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
+34
-4
@@ -2408,7 +2408,7 @@ declare namespace ts {
|
||||
export interface Symbol {
|
||||
flags: SymbolFlags;
|
||||
escapedName: __String;
|
||||
declarations: Declaration[];
|
||||
declarations?: Declaration[];
|
||||
valueDeclaration: Declaration;
|
||||
members?: SymbolTable;
|
||||
exports?: SymbolTable;
|
||||
@@ -5528,12 +5528,13 @@ declare namespace ts {
|
||||
*
|
||||
* @param fileName The path to the file
|
||||
* @param position A zero based index of the character where you want the entries
|
||||
* @param entryName The name from an existing completion which came from `getCompletionsAtPosition`
|
||||
* @param entryName The `name` from an existing completion which came from `getCompletionsAtPosition`
|
||||
* @param formatOptions How should code samples in the completions be formatted, can be undefined for backwards compatibility
|
||||
* @param source Source code for the current file, can be undefined for backwards compatibility
|
||||
* @param source `source` property from the completion entry
|
||||
* @param preferences User settings, can be undefined for backwards compatibility
|
||||
* @param data `data` property from the completion entry
|
||||
*/
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences | undefined): CompletionEntryDetails | undefined;
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences | undefined, data: CompletionEntryData | undefined): CompletionEntryDetails | undefined;
|
||||
getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol | undefined;
|
||||
/**
|
||||
* Gets semantic information about the identifier at a particular position in a
|
||||
@@ -6093,6 +6094,19 @@ declare namespace ts {
|
||||
isNewIdentifierLocation: boolean;
|
||||
entries: CompletionEntry[];
|
||||
}
|
||||
interface CompletionEntryData {
|
||||
/** The file name declaring the export's module symbol, if it was an external module */
|
||||
fileName?: string;
|
||||
/** The module name (with quotes stripped) of the export's module symbol, if it was an ambient module */
|
||||
ambientModuleName?: string;
|
||||
/** True if the export was found in the package.json AutoImportProvider */
|
||||
isPackageJsonImport?: true;
|
||||
/**
|
||||
* The name of the property or export in the module's symbol table. Differs from the completion name
|
||||
* in the case of InternalSymbolName.ExportEquals and InternalSymbolName.Default.
|
||||
*/
|
||||
exportName: string;
|
||||
}
|
||||
interface CompletionEntry {
|
||||
name: string;
|
||||
kind: ScriptElementKind;
|
||||
@@ -6110,6 +6124,15 @@ declare namespace ts {
|
||||
isRecommended?: true;
|
||||
isFromUncheckedFile?: true;
|
||||
isPackageJsonImport?: true;
|
||||
/**
|
||||
* A property to be sent back to TS Server in the CompletionDetailsRequest, along with `name`,
|
||||
* that allows TS Server to look up the symbol represented by the completion item, disambiguating
|
||||
* items with the same name. Currently only defined for auto-import completions, but the type is
|
||||
* `unknown` in the protocol, so it can be changed as needed to support other kinds of completions.
|
||||
* The presence of this property should generally not be used to assume that this completion entry
|
||||
* is an auto-import.
|
||||
*/
|
||||
data?: CompletionEntryData;
|
||||
}
|
||||
interface CompletionEntryDetails {
|
||||
name: string;
|
||||
@@ -8162,6 +8185,7 @@ declare namespace ts.server.protocol {
|
||||
interface CompletionEntryIdentifier {
|
||||
name: string;
|
||||
source?: string;
|
||||
data?: unknown;
|
||||
}
|
||||
/**
|
||||
* Completion entry details request; value of command field is
|
||||
@@ -8244,6 +8268,12 @@ declare namespace ts.server.protocol {
|
||||
* in the project package.json.
|
||||
*/
|
||||
isPackageJsonImport?: true;
|
||||
/**
|
||||
* A property to be sent back to TS Server in the CompletionDetailsRequest, along with `name`,
|
||||
* that allows TS Server to look up the symbol represented by the completion item, disambiguating
|
||||
* items with the same name.
|
||||
*/
|
||||
data?: unknown;
|
||||
}
|
||||
/**
|
||||
* Additional completion entry details, available on demand
|
||||
|
||||
+27
-4
@@ -2408,7 +2408,7 @@ declare namespace ts {
|
||||
export interface Symbol {
|
||||
flags: SymbolFlags;
|
||||
escapedName: __String;
|
||||
declarations: Declaration[];
|
||||
declarations?: Declaration[];
|
||||
valueDeclaration: Declaration;
|
||||
members?: SymbolTable;
|
||||
exports?: SymbolTable;
|
||||
@@ -5528,12 +5528,13 @@ declare namespace ts {
|
||||
*
|
||||
* @param fileName The path to the file
|
||||
* @param position A zero based index of the character where you want the entries
|
||||
* @param entryName The name from an existing completion which came from `getCompletionsAtPosition`
|
||||
* @param entryName The `name` from an existing completion which came from `getCompletionsAtPosition`
|
||||
* @param formatOptions How should code samples in the completions be formatted, can be undefined for backwards compatibility
|
||||
* @param source Source code for the current file, can be undefined for backwards compatibility
|
||||
* @param source `source` property from the completion entry
|
||||
* @param preferences User settings, can be undefined for backwards compatibility
|
||||
* @param data `data` property from the completion entry
|
||||
*/
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences | undefined): CompletionEntryDetails | undefined;
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: FormatCodeOptions | FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences | undefined, data: CompletionEntryData | undefined): CompletionEntryDetails | undefined;
|
||||
getCompletionEntrySymbol(fileName: string, position: number, name: string, source: string | undefined): Symbol | undefined;
|
||||
/**
|
||||
* Gets semantic information about the identifier at a particular position in a
|
||||
@@ -6093,6 +6094,19 @@ declare namespace ts {
|
||||
isNewIdentifierLocation: boolean;
|
||||
entries: CompletionEntry[];
|
||||
}
|
||||
interface CompletionEntryData {
|
||||
/** The file name declaring the export's module symbol, if it was an external module */
|
||||
fileName?: string;
|
||||
/** The module name (with quotes stripped) of the export's module symbol, if it was an ambient module */
|
||||
ambientModuleName?: string;
|
||||
/** True if the export was found in the package.json AutoImportProvider */
|
||||
isPackageJsonImport?: true;
|
||||
/**
|
||||
* The name of the property or export in the module's symbol table. Differs from the completion name
|
||||
* in the case of InternalSymbolName.ExportEquals and InternalSymbolName.Default.
|
||||
*/
|
||||
exportName: string;
|
||||
}
|
||||
interface CompletionEntry {
|
||||
name: string;
|
||||
kind: ScriptElementKind;
|
||||
@@ -6110,6 +6124,15 @@ declare namespace ts {
|
||||
isRecommended?: true;
|
||||
isFromUncheckedFile?: true;
|
||||
isPackageJsonImport?: true;
|
||||
/**
|
||||
* A property to be sent back to TS Server in the CompletionDetailsRequest, along with `name`,
|
||||
* that allows TS Server to look up the symbol represented by the completion item, disambiguating
|
||||
* items with the same name. Currently only defined for auto-import completions, but the type is
|
||||
* `unknown` in the protocol, so it can be changed as needed to support other kinds of completions.
|
||||
* The presence of this property should generally not be used to assume that this completion entry
|
||||
* is an auto-import.
|
||||
*/
|
||||
data?: CompletionEntryData;
|
||||
}
|
||||
interface CompletionEntryDetails {
|
||||
name: string;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/////*3*/
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", includes: ["B", "\u0042"] },
|
||||
{ marker: "2", excludes: ["C", "\u0043", "A", "\u0041"], isNewIdentifierLocation: true },
|
||||
{ marker: "3", includes: ["B", "\u0042", "A", "\u0041", "C", "\u0043"] },
|
||||
{ marker: "0", includes: ["B"] },
|
||||
{ marker: "2", excludes: ["C", "A"], isNewIdentifierLocation: true },
|
||||
{ marker: "3", includes: ["B", "A", "C"] },
|
||||
);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @noLib: true
|
||||
|
||||
// @Filename: /someModule.ts
|
||||
//// export const someModule = 0;
|
||||
//// export default 1;
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// someMo/**/
|
||||
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: [
|
||||
completion.globalThisEntry,
|
||||
completion.undefinedVarEntry,
|
||||
{
|
||||
name: "someModule",
|
||||
source: "/someModule",
|
||||
sourceDisplay: "./someModule",
|
||||
text: "const someModule: 0",
|
||||
kind: "const",
|
||||
kindModifiers: "export",
|
||||
hasAction: true,
|
||||
sortText: completion.SortText.AutoImportSuggestions
|
||||
},
|
||||
{
|
||||
name: "someModule",
|
||||
source: "/someModule",
|
||||
sourceDisplay: "./someModule",
|
||||
text: "(property) default: 1",
|
||||
kind: "property",
|
||||
kindModifiers: "export",
|
||||
hasAction: true,
|
||||
sortText: completion.SortText.AutoImportSuggestions
|
||||
},
|
||||
...completion.statementKeywordsWithTypes
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true
|
||||
}
|
||||
});
|
||||
|
||||
verify.applyCodeActionFromCompletion("", {
|
||||
name: "someModule",
|
||||
source: "/someModule",
|
||||
data: { exportName: "default", fileName: "/someModule.ts" },
|
||||
description: `Import default 'someModule' from module "./someModule"`,
|
||||
newFileContent: `import someModule from "./someModule";
|
||||
|
||||
someMo`
|
||||
});
|
||||
@@ -28,12 +28,20 @@ verify.completions(
|
||||
exact: [
|
||||
completion.globalThisEntry,
|
||||
completion.undefinedVarEntry,
|
||||
exportEntry,
|
||||
...completion.statementKeywordsWithTypes
|
||||
],
|
||||
preferences
|
||||
},
|
||||
{ marker: "1", includes: exportEntry, preferences }
|
||||
{
|
||||
marker: "1",
|
||||
exact: [
|
||||
completion.globalThisEntry,
|
||||
completion.undefinedVarEntry,
|
||||
exportEntry,
|
||||
...completion.statementKeywordsWithTypes
|
||||
],
|
||||
preferences
|
||||
}
|
||||
);
|
||||
verify.applyCodeActionFromCompletion("0", {
|
||||
name: "fooBar",
|
||||
|
||||
@@ -98,6 +98,13 @@ declare module ts {
|
||||
character: number;
|
||||
}
|
||||
|
||||
interface CompletionEntryData {
|
||||
fileName?: string;
|
||||
ambientModuleName?: string;
|
||||
isPackageJsonImport?: true;
|
||||
exportName: string;
|
||||
}
|
||||
|
||||
function flatMap<T, U>(array: ReadonlyArray<T>, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[];
|
||||
}
|
||||
|
||||
@@ -253,6 +260,7 @@ declare namespace FourSlashInterface {
|
||||
applyCodeActionFromCompletion(markerName: string, options: {
|
||||
name: string,
|
||||
source?: string,
|
||||
data?: ts.CompletionEntryData,
|
||||
description: string,
|
||||
newFileContent?: string,
|
||||
newRangeContent?: string,
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/// <reference path="../fourslash.ts" />
|
||||
|
||||
// @Filename: /tsconfig.json
|
||||
//// { "compilerOptions": { "noLib": true } }
|
||||
|
||||
// @Filename: /someModule.ts
|
||||
//// export const someModule = 0;
|
||||
//// export default 1;
|
||||
|
||||
// @Filename: /index.ts
|
||||
//// someMo/**/
|
||||
|
||||
verify.completions({
|
||||
marker: "",
|
||||
includes: [
|
||||
{
|
||||
name: "someModule",
|
||||
source: "/someModule",
|
||||
sourceDisplay: "./someModule",
|
||||
text: "const someModule: 0",
|
||||
kind: "const",
|
||||
kindModifiers: "export",
|
||||
hasAction: true,
|
||||
sortText: completion.SortText.AutoImportSuggestions
|
||||
},
|
||||
{
|
||||
name: "someModule",
|
||||
source: "/someModule",
|
||||
sourceDisplay: "./someModule",
|
||||
text: "(property) default: 1",
|
||||
kind: "property",
|
||||
kindModifiers: "export",
|
||||
hasAction: true,
|
||||
sortText: completion.SortText.AutoImportSuggestions
|
||||
},
|
||||
],
|
||||
preferences: {
|
||||
includeCompletionsForModuleExports: true
|
||||
}
|
||||
});
|
||||
|
||||
verify.applyCodeActionFromCompletion("", {
|
||||
name: "someModule",
|
||||
source: "/someModule",
|
||||
data: { exportName: "default", fileName: "/someModule.ts" },
|
||||
description: `Import default 'someModule' from module "./someModule"`,
|
||||
newFileContent: `import someModule from "./someModule";\r\n\r\nsomeMo`
|
||||
});
|
||||
Reference in New Issue
Block a user