Completion for default export should be '.default' (#16742)
* Completion for default export should be '.default' * Don't include empty string in name table * getSymbolsInScope() should return local symbols, not exported symbols * Fix bug: getSymbolAtLocation should work for local symbol too
This commit is contained in:
parent
a94e0c36b0
commit
aa2d1008bf
|
@ -11875,6 +11875,8 @@ namespace ts {
|
|||
}
|
||||
|
||||
function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) {
|
||||
symbol = symbol.exportSymbol || symbol;
|
||||
|
||||
// If we have an identifier or a property access at the given location, if the location is
|
||||
// an dotted name expression, and if the location is not an assignment target, obtain the type
|
||||
// of the expression (which will reflect control flow analysis). If the expression indeed
|
||||
|
@ -22281,11 +22283,6 @@ namespace ts {
|
|||
}
|
||||
|
||||
switch (location.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
if (!isExternalOrCommonJsModule(<SourceFile>location)) {
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember);
|
||||
break;
|
||||
|
@ -22337,7 +22334,7 @@ namespace ts {
|
|||
* @param meaning meaning of symbol to filter by before adding to symbol table
|
||||
*/
|
||||
function copySymbol(symbol: Symbol, meaning: SymbolFlags): void {
|
||||
if (symbol.flags & meaning) {
|
||||
if (getCombinedLocalAndExportSymbolFlags(symbol) & meaning) {
|
||||
const id = symbol.name;
|
||||
// We will copy all symbol regardless of its reserved name because
|
||||
// symbolsToArray will check whether the key is a reserved name and
|
||||
|
|
|
@ -3598,6 +3598,11 @@ namespace ts {
|
|||
}
|
||||
return previous[previous.length - 1];
|
||||
}
|
||||
|
||||
/** See comment on `declareModuleMember` in `binder.ts`. */
|
||||
export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags {
|
||||
return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
|
|
|
@ -949,6 +949,22 @@ namespace FourSlash {
|
|||
this.verifySymbol(symbol, declarationRanges);
|
||||
}
|
||||
|
||||
public symbolsInScope(range: Range): ts.Symbol[] {
|
||||
const node = this.goToAndGetNode(range);
|
||||
return this.getChecker().getSymbolsInScope(node, ts.SymbolFlags.Value | ts.SymbolFlags.Type | ts.SymbolFlags.Namespace);
|
||||
}
|
||||
|
||||
public verifyTypeOfSymbolAtLocation(range: Range, symbol: ts.Symbol, expected: string): void {
|
||||
const node = this.goToAndGetNode(range);
|
||||
const checker = this.getChecker();
|
||||
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
|
||||
|
||||
const actual = checker.typeToString(type);
|
||||
if (actual !== expected) {
|
||||
this.raiseError(`Expected: '${expected}', actual: '${actual}'`);
|
||||
}
|
||||
}
|
||||
|
||||
private verifyReferencesAre(expectedReferences: Range[]) {
|
||||
const actualReferences = this.getReferencesAtCaret() || [];
|
||||
|
||||
|
@ -3426,6 +3442,10 @@ namespace FourSlashInterface {
|
|||
public markerByName(s: string): FourSlash.Marker {
|
||||
return this.state.getMarkerByName(s);
|
||||
}
|
||||
|
||||
public symbolsInScope(range: FourSlash.Range): ts.Symbol[] {
|
||||
return this.state.symbolsInScope(range);
|
||||
}
|
||||
}
|
||||
|
||||
export class GoTo {
|
||||
|
@ -3694,6 +3714,10 @@ namespace FourSlashInterface {
|
|||
this.state.verifySymbolAtLocation(startRange, declarationRanges);
|
||||
}
|
||||
|
||||
public typeOfSymbolAtLocation(range: FourSlash.Range, symbol: ts.Symbol, expected: string) {
|
||||
this.state.verifyTypeOfSymbolAtLocation(range, symbol, expected);
|
||||
}
|
||||
|
||||
public referencesOf(start: FourSlash.Range, references: FourSlash.Range[]) {
|
||||
this.state.verifyReferencesOf(start, references);
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace ts.Completions {
|
|||
}
|
||||
else {
|
||||
if ((!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) {
|
||||
return undefined;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log);
|
||||
|
@ -112,7 +112,7 @@ namespace ts.Completions {
|
|||
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
|
||||
// We would like to only show things that can be added after a dot, so for instance numeric properties can
|
||||
// not be accessed with a dot (a.1 <- invalid)
|
||||
const displayName = getCompletionEntryDisplayNameForSymbol(typeChecker, symbol, target, performCharacterChecks, location);
|
||||
const displayName = getCompletionEntryDisplayNameForSymbol(symbol, target, performCharacterChecks);
|
||||
if (!displayName) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -307,7 +307,7 @@ namespace ts.Completions {
|
|||
// We don't need to perform character checks here because we're only comparing the
|
||||
// name against 'entryName' (which is known to be good), not building a new
|
||||
// completion entry.
|
||||
const symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(typeChecker, s, compilerOptions.target, /*performCharacterChecks*/ false, location) === entryName ? s : undefined);
|
||||
const symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false) === entryName ? s : undefined);
|
||||
|
||||
if (symbol) {
|
||||
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All);
|
||||
|
@ -341,20 +341,14 @@ namespace ts.Completions {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
export function getCompletionEntrySymbol(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): Symbol {
|
||||
export function getCompletionEntrySymbol(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): Symbol | undefined {
|
||||
// Compute all the completion symbols again.
|
||||
const completionData = getCompletionData(typeChecker, log, sourceFile, position);
|
||||
if (completionData) {
|
||||
const { symbols, location } = completionData;
|
||||
|
||||
// Find the symbol with the matching entry name.
|
||||
// We don't need to perform character checks here because we're only comparing the
|
||||
// name against 'entryName' (which is known to be good), not building a new
|
||||
// completion entry.
|
||||
return forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(typeChecker, s, compilerOptions.target, /*performCharacterChecks*/ false, location) === entryName ? s : undefined);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
// Find the symbol with the matching entry name.
|
||||
// We don't need to perform character checks here because we're only comparing the
|
||||
// name against 'entryName' (which is known to be good), not building a new
|
||||
// completion entry.
|
||||
return completionData && forEach(completionData.symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false) === entryName ? s : undefined);
|
||||
}
|
||||
|
||||
interface CompletionData {
|
||||
|
@ -369,7 +363,7 @@ namespace ts.Completions {
|
|||
}
|
||||
type Request = { kind: "JsDocTagName" } | { kind: "JsDocTag" } | { kind: "JsDocParameterName", tag: JSDocParameterTag };
|
||||
|
||||
function getCompletionData(typeChecker: TypeChecker, log: (message: string) => void, sourceFile: SourceFile, position: number): CompletionData {
|
||||
function getCompletionData(typeChecker: TypeChecker, log: (message: string) => void, sourceFile: SourceFile, position: number): CompletionData | undefined {
|
||||
const isJavaScriptFile = isSourceFileJavaScript(sourceFile);
|
||||
|
||||
let request: Request | undefined;
|
||||
|
@ -615,7 +609,7 @@ namespace ts.Completions {
|
|||
// Extract module or enum members
|
||||
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
|
||||
const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName());
|
||||
const isValidTypeAccess = (symbol: Symbol) => symbolCanbeReferencedAtTypeLocation(symbol);
|
||||
const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol);
|
||||
const isValidAccess = isRhsOfImportDeclaration ?
|
||||
// Any kind is allowed when dotting off namespace in internal import equals declaration
|
||||
(symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
|
||||
|
@ -630,7 +624,7 @@ namespace ts.Completions {
|
|||
|
||||
if (!isTypeLocation) {
|
||||
const type = typeChecker.getTypeAtLocation(node);
|
||||
addTypeProperties(type);
|
||||
if (type) addTypeProperties(type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,17 +636,17 @@ namespace ts.Completions {
|
|||
symbols.push(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isJavaScriptFile && type.flags & TypeFlags.Union) {
|
||||
// In javascript files, for union types, we don't just get the members that
|
||||
// the individual types have in common, we also include all the members that
|
||||
// each individual type has. This is because we're going to add all identifiers
|
||||
// anyways. So we might as well elevate the members that were at least part
|
||||
// of the individual types to a higher status since we know what they are.
|
||||
const unionType = <UnionType>type;
|
||||
for (const elementType of unionType.types) {
|
||||
addTypeProperties(elementType);
|
||||
}
|
||||
if (isJavaScriptFile && type.flags & TypeFlags.Union) {
|
||||
// In javascript files, for union types, we don't just get the members that
|
||||
// the individual types have in common, we also include all the members that
|
||||
// each individual type has. This is because we're going to add all identifiers
|
||||
// anyways. So we might as well elevate the members that were at least part
|
||||
// of the individual types to a higher status since we know what they are.
|
||||
const unionType = <UnionType>type;
|
||||
for (const elementType of unionType.types) {
|
||||
addTypeProperties(elementType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -777,12 +771,12 @@ namespace ts.Completions {
|
|||
(!isContextTokenValueLocation(contextToken) &&
|
||||
(isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken)))) {
|
||||
// Its a type, but you can reach it by namespace.type as well
|
||||
return symbolCanbeReferencedAtTypeLocation(symbol);
|
||||
return symbolCanBeReferencedAtTypeLocation(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// expressions are value space (which includes the value namespaces)
|
||||
return !!(symbol.flags & SymbolFlags.Value);
|
||||
return !!(getCombinedLocalAndExportSymbolFlags(symbol) & SymbolFlags.Value);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -812,7 +806,9 @@ namespace ts.Completions {
|
|||
}
|
||||
}
|
||||
|
||||
function symbolCanbeReferencedAtTypeLocation(symbol: Symbol): boolean {
|
||||
function symbolCanBeReferencedAtTypeLocation(symbol: Symbol): boolean {
|
||||
symbol = symbol.exportSymbol || symbol;
|
||||
|
||||
// This is an alias, follow what it aliases
|
||||
if (symbol && symbol.flags & SymbolFlags.Alias) {
|
||||
symbol = typeChecker.getAliasedSymbol(symbol);
|
||||
|
@ -826,7 +822,7 @@ namespace ts.Completions {
|
|||
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
|
||||
// If the exported symbols contains type,
|
||||
// symbol can be referenced at locations where type is allowed
|
||||
return forEach(exportedSymbols, symbolCanbeReferencedAtTypeLocation);
|
||||
return forEach(exportedSymbols, symbolCanBeReferencedAtTypeLocation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1598,22 +1594,23 @@ namespace ts.Completions {
|
|||
/**
|
||||
* Get the name to be display in completion from a given symbol.
|
||||
*
|
||||
* @return undefined if the name is of external module otherwise a name with striped of any quote
|
||||
* @return undefined if the name is of external module
|
||||
*/
|
||||
function getCompletionEntryDisplayNameForSymbol(typeChecker: TypeChecker, symbol: Symbol, target: ScriptTarget, performCharacterChecks: boolean, location: Node): string {
|
||||
const displayName: string = getDeclaredName(typeChecker, symbol, location);
|
||||
function getCompletionEntryDisplayNameForSymbol(symbol: Symbol, target: ScriptTarget, performCharacterChecks: boolean): string | undefined {
|
||||
const name = symbol.getUnescapedName();
|
||||
if (!name) return undefined;
|
||||
|
||||
if (displayName) {
|
||||
const firstCharCode = displayName.charCodeAt(0);
|
||||
// First check of the displayName is not external module; if it is an external module, it is not valid entry
|
||||
if ((symbol.flags & SymbolFlags.Namespace) && (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote)) {
|
||||
// First check of the displayName is not external module; if it is an external module, it is not valid entry
|
||||
if (symbol.flags & SymbolFlags.Namespace) {
|
||||
const firstCharCode = name.charCodeAt(0);
|
||||
if (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote) {
|
||||
// If the symbol is external module, don't show it in the completion list
|
||||
// (i.e declare module "http" { const x; } | // <= request completion here, "http" should not be there)
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return getCompletionEntryDisplayName(displayName, target, performCharacterChecks);
|
||||
return getCompletionEntryDisplayName(name, target, performCharacterChecks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1621,24 +1618,12 @@ namespace ts.Completions {
|
|||
* and checking whether the name is valid identifier name.
|
||||
*/
|
||||
function getCompletionEntryDisplayName(name: string, target: ScriptTarget, performCharacterChecks: boolean): string {
|
||||
if (!name) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
name = stripQuotes(name);
|
||||
|
||||
if (!name) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an
|
||||
// invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name.
|
||||
// e.g "b a" is valid quoted name but when we strip off the quotes, it is invalid.
|
||||
// We, thus, need to check if whatever was inside the quotes is actually a valid identifier name.
|
||||
if (performCharacterChecks) {
|
||||
if (!isIdentifierText(name, target)) {
|
||||
return undefined;
|
||||
}
|
||||
if (performCharacterChecks && !isIdentifierText(name, target)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return name;
|
||||
|
|
|
@ -2063,42 +2063,33 @@ namespace ts {
|
|||
}
|
||||
|
||||
function initializeNameTable(sourceFile: SourceFile): void {
|
||||
const nameTable = createUnderscoreEscapedMap<number>();
|
||||
|
||||
walk(sourceFile);
|
||||
sourceFile.nameTable = nameTable;
|
||||
|
||||
function walk(node: Node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
setNameTable((<Identifier>node).text, node);
|
||||
break;
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
// We want to store any numbers/strings if they were a name that could be
|
||||
// related to a declaration. So, if we have 'import x = require("something")'
|
||||
// then we want 'something' to be in the name table. Similarly, if we have
|
||||
// "a['propname']" then we want to store "propname" in the name table.
|
||||
if (isDeclarationName(node) ||
|
||||
node.parent.kind === SyntaxKind.ExternalModuleReference ||
|
||||
isArgumentOfElementAccessExpression(node) ||
|
||||
isLiteralComputedPropertyDeclarationName(node)) {
|
||||
setNameTable(getEscapedTextOfIdentifierOrLiteral((<LiteralExpression>node)), node);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
forEachChild(node, walk);
|
||||
if (node.jsDoc) {
|
||||
for (const jsDoc of node.jsDoc) {
|
||||
forEachChild(jsDoc, walk);
|
||||
}
|
||||
}
|
||||
const nameTable = sourceFile.nameTable = createUnderscoreEscapedMap<number>();
|
||||
sourceFile.forEachChild(function walk(node) {
|
||||
if ((isIdentifier(node) || isStringOrNumericLiteral(node) && literalIsName(node)) && node.text) {
|
||||
const text = getEscapedTextOfIdentifierOrLiteral(node);
|
||||
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
|
||||
}
|
||||
}
|
||||
|
||||
function setNameTable(text: __String, node: ts.Node): void {
|
||||
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
|
||||
}
|
||||
forEachChild(node, walk);
|
||||
if (node.jsDoc) {
|
||||
for (const jsDoc of node.jsDoc) {
|
||||
forEachChild(jsDoc, walk);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to store any numbers/strings if they were a name that could be
|
||||
* related to a declaration. So, if we have 'import x = require("something")'
|
||||
* then we want 'something' to be in the name table. Similarly, if we have
|
||||
* "a['propname']" then we want to store "propname" in the name table.
|
||||
*/
|
||||
function literalIsName(node: ts.StringLiteral | ts.NumericLiteral): boolean {
|
||||
return isDeclarationName(node) ||
|
||||
node.parent.kind === SyntaxKind.ExternalModuleReference ||
|
||||
isArgumentOfElementAccessExpression(node) ||
|
||||
isLiteralComputedPropertyDeclarationName(node);
|
||||
}
|
||||
|
||||
function isObjectLiteralElement(node: Node): node is ObjectLiteralElement {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
namespace ts.SymbolDisplay {
|
||||
// TODO(drosen): use contextual SemanticMeaning.
|
||||
export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind {
|
||||
const { flags } = symbol;
|
||||
const flags = getCombinedLocalAndExportSymbolFlags(symbol);
|
||||
|
||||
if (flags & SymbolFlags.Class) {
|
||||
return getDeclarationOfKind(symbol, SyntaxKind.ClassExpression) ?
|
||||
|
@ -34,7 +34,7 @@ namespace ts.SymbolDisplay {
|
|||
if (location.kind === SyntaxKind.ThisKeyword && isExpression(location)) {
|
||||
return ScriptElementKind.parameterElement;
|
||||
}
|
||||
const { flags } = symbol;
|
||||
const flags = getCombinedLocalAndExportSymbolFlags(symbol);
|
||||
if (flags & SymbolFlags.Variable) {
|
||||
if (isFirstDeclarationOfSymbolParameter(symbol)) {
|
||||
return ScriptElementKind.parameterElement;
|
||||
|
@ -96,7 +96,7 @@ namespace ts.SymbolDisplay {
|
|||
const displayParts: SymbolDisplayPart[] = [];
|
||||
let documentation: SymbolDisplayPart[];
|
||||
let tags: JSDocTagInfo[];
|
||||
const symbolFlags = symbol.flags;
|
||||
const symbolFlags = ts.getCombinedLocalAndExportSymbolFlags(symbol);
|
||||
let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location);
|
||||
let hasAddedSymbolInfo: boolean;
|
||||
const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isExpression(location);
|
||||
|
@ -110,7 +110,7 @@ namespace ts.SymbolDisplay {
|
|||
}
|
||||
|
||||
let signature: Signature;
|
||||
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol, location);
|
||||
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol.exportSymbol || symbol, location);
|
||||
if (type) {
|
||||
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
const right = (<PropertyAccessExpression>location.parent).name;
|
||||
|
@ -198,7 +198,7 @@ namespace ts.SymbolDisplay {
|
|||
hasAddedSymbolInfo = true;
|
||||
}
|
||||
}
|
||||
else if ((isNameOfFunctionDeclaration(location) && !(symbol.flags & SymbolFlags.Accessor)) || // name of function declaration
|
||||
else if ((isNameOfFunctionDeclaration(location) && !(symbolFlags & SymbolFlags.Accessor)) || // name of function declaration
|
||||
(location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration
|
||||
// get the signature from the declaration and write it
|
||||
const functionDeclaration = <FunctionLike>location.parent;
|
||||
|
@ -429,7 +429,7 @@ namespace ts.SymbolDisplay {
|
|||
if (!documentation) {
|
||||
documentation = symbol.getDocumentationComment();
|
||||
tags = symbol.getJsDocTags();
|
||||
if (documentation.length === 0 && symbol.flags & SymbolFlags.Property) {
|
||||
if (documentation.length === 0 && symbolFlags & SymbolFlags.Property) {
|
||||
// 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.
|
||||
|
|
|
@ -35,7 +35,7 @@ goTo.file("commentsExternalModules_file0.ts");
|
|||
verify.quickInfoAt("1", "namespace m1", "Namespace comment");
|
||||
|
||||
goTo.marker('2');
|
||||
verify.completionListContains("b", "var m1.b: number", "b's comment");
|
||||
verify.completionListContains("b", "var b: number", "b's comment");
|
||||
verify.completionListContains("foo", "function foo(): number", "foo's comment");
|
||||
|
||||
goTo.marker('3');
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
verify.quickInfoAt("1", "namespace m1", "Namespace comment");
|
||||
|
||||
goTo.marker('2');
|
||||
verify.completionListContains("b", "var m1.b: number", "b's comment");
|
||||
verify.completionListContains("b", "var b: number", "b's comment");
|
||||
verify.completionListContains("foo", "function foo(): number", "foo's comment");
|
||||
|
||||
goTo.marker('3');
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
/////*3*/
|
||||
|
||||
goTo.marker("0");
|
||||
verify.not.completionListContains("B");
|
||||
verify.not.completionListContains("\u0042");
|
||||
verify.completionListContains("B");
|
||||
verify.completionListContains("\u0042");
|
||||
|
||||
goTo.marker("2");
|
||||
verify.not.completionListContains("C");
|
||||
|
@ -18,10 +18,9 @@ verify.not.completionListContains("A");
|
|||
verify.not.completionListContains("\u0041");
|
||||
|
||||
goTo.marker("3");
|
||||
verify.not.completionListContains("B");
|
||||
verify.not.completionListContains("\u0042");
|
||||
verify.not.completionListContains("A");
|
||||
verify.not.completionListContains("\u0041");
|
||||
verify.not.completionListContains("C");
|
||||
verify.not.completionListContains("\u0043");
|
||||
|
||||
verify.completionListContains("B");
|
||||
verify.completionListContains("\u0042");
|
||||
verify.completionListContains("A");
|
||||
verify.completionListContains("\u0041");
|
||||
verify.completionListContains("C");
|
||||
verify.completionListContains("\u0043");
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @Filename: /a.ts
|
||||
////import * as self from "./a";
|
||||
////
|
||||
////declare module "a" {
|
||||
//// export const a: number;
|
||||
////}
|
||||
////
|
||||
/////**/
|
||||
|
||||
goTo.marker();
|
||||
verify.not.completionListContains("a");
|
|
@ -19,6 +19,7 @@ verify.completionListContains("bar");
|
|||
verify.completionListContains("break");
|
||||
verify.completionListContains("any");
|
||||
verify.completionListContains("$");
|
||||
verify.completionListContains("b");
|
||||
|
||||
// Nothing else should show up
|
||||
verify.completionListCount(4);
|
||||
verify.completionListCount(5);
|
||||
|
|
|
@ -41,19 +41,16 @@ function getVerify(isTypeLocation?: boolean) {
|
|||
verifyValueOrType: verify
|
||||
};
|
||||
}
|
||||
function typeLocationVerify(valueMarker: string, verify: (typeMarker: string) => void) {
|
||||
verify(valueMarker + "Type");
|
||||
return valueMarker;
|
||||
}
|
||||
|
||||
function verifyModuleM(marker: string) {
|
||||
const isTypeLocation = marker.indexOf("Type") !== -1;
|
||||
const { verifyValue, verifyType, verifyValueOrType } = getVerify(isTypeLocation);
|
||||
if (!isTypeLocation) {
|
||||
marker = typeLocationVerify(marker, verifyModuleM);
|
||||
}
|
||||
verifyModuleMWorker(marker, /*isTypeLocation*/ false);
|
||||
verifyModuleMWorker(`${marker}Type`, /*isTypeLocation*/ true);
|
||||
}
|
||||
|
||||
function verifyModuleMWorker(marker: string, isTypeLocation: boolean): void {
|
||||
goTo.marker(marker);
|
||||
|
||||
const { verifyValue, verifyType, verifyValueOrType } = getVerify(isTypeLocation);
|
||||
verifyType.completionListContains("I");
|
||||
verifyValueOrType.completionListContains("C");
|
||||
verifyValueOrType.completionListContains("E");
|
||||
|
@ -63,8 +60,9 @@ function verifyModuleM(marker: string) {
|
|||
verifyValueOrType.completionListContains("A");
|
||||
}
|
||||
|
||||
|
||||
// Module m
|
||||
goTo.marker("1");
|
||||
verify.completionListContains("A");
|
||||
verifyModuleM("1");
|
||||
|
||||
// Class C
|
||||
|
|
|
@ -232,6 +232,7 @@
|
|||
interface GotoMarkVerifyOptions {
|
||||
isClassScope?: boolean;
|
||||
isTypeLocation?: boolean;
|
||||
insideMod1?: boolean;
|
||||
}
|
||||
|
||||
function getVerify(isTypeLocation: boolean) {
|
||||
|
@ -243,30 +244,30 @@ function getVerify(isTypeLocation: boolean) {
|
|||
};
|
||||
}
|
||||
|
||||
function goToMarkAndGeneralVerify(marker: string, { isClassScope, isTypeLocation }: GotoMarkVerifyOptions = {})
|
||||
{
|
||||
function goToMarkAndGeneralVerify(marker: string, { isClassScope, isTypeLocation, insideMod1 }: GotoMarkVerifyOptions = {}) {
|
||||
goTo.marker(marker);
|
||||
|
||||
const mod1Dot = insideMod1 ? "" : "mod1.";
|
||||
const verifyValueInModule = isClassScope || isTypeLocation ? verify.not : verify;
|
||||
const verifyValueOrTypeInModule = isClassScope ? verify.not : verify;
|
||||
const verifyTypeInModule = isTypeLocation ? verify : verify.not;
|
||||
verifyValueInModule.completionListContains('mod1var', 'var mod1var: number');
|
||||
verifyValueInModule.completionListContains('mod1fn', 'function mod1fn(): void');
|
||||
verifyValueInModule.completionListContains('mod1evar', 'var mod1.mod1evar: number');
|
||||
verifyValueInModule.completionListContains('mod1efn', 'function mod1.mod1efn(): void');
|
||||
verifyValueInModule.completionListContains('mod1eexvar', 'var mod1.mod1eexvar: number');
|
||||
verifyValueInModule.completionListContains('mod1evar', `var ${mod1Dot}mod1evar: number`);
|
||||
verifyValueInModule.completionListContains('mod1efn', `function ${mod1Dot}mod1efn(): void`);
|
||||
verifyValueInModule.completionListContains('mod1eexvar', `var mod1.mod1eexvar: number`);
|
||||
verifyValueInModule.completionListContains('mod3', 'namespace mod3');
|
||||
verifyValueInModule.completionListContains('shwvar', 'var shwvar: number');
|
||||
verifyValueInModule.completionListContains('shwfn', 'function shwfn(): void');
|
||||
|
||||
verifyTypeInModule.completionListContains('mod1int', 'interface mod1int');
|
||||
verifyTypeInModule.completionListContains('mod1eint', 'interface mod1.mod1eint');
|
||||
verifyTypeInModule.completionListContains('mod1eint', `interface ${mod1Dot}mod1eint`);
|
||||
verifyTypeInModule.completionListContains('shwint', 'interface shwint');
|
||||
|
||||
verifyValueOrTypeInModule.completionListContains('mod1cls', 'class mod1cls');
|
||||
verifyValueOrTypeInModule.completionListContains('mod1mod', 'namespace mod1mod');
|
||||
verifyValueOrTypeInModule.completionListContains('mod1ecls', 'class mod1.mod1ecls');
|
||||
verifyValueOrTypeInModule.completionListContains('mod1emod', 'namespace mod1.mod1emod');
|
||||
verifyValueOrTypeInModule.completionListContains('mod1ecls', `class ${mod1Dot}mod1ecls`);
|
||||
verifyValueOrTypeInModule.completionListContains('mod1emod', `namespace ${mod1Dot}mod1emod`);
|
||||
verifyValueOrTypeInModule.completionListContains('mod2', 'namespace mod2');
|
||||
verifyValueOrTypeInModule.completionListContains('shwcls', 'class shwcls');
|
||||
|
||||
|
@ -295,12 +296,12 @@ function goToMarkAndGeneralVerify(marker: string, { isClassScope, isTypeLocation
|
|||
}
|
||||
|
||||
// from mod1
|
||||
goToMarkAndGeneralVerify('mod1');
|
||||
goToMarkAndGeneralVerify('mod1', { insideMod1: true });
|
||||
// from mod1 in type position
|
||||
goToMarkAndGeneralVerify('mod1Type', { isTypeLocation: true });
|
||||
goToMarkAndGeneralVerify('mod1Type', { isTypeLocation: true, insideMod1: true });
|
||||
|
||||
// from function in mod1
|
||||
goToMarkAndGeneralVerify('function');
|
||||
goToMarkAndGeneralVerify('function', { insideMod1: true });
|
||||
verify.completionListContains('bar', '(local var) bar: number');
|
||||
verify.completionListContains('foob', '(local function) foob(): void');
|
||||
|
||||
|
@ -310,34 +311,34 @@ goToMarkAndGeneralVerify('class', { isClassScope: true });
|
|||
//verify.not.completionListContains('ceVar');
|
||||
|
||||
// from interface in mod1
|
||||
goToMarkAndGeneralVerify('interface');
|
||||
goToMarkAndGeneralVerify('interface', { insideMod1: true });
|
||||
|
||||
// from namespace in mod1
|
||||
verifyNamespaceInMod1('namespace');
|
||||
verifyNamespaceInMod1('namespaceType', /*isTypeLocation*/ true);
|
||||
|
||||
function verifyNamespaceInMod1(marker: string, isTypeLocation?: boolean) {
|
||||
goToMarkAndGeneralVerify(marker, { isTypeLocation });
|
||||
goToMarkAndGeneralVerify(marker, { isTypeLocation, insideMod1: true });
|
||||
|
||||
const { verifyValue, verifyType, verifyValueOrType, verifyNotValueOrType } = getVerify(isTypeLocation);
|
||||
|
||||
verifyValue.completionListContains('m1X', 'var m1X: number');
|
||||
verifyValue.completionListContains('m1Func', 'function m1Func(): void');
|
||||
verifyValue.completionListContains('m1eX', 'var mod1mod.m1eX: number');
|
||||
verifyValue.completionListContains('m1eFunc', 'function mod1mod.m1eFunc(): void');
|
||||
verifyValue.completionListContains('m1eX', 'var m1eX: number');
|
||||
verifyValue.completionListContains('m1eFunc', 'function m1eFunc(): void');
|
||||
|
||||
verifyType.completionListContains('m1Int', 'interface m1Int');
|
||||
verifyType.completionListContains('m1eInt', 'interface mod1mod.m1eInt');
|
||||
verifyType.completionListContains('m1eInt', 'interface m1eInt');
|
||||
|
||||
verifyValueOrType.completionListContains('m1Class', 'class m1Class');
|
||||
verifyValueOrType.completionListContains('m1eClass', 'class mod1mod.m1eClass');
|
||||
verifyValueOrType.completionListContains('m1eClass', 'class m1eClass');
|
||||
|
||||
verifyNotValueOrType.completionListContains('m1Mod', 'namespace m1Mod');
|
||||
verifyNotValueOrType.completionListContains('m1eMod', 'namespace mod1mod.m1eMod');
|
||||
verifyNotValueOrType.completionListContains('m1eMod', 'namespace m1eMod');
|
||||
}
|
||||
|
||||
// from exported function in mod1
|
||||
goToMarkAndGeneralVerify('exportedFunction');
|
||||
goToMarkAndGeneralVerify('exportedFunction', { insideMod1: true });
|
||||
verify.completionListContains('bar', '(local var) bar: number');
|
||||
verify.completionListContains('foob', '(local function) foob(): void');
|
||||
|
||||
|
@ -347,27 +348,27 @@ verify.not.completionListContains('ceFunc');
|
|||
verify.not.completionListContains('ceVar');
|
||||
|
||||
// from exported interface in mod1
|
||||
goToMarkAndGeneralVerify('exportedInterface');
|
||||
goToMarkAndGeneralVerify('exportedInterface', { insideMod1: true });
|
||||
|
||||
// from exported namespace in mod1
|
||||
verifyExportedNamespace('exportedNamespace');
|
||||
verifyExportedNamespace('exportedNamespaceType', /*isTypeLocation*/ true);
|
||||
function verifyExportedNamespace(marker: string, isTypeLocation?: boolean) {
|
||||
goToMarkAndGeneralVerify(marker, { isTypeLocation });
|
||||
goToMarkAndGeneralVerify(marker, { isTypeLocation, insideMod1: true });
|
||||
const { verifyValue, verifyType, verifyValueOrType, verifyNotValueOrType } = getVerify(isTypeLocation);
|
||||
verifyValue.completionListContains('mX', 'var mX: number');
|
||||
verifyValue.completionListContains('mFunc', 'function mFunc(): void');
|
||||
verifyValue.completionListContains('meX', 'var mod1.mod1emod.meX: number');
|
||||
verifyValue.completionListContains('meFunc', 'function mod1.mod1emod.meFunc(): void');
|
||||
verifyValue.completionListContains('meX', 'var meX: number');
|
||||
verifyValue.completionListContains('meFunc', 'function meFunc(): void');
|
||||
|
||||
verifyType.completionListContains('mInt', 'interface mInt');
|
||||
verifyType.completionListContains('meInt', 'interface mod1.mod1emod.meInt');
|
||||
verifyType.completionListContains('meInt', 'interface meInt');
|
||||
|
||||
verifyValueOrType.completionListContains('mClass', 'class mClass');
|
||||
verifyValueOrType.completionListContains('meClass', 'class mod1.mod1emod.meClass');
|
||||
verifyValueOrType.completionListContains('meClass', 'class meClass');
|
||||
|
||||
verifyNotValueOrType.completionListContains('mMod', 'namespace mMod');
|
||||
verifyNotValueOrType.completionListContains('meMod', 'namespace mod1.mod1emod.meMod');
|
||||
verifyNotValueOrType.completionListContains('meMod', 'namespace meMod');
|
||||
}
|
||||
|
||||
// from extended namespace
|
||||
|
@ -380,7 +381,7 @@ function verifyExtendedNamespace(marker: string, isTypeLocation?: boolean) {
|
|||
|
||||
verifyValue.completionListContains('mod1evar', 'var mod1.mod1evar: number');
|
||||
verifyValue.completionListContains('mod1efn', 'function mod1.mod1efn(): void');
|
||||
verifyValue.completionListContains('mod1eexvar', 'var mod1.mod1eexvar: number');
|
||||
verifyValue.completionListContains('mod1eexvar', 'var mod1eexvar: number');
|
||||
verifyValue.completionListContains('mod3', 'namespace mod3');
|
||||
verifyValue.completionListContains('shwvar', 'var shwvar: number');
|
||||
verifyValue.completionListContains('shwfn', 'function shwfn(): void');
|
||||
|
@ -415,4 +416,4 @@ function verifyExtendedNamespace(marker: string, isTypeLocation?: boolean) {
|
|||
verify.not.completionListContains('sivar');
|
||||
verify.not.completionListContains('sifn');
|
||||
verify.not.completionListContains('mod2eexvar');
|
||||
}
|
||||
}
|
||||
|
|
11
tests/cases/fourslash/completionsDefaultExport.ts
Normal file
11
tests/cases/fourslash/completionsDefaultExport.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @Filename: /a.ts
|
||||
////export default function f() {}
|
||||
|
||||
// @Filename: /b.ts
|
||||
////import * as a from "./a";
|
||||
////a./**/;
|
||||
|
||||
goTo.marker();
|
||||
verify.completionListContains("default", "function f(): void");
|
|
@ -9,4 +9,4 @@ goTo.marker('1');
|
|||
verify.completionListContains("func", "function func(): void", /*documentation*/ undefined, "function");
|
||||
|
||||
goTo.marker('2');
|
||||
verify.completionListContains("func", "function func(): void", /*documentation*/ undefined, "function");
|
||||
verify.completionListContains("func", "function func(): void", /*documentation*/ undefined, "function");
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
const ranges = test.ranges();
|
||||
const [r0, r1, r2, r3, r4] = ranges;
|
||||
const fnRanges = [r0, r1, r2, r3];
|
||||
verify.singleReferenceGroup("function DefaultExportedFunction(): () => typeof DefaultExportedFunction", fnRanges);
|
||||
const fn = "function DefaultExportedFunction(): () => typeof DefaultExportedFunction";
|
||||
verify.singleReferenceGroup(fn, fnRanges);
|
||||
|
||||
// The namespace and function do not merge,
|
||||
// so the namespace should be all alone.
|
||||
verify.singleReferenceGroup("namespace DefaultExportedFunction", [r4]);
|
||||
verify.singleReferenceGroup(`namespace DefaultExportedFunction\n${fn}`, [r4]);
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
////namespace [|{| "isWriteAccess": true, "isDefinition": true |}DefaultExportedClass|] {
|
||||
////}
|
||||
|
||||
verify.noErrors();
|
||||
|
||||
// The namespace and class do not merge,
|
||||
// so the namespace should be all alone.
|
||||
verify.singleReferenceGroup("namespace DefaultExportedClass");
|
||||
verify.singleReferenceGroup("class DefaultExportedClass\nnamespace DefaultExportedClass");
|
||||
|
|
|
@ -116,6 +116,7 @@ declare namespace FourSlashInterface {
|
|||
ranges(): Range[];
|
||||
rangesByText(): ts.Map<Range[]>;
|
||||
markerByName(s: string): Marker;
|
||||
symbolsInScope(range: Range): any[];
|
||||
}
|
||||
class goTo {
|
||||
marker(name?: string | Marker): void;
|
||||
|
@ -192,6 +193,7 @@ declare namespace FourSlashInterface {
|
|||
verifyGetEmitOutputContentsForCurrentFile(expected: ts.OutputFile[]): void;
|
||||
noReferences(markerNameOrRange?: string | Range): void;
|
||||
symbolAtLocation(startRange: Range, ...declarationRanges: Range[]): void;
|
||||
typeOfSymbolAtLocation(range: Range, symbol: any, expected: string): void;
|
||||
/**
|
||||
* @deprecated, prefer 'referenceGroups'
|
||||
* Like `referencesAre`, but goes to `start` first.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//// var num: number;
|
||||
//// var str: string;
|
||||
//// if (typeof /*1*/nonExportedStrOrNum === "number") {
|
||||
//// num = /*2*/nonExportedStrOrNum;
|
||||
//// num = /*2*/nonExportedStrOrNum;
|
||||
//// }
|
||||
//// else {
|
||||
//// str = /*3*/nonExportedStrOrNum.length;
|
||||
|
@ -40,15 +40,15 @@ verify.completionListContains("nonExportedStrOrNum", "var nonExportedStrOrNum: s
|
|||
|
||||
goTo.marker('4');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string | number');
|
||||
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
|
||||
verify.completionListContains("exportedStrOrNum", "var exportedStrOrNum: string | number");
|
||||
|
||||
goTo.marker('5');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: number');
|
||||
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: number");
|
||||
verify.completionListContains("exportedStrOrNum", "var exportedStrOrNum: number");
|
||||
|
||||
goTo.marker('6');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string');
|
||||
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string");
|
||||
verify.completionListContains("exportedStrOrNum", "var exportedStrOrNum: string");
|
||||
|
||||
goTo.marker('7');
|
||||
verify.quickInfoIs('var m.exportedStrOrNum: string | number');
|
||||
|
|
12
tests/cases/fourslash/typeOfSymbol_localSymbolOfExport.ts
Normal file
12
tests/cases/fourslash/typeOfSymbol_localSymbolOfExport.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////export function f() {}
|
||||
////[|1|];
|
||||
|
||||
const ranges = test.ranges();
|
||||
const symbolsInScope = test.symbolsInScope(ranges[0]);
|
||||
const f = symbolsInScope.find(s => s.name === "f");
|
||||
if (f === undefined) throw new Error("'f' not in scope");
|
||||
if (f.exportSymbol === undefined) throw new Error("Expected to get the local symbol");
|
||||
|
||||
verify.typeOfSymbolAtLocation(ranges[0], f, "() => void");
|
Loading…
Reference in a new issue