mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into literalEnumTypes
# Conflicts: # src/compiler/checker.ts # src/compiler/diagnosticMessages.json # src/compiler/types.ts # src/compiler/utilities.ts # src/services/completions.ts
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code],
|
||||
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code,
|
||||
Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code],
|
||||
getCodeActions: getActionsForAddMissingMember
|
||||
});
|
||||
|
||||
@@ -110,16 +111,16 @@ namespace ts.codefix {
|
||||
if (!isStatic) {
|
||||
const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword);
|
||||
const indexingParameter = createParameter(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
"x",
|
||||
/*questionToken*/ undefined,
|
||||
/*questionToken*/ undefined,
|
||||
stringTypeNode,
|
||||
/*initializer*/ undefined);
|
||||
const indexSignature = createIndexSignatureDeclaration(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*initializer*/ undefined);
|
||||
const indexSignature = createIndexSignature(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
[indexingParameter],
|
||||
typeNode);
|
||||
|
||||
@@ -135,4 +136,4 @@ namespace ts.codefix {
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code,
|
||||
Diagnostics.Cannot_find_name_0_Did_you_mean_1.code],
|
||||
getCodeActions: getActionsForCorrectSpelling
|
||||
});
|
||||
|
||||
function getActionsForCorrectSpelling(context: CodeFixContext): CodeAction[] | undefined {
|
||||
const sourceFile = context.sourceFile;
|
||||
|
||||
// This is the identifier of the misspelled word. eg:
|
||||
// this.speling = 1;
|
||||
// ^^^^^^^
|
||||
const node = getTokenAtPosition(sourceFile, context.span.start);
|
||||
const checker = context.program.getTypeChecker();
|
||||
let suggestion: string;
|
||||
if (node.kind === SyntaxKind.Identifier && isPropertyAccessExpression(node.parent)) {
|
||||
const containingType = checker.getTypeAtLocation(node.parent.expression);
|
||||
suggestion = checker.getSuggestionForNonexistentProperty(node as Identifier, containingType);
|
||||
}
|
||||
else {
|
||||
const meaning = getMeaningFromLocation(node);
|
||||
suggestion = checker.getSuggestionForNonexistentSymbol(node, getTextOfNode(node), convertSemanticMeaningToSymbolFlags(meaning));
|
||||
}
|
||||
if (suggestion) {
|
||||
return [{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_spelling_to_0), [suggestion]),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: node.getStart(), length: node.getWidth() },
|
||||
newText: suggestion
|
||||
}],
|
||||
}],
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
function convertSemanticMeaningToSymbolFlags(meaning: SemanticMeaning): SymbolFlags {
|
||||
let flags = 0;
|
||||
if (meaning & SemanticMeaning.Namespace) {
|
||||
flags |= SymbolFlags.Namespace;
|
||||
}
|
||||
if (meaning & SemanticMeaning.Type) {
|
||||
flags |= SymbolFlags.Type;
|
||||
}
|
||||
if (meaning & SemanticMeaning.Value) {
|
||||
flags |= SymbolFlags.Value;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
/// <reference path="fixClassIncorrectlyImplementsInterface.ts" />
|
||||
/// <reference path="fixAddMissingMember.ts" />
|
||||
/// <reference path="fixSpelling.ts" />
|
||||
/// <reference path="fixClassDoesntImplementInheritedAbstractMember.ts" />
|
||||
/// <reference path="fixClassSuperMustPrecedeThisAccess.ts" />
|
||||
/// <reference path="fixConstructorForDerivedNeedSuperCall.ts" />
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace ts.codefix {
|
||||
|
||||
const declaration = declarations[0] as Declaration;
|
||||
// Clone name to remove leading trivia.
|
||||
const name = getSynthesizedClone(<PropertyName>declaration.name);
|
||||
const name = getSynthesizedClone(getNameOfDeclaration(declaration)) as PropertyName;
|
||||
const visibilityModifier = createVisibilityModifier(getModifierFlags(declaration));
|
||||
const modifiers = visibilityModifier ? createNodeArray([visibilityModifier]) : undefined;
|
||||
const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration));
|
||||
@@ -130,7 +130,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function signatureToMethodDeclaration(signature: Signature, enclosingDeclaration: Node, body?: Block) {
|
||||
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration);
|
||||
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.SuppressAnyReturnType);
|
||||
if (signatureDeclaration) {
|
||||
signatureDeclaration.decorators = undefined;
|
||||
signatureDeclaration.modifiers = modifiers;
|
||||
@@ -200,7 +200,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
export function createStubbedMethod(modifiers: Modifier[], name: PropertyName, optional: boolean, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) {
|
||||
return createMethodDeclaration(
|
||||
return createMethod(
|
||||
/*decorators*/ undefined,
|
||||
modifiers,
|
||||
/*asteriskToken*/ undefined,
|
||||
@@ -231,4 +231,4 @@ namespace ts.codefix {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [
|
||||
Diagnostics.Cannot_find_name_0.code,
|
||||
Diagnostics.Cannot_find_name_0_Did_you_mean_1.code,
|
||||
Diagnostics.Cannot_find_namespace_0.code,
|
||||
Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
|
||||
],
|
||||
getCodeActions: getImportCodeActions
|
||||
});
|
||||
|
||||
type ImportCodeActionKind = "CodeChange" | "InsertingIntoExistingImport" | "NewImport";
|
||||
interface ImportCodeAction extends CodeAction {
|
||||
@@ -113,465 +122,458 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes: [
|
||||
Diagnostics.Cannot_find_name_0.code,
|
||||
Diagnostics.Cannot_find_namespace_0.code,
|
||||
Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
|
||||
],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
const checker = context.program.getTypeChecker();
|
||||
const allSourceFiles = context.program.getSourceFiles();
|
||||
const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false;
|
||||
function getImportCodeActions(context: CodeFixContext): ImportCodeAction[] {
|
||||
const sourceFile = context.sourceFile;
|
||||
const checker = context.program.getTypeChecker();
|
||||
const allSourceFiles = context.program.getSourceFiles();
|
||||
const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false;
|
||||
|
||||
const token = getTokenAtPosition(sourceFile, context.span.start);
|
||||
const name = token.getText();
|
||||
const symbolIdActionMap = new ImportCodeActionMap();
|
||||
const token = getTokenAtPosition(sourceFile, context.span.start);
|
||||
const name = token.getText();
|
||||
const symbolIdActionMap = new ImportCodeActionMap();
|
||||
|
||||
// this is a module id -> module import declaration map
|
||||
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
|
||||
let lastImportDeclaration: Node;
|
||||
// this is a module id -> module import declaration map
|
||||
const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = [];
|
||||
let lastImportDeclaration: Node;
|
||||
|
||||
const currentTokenMeaning = getMeaningFromLocation(token);
|
||||
if (context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code) {
|
||||
const symbol = checker.getAliasedSymbol(checker.getSymbolAtLocation(token));
|
||||
return getCodeActionForImport(symbol, /*isDefault*/ false, /*isNamespaceImport*/ true);
|
||||
const currentTokenMeaning = getMeaningFromLocation(token);
|
||||
if (context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code) {
|
||||
const symbol = checker.getAliasedSymbol(checker.getSymbolAtLocation(token));
|
||||
return getCodeActionForImport(symbol, /*isDefault*/ false, /*isNamespaceImport*/ true);
|
||||
}
|
||||
|
||||
const candidateModules = checker.getAmbientModules();
|
||||
for (const otherSourceFile of allSourceFiles) {
|
||||
if (otherSourceFile !== sourceFile && isExternalOrCommonJsModule(otherSourceFile)) {
|
||||
candidateModules.push(otherSourceFile.symbol);
|
||||
}
|
||||
}
|
||||
|
||||
const candidateModules = checker.getAmbientModules();
|
||||
for (const otherSourceFile of allSourceFiles) {
|
||||
if (otherSourceFile !== sourceFile && isExternalOrCommonJsModule(otherSourceFile)) {
|
||||
candidateModules.push(otherSourceFile.symbol);
|
||||
for (const moduleSymbol of candidateModules) {
|
||||
context.cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
// check the default export
|
||||
const defaultExport = checker.tryGetMemberInModuleExports("default", moduleSymbol);
|
||||
if (defaultExport) {
|
||||
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
|
||||
if (localSymbol && localSymbol.name === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
|
||||
// check if this symbol is already used
|
||||
const symbolId = getUniqueSymbolId(localSymbol);
|
||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, /*isDefault*/ true));
|
||||
}
|
||||
}
|
||||
|
||||
for (const moduleSymbol of candidateModules) {
|
||||
context.cancellationToken.throwIfCancellationRequested();
|
||||
// check exports with the same name
|
||||
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol);
|
||||
if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
|
||||
const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName);
|
||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol));
|
||||
}
|
||||
}
|
||||
|
||||
// check the default export
|
||||
const defaultExport = checker.tryGetMemberInModuleExports("default", moduleSymbol);
|
||||
if (defaultExport) {
|
||||
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
|
||||
if (localSymbol && localSymbol.name === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
|
||||
// check if this symbol is already used
|
||||
const symbolId = getUniqueSymbolId(localSymbol);
|
||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, /*isDefault*/ true));
|
||||
return symbolIdActionMap.getAllActions();
|
||||
|
||||
function getImportDeclarations(moduleSymbol: Symbol) {
|
||||
const moduleSymbolId = getUniqueSymbolId(moduleSymbol);
|
||||
|
||||
const cached = cachedImportDeclarations[moduleSymbolId];
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = [];
|
||||
for (const importModuleSpecifier of sourceFile.imports) {
|
||||
const importSymbol = checker.getSymbolAtLocation(importModuleSpecifier);
|
||||
if (importSymbol === moduleSymbol) {
|
||||
existingDeclarations.push(getImportDeclaration(importModuleSpecifier));
|
||||
}
|
||||
}
|
||||
cachedImportDeclarations[moduleSymbolId] = existingDeclarations;
|
||||
return existingDeclarations;
|
||||
|
||||
function getImportDeclaration(moduleSpecifier: LiteralExpression) {
|
||||
let node: Node = moduleSpecifier;
|
||||
while (node) {
|
||||
if (node.kind === SyntaxKind.ImportDeclaration) {
|
||||
return <ImportDeclaration>node;
|
||||
}
|
||||
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
|
||||
return <ImportEqualsDeclaration>node;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// check exports with the same name
|
||||
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExports(name, moduleSymbol);
|
||||
if (exportSymbolWithIdenticalName && checkSymbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
|
||||
const symbolId = getUniqueSymbolId(exportSymbolWithIdenticalName);
|
||||
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol));
|
||||
}
|
||||
function getUniqueSymbolId(symbol: Symbol) {
|
||||
if (symbol.flags & SymbolFlags.Alias) {
|
||||
return getSymbolId(checker.getAliasedSymbol(symbol));
|
||||
}
|
||||
return getSymbolId(symbol);
|
||||
}
|
||||
|
||||
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {
|
||||
const declarations = symbol.getDeclarations();
|
||||
return declarations ? some(symbol.declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)) : false;
|
||||
}
|
||||
|
||||
function getCodeActionForImport(moduleSymbol: Symbol, isDefault?: boolean, isNamespaceImport?: boolean): ImportCodeAction[] {
|
||||
const existingDeclarations = getImportDeclarations(moduleSymbol);
|
||||
if (existingDeclarations.length > 0) {
|
||||
// With an existing import statement, there are more than one actions the user can do.
|
||||
return getCodeActionsForExistingImport(existingDeclarations);
|
||||
}
|
||||
else {
|
||||
return [getCodeActionForNewImport()];
|
||||
}
|
||||
|
||||
return symbolIdActionMap.getAllActions();
|
||||
function getCodeActionsForExistingImport(declarations: (ImportDeclaration | ImportEqualsDeclaration)[]): ImportCodeAction[] {
|
||||
const actions: ImportCodeAction[] = [];
|
||||
|
||||
function getImportDeclarations(moduleSymbol: Symbol) {
|
||||
const moduleSymbolId = getUniqueSymbolId(moduleSymbol);
|
||||
|
||||
const cached = cachedImportDeclarations[moduleSymbolId];
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = [];
|
||||
for (const importModuleSpecifier of sourceFile.imports) {
|
||||
const importSymbol = checker.getSymbolAtLocation(importModuleSpecifier);
|
||||
if (importSymbol === moduleSymbol) {
|
||||
existingDeclarations.push(getImportDeclaration(importModuleSpecifier));
|
||||
}
|
||||
}
|
||||
cachedImportDeclarations[moduleSymbolId] = existingDeclarations;
|
||||
return existingDeclarations;
|
||||
|
||||
function getImportDeclaration(moduleSpecifier: LiteralExpression) {
|
||||
let node: Node = moduleSpecifier;
|
||||
while (node) {
|
||||
if (node.kind === SyntaxKind.ImportDeclaration) {
|
||||
return <ImportDeclaration>node;
|
||||
// It is possible that multiple import statements with the same specifier exist in the file.
|
||||
// e.g.
|
||||
//
|
||||
// import * as ns from "foo";
|
||||
// import { member1, member2 } from "foo";
|
||||
//
|
||||
// member3/**/ <-- cusor here
|
||||
//
|
||||
// in this case we should provie 2 actions:
|
||||
// 1. change "member3" to "ns.member3"
|
||||
// 2. add "member3" to the second import statement's import list
|
||||
// and it is up to the user to decide which one fits best.
|
||||
let namespaceImportDeclaration: ImportDeclaration | ImportEqualsDeclaration;
|
||||
let namedImportDeclaration: ImportDeclaration;
|
||||
let existingModuleSpecifier: string;
|
||||
for (const declaration of declarations) {
|
||||
if (declaration.kind === SyntaxKind.ImportDeclaration) {
|
||||
const namedBindings = declaration.importClause && declaration.importClause.namedBindings;
|
||||
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
|
||||
// case:
|
||||
// import * as ns from "foo"
|
||||
namespaceImportDeclaration = declaration;
|
||||
}
|
||||
if (node.kind === SyntaxKind.ImportEqualsDeclaration) {
|
||||
return <ImportEqualsDeclaration>node;
|
||||
else {
|
||||
// cases:
|
||||
// import default from "foo"
|
||||
// import { bar } from "foo" or combination with the first one
|
||||
// import "foo"
|
||||
namedImportDeclaration = declaration;
|
||||
}
|
||||
node = node.parent;
|
||||
existingModuleSpecifier = declaration.moduleSpecifier.getText();
|
||||
}
|
||||
else {
|
||||
// case:
|
||||
// import foo = require("foo")
|
||||
namespaceImportDeclaration = declaration;
|
||||
existingModuleSpecifier = getModuleSpecifierFromImportEqualsDeclaration(declaration);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getUniqueSymbolId(symbol: Symbol) {
|
||||
if (symbol.flags & SymbolFlags.Alias) {
|
||||
return getSymbolId(checker.getAliasedSymbol(symbol));
|
||||
if (namespaceImportDeclaration) {
|
||||
actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration));
|
||||
}
|
||||
return getSymbolId(symbol);
|
||||
}
|
||||
|
||||
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {
|
||||
const declarations = symbol.getDeclarations();
|
||||
return declarations ? some(symbol.declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)) : false;
|
||||
}
|
||||
|
||||
function getCodeActionForImport(moduleSymbol: Symbol, isDefault?: boolean, isNamespaceImport?: boolean): ImportCodeAction[] {
|
||||
const existingDeclarations = getImportDeclarations(moduleSymbol);
|
||||
if (existingDeclarations.length > 0) {
|
||||
// With an existing import statement, there are more than one actions the user can do.
|
||||
return getCodeActionsForExistingImport(existingDeclarations);
|
||||
if (!isNamespaceImport && namedImportDeclaration && namedImportDeclaration.importClause &&
|
||||
(namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) {
|
||||
/**
|
||||
* If the existing import declaration already has a named import list, just
|
||||
* insert the identifier into that list.
|
||||
*/
|
||||
const fileTextChanges = getTextChangeForImportClause(namedImportDeclaration.importClause);
|
||||
const moduleSpecifierWithoutQuotes = stripQuotes(namedImportDeclaration.moduleSpecifier.getText());
|
||||
actions.push(createCodeAction(
|
||||
Diagnostics.Add_0_to_existing_import_declaration_from_1,
|
||||
[name, moduleSpecifierWithoutQuotes],
|
||||
fileTextChanges,
|
||||
"InsertingIntoExistingImport",
|
||||
moduleSpecifierWithoutQuotes
|
||||
));
|
||||
}
|
||||
else {
|
||||
return [getCodeActionForNewImport()];
|
||||
// we need to create a new import statement, but the existing module specifier can be reused.
|
||||
actions.push(getCodeActionForNewImport(existingModuleSpecifier));
|
||||
}
|
||||
return actions;
|
||||
|
||||
function getModuleSpecifierFromImportEqualsDeclaration(declaration: ImportEqualsDeclaration) {
|
||||
if (declaration.moduleReference && declaration.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
|
||||
return declaration.moduleReference.expression.getText();
|
||||
}
|
||||
return declaration.moduleReference.getText();
|
||||
}
|
||||
|
||||
function getCodeActionsForExistingImport(declarations: (ImportDeclaration | ImportEqualsDeclaration)[]): ImportCodeAction[] {
|
||||
const actions: ImportCodeAction[] = [];
|
||||
|
||||
// It is possible that multiple import statements with the same specifier exist in the file.
|
||||
// e.g.
|
||||
//
|
||||
// import * as ns from "foo";
|
||||
// import { member1, member2 } from "foo";
|
||||
//
|
||||
// member3/**/ <-- cusor here
|
||||
//
|
||||
// in this case we should provie 2 actions:
|
||||
// 1. change "member3" to "ns.member3"
|
||||
// 2. add "member3" to the second import statement's import list
|
||||
// and it is up to the user to decide which one fits best.
|
||||
let namespaceImportDeclaration: ImportDeclaration | ImportEqualsDeclaration;
|
||||
let namedImportDeclaration: ImportDeclaration;
|
||||
let existingModuleSpecifier: string;
|
||||
for (const declaration of declarations) {
|
||||
if (declaration.kind === SyntaxKind.ImportDeclaration) {
|
||||
const namedBindings = declaration.importClause && declaration.importClause.namedBindings;
|
||||
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
|
||||
// case:
|
||||
// import * as ns from "foo"
|
||||
namespaceImportDeclaration = declaration;
|
||||
}
|
||||
else {
|
||||
// cases:
|
||||
// import default from "foo"
|
||||
// import { bar } from "foo" or combination with the first one
|
||||
// import "foo"
|
||||
namedImportDeclaration = declaration;
|
||||
}
|
||||
existingModuleSpecifier = declaration.moduleSpecifier.getText();
|
||||
}
|
||||
else {
|
||||
// case:
|
||||
// import foo = require("foo")
|
||||
namespaceImportDeclaration = declaration;
|
||||
existingModuleSpecifier = getModuleSpecifierFromImportEqualsDeclaration(declaration);
|
||||
}
|
||||
function getTextChangeForImportClause(importClause: ImportClause): FileTextChanges[] {
|
||||
const importList = <NamedImports>importClause.namedBindings;
|
||||
const newImportSpecifier = createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name));
|
||||
// case 1:
|
||||
// original text: import default from "module"
|
||||
// change to: import default, { name } from "module"
|
||||
// case 2:
|
||||
// original text: import {} from "module"
|
||||
// change to: import { name } from "module"
|
||||
if (!importList || importList.elements.length === 0) {
|
||||
const newImportClause = createImportClause(importClause.name, createNamedImports([newImportSpecifier]));
|
||||
return createChangeTracker().replaceNode(sourceFile, importClause, newImportClause).getChanges();
|
||||
}
|
||||
|
||||
if (namespaceImportDeclaration) {
|
||||
actions.push(getCodeActionForNamespaceImport(namespaceImportDeclaration));
|
||||
}
|
||||
|
||||
if (!isNamespaceImport && namedImportDeclaration && namedImportDeclaration.importClause &&
|
||||
(namedImportDeclaration.importClause.name || namedImportDeclaration.importClause.namedBindings)) {
|
||||
/**
|
||||
* If the existing import declaration already has a named import list, just
|
||||
* insert the identifier into that list.
|
||||
*/
|
||||
const fileTextChanges = getTextChangeForImportClause(namedImportDeclaration.importClause);
|
||||
const moduleSpecifierWithoutQuotes = stripQuotes(namedImportDeclaration.moduleSpecifier.getText());
|
||||
actions.push(createCodeAction(
|
||||
Diagnostics.Add_0_to_existing_import_declaration_from_1,
|
||||
[name, moduleSpecifierWithoutQuotes],
|
||||
fileTextChanges,
|
||||
"InsertingIntoExistingImport",
|
||||
moduleSpecifierWithoutQuotes
|
||||
));
|
||||
}
|
||||
else {
|
||||
// we need to create a new import statement, but the existing module specifier can be reused.
|
||||
actions.push(getCodeActionForNewImport(existingModuleSpecifier));
|
||||
}
|
||||
return actions;
|
||||
|
||||
function getModuleSpecifierFromImportEqualsDeclaration(declaration: ImportEqualsDeclaration) {
|
||||
if (declaration.moduleReference && declaration.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
|
||||
return declaration.moduleReference.expression.getText();
|
||||
}
|
||||
return declaration.moduleReference.getText();
|
||||
}
|
||||
|
||||
function getTextChangeForImportClause(importClause: ImportClause): FileTextChanges[] {
|
||||
const importList = <NamedImports>importClause.namedBindings;
|
||||
const newImportSpecifier = createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name));
|
||||
// case 1:
|
||||
// original text: import default from "module"
|
||||
// change to: import default, { name } from "module"
|
||||
// case 2:
|
||||
// original text: import {} from "module"
|
||||
// change to: import { name } from "module"
|
||||
if (!importList || importList.elements.length === 0) {
|
||||
const newImportClause = createImportClause(importClause.name, createNamedImports([newImportSpecifier]));
|
||||
return createChangeTracker().replaceNode(sourceFile, importClause, newImportClause).getChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the import list has one import per line, preserve that. Otherwise, insert on same line as last element
|
||||
* import {
|
||||
* foo
|
||||
* } from "./module";
|
||||
*/
|
||||
return createChangeTracker().insertNodeInListAfter(
|
||||
sourceFile,
|
||||
importList.elements[importList.elements.length - 1],
|
||||
newImportSpecifier).getChanges();
|
||||
}
|
||||
|
||||
function getCodeActionForNamespaceImport(declaration: ImportDeclaration | ImportEqualsDeclaration): ImportCodeAction {
|
||||
let namespacePrefix: string;
|
||||
if (declaration.kind === SyntaxKind.ImportDeclaration) {
|
||||
namespacePrefix = (<NamespaceImport>declaration.importClause.namedBindings).name.getText();
|
||||
}
|
||||
else {
|
||||
namespacePrefix = declaration.name.getText();
|
||||
}
|
||||
namespacePrefix = stripQuotes(namespacePrefix);
|
||||
|
||||
/**
|
||||
* Cases:
|
||||
* import * as ns from "mod"
|
||||
* import default, * as ns from "mod"
|
||||
* import ns = require("mod")
|
||||
*
|
||||
* Because there is no import list, we alter the reference to include the
|
||||
* namespace instead of altering the import declaration. For example, "foo" would
|
||||
* become "ns.foo"
|
||||
*/
|
||||
return createCodeAction(
|
||||
Diagnostics.Change_0_to_1,
|
||||
[name, `${namespacePrefix}.${name}`],
|
||||
createChangeTracker().replaceNode(sourceFile, token, createPropertyAccess(createIdentifier(namespacePrefix), name)).getChanges(),
|
||||
"CodeChange"
|
||||
);
|
||||
}
|
||||
/**
|
||||
* If the import list has one import per line, preserve that. Otherwise, insert on same line as last element
|
||||
* import {
|
||||
* foo
|
||||
* } from "./module";
|
||||
*/
|
||||
return createChangeTracker().insertNodeInListAfter(
|
||||
sourceFile,
|
||||
importList.elements[importList.elements.length - 1],
|
||||
newImportSpecifier).getChanges();
|
||||
}
|
||||
|
||||
function getCodeActionForNewImport(moduleSpecifier?: string): ImportCodeAction {
|
||||
if (!lastImportDeclaration) {
|
||||
// insert after any existing imports
|
||||
for (let i = sourceFile.statements.length - 1; i >= 0; i--) {
|
||||
const statement = sourceFile.statements[i];
|
||||
if (statement.kind === SyntaxKind.ImportEqualsDeclaration || statement.kind === SyntaxKind.ImportDeclaration) {
|
||||
lastImportDeclaration = statement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier || getModuleSpecifierForNewImport());
|
||||
const changeTracker = createChangeTracker();
|
||||
const importClause = isDefault
|
||||
? createImportClause(createIdentifier(name), /*namedBindings*/ undefined)
|
||||
: isNamespaceImport
|
||||
? createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(name)))
|
||||
: createImportClause(/*name*/ undefined, createNamedImports([createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name))]));
|
||||
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, createLiteral(moduleSpecifierWithoutQuotes));
|
||||
if (!lastImportDeclaration) {
|
||||
changeTracker.insertNodeAt(sourceFile, sourceFile.getStart(), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
|
||||
function getCodeActionForNamespaceImport(declaration: ImportDeclaration | ImportEqualsDeclaration): ImportCodeAction {
|
||||
let namespacePrefix: string;
|
||||
if (declaration.kind === SyntaxKind.ImportDeclaration) {
|
||||
namespacePrefix = (<NamespaceImport>declaration.importClause.namedBindings).name.getText();
|
||||
}
|
||||
else {
|
||||
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter });
|
||||
namespacePrefix = declaration.name.getText();
|
||||
}
|
||||
namespacePrefix = stripQuotes(namespacePrefix);
|
||||
|
||||
// if this file doesn't have any import statements, insert an import statement and then insert a new line
|
||||
// between the only import statement and user code. Otherwise just insert the statement because chances
|
||||
// are there are already a new line seperating code and import statements.
|
||||
/**
|
||||
* Cases:
|
||||
* import * as ns from "mod"
|
||||
* import default, * as ns from "mod"
|
||||
* import ns = require("mod")
|
||||
*
|
||||
* Because there is no import list, we alter the reference to include the
|
||||
* namespace instead of altering the import declaration. For example, "foo" would
|
||||
* become "ns.foo"
|
||||
*/
|
||||
return createCodeAction(
|
||||
Diagnostics.Import_0_from_1,
|
||||
[name, `"${moduleSpecifierWithoutQuotes}"`],
|
||||
changeTracker.getChanges(),
|
||||
"NewImport",
|
||||
moduleSpecifierWithoutQuotes
|
||||
Diagnostics.Change_0_to_1,
|
||||
[name, `${namespacePrefix}.${name}`],
|
||||
createChangeTracker().replaceNode(sourceFile, token, createPropertyAccess(createIdentifier(namespacePrefix), name)).getChanges(),
|
||||
"CodeChange"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getModuleSpecifierForNewImport() {
|
||||
const fileName = sourceFile.fileName;
|
||||
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
|
||||
const sourceDirectory = getDirectoryPath(fileName);
|
||||
const options = context.program.getCompilerOptions();
|
||||
|
||||
return tryGetModuleNameFromAmbientModule() ||
|
||||
tryGetModuleNameFromTypeRoots() ||
|
||||
tryGetModuleNameAsNodeModule() ||
|
||||
tryGetModuleNameFromBaseUrl() ||
|
||||
tryGetModuleNameFromRootDirs() ||
|
||||
removeFileExtension(getRelativePath(moduleFileName, sourceDirectory));
|
||||
|
||||
function tryGetModuleNameFromAmbientModule(): string {
|
||||
if (moduleSymbol.valueDeclaration.kind !== SyntaxKind.SourceFile) {
|
||||
return moduleSymbol.name;
|
||||
}
|
||||
function getCodeActionForNewImport(moduleSpecifier?: string): ImportCodeAction {
|
||||
if (!lastImportDeclaration) {
|
||||
// insert after any existing imports
|
||||
for (let i = sourceFile.statements.length - 1; i >= 0; i--) {
|
||||
const statement = sourceFile.statements[i];
|
||||
if (statement.kind === SyntaxKind.ImportEqualsDeclaration || statement.kind === SyntaxKind.ImportDeclaration) {
|
||||
lastImportDeclaration = statement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tryGetModuleNameFromBaseUrl() {
|
||||
if (!options.baseUrl) {
|
||||
return undefined;
|
||||
}
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier || getModuleSpecifierForNewImport());
|
||||
const changeTracker = createChangeTracker();
|
||||
const importClause = isDefault
|
||||
? createImportClause(createIdentifier(name), /*namedBindings*/ undefined)
|
||||
: isNamespaceImport
|
||||
? createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(name)))
|
||||
: createImportClause(/*name*/ undefined, createNamedImports([createImportSpecifier(/*propertyName*/ undefined, createIdentifier(name))]));
|
||||
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, createLiteral(moduleSpecifierWithoutQuotes));
|
||||
if (!lastImportDeclaration) {
|
||||
changeTracker.insertNodeAt(sourceFile, sourceFile.getStart(), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
|
||||
}
|
||||
else {
|
||||
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter });
|
||||
}
|
||||
|
||||
let relativeName = getRelativePathIfInDirectory(moduleFileName, options.baseUrl);
|
||||
if (!relativeName) {
|
||||
return undefined;
|
||||
}
|
||||
// if this file doesn't have any import statements, insert an import statement and then insert a new line
|
||||
// between the only import statement and user code. Otherwise just insert the statement because chances
|
||||
// are there are already a new line seperating code and import statements.
|
||||
return createCodeAction(
|
||||
Diagnostics.Import_0_from_1,
|
||||
[name, `"${moduleSpecifierWithoutQuotes}"`],
|
||||
changeTracker.getChanges(),
|
||||
"NewImport",
|
||||
moduleSpecifierWithoutQuotes
|
||||
);
|
||||
|
||||
const relativeNameWithIndex = removeFileExtension(relativeName);
|
||||
relativeName = removeExtensionAndIndexPostFix(relativeName);
|
||||
function getModuleSpecifierForNewImport() {
|
||||
const fileName = sourceFile.fileName;
|
||||
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
|
||||
const sourceDirectory = getDirectoryPath(fileName);
|
||||
const options = context.program.getCompilerOptions();
|
||||
|
||||
if (options.paths) {
|
||||
for (const key in options.paths) {
|
||||
for (const pattern of options.paths[key]) {
|
||||
const indexOfStar = pattern.indexOf("*");
|
||||
if (indexOfStar === 0 && pattern.length === 1) {
|
||||
continue;
|
||||
}
|
||||
else if (indexOfStar !== -1) {
|
||||
const prefix = pattern.substr(0, indexOfStar);
|
||||
const suffix = pattern.substr(indexOfStar + 1);
|
||||
if (relativeName.length >= prefix.length + suffix.length &&
|
||||
startsWith(relativeName, prefix) &&
|
||||
endsWith(relativeName, suffix)) {
|
||||
const matchedStar = relativeName.substr(prefix.length, relativeName.length - suffix.length);
|
||||
return key.replace("\*", matchedStar);
|
||||
}
|
||||
}
|
||||
else if (pattern === relativeName || pattern === relativeNameWithIndex) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tryGetModuleNameFromAmbientModule() ||
|
||||
tryGetModuleNameFromTypeRoots() ||
|
||||
tryGetModuleNameAsNodeModule() ||
|
||||
tryGetModuleNameFromBaseUrl() ||
|
||||
tryGetModuleNameFromRootDirs() ||
|
||||
removeFileExtension(getRelativePath(moduleFileName, sourceDirectory));
|
||||
|
||||
return relativeName;
|
||||
function tryGetModuleNameFromAmbientModule(): string {
|
||||
if (moduleSymbol.valueDeclaration.kind !== SyntaxKind.SourceFile) {
|
||||
return moduleSymbol.name;
|
||||
}
|
||||
}
|
||||
|
||||
function tryGetModuleNameFromRootDirs() {
|
||||
if (options.rootDirs) {
|
||||
const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, options.rootDirs);
|
||||
const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, options.rootDirs);
|
||||
if (normalizedTargetPath !== undefined) {
|
||||
const relativePath = normalizedSourcePath !== undefined ? getRelativePath(normalizedTargetPath, normalizedSourcePath) : normalizedTargetPath;
|
||||
return removeFileExtension(relativePath);
|
||||
}
|
||||
}
|
||||
function tryGetModuleNameFromBaseUrl() {
|
||||
if (!options.baseUrl) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function tryGetModuleNameFromTypeRoots() {
|
||||
const typeRoots = getEffectiveTypeRoots(options, context.host);
|
||||
if (typeRoots) {
|
||||
const normalizedTypeRoots = map(typeRoots, typeRoot => toPath(typeRoot, /*basePath*/ undefined, getCanonicalFileName));
|
||||
for (const typeRoot of normalizedTypeRoots) {
|
||||
if (startsWith(moduleFileName, typeRoot)) {
|
||||
const relativeFileName = moduleFileName.substring(typeRoot.length + 1);
|
||||
return removeExtensionAndIndexPostFix(relativeFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
let relativeName = getRelativePathIfInDirectory(moduleFileName, options.baseUrl);
|
||||
if (!relativeName) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function tryGetModuleNameAsNodeModule() {
|
||||
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
|
||||
// nothing to do here
|
||||
return undefined;
|
||||
}
|
||||
const relativeNameWithIndex = removeFileExtension(relativeName);
|
||||
relativeName = removeExtensionAndIndexPostFix(relativeName);
|
||||
|
||||
const indexOfNodeModules = moduleFileName.indexOf("node_modules");
|
||||
if (indexOfNodeModules < 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let relativeFileName: string;
|
||||
if (sourceDirectory.indexOf(moduleFileName.substring(0, indexOfNodeModules - 1)) === 0) {
|
||||
// if node_modules folder is in this folder or any of its parent folder, no need to keep it.
|
||||
relativeFileName = moduleFileName.substring(indexOfNodeModules + 13 /* "node_modules\".length */);
|
||||
}
|
||||
else {
|
||||
relativeFileName = getRelativePath(moduleFileName, sourceDirectory);
|
||||
}
|
||||
|
||||
relativeFileName = removeFileExtension(relativeFileName);
|
||||
if (endsWith(relativeFileName, "/index")) {
|
||||
relativeFileName = getDirectoryPath(relativeFileName);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
const moduleDirectory = getDirectoryPath(moduleFileName);
|
||||
const packageJsonContent = JSON.parse(context.host.readFile(combinePaths(moduleDirectory, "package.json")));
|
||||
if (packageJsonContent) {
|
||||
const mainFile = packageJsonContent.main || packageJsonContent.typings;
|
||||
if (mainFile) {
|
||||
const mainExportFile = toPath(mainFile, moduleDirectory, getCanonicalFileName);
|
||||
if (removeFileExtension(mainExportFile) === removeFileExtension(moduleFileName)) {
|
||||
relativeFileName = getDirectoryPath(relativeFileName);
|
||||
}
|
||||
if (options.paths) {
|
||||
for (const key in options.paths) {
|
||||
for (const pattern of options.paths[key]) {
|
||||
const indexOfStar = pattern.indexOf("*");
|
||||
if (indexOfStar === 0 && pattern.length === 1) {
|
||||
continue;
|
||||
}
|
||||
else if (indexOfStar !== -1) {
|
||||
const prefix = pattern.substr(0, indexOfStar);
|
||||
const suffix = pattern.substr(indexOfStar + 1);
|
||||
if (relativeName.length >= prefix.length + suffix.length &&
|
||||
startsWith(relativeName, prefix) &&
|
||||
endsWith(relativeName, suffix)) {
|
||||
const matchedStar = relativeName.substr(prefix.length, relativeName.length - suffix.length);
|
||||
return key.replace("\*", matchedStar);
|
||||
}
|
||||
}
|
||||
else if (pattern === relativeName || pattern === relativeNameWithIndex) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
|
||||
return relativeFileName;
|
||||
}
|
||||
|
||||
return relativeName;
|
||||
}
|
||||
|
||||
function getPathRelativeToRootDirs(path: string, rootDirs: string[]) {
|
||||
for (const rootDir of rootDirs) {
|
||||
const relativeName = getRelativePathIfInDirectory(path, rootDir);
|
||||
if (relativeName !== undefined) {
|
||||
return relativeName;
|
||||
function tryGetModuleNameFromRootDirs() {
|
||||
if (options.rootDirs) {
|
||||
const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, options.rootDirs);
|
||||
const normalizedSourcePath = getPathRelativeToRootDirs(sourceDirectory, options.rootDirs);
|
||||
if (normalizedTargetPath !== undefined) {
|
||||
const relativePath = normalizedSourcePath !== undefined ? getRelativePath(normalizedTargetPath, normalizedSourcePath) : normalizedTargetPath;
|
||||
return removeFileExtension(relativePath);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function removeExtensionAndIndexPostFix(fileName: string) {
|
||||
fileName = removeFileExtension(fileName);
|
||||
if (endsWith(fileName, "/index")) {
|
||||
fileName = fileName.substr(0, fileName.length - 6/* "/index".length */);
|
||||
function tryGetModuleNameFromTypeRoots() {
|
||||
const typeRoots = getEffectiveTypeRoots(options, context.host);
|
||||
if (typeRoots) {
|
||||
const normalizedTypeRoots = map(typeRoots, typeRoot => toPath(typeRoot, /*basePath*/ undefined, getCanonicalFileName));
|
||||
for (const typeRoot of normalizedTypeRoots) {
|
||||
if (startsWith(moduleFileName, typeRoot)) {
|
||||
const relativeFileName = moduleFileName.substring(typeRoot.length + 1);
|
||||
return removeExtensionAndIndexPostFix(relativeFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
function getRelativePathIfInDirectory(path: string, directoryPath: string) {
|
||||
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
return isRootedDiskPath(relativePath) || startsWith(relativePath, "..") ? undefined : relativePath;
|
||||
}
|
||||
function tryGetModuleNameAsNodeModule() {
|
||||
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
|
||||
// nothing to do here
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getRelativePath(path: string, directoryPath: string) {
|
||||
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath;
|
||||
const indexOfNodeModules = moduleFileName.indexOf("node_modules");
|
||||
if (indexOfNodeModules < 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let relativeFileName: string;
|
||||
if (sourceDirectory.indexOf(moduleFileName.substring(0, indexOfNodeModules - 1)) === 0) {
|
||||
// if node_modules folder is in this folder or any of its parent folder, no need to keep it.
|
||||
relativeFileName = moduleFileName.substring(indexOfNodeModules + 13 /* "node_modules\".length */);
|
||||
}
|
||||
else {
|
||||
relativeFileName = getRelativePath(moduleFileName, sourceDirectory);
|
||||
}
|
||||
|
||||
relativeFileName = removeFileExtension(relativeFileName);
|
||||
if (endsWith(relativeFileName, "/index")) {
|
||||
relativeFileName = getDirectoryPath(relativeFileName);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
const moduleDirectory = getDirectoryPath(moduleFileName);
|
||||
const packageJsonContent = JSON.parse(context.host.readFile(combinePaths(moduleDirectory, "package.json")));
|
||||
if (packageJsonContent) {
|
||||
const mainFile = packageJsonContent.main || packageJsonContent.typings;
|
||||
if (mainFile) {
|
||||
const mainExportFile = toPath(mainFile, moduleDirectory, getCanonicalFileName);
|
||||
if (removeFileExtension(mainExportFile) === removeFileExtension(moduleFileName)) {
|
||||
relativeFileName = getDirectoryPath(relativeFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
|
||||
return relativeFileName;
|
||||
}
|
||||
}
|
||||
|
||||
function getPathRelativeToRootDirs(path: string, rootDirs: string[]) {
|
||||
for (const rootDir of rootDirs) {
|
||||
const relativeName = getRelativePathIfInDirectory(path, rootDir);
|
||||
if (relativeName !== undefined) {
|
||||
return relativeName;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function removeExtensionAndIndexPostFix(fileName: string) {
|
||||
fileName = removeFileExtension(fileName);
|
||||
if (endsWith(fileName, "/index")) {
|
||||
fileName = fileName.substr(0, fileName.length - 6/* "/index".length */);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
function getRelativePathIfInDirectory(path: string, directoryPath: string) {
|
||||
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
return isRootedDiskPath(relativePath) || startsWith(relativePath, "..") ? undefined : relativePath;
|
||||
}
|
||||
|
||||
function getRelativePath(path: string, directoryPath: string) {
|
||||
const relativePath = getRelativePathToDirectoryOrUrl(directoryPath, path, directoryPath, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
|
||||
return moduleHasNonRelativeName(relativePath) ? "./" + relativePath : relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
function createChangeTracker() {
|
||||
return textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
}
|
||||
|
||||
function createCodeAction(
|
||||
description: DiagnosticMessage,
|
||||
diagnosticArgs: string[],
|
||||
changes: FileTextChanges[],
|
||||
kind: ImportCodeActionKind,
|
||||
moduleSpecifier?: string): ImportCodeAction {
|
||||
return {
|
||||
description: formatMessage.apply(undefined, [undefined, description].concat(<any[]>diagnosticArgs)),
|
||||
changes,
|
||||
kind,
|
||||
moduleSpecifier
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function createChangeTracker() {
|
||||
return textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
}
|
||||
|
||||
function createCodeAction(
|
||||
description: DiagnosticMessage,
|
||||
diagnosticArgs: string[],
|
||||
changes: FileTextChanges[],
|
||||
kind: ImportCodeActionKind,
|
||||
moduleSpecifier?: string): ImportCodeAction {
|
||||
return {
|
||||
description: formatMessage.apply(undefined, [undefined, description].concat(<any[]>diagnosticArgs)),
|
||||
changes,
|
||||
kind,
|
||||
moduleSpecifier
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,142 +18,150 @@ namespace ts.codefix {
|
||||
|
||||
switch (token.kind) {
|
||||
case ts.SyntaxKind.Identifier:
|
||||
switch (token.parent.kind) {
|
||||
case ts.SyntaxKind.VariableDeclaration:
|
||||
switch (token.parent.parent.parent.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
const forStatement = <ForStatement>token.parent.parent.parent;
|
||||
const forInitializer = <VariableDeclarationList>forStatement.initializer;
|
||||
if (forInitializer.declarations.length === 1) {
|
||||
return deleteNode(forInitializer);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
case SyntaxKind.ForOfStatement:
|
||||
const forOfStatement = <ForOfStatement>token.parent.parent.parent;
|
||||
if (forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
|
||||
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
|
||||
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
|
||||
}
|
||||
break;
|
||||
|
||||
case SyntaxKind.ForInStatement:
|
||||
// There is no valid fix in the case of:
|
||||
// for .. in
|
||||
return undefined;
|
||||
|
||||
case SyntaxKind.CatchClause:
|
||||
const catchClause = <CatchClause>token.parent.parent;
|
||||
const parameter = catchClause.variableDeclaration.getChildren()[0];
|
||||
return deleteNode(parameter);
|
||||
|
||||
default:
|
||||
const variableStatement = <VariableStatement>token.parent.parent.parent;
|
||||
if (variableStatement.declarationList.declarations.length === 1) {
|
||||
return deleteNode(variableStatement);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
}
|
||||
// TODO: #14885
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.TypeParameter:
|
||||
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
|
||||
if (typeParameters.length === 1) {
|
||||
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1);
|
||||
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end);
|
||||
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
return deleteNodeRange(previousToken, nextToken);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
case ts.SyntaxKind.Parameter:
|
||||
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
|
||||
if (functionDeclaration.parameters.length === 1) {
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where 'import a = A;'
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
|
||||
return deleteNode(importEquals);
|
||||
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
const namedImports = <NamedImports>token.parent.parent;
|
||||
if (namedImports.elements.length === 1) {
|
||||
// Only 1 import and it is unused. So the entire declaration should be removed.
|
||||
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importSpec);
|
||||
}
|
||||
else {
|
||||
// delete import specifier
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where "import d, * as ns from './file'"
|
||||
// or "'import {a, b as ns} from './file'"
|
||||
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
|
||||
const importClause = <ImportClause>token.parent;
|
||||
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
|
||||
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
// import |d,| * as ns from './file'
|
||||
const start = importClause.name.getStart(sourceFile);
|
||||
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
|
||||
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
|
||||
// shift first non-whitespace position after comma to the start position of the node
|
||||
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) });
|
||||
}
|
||||
else {
|
||||
return deleteNode(importClause.name);
|
||||
}
|
||||
}
|
||||
|
||||
case SyntaxKind.NamespaceImport:
|
||||
const namespaceImport = <NamespaceImport>token.parent;
|
||||
if (namespaceImport.name === token && !(<ImportClause>namespaceImport.parent).name) {
|
||||
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1);
|
||||
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
|
||||
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart);
|
||||
return deleteRange({ pos: startPosition, end: namespaceImport.end });
|
||||
}
|
||||
return deleteRange(namespaceImport);
|
||||
}
|
||||
}
|
||||
break;
|
||||
return deleteIdentifier();
|
||||
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.NamespaceImport:
|
||||
return deleteNode(token.parent);
|
||||
|
||||
default:
|
||||
return deleteDefault();
|
||||
}
|
||||
if (isDeclarationName(token)) {
|
||||
return deleteNode(token.parent);
|
||||
|
||||
function deleteDefault() {
|
||||
if (isDeclarationName(token)) {
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
else if (isLiteralComputedPropertyDeclarationName(token)) {
|
||||
return deleteNode(token.parent.parent);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else if (isLiteralComputedPropertyDeclarationName(token)) {
|
||||
return deleteNode(token.parent.parent);
|
||||
|
||||
function deleteIdentifier(): CodeAction[] | undefined {
|
||||
switch (token.parent.kind) {
|
||||
case ts.SyntaxKind.VariableDeclaration:
|
||||
return deleteVariableDeclaration(<ts.VariableDeclaration>token.parent);
|
||||
|
||||
case SyntaxKind.TypeParameter:
|
||||
const typeParameters = (<DeclarationWithTypeParameters>token.parent.parent).typeParameters;
|
||||
if (typeParameters.length === 1) {
|
||||
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1);
|
||||
if (!previousToken || previousToken.kind !== SyntaxKind.LessThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end);
|
||||
if (!nextToken || nextToken.kind !== SyntaxKind.GreaterThanToken) {
|
||||
return deleteRange(typeParameters);
|
||||
}
|
||||
return deleteNodeRange(previousToken, nextToken);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
case ts.SyntaxKind.Parameter:
|
||||
const functionDeclaration = <FunctionDeclaration>token.parent.parent;
|
||||
if (functionDeclaration.parameters.length === 1) {
|
||||
return deleteNode(token.parent);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where 'import a = A;'
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
const importEquals = getAncestor(token, SyntaxKind.ImportEqualsDeclaration);
|
||||
return deleteNode(importEquals);
|
||||
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
const namedImports = <NamedImports>token.parent.parent;
|
||||
if (namedImports.elements.length === 1) {
|
||||
// Only 1 import and it is unused. So the entire declaration should be removed.
|
||||
const importSpec = getAncestor(token, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importSpec);
|
||||
}
|
||||
else {
|
||||
// delete import specifier
|
||||
return deleteNodeInList(token.parent);
|
||||
}
|
||||
|
||||
// handle case where "import d, * as ns from './file'"
|
||||
// or "'import {a, b as ns} from './file'"
|
||||
case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *'
|
||||
const importClause = <ImportClause>token.parent;
|
||||
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
|
||||
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
// import |d,| * as ns from './file'
|
||||
const start = importClause.name.getStart(sourceFile);
|
||||
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
|
||||
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
|
||||
// shift first non-whitespace position after comma to the start position of the node
|
||||
return deleteRange({ pos: start, end: skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true) });
|
||||
}
|
||||
else {
|
||||
return deleteNode(importClause.name);
|
||||
}
|
||||
}
|
||||
|
||||
case SyntaxKind.NamespaceImport:
|
||||
const namespaceImport = <NamespaceImport>token.parent;
|
||||
if (namespaceImport.name === token && !(<ImportClause>namespaceImport.parent).name) {
|
||||
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
|
||||
return deleteNode(importDecl);
|
||||
}
|
||||
else {
|
||||
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1);
|
||||
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
|
||||
const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart);
|
||||
return deleteRange({ pos: startPosition, end: namespaceImport.end });
|
||||
}
|
||||
return deleteRange(namespaceImport);
|
||||
}
|
||||
|
||||
default:
|
||||
return deleteDefault();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
|
||||
// token.parent is a variableDeclaration
|
||||
function deleteVariableDeclaration(varDecl: ts.VariableDeclaration): CodeAction[] | undefined {
|
||||
switch (varDecl.parent.parent.kind) {
|
||||
case SyntaxKind.ForStatement:
|
||||
const forStatement = <ForStatement>varDecl.parent.parent;
|
||||
const forInitializer = <VariableDeclarationList>forStatement.initializer;
|
||||
if (forInitializer.declarations.length === 1) {
|
||||
return deleteNode(forInitializer);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(varDecl);
|
||||
}
|
||||
|
||||
case SyntaxKind.ForOfStatement:
|
||||
const forOfStatement = <ForOfStatement>varDecl.parent.parent;
|
||||
Debug.assert(forOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList);
|
||||
const forOfInitializer = <VariableDeclarationList>forOfStatement.initializer;
|
||||
return replaceNode(forOfInitializer.declarations[0], createObjectLiteral());
|
||||
|
||||
case SyntaxKind.ForInStatement:
|
||||
// There is no valid fix in the case of:
|
||||
// for .. in
|
||||
return undefined;
|
||||
|
||||
default:
|
||||
const variableStatement = <VariableStatement>varDecl.parent.parent;
|
||||
if (variableStatement.declarationList.declarations.length === 1) {
|
||||
return deleteNode(variableStatement);
|
||||
}
|
||||
else {
|
||||
return deleteNodeInList(varDecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteNode(n: Node) {
|
||||
|
||||
+236
-36
@@ -18,7 +18,7 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag } = completionData;
|
||||
const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords } = completionData;
|
||||
|
||||
if (requestJsDocTagName) {
|
||||
// If the current position is a jsDoc tag name, only tag names should be provided for completion
|
||||
@@ -52,7 +52,7 @@ namespace ts.Completions {
|
||||
sortText: "0",
|
||||
});
|
||||
}
|
||||
else {
|
||||
else if (!hasFilteredClassMemberKeywords) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -60,8 +60,11 @@ namespace ts.Completions {
|
||||
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log);
|
||||
}
|
||||
|
||||
if (hasFilteredClassMemberKeywords) {
|
||||
addRange(entries, classMemberKeywordCompletions);
|
||||
}
|
||||
// Add keywords if this is not a member completion list
|
||||
if (!isMemberCompletion && !requestJsDocTag && !requestJsDocTagName) {
|
||||
else if (!isMemberCompletion && !requestJsDocTag && !requestJsDocTagName) {
|
||||
addRange(entries, keywordCompletions);
|
||||
}
|
||||
|
||||
@@ -221,11 +224,12 @@ namespace ts.Completions {
|
||||
function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo, typeChecker: TypeChecker): CompletionInfo | undefined {
|
||||
const candidates: Signature[] = [];
|
||||
const entries: CompletionEntry[] = [];
|
||||
const uniques = createMap<true>();
|
||||
|
||||
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates);
|
||||
|
||||
for (const candidate of candidates) {
|
||||
addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker);
|
||||
addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker, uniques);
|
||||
}
|
||||
|
||||
if (entries.length) {
|
||||
@@ -258,25 +262,29 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>, typeChecker: TypeChecker): void {
|
||||
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>, typeChecker: TypeChecker, uniques = createMap<true>()): void {
|
||||
if (type && type.flags & TypeFlags.TypeParameter) {
|
||||
type = typeChecker.getApparentType(type);
|
||||
type = typeChecker.getBaseConstraintOfType(type);
|
||||
}
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
for (const t of (<UnionType>type).types) {
|
||||
addStringLiteralCompletionsFromType(t, result, typeChecker);
|
||||
addStringLiteralCompletionsFromType(t, result, typeChecker, uniques);
|
||||
}
|
||||
}
|
||||
else if (type.flags & TypeFlags.StringLiteral) {
|
||||
result.push({
|
||||
name: (<StringLiteralType>type).value,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
kind: ScriptElementKind.variableElement,
|
||||
sortText: "0"
|
||||
});
|
||||
const name = (<StringLiteralType>type).value;
|
||||
if (!uniques.has(name)) {
|
||||
uniques.set(name, true);
|
||||
result.push({
|
||||
name,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
kind: ScriptElementKind.variableElement,
|
||||
sortText: "0"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +359,7 @@ namespace ts.Completions {
|
||||
|
||||
start = timestamp();
|
||||
// Completion not allowed inside comments, bail out if this is the case
|
||||
const insideComment = isInsideComment(sourceFile, currentToken, position);
|
||||
const insideComment = isInComment(sourceFile, position, currentToken);
|
||||
log("getCompletionData: Is inside comment: " + (timestamp() - start));
|
||||
|
||||
if (insideComment) {
|
||||
@@ -406,7 +414,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
if (requestJsDocTagName || requestJsDocTag) {
|
||||
return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, requestJsDocTagName, requestJsDocTag };
|
||||
return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords: false };
|
||||
}
|
||||
|
||||
if (!insideJsDocTagExpression) {
|
||||
@@ -505,6 +513,7 @@ namespace ts.Completions {
|
||||
let isGlobalCompletion = false;
|
||||
let isMemberCompletion: boolean;
|
||||
let isNewIdentifierLocation: boolean;
|
||||
let hasFilteredClassMemberKeywords = false;
|
||||
let symbols: Symbol[] = [];
|
||||
|
||||
if (isRightOfDot) {
|
||||
@@ -542,7 +551,7 @@ namespace ts.Completions {
|
||||
|
||||
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
|
||||
|
||||
return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), requestJsDocTagName, requestJsDocTag };
|
||||
return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords };
|
||||
|
||||
function getTypeScriptMemberSymbols(): void {
|
||||
// Right of dot member completion list
|
||||
@@ -599,6 +608,7 @@ namespace ts.Completions {
|
||||
function tryGetGlobalSymbols(): boolean {
|
||||
let objectLikeContainer: ObjectLiteralExpression | BindingPattern;
|
||||
let namedImportsOrExports: NamedImportsOrExports;
|
||||
let classLikeContainer: ClassLikeDeclaration;
|
||||
let jsxContainer: JsxOpeningLikeElement;
|
||||
|
||||
if (objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken)) {
|
||||
@@ -611,6 +621,12 @@ namespace ts.Completions {
|
||||
return tryGetImportOrExportClauseCompletionSymbols(namedImportsOrExports);
|
||||
}
|
||||
|
||||
if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) {
|
||||
// cursor inside class declaration
|
||||
getGetClassLikeCompletionSymbols(classLikeContainer);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (jsxContainer = tryGetContainingJsxElement(contextToken)) {
|
||||
let attrsType: Type;
|
||||
if ((jsxContainer.kind === SyntaxKind.JsxSelfClosingElement) || (jsxContainer.kind === SyntaxKind.JsxOpeningElement)) {
|
||||
@@ -813,19 +829,16 @@ namespace ts.Completions {
|
||||
// We're looking up possible property names from contextual/inferred/declared type.
|
||||
isMemberCompletion = true;
|
||||
|
||||
let typeForObject: Type;
|
||||
let typeMembers: Symbol[];
|
||||
let existingMembers: Declaration[];
|
||||
|
||||
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
// We are completing on contextual types, but may also include properties
|
||||
// other than those within the declared type.
|
||||
isNewIdentifierLocation = true;
|
||||
|
||||
// If the object literal is being assigned to something of type 'null | { hello: string }',
|
||||
// it clearly isn't trying to satisfy the 'null' type. So we grab the non-nullable type if possible.
|
||||
typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
|
||||
typeForObject = typeForObject && typeForObject.getNonNullableType();
|
||||
|
||||
const typeForObject = typeChecker.getContextualType(<ObjectLiteralExpression>objectLikeContainer);
|
||||
if (!typeForObject) return false;
|
||||
typeMembers = typeChecker.getAllPossiblePropertiesOfType(typeForObject);
|
||||
existingMembers = (<ObjectLiteralExpression>objectLikeContainer).properties;
|
||||
}
|
||||
else if (objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern) {
|
||||
@@ -849,7 +862,10 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
if (canGetType) {
|
||||
typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
|
||||
const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
|
||||
if (!typeForObject) return false;
|
||||
// In a binding pattern, get only known properties. Everywhere else we will get all possible properties.
|
||||
typeMembers = typeChecker.getPropertiesOfType(typeForObject);
|
||||
existingMembers = (<ObjectBindingPattern>objectLikeContainer).elements;
|
||||
}
|
||||
}
|
||||
@@ -861,11 +877,6 @@ namespace ts.Completions {
|
||||
Debug.fail("Expected object literal or binding pattern, got " + objectLikeContainer.kind);
|
||||
}
|
||||
|
||||
if (!typeForObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const typeMembers = typeChecker.getPropertiesOfType(typeForObject);
|
||||
if (typeMembers && typeMembers.length > 0) {
|
||||
// Add filtered items to the completion list
|
||||
symbols = filterObjectMembersList(typeMembers, existingMembers);
|
||||
@@ -913,6 +924,62 @@ namespace ts.Completions {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates relevant symbols for completion in class declaration
|
||||
* Relevant symbols are stored in the captured 'symbols' variable.
|
||||
*/
|
||||
function getGetClassLikeCompletionSymbols(classLikeDeclaration: ClassLikeDeclaration) {
|
||||
// We're looking up possible property names from parent type.
|
||||
isMemberCompletion = true;
|
||||
// Declaring new property/method/accessor
|
||||
isNewIdentifierLocation = true;
|
||||
// Has keywords for class elements
|
||||
hasFilteredClassMemberKeywords = true;
|
||||
|
||||
const baseTypeNode = getClassExtendsHeritageClauseElement(classLikeDeclaration);
|
||||
const implementsTypeNodes = getClassImplementsHeritageClauseElements(classLikeDeclaration);
|
||||
if (baseTypeNode || implementsTypeNodes) {
|
||||
const classElement = contextToken.parent;
|
||||
let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement);
|
||||
// If this is context token is not something we are editing now, consider if this would lead to be modifier
|
||||
if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
|
||||
switch (contextToken.getText()) {
|
||||
case "private":
|
||||
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
|
||||
break;
|
||||
case "static":
|
||||
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No member list for private methods
|
||||
if (!(classElementModifierFlags & ModifierFlags.Private)) {
|
||||
let baseClassTypeToGetPropertiesFrom: Type;
|
||||
if (baseTypeNode) {
|
||||
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode);
|
||||
if (classElementModifierFlags & ModifierFlags.Static) {
|
||||
// Use static class to get property symbols from
|
||||
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation(
|
||||
baseClassTypeToGetPropertiesFrom.symbol, classLikeDeclaration);
|
||||
}
|
||||
}
|
||||
const implementedInterfaceTypePropertySymbols = (classElementModifierFlags & ModifierFlags.Static) ?
|
||||
undefined :
|
||||
flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode)));
|
||||
|
||||
// List of property symbols of base type that are not private and already implemented
|
||||
symbols = filterClassMembersList(
|
||||
baseClassTypeToGetPropertiesFrom ?
|
||||
typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) :
|
||||
undefined,
|
||||
implementedInterfaceTypePropertySymbols,
|
||||
classLikeDeclaration.members,
|
||||
classElementModifierFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the immediate owning object literal or binding pattern of a context token,
|
||||
* on the condition that one exists and that the context implies completion should be given.
|
||||
@@ -953,6 +1020,49 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isFromClassElementDeclaration(node: Node) {
|
||||
return isClassElement(node.parent) && isClassLike(node.parent.parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the immediate owning class declaration of a context token,
|
||||
* on the condition that one exists and that the context implies completion should be given.
|
||||
*/
|
||||
function tryGetClassLikeCompletionContainer(contextToken: Node): ClassLikeDeclaration {
|
||||
if (contextToken) {
|
||||
switch (contextToken.kind) {
|
||||
case SyntaxKind.OpenBraceToken: // class c { |
|
||||
if (isClassLike(contextToken.parent)) {
|
||||
return contextToken.parent;
|
||||
}
|
||||
break;
|
||||
|
||||
// class c {getValue(): number; | }
|
||||
case SyntaxKind.CommaToken:
|
||||
case SyntaxKind.SemicolonToken:
|
||||
// class c { method() { } | }
|
||||
case SyntaxKind.CloseBraceToken:
|
||||
if (isClassLike(location)) {
|
||||
return location;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isFromClassElementDeclaration(contextToken) &&
|
||||
(isClassMemberCompletionKeyword(contextToken.kind) ||
|
||||
isClassMemberCompletionKeywordText(contextToken.getText()))) {
|
||||
return contextToken.parent.parent as ClassLikeDeclaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// class c { method() { } | method2() { } }
|
||||
if (location && location.kind === SyntaxKind.SyntaxList && isClassLike(location.parent)) {
|
||||
return location.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement {
|
||||
if (contextToken) {
|
||||
const parent = contextToken.parent;
|
||||
@@ -1081,7 +1191,7 @@ namespace ts.Completions {
|
||||
isFunction(containingNodeKind);
|
||||
|
||||
case SyntaxKind.StaticKeyword:
|
||||
return containingNodeKind === SyntaxKind.PropertyDeclaration;
|
||||
return containingNodeKind === SyntaxKind.PropertyDeclaration && !isClassLike(contextToken.parent.parent);
|
||||
|
||||
case SyntaxKind.DotDotDotToken:
|
||||
return containingNodeKind === SyntaxKind.Parameter ||
|
||||
@@ -1098,13 +1208,17 @@ namespace ts.Completions {
|
||||
containingNodeKind === SyntaxKind.ExportSpecifier ||
|
||||
containingNodeKind === SyntaxKind.NamespaceImport;
|
||||
|
||||
case SyntaxKind.GetKeyword:
|
||||
case SyntaxKind.SetKeyword:
|
||||
if (isFromClassElementDeclaration(contextToken)) {
|
||||
return false;
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.ClassKeyword:
|
||||
case SyntaxKind.EnumKeyword:
|
||||
case SyntaxKind.InterfaceKeyword:
|
||||
case SyntaxKind.FunctionKeyword:
|
||||
case SyntaxKind.VarKeyword:
|
||||
case SyntaxKind.GetKeyword:
|
||||
case SyntaxKind.SetKeyword:
|
||||
case SyntaxKind.ImportKeyword:
|
||||
case SyntaxKind.LetKeyword:
|
||||
case SyntaxKind.ConstKeyword:
|
||||
@@ -1113,6 +1227,13 @@ namespace ts.Completions {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the previous token is keyword correspoding to class member completion keyword
|
||||
// there will be completion available here
|
||||
if (isClassMemberCompletionKeywordText(contextToken.getText()) &&
|
||||
isFromClassElementDeclaration(contextToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Previous token may have been a keyword that was converted to an identifier.
|
||||
switch (contextToken.getText()) {
|
||||
case "abstract":
|
||||
@@ -1159,7 +1280,7 @@ namespace ts.Completions {
|
||||
|
||||
for (const element of namedImportsOrExports) {
|
||||
// If this is the current item we are editing right now, do not filter it out
|
||||
if (element.getStart() <= position && position <= element.getEnd()) {
|
||||
if (isCurrentlyEditingNode(element)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1198,7 +1319,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
// If this is the current item we are editing right now, do not filter it out
|
||||
if (m.getStart() <= position && position <= m.getEnd()) {
|
||||
if (isCurrentlyEditingNode(m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1214,7 +1335,7 @@ namespace ts.Completions {
|
||||
// TODO(jfreeman): Account for computed property name
|
||||
// NOTE: if one only performs this step when m.name is an identifier,
|
||||
// things like '__proto__' are not filtered out.
|
||||
existingName = (<Identifier>m.name).text;
|
||||
existingName = (getNameOfDeclaration(m) as Identifier).text;
|
||||
}
|
||||
|
||||
existingMemberNames.set(existingName, true);
|
||||
@@ -1223,6 +1344,58 @@ namespace ts.Completions {
|
||||
return filter(contextualMemberSymbols, m => !existingMemberNames.get(m.name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out completion suggestions for class elements.
|
||||
*
|
||||
* @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
|
||||
*/
|
||||
function filterClassMembersList(baseSymbols: Symbol[], implementingTypeSymbols: Symbol[], existingMembers: ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] {
|
||||
const existingMemberNames = createMap<boolean>();
|
||||
for (const m of existingMembers) {
|
||||
// Ignore omitted expressions for missing members
|
||||
if (m.kind !== SyntaxKind.PropertyDeclaration &&
|
||||
m.kind !== SyntaxKind.MethodDeclaration &&
|
||||
m.kind !== SyntaxKind.GetAccessor &&
|
||||
m.kind !== SyntaxKind.SetAccessor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is the current item we are editing right now, do not filter it out
|
||||
if (isCurrentlyEditingNode(m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dont filter member even if the name matches if it is declared private in the list
|
||||
if (hasModifier(m, ModifierFlags.Private)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// do not filter it out if the static presence doesnt match
|
||||
const mIsStatic = hasModifier(m, ModifierFlags.Static);
|
||||
const currentElementIsStatic = !!(currentClassElementModifierFlags & ModifierFlags.Static);
|
||||
if ((mIsStatic && !currentElementIsStatic) ||
|
||||
(!mIsStatic && currentElementIsStatic)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const existingName = getPropertyNameForPropertyNameNode(m.name);
|
||||
if (existingName) {
|
||||
existingMemberNames.set(existingName, true);
|
||||
}
|
||||
}
|
||||
|
||||
return concatenate(
|
||||
filter(baseSymbols, baseProperty => isValidProperty(baseProperty, ModifierFlags.Private)),
|
||||
filter(implementingTypeSymbols, implementingProperty => isValidProperty(implementingProperty, ModifierFlags.NonPublicAccessibilityModifier))
|
||||
);
|
||||
|
||||
function isValidProperty(propertySymbol: Symbol, inValidModifierFlags: ModifierFlags) {
|
||||
return !existingMemberNames.get(propertySymbol.name) &&
|
||||
propertySymbol.getDeclarations() &&
|
||||
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & inValidModifierFlags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out completion suggestions from 'symbols' according to existing JSX attributes.
|
||||
*
|
||||
@@ -1233,7 +1406,7 @@ namespace ts.Completions {
|
||||
const seenNames = createMap<boolean>();
|
||||
for (const attr of attributes) {
|
||||
// If this is the current item we are editing right now, do not filter it out
|
||||
if (attr.getStart() <= position && position <= attr.getEnd()) {
|
||||
if (isCurrentlyEditingNode(attr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1244,6 +1417,10 @@ namespace ts.Completions {
|
||||
|
||||
return filter(symbols, a => !seenNames.get(a.name));
|
||||
}
|
||||
|
||||
function isCurrentlyEditingNode(node: Node): boolean {
|
||||
return node.getStart() <= position && position <= node.getEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1306,6 +1483,29 @@ namespace ts.Completions {
|
||||
});
|
||||
}
|
||||
|
||||
function isClassMemberCompletionKeyword(kind: SyntaxKind) {
|
||||
switch (kind) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
case SyntaxKind.ProtectedKeyword:
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
case SyntaxKind.AbstractKeyword:
|
||||
case SyntaxKind.StaticKeyword:
|
||||
case SyntaxKind.ConstructorKeyword:
|
||||
case SyntaxKind.ReadonlyKeyword:
|
||||
case SyntaxKind.GetKeyword:
|
||||
case SyntaxKind.SetKeyword:
|
||||
case SyntaxKind.AsyncKeyword:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function isClassMemberCompletionKeywordText(text: string) {
|
||||
return isClassMemberCompletionKeyword(stringToToken(text));
|
||||
}
|
||||
|
||||
const classMemberKeywordCompletions = filter(keywordCompletions, entry =>
|
||||
isClassMemberCompletionKeywordText(entry.name));
|
||||
|
||||
function isEqualityExpression(node: Node): node is BinaryExpression {
|
||||
return isBinaryExpression(node) && isEqualityOperatorKind(node.operatorToken.kind);
|
||||
}
|
||||
|
||||
@@ -108,10 +108,6 @@ namespace ts.FindAllReferences {
|
||||
switch (def.type) {
|
||||
case "symbol": {
|
||||
const { symbol, node } = def;
|
||||
const declarations = symbol.declarations;
|
||||
if (!declarations || declarations.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, node, checker);
|
||||
const name = displayParts.map(p => p.text).join("");
|
||||
return { node, name, kind, displayParts };
|
||||
@@ -285,11 +281,6 @@ namespace ts.FindAllReferences.Core {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// The symbol was an internal symbol and does not have a declaration e.g. undefined symbol
|
||||
if (!symbol.declarations || !symbol.declarations.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getReferencedSymbolsForSymbol(symbol, node, sourceFiles, checker, cancellationToken, options);
|
||||
}
|
||||
|
||||
@@ -559,7 +550,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
function isObjectBindingPatternElementWithoutPropertyName(symbol: Symbol): boolean {
|
||||
const bindingElement = <BindingElement>getDeclarationOfKind(symbol, SyntaxKind.BindingElement);
|
||||
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
|
||||
return bindingElement &&
|
||||
bindingElement.parent.kind === SyntaxKind.ObjectBindingPattern &&
|
||||
!bindingElement.propertyName;
|
||||
@@ -567,7 +558,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
function getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol: Symbol, checker: TypeChecker): Symbol | undefined {
|
||||
if (isObjectBindingPatternElementWithoutPropertyName(symbol)) {
|
||||
const bindingElement = <BindingElement>getDeclarationOfKind(symbol, SyntaxKind.BindingElement);
|
||||
const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
|
||||
const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent);
|
||||
return typeOfPattern && checker.getPropertyOfType(typeOfPattern, (<Identifier>bindingElement.name).text);
|
||||
}
|
||||
@@ -646,7 +637,9 @@ namespace ts.FindAllReferences.Core {
|
||||
return parent ? scope.getSourceFile() : scope;
|
||||
}
|
||||
|
||||
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, start: number, end: number): number[] {
|
||||
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile, fullStart = false): number[] {
|
||||
const start = fullStart ? container.getFullStart() : container.getStart(sourceFile);
|
||||
const end = container.getEnd();
|
||||
const positions: number[] = [];
|
||||
|
||||
/// TODO: Cache symbol existence for files to save text search
|
||||
@@ -685,16 +678,11 @@ namespace ts.FindAllReferences.Core {
|
||||
const references: Entry[] = [];
|
||||
const sourceFile = container.getSourceFile();
|
||||
const labelName = targetLabel.text;
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container.getStart(), container.getEnd());
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container);
|
||||
for (const position of possiblePositions) {
|
||||
const node = getTouchingWord(sourceFile, position);
|
||||
if (!node || node.getWidth() !== labelName.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only pick labels that are either the target label, or have a target that is the target label
|
||||
if (node === targetLabel ||
|
||||
(isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel)) {
|
||||
if (node && (node === targetLabel || (isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel))) {
|
||||
references.push(nodeEntry(node));
|
||||
}
|
||||
}
|
||||
@@ -706,15 +694,14 @@ namespace ts.FindAllReferences.Core {
|
||||
// Compare the length so we filter out strict superstrings of the symbol we are looking for
|
||||
switch (node && node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
return node.getWidth() === searchSymbolName.length;
|
||||
return unescapeIdentifier((node as Identifier).text).length === searchSymbolName.length;
|
||||
|
||||
case SyntaxKind.StringLiteral:
|
||||
return (isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) &&
|
||||
// For string literals we have two additional chars for the quotes
|
||||
node.getWidth() === searchSymbolName.length + 2;
|
||||
(node as StringLiteral).text.length === searchSymbolName.length;
|
||||
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return isLiteralNameOfPropertyDeclarationOrIndexAccess(node) && node.getWidth() === searchSymbolName.length;
|
||||
return isLiteralNameOfPropertyDeclarationOrIndexAccess(node) && (node as NumericLiteral).text.length === searchSymbolName.length;
|
||||
|
||||
default:
|
||||
return false;
|
||||
@@ -731,7 +718,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, references: Push<NodeEntry>): void {
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile.getStart(), sourceFile.getEnd());
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText);
|
||||
for (const position of possiblePositions) {
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position);
|
||||
if (referenceLocation.kind === kind) {
|
||||
@@ -755,9 +742,7 @@ namespace ts.FindAllReferences.Core {
|
||||
return;
|
||||
}
|
||||
|
||||
const start = state.findInComments ? container.getFullStart() : container.getStart();
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, search.text, start, container.getEnd());
|
||||
for (const position of possiblePositions) {
|
||||
for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container, /*fullStart*/ state.findInComments)) {
|
||||
getReferencesAtLocation(sourceFile, position, search, state);
|
||||
}
|
||||
}
|
||||
@@ -908,7 +893,7 @@ namespace ts.FindAllReferences.Core {
|
||||
* position of property accessing, the referenceEntry of such position will be handled in the first case.
|
||||
*/
|
||||
if (!(flags & SymbolFlags.Transient) && search.includes(shorthandValueSymbol)) {
|
||||
addReference(valueDeclaration.name, shorthandValueSymbol, search.location, state);
|
||||
addReference(getNameOfDeclaration(valueDeclaration), shorthandValueSymbol, search.location, state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1201,7 +1186,7 @@ namespace ts.FindAllReferences.Core {
|
||||
const references: Entry[] = [];
|
||||
|
||||
const sourceFile = searchSpaceNode.getSourceFile();
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode.getStart(), searchSpaceNode.getEnd());
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode);
|
||||
for (const position of possiblePositions) {
|
||||
const node = getTouchingWord(sourceFile, position);
|
||||
|
||||
@@ -1263,13 +1248,13 @@ namespace ts.FindAllReferences.Core {
|
||||
if (searchSpaceNode.kind === SyntaxKind.SourceFile) {
|
||||
forEach(sourceFiles, sourceFile => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", sourceFile.getStart(), sourceFile.getEnd());
|
||||
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this");
|
||||
getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, references);
|
||||
});
|
||||
}
|
||||
else {
|
||||
const sourceFile = searchSpaceNode.getSourceFile();
|
||||
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode.getStart(), searchSpaceNode.getEnd());
|
||||
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode);
|
||||
getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references);
|
||||
}
|
||||
|
||||
@@ -1323,7 +1308,7 @@ namespace ts.FindAllReferences.Core {
|
||||
|
||||
for (const sourceFile of sourceFiles) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, node.text, sourceFile.getStart(), sourceFile.getEnd());
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, node.text);
|
||||
getReferencesForStringLiteralInFile(sourceFile, node.text, possiblePositions, references);
|
||||
}
|
||||
|
||||
|
||||
@@ -362,7 +362,7 @@ namespace ts.formatting {
|
||||
sourceFile: SourceFileLike): TextChange[] {
|
||||
|
||||
// formatting context is used by rules provider
|
||||
const formattingContext = new FormattingContext(sourceFile, requestKind);
|
||||
const formattingContext = new FormattingContext(sourceFile, requestKind, options);
|
||||
let previousRangeHasError: boolean;
|
||||
let previousRange: TextRangeWithKind;
|
||||
let previousParent: Node;
|
||||
@@ -487,7 +487,7 @@ namespace ts.formatting {
|
||||
// falls through
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.Parameter:
|
||||
return (<Declaration>node).name.kind;
|
||||
return getNameOfDeclaration(<Declaration>node).kind;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace ts.formatting {
|
||||
private contextNodeBlockIsOnOneLine: boolean;
|
||||
private nextNodeBlockIsOnOneLine: boolean;
|
||||
|
||||
constructor(public readonly sourceFile: SourceFileLike, public formattingRequestKind: FormattingRequestKind) {
|
||||
constructor(public readonly sourceFile: SourceFileLike, public formattingRequestKind: FormattingRequestKind, public options: ts.FormatCodeSettings) {
|
||||
}
|
||||
|
||||
public updateContext(currentRange: TextRangeWithKind, currentTokenParent: Node, nextRange: TextRangeWithKind, nextTokenParent: Node, commonParent: Node) {
|
||||
|
||||
@@ -12,12 +12,11 @@ namespace ts.formatting {
|
||||
|
||||
static Any: RuleOperationContext = new RuleOperationContext();
|
||||
|
||||
|
||||
public IsAny(): boolean {
|
||||
return this === RuleOperationContext.Any;
|
||||
}
|
||||
|
||||
public InContext(context: FormattingContext): boolean {
|
||||
public InContext(context: FormattingContext): boolean {
|
||||
if (this.IsAny()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace ts.formatting {
|
||||
public NoSpaceBeforeComma: Rule;
|
||||
|
||||
public SpaceAfterCertainKeywords: Rule;
|
||||
public NoSpaceAfterNewKeywordOnConstructorSignature: Rule;
|
||||
public SpaceAfterLetConstInVariableDeclaration: Rule;
|
||||
public NoSpaceBeforeOpenParenInFuncCall: Rule;
|
||||
public SpaceAfterFunctionInFuncDecl: Rule;
|
||||
@@ -147,6 +148,9 @@ namespace ts.formatting {
|
||||
// These rules are higher in priority than user-configurable rules.
|
||||
public HighPriorityCommonRules: Rule[];
|
||||
|
||||
// These rules are applied after high priority rules.
|
||||
public UserConfigurableRules: Rule[];
|
||||
|
||||
// These rules are lower in priority than user-configurable rules.
|
||||
public LowPriorityCommonRules: Rule[];
|
||||
|
||||
@@ -279,7 +283,9 @@ namespace ts.formatting {
|
||||
this.NoSpaceAfterDot = new Rule(RuleDescriptor.create3(SyntaxKind.DotToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// No space before and after indexer
|
||||
this.NoSpaceBeforeOpenBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeOpenBracket = new Rule(
|
||||
RuleDescriptor.create2(Shared.TokenRange.AnyExcept(SyntaxKind.AsyncKeyword), SyntaxKind.OpenBracketToken),
|
||||
RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterCloseBracket = new Rule(RuleDescriptor.create3(SyntaxKind.CloseBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNotBeforeBlockInFunctionDeclarationContext), RuleAction.Delete));
|
||||
|
||||
// Place a space before open brace in a function declaration
|
||||
@@ -295,10 +301,10 @@ namespace ts.formatting {
|
||||
this.SpaceBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}.
|
||||
this.SpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsBraceWrappedContext), RuleAction.Space));
|
||||
this.SpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsBraceWrappedContext), RuleAction.Space));
|
||||
this.NoSpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.SpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsBraceWrappedContext), RuleAction.Space));
|
||||
this.SpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsBraceWrappedContext), RuleAction.Space));
|
||||
this.NoSpaceAfterOpenBrace = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeCloseBrace = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBetweenEmptyBraceBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsObjectContext), RuleAction.Delete));
|
||||
|
||||
// Insert new line after { and before } in multi-line contexts.
|
||||
@@ -331,11 +337,12 @@ namespace ts.formatting {
|
||||
this.NoSpaceBeforeComma = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CommaToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceAfterNewKeywordOnConstructorSignature = new Rule(RuleDescriptor.create1(SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsConstructorSignatureContext), RuleAction.Delete));
|
||||
this.SpaceAfterLetConstInVariableDeclaration = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsStartOfVariableDeclarationList), RuleAction.Space));
|
||||
this.NoSpaceBeforeOpenParenInFuncCall = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionCallOrNewContext, Rules.IsPreviousTokenNotComma), RuleAction.Delete));
|
||||
this.SpaceAfterFunctionInFuncDecl = new Rule(RuleDescriptor.create3(SyntaxKind.FunctionKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
this.SpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Delete));
|
||||
this.SpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceBeforeFunctionParenthesis"), Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeOpenParenInFuncDecl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), Rules.IsNonJsxSameLineTokenContext, Rules.IsFunctionDeclContext), RuleAction.Delete));
|
||||
this.SpaceAfterVoidOperator = new Rule(RuleDescriptor.create3(SyntaxKind.VoidKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsVoidOpContext), RuleAction.Space));
|
||||
|
||||
this.NoSpaceBetweenReturnAndSemicolon = new Rule(RuleDescriptor.create1(SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
@@ -357,9 +364,8 @@ namespace ts.formatting {
|
||||
|
||||
// TypeScript-specific higher priority rules
|
||||
|
||||
// Treat constructor as an identifier in a function declaration, and remove spaces between constructor and following left parentheses
|
||||
this.SpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.SpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterConstructor"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceAfterConstructor = new Rule(RuleDescriptor.create1(SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterConstructor"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// Use of module as a function call. e.g.: import m2 = module("m2");
|
||||
this.NoSpaceAfterModuleImport = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword]), SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
@@ -416,6 +422,72 @@ namespace ts.formatting {
|
||||
// No space before non-null assertion operator
|
||||
this.NoSpaceBeforeNonNullAssertionOperator = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.ExclamationToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonNullAssertionContext), RuleAction.Delete));
|
||||
|
||||
///
|
||||
/// Rules controlled by user options
|
||||
///
|
||||
|
||||
// Insert space after comma delimiter
|
||||
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterCommaDelimiter"), Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
|
||||
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext), RuleAction.Delete));
|
||||
|
||||
// Insert space before and after binary operators
|
||||
this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
|
||||
this.SpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
|
||||
|
||||
// Insert space after keywords in control flow statements
|
||||
this.SpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterKeywordsInControlFlowStatements"), Rules.IsControlDeclContext), RuleAction.Space));
|
||||
this.NoSpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), Rules.IsControlDeclContext), RuleAction.Delete));
|
||||
|
||||
// Open Brace braces after function
|
||||
// TypeScript: Function can have return types, which can be made of tons of different token kinds
|
||||
this.NewLineBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("placeOpenBraceOnNewLineForFunctions"), Rules.IsFunctionDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Open Brace braces after TypeScript module/class/interface
|
||||
this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("placeOpenBraceOnNewLineForFunctions"), Rules.IsTypeScriptDeclWithBlockContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Open Brace braces after control block
|
||||
this.NewLineBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("placeOpenBraceOnNewLineForControlBlocks"), Rules.IsControlDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Insert space after semicolon in for statement
|
||||
this.SpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterSemicolonInForStatements"), Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Space));
|
||||
this.NoSpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Delete));
|
||||
|
||||
// Insert space after opening and before closing nonempty parenthesis
|
||||
this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// Insert space after opening and before closing nonempty brackets
|
||||
this.SpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.SpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceBetweenBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// Insert space after opening and before closing template string braces
|
||||
this.NoSpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.SpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
|
||||
// No space after { and before } in JSX expression
|
||||
this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
|
||||
this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
|
||||
this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
|
||||
|
||||
// Insert space after function keyword for anonymous functions
|
||||
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), Rules.IsFunctionDeclContext), RuleAction.Delete));
|
||||
|
||||
// No space after type assertion
|
||||
this.NoSpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Delete));
|
||||
this.SpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterTypeAssertion"), Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Space));
|
||||
|
||||
// These rules are higher in priority than user-configurable rules.
|
||||
this.HighPriorityCommonRules = [
|
||||
this.IgnoreBeforeComment, this.IgnoreAfterLineComment,
|
||||
@@ -462,7 +534,27 @@ namespace ts.formatting {
|
||||
this.SpaceBeforeAt,
|
||||
this.NoSpaceAfterAt,
|
||||
this.SpaceAfterDecorator,
|
||||
this.NoSpaceBeforeNonNullAssertionOperator
|
||||
this.NoSpaceBeforeNonNullAssertionOperator,
|
||||
this.NoSpaceAfterNewKeywordOnConstructorSignature
|
||||
];
|
||||
|
||||
// These rules are applied after high priority rules.
|
||||
this.UserConfigurableRules = [
|
||||
this.SpaceAfterConstructor, this.NoSpaceAfterConstructor,
|
||||
this.SpaceAfterComma, this.NoSpaceAfterComma,
|
||||
this.SpaceAfterAnonymousFunctionKeyword, this.NoSpaceAfterAnonymousFunctionKeyword,
|
||||
this.SpaceAfterKeywordInControl, this.NoSpaceAfterKeywordInControl,
|
||||
this.SpaceAfterOpenParen, this.SpaceBeforeCloseParen, this.NoSpaceBetweenParens, this.NoSpaceAfterOpenParen, this.NoSpaceBeforeCloseParen,
|
||||
this.SpaceAfterOpenBracket, this.SpaceBeforeCloseBracket, this.NoSpaceBetweenBrackets, this.NoSpaceAfterOpenBracket, this.NoSpaceBeforeCloseBracket,
|
||||
this.SpaceAfterOpenBrace, this.SpaceBeforeCloseBrace, this.NoSpaceBetweenEmptyBraceBrackets, this.NoSpaceAfterOpenBrace, this.NoSpaceBeforeCloseBrace,
|
||||
this.SpaceAfterTemplateHeadAndMiddle, this.SpaceBeforeTemplateMiddleAndTail, this.NoSpaceAfterTemplateHeadAndMiddle, this.NoSpaceBeforeTemplateMiddleAndTail,
|
||||
this.SpaceAfterOpenBraceInJsxExpression, this.SpaceBeforeCloseBraceInJsxExpression, this.NoSpaceAfterOpenBraceInJsxExpression, this.NoSpaceBeforeCloseBraceInJsxExpression,
|
||||
this.SpaceAfterSemicolonInFor, this.NoSpaceAfterSemicolonInFor,
|
||||
this.SpaceBeforeBinaryOperator, this.SpaceAfterBinaryOperator, this.NoSpaceBeforeBinaryOperator, this.NoSpaceAfterBinaryOperator,
|
||||
this.SpaceBeforeOpenParenInFuncDecl, this.NoSpaceBeforeOpenParenInFuncDecl,
|
||||
this.NewLineBeforeOpenBraceInControl,
|
||||
this.NewLineBeforeOpenBraceInFunction, this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock,
|
||||
this.SpaceAfterTypeAssertion, this.NoSpaceAfterTypeAssertion
|
||||
];
|
||||
|
||||
// These rules are lower in priority than user-configurable rules.
|
||||
@@ -475,79 +567,28 @@ namespace ts.formatting {
|
||||
this.SpaceAfterSemicolon,
|
||||
this.SpaceBetweenStatements, this.SpaceAfterTryFinally
|
||||
];
|
||||
|
||||
///
|
||||
/// Rules controlled by user options
|
||||
///
|
||||
|
||||
// Insert space after comma delimiter
|
||||
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
|
||||
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext), RuleAction.Delete));
|
||||
|
||||
// Insert space before and after binary operators
|
||||
this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
|
||||
this.SpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.BinaryOperators, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Delete));
|
||||
|
||||
// Insert space after keywords in control flow statements
|
||||
this.SpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext), RuleAction.Space));
|
||||
this.NoSpaceAfterKeywordInControl = new Rule(RuleDescriptor.create2(Shared.TokenRange.Keywords, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext), RuleAction.Delete));
|
||||
|
||||
// Open Brace braces after function
|
||||
// TypeScript: Function can have return types, which can be made of tons of different token kinds
|
||||
this.NewLineBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Open Brace braces after TypeScript module/class/interface
|
||||
this.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsTypeScriptDeclWithBlockContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Open Brace braces after control block
|
||||
this.NewLineBeforeOpenBraceInControl = new Rule(RuleDescriptor.create2(this.ControlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsControlDeclContext, Rules.IsBeforeMultilineBlockContext), RuleAction.NewLine), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Insert space after semicolon in for statement
|
||||
this.SpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Space));
|
||||
this.NoSpaceAfterSemicolonInFor = new Rule(RuleDescriptor.create3(SyntaxKind.SemicolonToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsForContext), RuleAction.Delete));
|
||||
|
||||
// Insert space after opening and before closing nonempty parenthesis
|
||||
this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// Insert space after opening and before closing nonempty brackets
|
||||
this.SpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.SpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceBetweenBrackets = new Rule(RuleDescriptor.create1(SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceAfterOpenBracket = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.NoSpaceBeforeCloseBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// Insert space after opening and before closing template string braces
|
||||
this.NoSpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.SpaceAfterTemplateHeadAndMiddle = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
|
||||
this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
|
||||
|
||||
// No space after { and before } in JSX expression
|
||||
this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
|
||||
this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
|
||||
this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
|
||||
|
||||
// Insert space after function keyword for anonymous functions
|
||||
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
|
||||
this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Delete));
|
||||
|
||||
// No space after type assertion
|
||||
this.NoSpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Delete));
|
||||
this.SpaceAfterTypeAssertion = new Rule(RuleDescriptor.create3(SyntaxKind.GreaterThanToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsTypeAssertionContext), RuleAction.Space));
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Contexts
|
||||
///
|
||||
|
||||
static IsOptionEnabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
|
||||
return (context) => context.options && context.options.hasOwnProperty(optionName) && !!context.options[optionName];
|
||||
}
|
||||
|
||||
static IsOptionDisabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
|
||||
return (context) => context.options && context.options.hasOwnProperty(optionName) && !context.options[optionName];
|
||||
}
|
||||
|
||||
static IsOptionDisabledOrUndefined(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
|
||||
return (context) => !context.options || !context.options.hasOwnProperty(optionName) || !context.options[optionName];
|
||||
}
|
||||
|
||||
static IsOptionEnabledOrUndefined(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
|
||||
return (context) => !context.options || !context.options.hasOwnProperty(optionName) || !!context.options[optionName];
|
||||
}
|
||||
|
||||
static IsForContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind === SyntaxKind.ForStatement;
|
||||
}
|
||||
@@ -845,6 +886,10 @@ namespace ts.formatting {
|
||||
return context.contextNode.kind === SyntaxKind.TypeLiteral; // && context.contextNode.parent.kind !== SyntaxKind.InterfaceDeclaration;
|
||||
}
|
||||
|
||||
static IsConstructorSignatureContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind === SyntaxKind.ConstructSignature;
|
||||
}
|
||||
|
||||
static IsTypeArgumentOrParameterOrAssertion(token: TextRangeWithKind, parent: Node): boolean {
|
||||
if (token.kind !== SyntaxKind.LessThanToken && token.kind !== SyntaxKind.GreaterThanToken) {
|
||||
return false;
|
||||
|
||||
@@ -41,8 +41,7 @@ namespace ts.formatting {
|
||||
}
|
||||
|
||||
private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void {
|
||||
const specificRule = rule.Descriptor.LeftTokenRange !== Shared.TokenRange.Any &&
|
||||
rule.Descriptor.RightTokenRange !== Shared.TokenRange.Any;
|
||||
const specificRule = rule.Descriptor.LeftTokenRange.isSpecific() && rule.Descriptor.RightTokenRange.isSpecific();
|
||||
|
||||
rule.Descriptor.LeftTokenRange.GetTokens().forEach((left) => {
|
||||
rule.Descriptor.RightTokenRange.GetTokens().forEach((right) => {
|
||||
|
||||
@@ -5,11 +5,12 @@ namespace ts.formatting {
|
||||
export class RulesProvider {
|
||||
private globalRules: Rules;
|
||||
private options: ts.FormatCodeSettings;
|
||||
private activeRules: Rule[];
|
||||
private rulesMap: RulesMap;
|
||||
|
||||
constructor() {
|
||||
this.globalRules = new Rules();
|
||||
const activeRules = this.globalRules.HighPriorityCommonRules.slice(0).concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules);
|
||||
this.rulesMap = RulesMap.create(activeRules);
|
||||
}
|
||||
|
||||
public getRuleName(rule: Rule): string {
|
||||
@@ -30,141 +31,8 @@ namespace ts.formatting {
|
||||
|
||||
public ensureUpToDate(options: ts.FormatCodeSettings) {
|
||||
if (!this.options || !ts.compareDataObjects(this.options, options)) {
|
||||
const activeRules = this.createActiveRules(options);
|
||||
const rulesMap = RulesMap.create(activeRules);
|
||||
|
||||
this.activeRules = activeRules;
|
||||
this.rulesMap = rulesMap;
|
||||
this.options = ts.clone(options);
|
||||
}
|
||||
}
|
||||
|
||||
private createActiveRules(options: ts.FormatCodeSettings): Rule[] {
|
||||
let rules = this.globalRules.HighPriorityCommonRules.slice(0);
|
||||
|
||||
if (options.insertSpaceAfterConstructor) {
|
||||
rules.push(this.globalRules.SpaceAfterConstructor);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterConstructor);
|
||||
}
|
||||
|
||||
if (options.insertSpaceAfterCommaDelimiter) {
|
||||
rules.push(this.globalRules.SpaceAfterComma);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterComma);
|
||||
}
|
||||
|
||||
if (options.insertSpaceAfterFunctionKeywordForAnonymousFunctions) {
|
||||
rules.push(this.globalRules.SpaceAfterAnonymousFunctionKeyword);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterAnonymousFunctionKeyword);
|
||||
}
|
||||
|
||||
if (options.insertSpaceAfterKeywordsInControlFlowStatements) {
|
||||
rules.push(this.globalRules.SpaceAfterKeywordInControl);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterKeywordInControl);
|
||||
}
|
||||
|
||||
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis) {
|
||||
rules.push(this.globalRules.SpaceAfterOpenParen);
|
||||
rules.push(this.globalRules.SpaceBeforeCloseParen);
|
||||
rules.push(this.globalRules.NoSpaceBetweenParens);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterOpenParen);
|
||||
rules.push(this.globalRules.NoSpaceBeforeCloseParen);
|
||||
rules.push(this.globalRules.NoSpaceBetweenParens);
|
||||
}
|
||||
|
||||
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets) {
|
||||
rules.push(this.globalRules.SpaceAfterOpenBracket);
|
||||
rules.push(this.globalRules.SpaceBeforeCloseBracket);
|
||||
rules.push(this.globalRules.NoSpaceBetweenBrackets);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterOpenBracket);
|
||||
rules.push(this.globalRules.NoSpaceBeforeCloseBracket);
|
||||
rules.push(this.globalRules.NoSpaceBetweenBrackets);
|
||||
}
|
||||
|
||||
// The default value of InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces is true
|
||||
// so if the option is undefined, we should treat it as true as well
|
||||
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces !== false) {
|
||||
rules.push(this.globalRules.SpaceAfterOpenBrace);
|
||||
rules.push(this.globalRules.SpaceBeforeCloseBrace);
|
||||
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterOpenBrace);
|
||||
rules.push(this.globalRules.NoSpaceBeforeCloseBrace);
|
||||
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
|
||||
}
|
||||
|
||||
if (options.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) {
|
||||
rules.push(this.globalRules.SpaceAfterTemplateHeadAndMiddle);
|
||||
rules.push(this.globalRules.SpaceBeforeTemplateMiddleAndTail);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterTemplateHeadAndMiddle);
|
||||
rules.push(this.globalRules.NoSpaceBeforeTemplateMiddleAndTail);
|
||||
}
|
||||
|
||||
if (options.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces) {
|
||||
rules.push(this.globalRules.SpaceAfterOpenBraceInJsxExpression);
|
||||
rules.push(this.globalRules.SpaceBeforeCloseBraceInJsxExpression);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterOpenBraceInJsxExpression);
|
||||
rules.push(this.globalRules.NoSpaceBeforeCloseBraceInJsxExpression);
|
||||
}
|
||||
|
||||
if (options.insertSpaceAfterSemicolonInForStatements) {
|
||||
rules.push(this.globalRules.SpaceAfterSemicolonInFor);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterSemicolonInFor);
|
||||
}
|
||||
|
||||
if (options.insertSpaceBeforeAndAfterBinaryOperators) {
|
||||
rules.push(this.globalRules.SpaceBeforeBinaryOperator);
|
||||
rules.push(this.globalRules.SpaceAfterBinaryOperator);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceBeforeBinaryOperator);
|
||||
rules.push(this.globalRules.NoSpaceAfterBinaryOperator);
|
||||
}
|
||||
|
||||
if (options.insertSpaceBeforeFunctionParenthesis) {
|
||||
rules.push(this.globalRules.SpaceBeforeOpenParenInFuncDecl);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceBeforeOpenParenInFuncDecl);
|
||||
}
|
||||
|
||||
if (options.placeOpenBraceOnNewLineForControlBlocks) {
|
||||
rules.push(this.globalRules.NewLineBeforeOpenBraceInControl);
|
||||
}
|
||||
|
||||
if (options.placeOpenBraceOnNewLineForFunctions) {
|
||||
rules.push(this.globalRules.NewLineBeforeOpenBraceInFunction);
|
||||
rules.push(this.globalRules.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock);
|
||||
}
|
||||
|
||||
if (options.insertSpaceAfterTypeAssertion) {
|
||||
rules.push(this.globalRules.SpaceAfterTypeAssertion);
|
||||
}
|
||||
else {
|
||||
rules.push(this.globalRules.NoSpaceAfterTypeAssertion);
|
||||
}
|
||||
|
||||
rules = rules.concat(this.globalRules.LowPriorityCommonRules);
|
||||
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,10 +341,7 @@ namespace ts.formatting {
|
||||
return Value.Unknown;
|
||||
}
|
||||
|
||||
if (node.parent && (
|
||||
node.parent.kind === SyntaxKind.CallExpression ||
|
||||
node.parent.kind === SyntaxKind.NewExpression) &&
|
||||
(<CallExpression>node.parent).expression !== node) {
|
||||
if (node.parent && isCallOrNewExpression(node.parent) && (<CallExpression>node.parent).expression !== node) {
|
||||
|
||||
const fullCallOrNewExpression = (<CallExpression | NewExpression>node.parent).expression;
|
||||
const startingExpression = getStartingExpression(fullCallOrNewExpression);
|
||||
|
||||
@@ -3,22 +3,13 @@
|
||||
/* @internal */
|
||||
namespace ts.formatting {
|
||||
export namespace Shared {
|
||||
export interface ITokenAccess {
|
||||
GetTokens(): SyntaxKind[];
|
||||
Contains(token: SyntaxKind): boolean;
|
||||
const allTokens: SyntaxKind[] = [];
|
||||
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
|
||||
allTokens.push(token);
|
||||
}
|
||||
|
||||
export class TokenRangeAccess implements ITokenAccess {
|
||||
private tokens: SyntaxKind[];
|
||||
|
||||
constructor(from: SyntaxKind, to: SyntaxKind, except: SyntaxKind[]) {
|
||||
this.tokens = [];
|
||||
for (let token = from; token <= to; token++) {
|
||||
if (ts.indexOf(except, token) < 0) {
|
||||
this.tokens.push(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
class TokenValuesAccess implements TokenRange {
|
||||
constructor(private readonly tokens: SyntaxKind[] = []) { }
|
||||
|
||||
public GetTokens(): SyntaxKind[] {
|
||||
return this.tokens;
|
||||
@@ -27,27 +18,12 @@ namespace ts.formatting {
|
||||
public Contains(token: SyntaxKind): boolean {
|
||||
return this.tokens.indexOf(token) >= 0;
|
||||
}
|
||||
|
||||
public isSpecific() { return true; }
|
||||
}
|
||||
|
||||
export class TokenValuesAccess implements ITokenAccess {
|
||||
private tokens: SyntaxKind[];
|
||||
|
||||
constructor(tks: SyntaxKind[]) {
|
||||
this.tokens = tks && tks.length ? tks : <SyntaxKind[]>[];
|
||||
}
|
||||
|
||||
public GetTokens(): SyntaxKind[] {
|
||||
return this.tokens;
|
||||
}
|
||||
|
||||
public Contains(token: SyntaxKind): boolean {
|
||||
return this.tokens.indexOf(token) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class TokenSingleValueAccess implements ITokenAccess {
|
||||
constructor(public token: SyntaxKind) {
|
||||
}
|
||||
class TokenSingleValueAccess implements TokenRange {
|
||||
constructor(private readonly token: SyntaxKind) {}
|
||||
|
||||
public GetTokens(): SyntaxKind[] {
|
||||
return [this.token];
|
||||
@@ -56,15 +32,13 @@ namespace ts.formatting {
|
||||
public Contains(tokenValue: SyntaxKind): boolean {
|
||||
return tokenValue === this.token;
|
||||
}
|
||||
|
||||
public isSpecific() { return true; }
|
||||
}
|
||||
|
||||
export class TokenAllAccess implements ITokenAccess {
|
||||
class TokenAllAccess implements TokenRange {
|
||||
public GetTokens(): SyntaxKind[] {
|
||||
const result: SyntaxKind[] = [];
|
||||
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
|
||||
result.push(token);
|
||||
}
|
||||
return result;
|
||||
return allTokens;
|
||||
}
|
||||
|
||||
public Contains(): boolean {
|
||||
@@ -74,53 +48,76 @@ namespace ts.formatting {
|
||||
public toString(): string {
|
||||
return "[allTokens]";
|
||||
}
|
||||
|
||||
public isSpecific() { return false; }
|
||||
}
|
||||
|
||||
export class TokenRange {
|
||||
constructor(public tokenAccess: ITokenAccess) {
|
||||
}
|
||||
|
||||
static FromToken(token: SyntaxKind): TokenRange {
|
||||
return new TokenRange(new TokenSingleValueAccess(token));
|
||||
}
|
||||
|
||||
static FromTokens(tokens: SyntaxKind[]): TokenRange {
|
||||
return new TokenRange(new TokenValuesAccess(tokens));
|
||||
}
|
||||
|
||||
static FromRange(f: SyntaxKind, to: SyntaxKind, except: SyntaxKind[] = []): TokenRange {
|
||||
return new TokenRange(new TokenRangeAccess(f, to, except));
|
||||
}
|
||||
|
||||
static AllTokens(): TokenRange {
|
||||
return new TokenRange(new TokenAllAccess());
|
||||
}
|
||||
class TokenAllExceptAccess implements TokenRange {
|
||||
constructor(private readonly except: SyntaxKind) {}
|
||||
|
||||
public GetTokens(): SyntaxKind[] {
|
||||
return this.tokenAccess.GetTokens();
|
||||
return allTokens.filter(t => t !== this.except);
|
||||
}
|
||||
|
||||
public Contains(token: SyntaxKind): boolean {
|
||||
return this.tokenAccess.Contains(token);
|
||||
return token !== this.except;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this.tokenAccess.toString();
|
||||
public isSpecific() { return false; }
|
||||
}
|
||||
|
||||
export interface TokenRange {
|
||||
GetTokens(): SyntaxKind[];
|
||||
Contains(token: SyntaxKind): boolean;
|
||||
isSpecific(): boolean;
|
||||
}
|
||||
|
||||
export namespace TokenRange {
|
||||
export function FromToken(token: SyntaxKind): TokenRange {
|
||||
return new TokenSingleValueAccess(token);
|
||||
}
|
||||
|
||||
static Any: TokenRange = TokenRange.AllTokens();
|
||||
static AnyIncludingMultilineComments = TokenRange.FromTokens(TokenRange.Any.GetTokens().concat([SyntaxKind.MultiLineCommentTrivia]));
|
||||
static Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
|
||||
static BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
|
||||
static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]);
|
||||
static UnaryPrefixOperators = TokenRange.FromTokens([SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken]);
|
||||
static UnaryPrefixExpressions = TokenRange.FromTokens([SyntaxKind.NumericLiteral, SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPreincrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPostincrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
|
||||
static UnaryPredecrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
static UnaryPostdecrementExpressions = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
|
||||
static Comments = TokenRange.FromTokens([SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia]);
|
||||
static TypeNames = TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword, SyntaxKind.SymbolKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]);
|
||||
export function FromTokens(tokens: SyntaxKind[]): TokenRange {
|
||||
return new TokenValuesAccess(tokens);
|
||||
}
|
||||
|
||||
export function FromRange(from: SyntaxKind, to: SyntaxKind, except: SyntaxKind[] = []): TokenRange {
|
||||
const tokens: SyntaxKind[] = [];
|
||||
for (let token = from; token <= to; token++) {
|
||||
if (ts.indexOf(except, token) < 0) {
|
||||
tokens.push(token);
|
||||
}
|
||||
}
|
||||
return new TokenValuesAccess(tokens);
|
||||
}
|
||||
|
||||
export function AnyExcept(token: SyntaxKind): TokenRange {
|
||||
return new TokenAllExceptAccess(token);
|
||||
}
|
||||
|
||||
export const Any: TokenRange = new TokenAllAccess();
|
||||
export const AnyIncludingMultilineComments = TokenRange.FromTokens([...allTokens, SyntaxKind.MultiLineCommentTrivia]);
|
||||
export const Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
|
||||
export const BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
|
||||
export const BinaryKeywordOperators = TokenRange.FromTokens([
|
||||
SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]);
|
||||
export const UnaryPrefixOperators = TokenRange.FromTokens([
|
||||
SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken]);
|
||||
export const UnaryPrefixExpressions = TokenRange.FromTokens([
|
||||
SyntaxKind.NumericLiteral, SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken,
|
||||
SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
export const UnaryPreincrementExpressions = TokenRange.FromTokens([
|
||||
SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
export const UnaryPostincrementExpressions = TokenRange.FromTokens([
|
||||
SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
|
||||
export const UnaryPredecrementExpressions = TokenRange.FromTokens([
|
||||
SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
|
||||
export const UnaryPostdecrementExpressions = TokenRange.FromTokens([
|
||||
SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
|
||||
export const Comments = TokenRange.FromTokens([SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia]);
|
||||
export const TypeNames = TokenRange.FromTokens([
|
||||
SyntaxKind.Identifier, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword,
|
||||
SyntaxKind.SymbolKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,19 +50,10 @@ 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.flags & SymbolFlags.Alias) {
|
||||
const declaration = symbol.declarations[0];
|
||||
|
||||
// Go to the original declaration for cases:
|
||||
//
|
||||
// (1) when the aliased symbol was declared in the location(parent).
|
||||
// (2) when the aliased symbol is originating from a named import.
|
||||
//
|
||||
if (node.kind === SyntaxKind.Identifier &&
|
||||
(node.parent === declaration ||
|
||||
(declaration.kind === SyntaxKind.ImportSpecifier && declaration.parent && declaration.parent.kind === SyntaxKind.NamedImports))) {
|
||||
|
||||
symbol = typeChecker.getAliasedSymbol(symbol);
|
||||
if (symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) {
|
||||
const aliased = typeChecker.getAliasedSymbol(symbol);
|
||||
if (aliased.declarations) {
|
||||
symbol = aliased;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,17 +76,6 @@ namespace ts.GoToDefinition {
|
||||
declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName));
|
||||
}
|
||||
|
||||
if (isJsxOpeningLikeElement(node.parent)) {
|
||||
// If there are errors when trying to figure out stateless component function, just return the first declaration
|
||||
// For example:
|
||||
// declare function /*firstSource*/MainButton(buttonProps: ButtonProps): JSX.Element;
|
||||
// declare function /*secondSource*/MainButton(linkProps: LinkProps): JSX.Element;
|
||||
// declare function /*thirdSource*/MainButton(props: ButtonProps | LinkProps): JSX.Element;
|
||||
// let opt = <Main/*firstTarget*/Button />; // Error - We get undefined for resolved signature indicating an error, then just return the first declaration
|
||||
const {symbolName, symbolKind, containerName} = getSymbolInfo(typeChecker, symbol, node);
|
||||
return [createDefinitionInfo(symbol.valueDeclaration, symbolKind, symbolName, containerName)];
|
||||
}
|
||||
|
||||
// If the current location we want to find its definition is in an object literal, try to get the contextual type for the
|
||||
// object literal, lookup the property symbol in the contextual type, and use this for goto-definition.
|
||||
// For example
|
||||
@@ -106,15 +86,9 @@ namespace ts.GoToDefinition {
|
||||
// function Foo(arg: Props) {}
|
||||
// Foo( { pr/*1*/op1: 10, prop2: true })
|
||||
const element = getContainingObjectLiteralElement(node);
|
||||
if (element) {
|
||||
if (typeChecker.getContextualType(element.parent as Expression)) {
|
||||
const result: DefinitionInfo[] = [];
|
||||
const propertySymbols = getPropertySymbolsFromContextualType(typeChecker, element);
|
||||
for (const propertySymbol of propertySymbols) {
|
||||
result.push(...getDefinitionFromSymbol(typeChecker, propertySymbol, node));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (element && typeChecker.getContextualType(element.parent as Expression)) {
|
||||
return flatMap(getPropertySymbolsFromContextualType(typeChecker, element), propertySymbol =>
|
||||
getDefinitionFromSymbol(typeChecker, propertySymbol, node));
|
||||
}
|
||||
return getDefinitionFromSymbol(typeChecker, symbol, node);
|
||||
}
|
||||
@@ -153,6 +127,29 @@ namespace ts.GoToDefinition {
|
||||
return getDefinitionFromSymbol(typeChecker, type.symbol, node);
|
||||
}
|
||||
|
||||
// Go to the original declaration for cases:
|
||||
//
|
||||
// (1) when the aliased symbol was declared in the location(parent).
|
||||
// (2) when the aliased symbol is originating from an import.
|
||||
//
|
||||
function shouldSkipAlias(node: Node, declaration: Node): boolean {
|
||||
if (node.kind !== SyntaxKind.Identifier) {
|
||||
return false;
|
||||
}
|
||||
if (node.parent === declaration) {
|
||||
return true;
|
||||
}
|
||||
switch (declaration.kind) {
|
||||
case SyntaxKind.ImportClause:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
return true;
|
||||
case SyntaxKind.ImportSpecifier:
|
||||
return declaration.parent.kind === SyntaxKind.NamedImports;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo[] {
|
||||
const result: DefinitionInfo[] = [];
|
||||
const declarations = symbol.getDeclarations();
|
||||
@@ -235,7 +232,7 @@ namespace ts.GoToDefinition {
|
||||
|
||||
/** Creates a DefinitionInfo from a Declaration, using the declaration's name if possible. */
|
||||
function createDefinitionInfo(node: Declaration, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo {
|
||||
return createDefinitionInfoFromName(node.name || node, symbolKind, symbolName, containerName);
|
||||
return createDefinitionInfoFromName(getNameOfDeclaration(node) || node, symbolKind, symbolName, containerName);
|
||||
}
|
||||
|
||||
/** Creates a DefinitionInfo directly from the name of a declaration. */
|
||||
|
||||
@@ -212,6 +212,10 @@ namespace ts.FindAllReferences {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decl.importClause) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { importClause } = decl;
|
||||
|
||||
const { namedBindings } = importClause;
|
||||
@@ -326,7 +330,7 @@ namespace ts.FindAllReferences {
|
||||
|
||||
/** Calls `action` for each import, re-export, or require() in a file. */
|
||||
function forEachImport(sourceFile: SourceFile, action: (importStatement: ImporterOrCallExpression, imported: StringLiteral) => void): void {
|
||||
if (sourceFile.externalModuleIndicator) {
|
||||
if (sourceFile.externalModuleIndicator || sourceFile.imports !== undefined) {
|
||||
for (const moduleSpecifier of sourceFile.imports) {
|
||||
action(importerFromModuleSpecifier(moduleSpecifier), moduleSpecifier);
|
||||
}
|
||||
@@ -354,27 +358,21 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (sourceFile.flags & NodeFlags.JavaScriptFile) {
|
||||
// Find all 'require()' calls.
|
||||
sourceFile.forEachChild(function recur(node: Node): void {
|
||||
if (isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) {
|
||||
action(node, node.arguments[0] as StringLiteral);
|
||||
} else {
|
||||
node.forEachChild(recur);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function importerFromModuleSpecifier(moduleSpecifier: StringLiteral): Importer {
|
||||
function importerFromModuleSpecifier(moduleSpecifier: StringLiteral): ImporterOrCallExpression {
|
||||
const decl = moduleSpecifier.parent;
|
||||
if (decl.kind === SyntaxKind.ImportDeclaration || decl.kind === SyntaxKind.ExportDeclaration) {
|
||||
return decl as ImportDeclaration | ExportDeclaration;
|
||||
switch (decl.kind) {
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
return decl as ImportDeclaration | ExportDeclaration | CallExpression;
|
||||
case SyntaxKind.ExternalModuleReference:
|
||||
return (decl as ExternalModuleReference).parent;
|
||||
default:
|
||||
Debug.assert(false);
|
||||
}
|
||||
Debug.assert(decl.kind === SyntaxKind.ExternalModuleReference);
|
||||
return (decl as ExternalModuleReference).parent;
|
||||
}
|
||||
|
||||
export interface ImportedSymbol {
|
||||
@@ -528,14 +526,18 @@ namespace ts.FindAllReferences {
|
||||
return isExternalModuleSymbol(exportingModuleSymbol) ? { exportingModuleSymbol, exportKind } : undefined;
|
||||
}
|
||||
|
||||
function symbolName(symbol: Symbol): string {
|
||||
function symbolName(symbol: Symbol): string | undefined {
|
||||
if (symbol.name !== "default") {
|
||||
return symbol.name;
|
||||
}
|
||||
|
||||
const name = forEach(symbol.declarations, ({ name }) => name && name.kind === SyntaxKind.Identifier && name.text);
|
||||
Debug.assert(!!name);
|
||||
return name;
|
||||
return forEach(symbol.declarations, decl => {
|
||||
if (isExportAssignment(decl)) {
|
||||
return isIdentifier(decl.expression) ? decl.expression.text : undefined;
|
||||
}
|
||||
const name = getNameOfDeclaration(decl);
|
||||
return name && name.kind === SyntaxKind.Identifier && name.text;
|
||||
});
|
||||
}
|
||||
|
||||
/** If at an export specifier, go to the symbol it refers to. */
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace ts.JsDoc {
|
||||
"namespace",
|
||||
"param",
|
||||
"private",
|
||||
"prop",
|
||||
"property",
|
||||
"public",
|
||||
"requires",
|
||||
@@ -38,8 +39,6 @@ namespace ts.JsDoc {
|
||||
"throws",
|
||||
"type",
|
||||
"typedef",
|
||||
"property",
|
||||
"prop",
|
||||
"version"
|
||||
];
|
||||
let jsDocTagNameCompletionEntries: CompletionEntry[];
|
||||
|
||||
+22
-17
@@ -52,7 +52,7 @@ namespace ts.NavigateTo {
|
||||
rawItems = filter(rawItems, item => {
|
||||
const decl = item.declaration;
|
||||
if (decl.kind === SyntaxKind.ImportClause || decl.kind === SyntaxKind.ImportSpecifier || decl.kind === SyntaxKind.ImportEqualsDeclaration) {
|
||||
const importer = checker.getSymbolAtLocation(decl.name);
|
||||
const importer = checker.getSymbolAtLocation((decl as NamedDeclaration).name);
|
||||
const imported = checker.getAliasedSymbol(importer);
|
||||
return importer.name !== imported.name;
|
||||
}
|
||||
@@ -97,17 +97,20 @@ namespace ts.NavigateTo {
|
||||
}
|
||||
|
||||
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]) {
|
||||
if (declaration && declaration.name) {
|
||||
const text = getTextOfIdentifierOrLiteral(declaration.name);
|
||||
if (text !== undefined) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
else if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
else {
|
||||
// Don't know how to add this.
|
||||
return false;
|
||||
if (declaration) {
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name) {
|
||||
const text = getTextOfIdentifierOrLiteral(name);
|
||||
if (text !== undefined) {
|
||||
containers.unshift(text);
|
||||
}
|
||||
else if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
return tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ true);
|
||||
}
|
||||
else {
|
||||
// Don't know how to add this.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,8 +146,9 @@ namespace ts.NavigateTo {
|
||||
|
||||
// First, if we started with a computed property name, then add all but the last
|
||||
// portion into the container array.
|
||||
if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
if (!tryAddComputedPropertyName((<ComputedPropertyName>declaration.name).expression, containers, /*includeLastPortion*/ false)) {
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
if (!tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ false)) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -190,6 +194,7 @@ namespace ts.NavigateTo {
|
||||
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
|
||||
const declaration = rawItem.declaration;
|
||||
const container = <Declaration>getContainerNode(declaration);
|
||||
const containerName = container && getNameOfDeclaration(container);
|
||||
return {
|
||||
name: rawItem.name,
|
||||
kind: getNodeKind(declaration),
|
||||
@@ -199,9 +204,9 @@ namespace ts.NavigateTo {
|
||||
fileName: rawItem.fileName,
|
||||
textSpan: createTextSpanFromNode(declaration),
|
||||
// TODO(jfreeman): What should be the containerName when the container has a computed name?
|
||||
containerName: container && container.name ? (<Identifier>container.name).text : "",
|
||||
containerKind: container && container.name ? getNodeKind(container) : ""
|
||||
containerName: containerName ? (<Identifier>containerName).text : "",
|
||||
containerKind: containerName ? getNodeKind(container) : ""
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,8 +279,8 @@ namespace ts.NavigationBar {
|
||||
function mergeChildren(children: NavigationBarNode[]): void {
|
||||
const nameToItems = createMap<NavigationBarNode | NavigationBarNode[]>();
|
||||
filterMutate(children, child => {
|
||||
const decl = <Declaration>child.node;
|
||||
const name = decl.name && nodeText(decl.name);
|
||||
const declName = getNameOfDeclaration(<Declaration>child.node);
|
||||
const name = declName && nodeText(declName);
|
||||
if (!name) {
|
||||
// Anonymous items are never merged.
|
||||
return true;
|
||||
@@ -378,9 +378,9 @@ namespace ts.NavigationBar {
|
||||
return getModuleName(<ModuleDeclaration>node);
|
||||
}
|
||||
|
||||
const decl = <Declaration>node;
|
||||
if (decl.name) {
|
||||
return getPropertyNameForPropertyNameNode(decl.name);
|
||||
const declName = getNameOfDeclaration(<Declaration>node);
|
||||
if (declName) {
|
||||
return getPropertyNameForPropertyNameNode(declName);
|
||||
}
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.FunctionExpression:
|
||||
@@ -399,7 +399,7 @@ namespace ts.NavigationBar {
|
||||
return getModuleName(<ModuleDeclaration>node);
|
||||
}
|
||||
|
||||
const name = (<Declaration>node).name;
|
||||
const name = getNameOfDeclaration(<Declaration>node);
|
||||
if (name) {
|
||||
const text = nodeText(name);
|
||||
if (text.length > 0) {
|
||||
|
||||
+12
-13
@@ -31,6 +31,9 @@ namespace ts {
|
||||
/** The version of the language service API */
|
||||
export const servicesVersion = "0.5";
|
||||
|
||||
/* @internal */
|
||||
let ruleProvider: formatting.RulesProvider;
|
||||
|
||||
function createNode<TKind extends SyntaxKind>(kind: TKind, pos: number, end: number, parent?: Node): NodeObject | TokenObject<TKind> | IdentifierObject {
|
||||
const node = kind >= SyntaxKind.FirstNode ? new NodeObject(kind, pos, end) :
|
||||
kind === SyntaxKind.Identifier ? new IdentifierObject(SyntaxKind.Identifier, pos, end) :
|
||||
@@ -357,6 +360,7 @@ namespace ts {
|
||||
_incrementExpressionBrand: any;
|
||||
_unaryExpressionBrand: any;
|
||||
_expressionBrand: any;
|
||||
/*@internal*/typeArguments: NodeArray<TypeNode>;
|
||||
constructor(_kind: SyntaxKind.Identifier, pos: number, end: number) {
|
||||
super(pos, end);
|
||||
}
|
||||
@@ -575,14 +579,15 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getDeclarationName(declaration: Declaration) {
|
||||
if (declaration.name) {
|
||||
const result = getTextOfIdentifierOrLiteral(declaration.name);
|
||||
const name = getNameOfDeclaration(declaration);
|
||||
if (name) {
|
||||
const result = getTextOfIdentifierOrLiteral(name);
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (declaration.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
const expr = (<ComputedPropertyName>declaration.name).expression;
|
||||
if (name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
const expr = (<ComputedPropertyName>name).expression;
|
||||
if (expr.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
return (<PropertyAccessExpression>expr).name.text;
|
||||
}
|
||||
@@ -1040,10 +1045,9 @@ namespace ts {
|
||||
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService {
|
||||
|
||||
const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
|
||||
let ruleProvider: formatting.RulesProvider;
|
||||
ruleProvider = ruleProvider || new formatting.RulesProvider();
|
||||
let program: Program;
|
||||
let lastProjectVersion: string;
|
||||
|
||||
let lastTypesRootVersion = 0;
|
||||
|
||||
const useCaseSensitivefileNames = host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames();
|
||||
@@ -1072,11 +1076,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getRuleProvider(options: FormatCodeSettings) {
|
||||
// Ensure rules are initialized and up to date wrt to formatting options
|
||||
if (!ruleProvider) {
|
||||
ruleProvider = new formatting.RulesProvider();
|
||||
}
|
||||
|
||||
ruleProvider.ensureUpToDate(options);
|
||||
return ruleProvider;
|
||||
}
|
||||
@@ -1855,8 +1854,8 @@ namespace ts {
|
||||
|
||||
// OK, we have found a match in the file. This is only an acceptable match if
|
||||
// it is contained within a comment.
|
||||
const token = getTokenAtPosition(sourceFile, matchPosition);
|
||||
if (!isInsideComment(sourceFile, token, matchPosition)) {
|
||||
|
||||
if (!isInComment(sourceFile, matchPosition)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1038,7 +1038,11 @@ namespace ts {
|
||||
return this.forwardJSONCall(`resolveModuleName('${fileName}')`, () => {
|
||||
const compilerOptions = <CompilerOptions>JSON.parse(compilerOptionsJson);
|
||||
const result = resolveModuleName(moduleName, normalizeSlashes(fileName), compilerOptions, this.host);
|
||||
const resolvedFileName = result.resolvedModule ? result.resolvedModule.resolvedFileName : undefined;
|
||||
let resolvedFileName = result.resolvedModule ? result.resolvedModule.resolvedFileName : undefined;
|
||||
if (result.resolvedModule && result.resolvedModule.extension !== Extension.Ts && result.resolvedModule.extension !== Extension.Tsx && result.resolvedModule.extension !== Extension.Dts) {
|
||||
resolvedFileName = undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
resolvedFileName,
|
||||
failedLookupLocations: result.failedLookupLocations
|
||||
@@ -1244,4 +1248,4 @@ namespace TypeScript.Services {
|
||||
// TODO: it should be moved into a namespace though.
|
||||
|
||||
/* @internal */
|
||||
const toolsVersion = "2.3";
|
||||
const toolsVersion = "2.4";
|
||||
@@ -1,168 +1,6 @@
|
||||
///<reference path='services.ts' />
|
||||
/* @internal */
|
||||
namespace ts.SignatureHelp {
|
||||
|
||||
// A partially written generic type expression is not guaranteed to have the correct syntax tree. the expression could be parsed as less than/greater than expression or a comma expression
|
||||
// or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference.
|
||||
// To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it
|
||||
// will return the generic identifier that started the expression (e.g. "foo" in "foo<any, |"). It is then up to the caller to ensure that this is a valid generic expression through
|
||||
// looking up the type. The method will also keep track of the parameter index inside the expression.
|
||||
// public static isInPartiallyWrittenTypeArgumentList(syntaxTree: TypeScript.SyntaxTree, position: number): any {
|
||||
// let token = Syntax.findTokenOnLeft(syntaxTree.sourceUnit(), position, /*includeSkippedTokens*/ true);
|
||||
|
||||
// if (token && TypeScript.Syntax.hasAncestorOfKind(token, TypeScript.SyntaxKind.TypeParameterList)) {
|
||||
// // We are in the wrong generic list. bail out
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// let stack = 0;
|
||||
// let argumentIndex = 0;
|
||||
|
||||
// whileLoop:
|
||||
// while (token) {
|
||||
// switch (token.kind()) {
|
||||
// case TypeScript.SyntaxKind.LessThanToken:
|
||||
// if (stack === 0) {
|
||||
// // Found the beginning of the generic argument expression
|
||||
// let lessThanToken = token;
|
||||
// token = previousToken(token, /*includeSkippedTokens*/ true);
|
||||
// if (!token || token.kind() !== TypeScript.SyntaxKind.IdentifierName) {
|
||||
// break whileLoop;
|
||||
// }
|
||||
|
||||
// // Found the name, return the data
|
||||
// return {
|
||||
// genericIdentifer: token,
|
||||
// lessThanToken: lessThanToken,
|
||||
// argumentIndex: argumentIndex
|
||||
// };
|
||||
// }
|
||||
// else if (stack < 0) {
|
||||
// // Seen one too many less than tokens, bail out
|
||||
// break whileLoop;
|
||||
// }
|
||||
// else {
|
||||
// stack--;
|
||||
// }
|
||||
|
||||
// break;
|
||||
|
||||
// case TypeScript.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
// stack++;
|
||||
|
||||
// // Intentional fall through
|
||||
// case TypeScript.SyntaxKind.GreaterThanToken:
|
||||
// stack++;
|
||||
// break;
|
||||
|
||||
// case TypeScript.SyntaxKind.CommaToken:
|
||||
// if (stack === 0) {
|
||||
// argumentIndex++;
|
||||
// }
|
||||
|
||||
// break;
|
||||
|
||||
// case TypeScript.SyntaxKind.CloseBraceToken:
|
||||
// // This can be object type, skip untill we find the matching open brace token
|
||||
// let unmatchedOpenBraceTokens = 0;
|
||||
|
||||
// // Skip untill the matching open brace token
|
||||
// token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.CloseBraceToken, TypeScript.SyntaxKind.OpenBraceToken);
|
||||
// if (!token) {
|
||||
// // No matching token was found. bail out
|
||||
// break whileLoop;
|
||||
// }
|
||||
|
||||
// break;
|
||||
|
||||
// case TypeScript.SyntaxKind.EqualsGreaterThanToken:
|
||||
// // This can be a function type or a constructor type. In either case, we want to skip the function definition
|
||||
// token = previousToken(token, /*includeSkippedTokens*/ true);
|
||||
|
||||
// if (token && token.kind() === TypeScript.SyntaxKind.CloseParenToken) {
|
||||
// // Skip untill the matching open paren token
|
||||
// token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.CloseParenToken, TypeScript.SyntaxKind.OpenParenToken);
|
||||
|
||||
// if (token && token.kind() === TypeScript.SyntaxKind.GreaterThanToken) {
|
||||
// // Another generic type argument list, skip it\
|
||||
// token = SignatureInfoHelpers.moveBackUpTillMatchingTokenKind(token, TypeScript.SyntaxKind.GreaterThanToken, TypeScript.SyntaxKind.LessThanToken);
|
||||
// }
|
||||
|
||||
// if (token && token.kind() === TypeScript.SyntaxKind.NewKeyword) {
|
||||
// // In case this was a constructor type, skip the new keyword
|
||||
// token = previousToken(token, /*includeSkippedTokens*/ true);
|
||||
// }
|
||||
|
||||
// if (!token) {
|
||||
// // No matching token was found. bail out
|
||||
// break whileLoop;
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// // This is not a function type. exit the main loop
|
||||
// break whileLoop;
|
||||
// }
|
||||
|
||||
// break;
|
||||
|
||||
// case TypeScript.SyntaxKind.IdentifierName:
|
||||
// case TypeScript.SyntaxKind.AnyKeyword:
|
||||
// case TypeScript.SyntaxKind.NumberKeyword:
|
||||
// case TypeScript.SyntaxKind.StringKeyword:
|
||||
// case TypeScript.SyntaxKind.VoidKeyword:
|
||||
// case TypeScript.SyntaxKind.BooleanKeyword:
|
||||
// case TypeScript.SyntaxKind.DotToken:
|
||||
// case TypeScript.SyntaxKind.OpenBracketToken:
|
||||
// case TypeScript.SyntaxKind.CloseBracketToken:
|
||||
// // Valid tokens in a type name. Skip.
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// break whileLoop;
|
||||
// }
|
||||
|
||||
// token = previousToken(token, /*includeSkippedTokens*/ true);
|
||||
// }
|
||||
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// private static moveBackUpTillMatchingTokenKind(token: TypeScript.ISyntaxToken, tokenKind: TypeScript.SyntaxKind, matchingTokenKind: TypeScript.SyntaxKind): TypeScript.ISyntaxToken {
|
||||
// if (!token || token.kind() !== tokenKind) {
|
||||
// throw TypeScript.Errors.invalidOperation();
|
||||
// }
|
||||
|
||||
// // Skip the current token
|
||||
// token = previousToken(token, /*includeSkippedTokens*/ true);
|
||||
|
||||
// let stack = 0;
|
||||
|
||||
// while (token) {
|
||||
// if (token.kind() === matchingTokenKind) {
|
||||
// if (stack === 0) {
|
||||
// // Found the matching token, return
|
||||
// return token;
|
||||
// }
|
||||
// else if (stack < 0) {
|
||||
// // tokens overlapped.. bail out.
|
||||
// break;
|
||||
// }
|
||||
// else {
|
||||
// stack--;
|
||||
// }
|
||||
// }
|
||||
// else if (token.kind() === tokenKind) {
|
||||
// stack++;
|
||||
// }
|
||||
|
||||
// // Move back
|
||||
// token = previousToken(token, /*includeSkippedTokens*/ true);
|
||||
// }
|
||||
|
||||
// // Did not find matching token
|
||||
// return null;
|
||||
// }
|
||||
|
||||
const emptyArray: any[] = [];
|
||||
|
||||
export const enum ArgumentListKind {
|
||||
@@ -262,7 +100,7 @@ namespace ts.SignatureHelp {
|
||||
* in the argument of an invocation; returns undefined otherwise.
|
||||
*/
|
||||
export function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo {
|
||||
if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) {
|
||||
if (isCallOrNewExpression(node.parent)) {
|
||||
const callExpression = <CallExpression>node.parent;
|
||||
// There are 3 cases to handle:
|
||||
// 1. The token introduces a list, and should begin a signature help session
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace ts.SymbolDisplay {
|
||||
|
||||
// try get the call/construct signature from the type if it matches
|
||||
let callExpressionLike: CallExpression | NewExpression | JsxOpeningLikeElement;
|
||||
if (location.kind === SyntaxKind.CallExpression || location.kind === SyntaxKind.NewExpression) {
|
||||
if (isCallOrNewExpression(location)) {
|
||||
callExpressionLike = <CallExpression | NewExpression>location;
|
||||
}
|
||||
else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) {
|
||||
@@ -200,27 +200,33 @@ namespace ts.SymbolDisplay {
|
||||
(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 = <FunctionLikeDeclaration>location.parent;
|
||||
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
|
||||
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
|
||||
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
|
||||
}
|
||||
else {
|
||||
signature = allSignatures[0];
|
||||
}
|
||||
// Use function declaration to write the signatures only if the symbol corresponding to this declaration
|
||||
const locationIsSymbolDeclaration = findDeclaration(symbol, declaration =>
|
||||
declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration));
|
||||
|
||||
if (functionDeclaration.kind === SyntaxKind.Constructor) {
|
||||
// show (constructor) Type(...) signature
|
||||
symbolKind = ScriptElementKind.constructorImplementationElement;
|
||||
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
|
||||
}
|
||||
else {
|
||||
// (function/method) symbol(..signature)
|
||||
addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature &&
|
||||
!(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind);
|
||||
}
|
||||
if (locationIsSymbolDeclaration) {
|
||||
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
|
||||
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
|
||||
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
|
||||
}
|
||||
else {
|
||||
signature = allSignatures[0];
|
||||
}
|
||||
|
||||
addSignatureDisplayParts(signature, allSignatures);
|
||||
hasAddedSymbolInfo = true;
|
||||
if (functionDeclaration.kind === SyntaxKind.Constructor) {
|
||||
// show (constructor) Type(...) signature
|
||||
symbolKind = ScriptElementKind.constructorImplementationElement;
|
||||
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
|
||||
}
|
||||
else {
|
||||
// (function/method) symbol(..signature)
|
||||
addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature &&
|
||||
!(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind);
|
||||
}
|
||||
|
||||
addSignatureDisplayParts(signature, allSignatures);
|
||||
hasAddedSymbolInfo = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,7 +275,7 @@ namespace ts.SymbolDisplay {
|
||||
}
|
||||
if (symbolFlags & SymbolFlags.Module) {
|
||||
addNewLineIfDisplayPartsExist();
|
||||
const declaration = <ModuleDeclaration>getDeclarationOfKind(symbol, SyntaxKind.ModuleDeclaration);
|
||||
const declaration = getDeclarationOfKind<ModuleDeclaration>(symbol, SyntaxKind.ModuleDeclaration);
|
||||
const isNamespace = declaration && declaration.name && declaration.name.kind === SyntaxKind.Identifier;
|
||||
displayParts.push(keywordPart(isNamespace ? SyntaxKind.NamespaceKeyword : SyntaxKind.ModuleKeyword));
|
||||
displayParts.push(spacePart());
|
||||
@@ -290,9 +296,9 @@ namespace ts.SymbolDisplay {
|
||||
}
|
||||
else {
|
||||
// Method/function type parameter
|
||||
let declaration = <Node>getDeclarationOfKind(symbol, SyntaxKind.TypeParameter);
|
||||
Debug.assert(declaration !== undefined);
|
||||
declaration = declaration.parent;
|
||||
const decl = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter);
|
||||
Debug.assert(decl !== undefined);
|
||||
const declaration = decl.parent;
|
||||
|
||||
if (declaration) {
|
||||
if (isFunctionLikeKind(declaration.kind)) {
|
||||
|
||||
@@ -497,8 +497,8 @@ namespace ts.textChanges {
|
||||
readonly node: Node;
|
||||
}
|
||||
|
||||
export function getNonformattedText(node: Node, sourceFile: SourceFile, newLine: NewLineKind): NonFormattedText {
|
||||
const options = { newLine, target: sourceFile.languageVersion };
|
||||
export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLine: NewLineKind): NonFormattedText {
|
||||
const options = { newLine, target: sourceFile && sourceFile.languageVersion };
|
||||
const writer = new Writer(getNewLineCharacter(options));
|
||||
const printer = createPrinter(options, writer);
|
||||
printer.writeNode(EmitHint.Unspecified, node, sourceFile, writer);
|
||||
@@ -528,26 +528,6 @@ namespace ts.textChanges {
|
||||
return skipTrivia(s, 0) === s.length;
|
||||
}
|
||||
|
||||
const nullTransformationContext: TransformationContext = {
|
||||
enableEmitNotification: noop,
|
||||
enableSubstitution: noop,
|
||||
endLexicalEnvironment: () => undefined,
|
||||
getCompilerOptions: notImplemented,
|
||||
getEmitHost: notImplemented,
|
||||
getEmitResolver: notImplemented,
|
||||
hoistFunctionDeclaration: noop,
|
||||
hoistVariableDeclaration: noop,
|
||||
isEmitNotificationEnabled: notImplemented,
|
||||
isSubstitutionEnabled: notImplemented,
|
||||
onEmitNode: noop,
|
||||
onSubstituteNode: notImplemented,
|
||||
readEmitHelpers: notImplemented,
|
||||
requestEmitHelper: noop,
|
||||
resumeLexicalEnvironment: noop,
|
||||
startLexicalEnvironment: noop,
|
||||
suspendLexicalEnvironment: noop
|
||||
};
|
||||
|
||||
function assignPositionsToNode(node: Node): Node {
|
||||
const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray, assignPositionsToNode);
|
||||
// create proxy node for non synthesized nodes
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace ts {
|
||||
reportDiagnostics?: boolean;
|
||||
moduleName?: string;
|
||||
renamedDependencies?: MapLike<string>;
|
||||
transformers?: CustomTransformers;
|
||||
}
|
||||
|
||||
export interface TranspileOutput {
|
||||
@@ -103,7 +104,7 @@ namespace ts {
|
||||
addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics());
|
||||
}
|
||||
// Emit
|
||||
program.emit();
|
||||
program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, transpileOptions.transformers);
|
||||
|
||||
Debug.assert(outputText !== undefined, "Output generation failed");
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
"formatting/tokenRange.ts",
|
||||
"codeFixProvider.ts",
|
||||
"codefixes/fixAddMissingMember.ts",
|
||||
"codefixes/fixSpelling.ts",
|
||||
"codefixes/fixExtendsInterfaceBecomesImplements.ts",
|
||||
"codefixes/fixClassIncorrectlyImplementsInterface.ts",
|
||||
"codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
|
||||
@@ -94,4 +95,4 @@
|
||||
"codefixes/unusedIdentifierFixes.ts",
|
||||
"codefixes/disableJsDiagnostics.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+52
-94
@@ -21,7 +21,6 @@ namespace ts {
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
case SyntaxKind.ShorthandPropertyAssignment:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.Constructor:
|
||||
@@ -40,10 +39,13 @@ namespace ts {
|
||||
case SyntaxKind.TypeLiteral:
|
||||
return SemanticMeaning.Type;
|
||||
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type;
|
||||
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
return SemanticMeaning.All;
|
||||
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if (isAmbientModule(<ModuleDeclaration>node)) {
|
||||
return SemanticMeaning.Namespace | SemanticMeaning.Value;
|
||||
@@ -61,7 +63,7 @@ namespace ts {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
case SyntaxKind.ExportAssignment:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
return SemanticMeaning.All;
|
||||
|
||||
// An external module can be a Value
|
||||
case SyntaxKind.SourceFile:
|
||||
@@ -238,7 +240,7 @@ namespace ts {
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
return (<Declaration>node.parent).name === node;
|
||||
return getNameOfDeclaration(<Declaration>node.parent) === node;
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
return (<ElementAccessExpression>node.parent).argumentExpression === node;
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
@@ -254,37 +256,6 @@ namespace ts {
|
||||
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node;
|
||||
}
|
||||
|
||||
/** Returns true if the position is within a comment */
|
||||
export function isInsideComment(sourceFile: SourceFile, token: Node, position: number): boolean {
|
||||
// The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment
|
||||
return position <= token.getStart(sourceFile) &&
|
||||
(isInsideCommentRange(getTrailingCommentRanges(sourceFile.text, token.getFullStart())) ||
|
||||
isInsideCommentRange(getLeadingCommentRanges(sourceFile.text, token.getFullStart())));
|
||||
|
||||
function isInsideCommentRange(comments: CommentRange[]): boolean {
|
||||
return forEach(comments, comment => {
|
||||
// either we are 1. completely inside the comment, or 2. at the end of the comment
|
||||
if (comment.pos < position && position < comment.end) {
|
||||
return true;
|
||||
}
|
||||
else if (position === comment.end) {
|
||||
const text = sourceFile.text;
|
||||
const width = comment.end - comment.pos;
|
||||
// is single line comment or just /*
|
||||
if (width <= 2 || text.charCodeAt(comment.pos + 1) === CharacterCodes.slash) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// is unterminated multi-line comment
|
||||
return !(text.charCodeAt(comment.end - 1) === CharacterCodes.slash &&
|
||||
text.charCodeAt(comment.end - 2) === CharacterCodes.asterisk);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function getContainerNode(node: Node): Declaration {
|
||||
while (true) {
|
||||
node = node.parent;
|
||||
@@ -649,44 +620,26 @@ namespace ts {
|
||||
return current;
|
||||
}
|
||||
|
||||
if (includeJsDocComment) {
|
||||
const jsDocChildren = ts.filter(current.getChildren(), isJSDocNode);
|
||||
for (const jsDocChild of jsDocChildren) {
|
||||
const start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment);
|
||||
if (start <= position) {
|
||||
const end = jsDocChild.getEnd();
|
||||
if (position < end || (position === end && jsDocChild.kind === SyntaxKind.EndOfFileToken)) {
|
||||
current = jsDocChild;
|
||||
continue outer;
|
||||
}
|
||||
else if (includeItemAtEndPosition && end === position) {
|
||||
const previousToken = findPrecedingToken(position, sourceFile, jsDocChild);
|
||||
if (previousToken && includeItemAtEndPosition(previousToken)) {
|
||||
return previousToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find the child that contains 'position'
|
||||
for (const child of current.getChildren()) {
|
||||
// all jsDocComment nodes were already visited
|
||||
if (isJSDocNode(child)) {
|
||||
if (isJSDocNode(child) && !includeJsDocComment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, includeJsDocComment);
|
||||
if (start <= position) {
|
||||
const end = child.getEnd();
|
||||
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
|
||||
current = child;
|
||||
continue outer;
|
||||
}
|
||||
else if (includeItemAtEndPosition && end === position) {
|
||||
const previousToken = findPrecedingToken(position, sourceFile, child);
|
||||
if (previousToken && includeItemAtEndPosition(previousToken)) {
|
||||
return previousToken;
|
||||
}
|
||||
if (start > position) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const end = child.getEnd();
|
||||
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
|
||||
current = child;
|
||||
continue outer;
|
||||
}
|
||||
else if (includeItemAtEndPosition && end === position) {
|
||||
const previousToken = findPrecedingToken(position, sourceFile, child);
|
||||
if (previousToken && includeItemAtEndPosition(previousToken)) {
|
||||
return previousToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -832,10 +785,6 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isInComment(sourceFile: SourceFile, position: number) {
|
||||
return isInCommentHelper(sourceFile, position, /*predicate*/ undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the position is in between the open and close elements of an JSX expression.
|
||||
*/
|
||||
@@ -881,15 +830,26 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the cursor at position in sourceFile is within a comment that additionally
|
||||
* satisfies predicate, and false otherwise.
|
||||
* Returns true if the cursor at position in sourceFile is within a comment.
|
||||
*
|
||||
* @param tokenAtPosition Must equal `getTokenAtPosition(sourceFile, position)
|
||||
* @param predicate Additional predicate to test on the comment range.
|
||||
*/
|
||||
export function isInCommentHelper(sourceFile: SourceFile, position: number, predicate?: (c: CommentRange) => boolean): boolean {
|
||||
const token = getTokenAtPosition(sourceFile, position);
|
||||
export function isInComment(sourceFile: SourceFile, position: number, tokenAtPosition = getTokenAtPosition(sourceFile, position), predicate?: (c: CommentRange) => boolean): boolean {
|
||||
return position <= tokenAtPosition.getStart(sourceFile) &&
|
||||
(isInCommentRange(getLeadingCommentRanges(sourceFile.text, tokenAtPosition.pos)) ||
|
||||
isInCommentRange(getTrailingCommentRanges(sourceFile.text, tokenAtPosition.pos)));
|
||||
|
||||
if (token && position <= token.getStart(sourceFile)) {
|
||||
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
|
||||
function isInCommentRange(commentRanges: CommentRange[]): boolean {
|
||||
return forEach(commentRanges, c => isPositionInCommentRange(c, position, sourceFile.text) && (!predicate || predicate(c)));
|
||||
}
|
||||
}
|
||||
|
||||
function isPositionInCommentRange({ pos, end, kind }: ts.CommentRange, position: number, text: string): boolean {
|
||||
if (pos < position && position < end) {
|
||||
return true;
|
||||
}
|
||||
else if (position === end) {
|
||||
// The end marker of a single-line comment does not include the newline character.
|
||||
// In the following case, we are inside a comment (^ denotes the cursor position):
|
||||
//
|
||||
@@ -900,15 +860,13 @@ namespace ts {
|
||||
// /* asdf */^
|
||||
//
|
||||
// Internally, we represent the end of the comment at the newline and closing '/', respectively.
|
||||
return predicate ?
|
||||
forEach(commentRanges, c => c.pos < position &&
|
||||
(c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end) &&
|
||||
predicate(c)) :
|
||||
forEach(commentRanges, c => c.pos < position &&
|
||||
(c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end));
|
||||
return kind === SyntaxKind.SingleLineCommentTrivia ||
|
||||
// true for unterminated multi-line comment
|
||||
!(text.charCodeAt(end - 1) === CharacterCodes.slash && text.charCodeAt(end - 2) === CharacterCodes.asterisk);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function hasDocComment(sourceFile: SourceFile, position: number) {
|
||||
@@ -1036,6 +994,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function compareDataObjects(dst: any, src: any): boolean {
|
||||
if (!dst || !src || Object.keys(dst).length !== Object.keys(src).length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const e in dst) {
|
||||
if (typeof dst[e] === "object") {
|
||||
if (!compareDataObjects(dst[e], src[e])) {
|
||||
@@ -1087,21 +1049,17 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean {
|
||||
return isInCommentHelper(sourceFile, position, isReferenceComment);
|
||||
|
||||
function isReferenceComment(c: CommentRange): boolean {
|
||||
return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => {
|
||||
const commentText = sourceFile.text.substring(c.pos, c.end);
|
||||
return tripleSlashDirectivePrefixRegex.test(commentText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean {
|
||||
return isInCommentHelper(sourceFile, position, isNonReferenceComment);
|
||||
|
||||
function isNonReferenceComment(c: CommentRange): boolean {
|
||||
return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => {
|
||||
const commentText = sourceFile.text.substring(c.pos, c.end);
|
||||
return !tripleSlashDirectivePrefixRegex.test(commentText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan {
|
||||
|
||||
Reference in New Issue
Block a user