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:
Andy 2017-07-11 07:23:32 -07:00 committed by GitHub
parent a94e0c36b0
commit aa2d1008bf
20 changed files with 200 additions and 158 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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);
}

View file

@ -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;

View file

@ -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 {

View file

@ -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.

View 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');

View file

@ -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');

View file

@ -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");

View file

@ -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");

View file

@ -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);

View file

@ -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

View file

@ -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');
}
}

View 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");

View file

@ -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");

View file

@ -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]);

View file

@ -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");

View file

@ -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.

View file

@ -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');

View 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");