Merge branch 'master' into watchImprovements

This commit is contained in:
Sheetal Nandi
2017-07-13 14:13:14 -07:00
94 changed files with 1381 additions and 883 deletions
+5 -9
View File
@@ -416,9 +416,8 @@ namespace ts {
}
}
else {
// Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue,
// ExportType, or ExportContainer flag, and an associated export symbol with all the correct flags set
// on it. There are 2 main reasons:
// Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue flag,
// and an associated export symbol with all the correct flags set on it. There are 2 main reasons:
//
// 1. We treat locals and exports of the same name as mutually exclusive within a container.
// That means the binder will issue a Duplicate Identifier error if you mix locals and exports
@@ -438,10 +437,7 @@ namespace ts {
(node as JSDocTypedefTag).name.kind === SyntaxKind.Identifier &&
((node as JSDocTypedefTag).name as Identifier).isInJSDocNamespace;
if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypedefInJSDocNamespace) {
const exportKind =
(symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) |
(symbolFlags & SymbolFlags.Type ? SymbolFlags.ExportType : 0) |
(symbolFlags & SymbolFlags.Namespace ? SymbolFlags.ExportNamespace : 0);
const exportKind = symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0;
const local = declareSymbol(container.locals, /*parent*/ undefined, node, exportKind, symbolExcludes);
local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes);
node.localSymbol = local;
@@ -2288,7 +2284,7 @@ namespace ts {
// When we create a property via 'exports.foo = bar', the 'exports.foo' property access
// expression is the declaration
setCommonJsModuleIndicator(node);
declareSymbol(file.symbol.exports, file.symbol, <PropertyAccessExpression>node.left, SymbolFlags.Property | SymbolFlags.Export, SymbolFlags.None);
declareSymbol(file.symbol.exports, file.symbol, <PropertyAccessExpression>node.left, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
}
function isExportsOrModuleExportsOrAlias(node: Node): boolean {
@@ -2329,7 +2325,7 @@ namespace ts {
// 'module.exports = expr' assignment
setCommonJsModuleIndicator(node);
declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.Export | SymbolFlags.ValueModule, SymbolFlags.None);
declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule, SymbolFlags.None);
}
function bindThisPropertyAssignment(node: BinaryExpression) {
+50 -45
View File
@@ -2010,7 +2010,7 @@ namespace ts {
}
function getAccessibleSymbolChainFromSymbolTableWorker(symbols: SymbolTable, visitedSymbolTables: SymbolTable[]): Symbol[] {
if (contains(visitedSymbolTables, symbols)) {
if (contains<SymbolTable>(visitedSymbolTables, symbols)) {
return undefined;
}
visitedSymbolTables.push(symbols);
@@ -2285,21 +2285,15 @@ namespace ts {
}
function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning);
const result = writer.string();
releaseStringWriter(writer);
return result;
return usingSingleLineStringWriter(writer => {
getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning);
});
}
function signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags, kind);
const result = writer.string();
releaseStringWriter(writer);
return result;
return usingSingleLineStringWriter(writer => {
getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags, kind);
});
}
function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string {
@@ -2996,12 +2990,9 @@ namespace ts {
}
function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string {
const writer = getSingleLineStringWriter();
getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags);
const result = writer.string();
releaseStringWriter(writer);
return result;
return usingSingleLineStringWriter(writer => {
getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags);
});
}
function formatUnionTypes(types: Type[]): Type[] {
@@ -4239,7 +4230,7 @@ namespace ts {
return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality);
}
if ((noImplicitAny || declaration.flags & NodeFlags.JavaScriptFile) &&
if ((noImplicitAny || isInJavaScriptFile(declaration)) &&
declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) &&
!(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !isInAmbientContext(declaration)) {
// If --noImplicitAny is on or the declaration is in a Javascript file,
@@ -4493,7 +4484,7 @@ namespace ts {
if (declaration.kind === SyntaxKind.ExportAssignment) {
return links.type = checkExpression((<ExportAssignment>declaration).expression);
}
if (declaration.flags & NodeFlags.JavaScriptFile && declaration.kind === SyntaxKind.JSDocPropertyTag && (<JSDocPropertyTag>declaration).typeExpression) {
if (isInJavaScriptFile(declaration) && declaration.kind === SyntaxKind.JSDocPropertyTag && (<JSDocPropertyTag>declaration).typeExpression) {
return links.type = getTypeFromTypeNode((<JSDocPropertyTag>declaration).typeExpression.type);
}
// Handle variable, parameter or property
@@ -4552,7 +4543,7 @@ namespace ts {
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
if (getter && getter.flags & NodeFlags.JavaScriptFile) {
if (getter && isInJavaScriptFile(getter)) {
const jsDocType = getTypeForDeclarationFromJSDocComment(getter);
if (jsDocType) {
return links.type = jsDocType;
@@ -6206,7 +6197,7 @@ namespace ts {
}
function isJSDocOptionalParameter(node: ParameterDeclaration) {
if (node.flags & NodeFlags.JavaScriptFile) {
if (isInJavaScriptFile(node)) {
if (node.type && node.type.kind === SyntaxKind.JSDocOptionalType) {
return true;
}
@@ -6892,6 +6883,7 @@ namespace ts {
case "Null":
return nullType;
case "Object":
case "object":
return anyType;
case "Function":
case "function":
@@ -11065,8 +11057,14 @@ namespace ts {
if (!links.switchTypes) {
// If all case clauses specify expressions that have unit types, we return an array
// of those unit types. Otherwise we return an empty array.
const types = map(switchStatement.caseBlock.clauses, getTypeOfSwitchClause);
links.switchTypes = !contains(types, undefined) ? types : emptyArray;
links.switchTypes = [];
for (const clause of switchStatement.caseBlock.clauses) {
const type = getTypeOfSwitchClause(clause);
if (type === undefined) {
return links.switchTypes = emptyArray;
}
links.switchTypes.push(type);
}
}
return links.switchTypes;
}
@@ -11875,6 +11873,8 @@ namespace ts {
}
function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) {
symbol = symbol.exportSymbol || symbol;
// If we have an identifier or a property access at the given location, if the location is
// an dotted name expression, and if the location is not an assignment target, obtain the type
// of the expression (which will reflect control flow analysis). If the expression indeed
@@ -18800,7 +18800,7 @@ namespace ts {
// local symbol is undefined => this declaration is non-exported.
// however symbol might contain other declarations that are exported
symbol = getSymbolOfNode(node);
if (!(symbol.flags & SymbolFlags.Export)) {
if (!symbol.exportSymbol) {
// this is a pure local symbol (all declarations are non-exported) - no need to check anything
return;
}
@@ -18811,11 +18811,9 @@ namespace ts {
return;
}
// we use SymbolFlags.ExportValue, SymbolFlags.ExportType and SymbolFlags.ExportNamespace
// to denote disjoint declarationSpaces (without making new enum type).
let exportedDeclarationSpaces = SymbolFlags.None;
let nonExportedDeclarationSpaces = SymbolFlags.None;
let defaultExportedDeclarationSpaces = SymbolFlags.None;
let exportedDeclarationSpaces = DeclarationSpaces.None;
let nonExportedDeclarationSpaces = DeclarationSpaces.None;
let defaultExportedDeclarationSpaces = DeclarationSpaces.None;
for (const d of symbol.declarations) {
const declarationSpaces = getDeclarationSpaces(d);
const effectiveDeclarationFlags = getEffectiveDeclarationFlags(d, ModifierFlags.Export | ModifierFlags.Default);
@@ -18855,24 +18853,36 @@ namespace ts {
}
}
function getDeclarationSpaces(d: Declaration): SymbolFlags {
const enum DeclarationSpaces {
None = 0,
ExportValue = 1 << 0,
ExportType = 1 << 1,
ExportNamespace = 1 << 2,
}
function getDeclarationSpaces(d: Declaration): DeclarationSpaces {
switch (d.kind) {
case SyntaxKind.InterfaceDeclaration:
return SymbolFlags.ExportType;
case SyntaxKind.TypeAliasDeclaration:
return DeclarationSpaces.ExportType;
case SyntaxKind.ModuleDeclaration:
return isAmbientModule(d) || getModuleInstanceState(d) !== ModuleInstanceState.NonInstantiated
? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue
: SymbolFlags.ExportNamespace;
? DeclarationSpaces.ExportNamespace | DeclarationSpaces.ExportValue
: DeclarationSpaces.ExportNamespace;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.EnumDeclaration:
return SymbolFlags.ExportType | SymbolFlags.ExportValue;
return DeclarationSpaces.ExportType | DeclarationSpaces.ExportValue;
case SyntaxKind.ImportEqualsDeclaration:
let result: SymbolFlags = 0;
let result = DeclarationSpaces.None;
const target = resolveAlias(getSymbolOfNode(d));
forEach(target.declarations, d => { result |= getDeclarationSpaces(d); });
return result;
case SyntaxKind.VariableDeclaration:
case SyntaxKind.BindingElement:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ImportSpecifier: // https://github.com/Microsoft/TypeScript/pull/7591
return DeclarationSpaces.ExportValue;
default:
return SymbolFlags.ExportValue;
Debug.fail((ts as any).SyntaxKind[d.kind]);
}
}
}
@@ -20909,7 +20919,7 @@ namespace ts {
}
}
function areTypeParametersIdentical(declarations: (ClassDeclaration | InterfaceDeclaration)[], typeParameters: TypeParameter[]) {
function areTypeParametersIdentical(declarations: ReadonlyArray<ClassDeclaration | InterfaceDeclaration>, typeParameters: TypeParameter[]) {
const maxTypeArgumentCount = length(typeParameters);
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
@@ -22281,11 +22291,6 @@ namespace ts {
}
switch (location.kind) {
case SyntaxKind.SourceFile:
if (!isExternalOrCommonJsModule(<SourceFile>location)) {
break;
}
// falls through
case SyntaxKind.ModuleDeclaration:
copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember);
break;
@@ -22337,7 +22342,7 @@ namespace ts {
* @param meaning meaning of symbol to filter by before adding to symbol table
*/
function copySymbol(symbol: Symbol, meaning: SymbolFlags): void {
if (symbol.flags & meaning) {
if (getCombinedLocalAndExportSymbolFlags(symbol) & meaning) {
const id = symbol.name;
// We will copy all symbol regardless of its reserved name because
// symbolsToArray will check whether the key is a reserved name and
+3 -3
View File
@@ -105,7 +105,7 @@ namespace ts {
paramType: Diagnostics.KIND,
showInSimplifiedHelpView: true,
category: Diagnostics.Basic_Options,
description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_es2015_or_ESNext,
description: Diagnostics.Specify_module_code_generation_Colon_none_commonjs_amd_system_umd_es2015_or_ESNext,
},
{
name: "lib",
@@ -2162,7 +2162,7 @@ namespace ts {
* @param extensionPriority The priority of the extension.
* @param context The expansion context.
*/
function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map<string>, wildcardFiles: Map<string>, extensions: string[], keyMapper: (value: string) => string) {
function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map<string>, wildcardFiles: Map<string>, extensions: ReadonlyArray<string>, keyMapper: (value: string) => string) {
const extensionPriority = getExtensionPriority(file, extensions);
const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority, extensions);
for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) {
@@ -2184,7 +2184,7 @@ namespace ts {
* @param extensionPriority The priority of the extension.
* @param context The expansion context.
*/
function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map<string>, extensions: string[], keyMapper: (value: string) => string) {
function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map<string>, extensions: ReadonlyArray<string>, keyMapper: (value: string) => string) {
const extensionPriority = getExtensionPriority(file, extensions);
const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority, extensions);
for (let i = nextExtensionPriority; i < extensions.length; i++) {
+94 -69
View File
@@ -53,7 +53,7 @@ namespace ts {
}
/* @internal */
export function createSymbolTable(symbols?: Symbol[]): SymbolTable {
export function createSymbolTable(symbols?: ReadonlyArray<Symbol>): SymbolTable {
const result = createMap<Symbol>() as SymbolTable;
if (symbols) {
for (const symbol of symbols) {
@@ -88,7 +88,7 @@ namespace ts {
class MapIterator<T, U extends (string | T | [string, T])> {
private data: MapLike<T>;
private keys: string[];
private keys: ReadonlyArray<string>;
private index = 0;
private selector: (data: MapLike<T>, key: string) => U;
constructor(data: MapLike<T>, selector: (data: MapLike<T>, key: string) => U) {
@@ -175,7 +175,7 @@ namespace ts {
GreaterThan = 1
}
export function length(array: any[]) {
export function length(array: ReadonlyArray<any>) {
return array ? array.length : 0;
}
@@ -184,7 +184,7 @@ namespace ts {
* returns a truthy value, then returns that value.
* If no such value is found, the callback is applied to each element of array and undefined is returned.
*/
export function forEach<T, U>(array: T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
export function forEach<T, U>(array: ReadonlyArray<T> | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
if (array) {
for (let i = 0; i < array.length; i++) {
const result = callback(array[i], i);
@@ -217,14 +217,14 @@ namespace ts {
return undefined;
}
export function zipWith<T, U>(arrayA: T[], arrayB: U[], callback: (a: T, b: U, index: number) => void): void {
export function zipWith<T, U>(arrayA: ReadonlyArray<T>, arrayB: ReadonlyArray<U>, callback: (a: T, b: U, index: number) => void): void {
Debug.assert(arrayA.length === arrayB.length);
for (let i = 0; i < arrayA.length; i++) {
callback(arrayA[i], arrayB[i], i);
}
}
export function zipToMap<T>(keys: string[], values: T[]): Map<T> {
export function zipToMap<T>(keys: ReadonlyArray<string>, values: ReadonlyArray<T>): Map<T> {
Debug.assert(keys.length === values.length);
const map = createMap<T>();
for (let i = 0; i < keys.length; ++i) {
@@ -238,7 +238,7 @@ namespace ts {
* returns a falsey value, then returns false.
* If no such value is found, the callback is applied to each element of array and `true` is returned.
*/
export function every<T>(array: T[], callback: (element: T, index: number) => boolean): boolean {
export function every<T>(array: ReadonlyArray<T>, callback: (element: T, index: number) => boolean): boolean {
if (array) {
for (let i = 0; i < array.length; i++) {
if (!callback(array[i], i)) {
@@ -251,7 +251,7 @@ namespace ts {
}
/** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */
export function find<T>(array: T[], predicate: (element: T, index: number) => boolean): T | undefined {
export function find<T>(array: ReadonlyArray<T>, predicate: (element: T, index: number) => boolean): T | undefined {
for (let i = 0; i < array.length; i++) {
const value = array[i];
if (predicate(value, i)) {
@@ -262,7 +262,7 @@ namespace ts {
}
/** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found. */
export function findIndex<T>(array: T[], predicate: (element: T, index: number) => boolean): number {
export function findIndex<T>(array: ReadonlyArray<T>, predicate: (element: T, index: number) => boolean): number {
for (let i = 0; i < array.length; i++) {
if (predicate(array[i], i)) {
return i;
@@ -275,7 +275,7 @@ namespace ts {
* Returns the first truthy result of `callback`, or else fails.
* This is like `forEach`, but never returns undefined.
*/
export function findMap<T, U>(array: T[], callback: (element: T, index: number) => U | undefined): U {
export function findMap<T, U>(array: ReadonlyArray<T>, callback: (element: T, index: number) => U | undefined): U {
for (let i = 0; i < array.length; i++) {
const result = callback(array[i], i);
if (result) {
@@ -285,7 +285,7 @@ namespace ts {
Debug.fail();
}
export function contains<T>(array: T[], value: T): boolean {
export function contains<T>(array: ReadonlyArray<T>, value: T): boolean {
if (array) {
for (const v of array) {
if (v === value) {
@@ -296,7 +296,7 @@ namespace ts {
return false;
}
export function indexOf<T>(array: T[], value: T): number {
export function indexOf<T>(array: ReadonlyArray<T>, value: T): number {
if (array) {
for (let i = 0; i < array.length; i++) {
if (array[i] === value) {
@@ -307,7 +307,7 @@ namespace ts {
return -1;
}
export function indexOfAnyCharCode(text: string, charCodes: number[], start?: number): number {
export function indexOfAnyCharCode(text: string, charCodes: ReadonlyArray<number>, start?: number): number {
for (let i = start || 0; i < text.length; i++) {
if (contains(charCodes, text.charCodeAt(i))) {
return i;
@@ -316,7 +316,7 @@ namespace ts {
return -1;
}
export function countWhere<T>(array: T[], predicate: (x: T, i: number) => boolean): number {
export function countWhere<T>(array: ReadonlyArray<T>, predicate: (x: T, i: number) => boolean): number {
let count = 0;
if (array) {
for (let i = 0; i < array.length; i++) {
@@ -335,6 +335,8 @@ namespace ts {
*/
export function filter<T, U extends T>(array: T[], f: (x: T) => x is U): U[];
export function filter<T>(array: T[], f: (x: T) => boolean): T[];
export function filter<T, U extends T>(array: ReadonlyArray<T>, f: (x: T) => x is U): ReadonlyArray<U>;
export function filter<T, U extends T>(array: ReadonlyArray<T>, f: (x: T) => boolean): ReadonlyArray<T>;
export function filter<T>(array: T[], f: (x: T) => boolean): T[] {
if (array) {
const len = array.length;
@@ -382,7 +384,7 @@ namespace ts {
array.length = outIndex;
}
export function map<T, U>(array: T[], f: (x: T, i: number) => U): U[] {
export function map<T, U>(array: ReadonlyArray<T>, f: (x: T, i: number) => U): U[] {
let result: U[];
if (array) {
result = [];
@@ -394,6 +396,8 @@ namespace ts {
}
// Maps from T to T and avoids allocation if all elements map to themselves
export function sameMap<T>(array: T[], f: (x: T, i: number) => T): T[];
export function sameMap<T>(array: ReadonlyArray<T>, f: (x: T, i: number) => T): ReadonlyArray<T>;
export function sameMap<T>(array: T[], f: (x: T, i: number) => T): T[] {
let result: T[];
if (array) {
@@ -419,7 +423,7 @@ namespace ts {
*
* @param array The array to flatten.
*/
export function flatten<T>(array: (T | T[])[]): T[] {
export function flatten<T>(array: ReadonlyArray<T | ReadonlyArray<T>>): T[] {
let result: T[];
if (array) {
result = [];
@@ -444,7 +448,7 @@ namespace ts {
* @param array The array to map.
* @param mapfn The callback used to map the result into one or more values.
*/
export function flatMap<T, U>(array: T[] | undefined, mapfn: (x: T, i: number) => U | U[] | undefined): U[] | undefined {
export function flatMap<T, U>(array: ReadonlyArray<T> | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray<U> | undefined): U[] | undefined {
let result: U[];
if (array) {
result = [];
@@ -463,6 +467,17 @@ namespace ts {
return result;
}
export function flatMapIter<T, U>(iter: Iterator<T>, mapfn: (x: T) => U[] | undefined): U[] {
const result: U[] = [];
while (true) {
const { value, done } = iter.next();
if (done) break;
const res = mapfn(value);
if (res) result.push(...res);
}
return result;
}
/**
* Maps an array. If the mapped value is an array, it is spread into the result.
* Avoids allocation if all elements map to themselves.
@@ -470,6 +485,8 @@ namespace ts {
* @param array The array to map.
* @param mapfn The callback used to map the result into one or more values.
*/
export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | ReadonlyArray<T>): T[];
export function sameFlatMap<T>(array: ReadonlyArray<T>, mapfn: (x: T, i: number) => T | ReadonlyArray<T>): ReadonlyArray<T>;
export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] {
let result: T[];
if (array) {
@@ -508,7 +525,7 @@ namespace ts {
* Computes the first matching span of elements and returns a tuple of the first span
* and the remaining elements.
*/
export function span<T>(array: T[], f: (x: T, i: number) => boolean): [T[], T[]] {
export function span<T>(array: ReadonlyArray<T>, f: (x: T, i: number) => boolean): [T[], T[]] {
if (array) {
for (let i = 0; i < array.length; i++) {
if (!f(array[i], i)) {
@@ -528,7 +545,7 @@ namespace ts {
* @param keyfn A callback used to select the key for an element.
* @param mapfn A callback used to map a contiguous chunk of values to a single value.
*/
export function spanMap<T, K, U>(array: T[], keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] {
export function spanMap<T, K, U>(array: ReadonlyArray<T>, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] {
let result: U[];
if (array) {
result = [];
@@ -581,7 +598,7 @@ namespace ts {
return result;
}
export function some<T>(array: T[], predicate?: (value: T) => boolean): boolean {
export function some<T>(array: ReadonlyArray<T>, predicate?: (value: T) => boolean): boolean {
if (array) {
if (predicate) {
for (const v of array) {
@@ -597,6 +614,8 @@ namespace ts {
return false;
}
export function concatenate<T>(array1: T[], array2: T[]): T[];
export function concatenate<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>): ReadonlyArray<T>;
export function concatenate<T>(array1: T[], array2: T[]): T[] {
if (!some(array2)) return array1;
if (!some(array1)) return array2;
@@ -604,7 +623,7 @@ namespace ts {
}
// TODO: fixme (N^2) - add optional comparer so collection can be sorted before deduplication.
export function deduplicate<T>(array: T[], areEqual?: (a: T, b: T) => boolean): T[] {
export function deduplicate<T>(array: ReadonlyArray<T>, areEqual?: (a: T, b: T) => boolean): T[] {
let result: T[];
if (array) {
result = [];
@@ -661,6 +680,8 @@ namespace ts {
/**
* Compacts an array, removing any falsey elements.
*/
export function compact<T>(array: T[]): T[];
export function compact<T>(array: ReadonlyArray<T>): ReadonlyArray<T>;
export function compact<T>(array: T[]): T[] {
let result: T[];
if (array) {
@@ -700,10 +721,11 @@ namespace ts {
return result;
}
export function sum(array: any[], prop: string): number {
export function sum<T extends Record<K, number>, K extends string>(array: T[], prop: K): number {
let result = 0;
for (const v of array) {
result += v[prop];
// Note: we need the following type assertion because of GH #17069
result += v[prop] as number;
}
return result;
}
@@ -727,7 +749,7 @@ namespace ts {
* Gets the actual offset into an array for a relative offset. Negative offsets indicate a
* position offset from the end of the array.
*/
function toOffset(array: any[], offset: number) {
function toOffset(array: ReadonlyArray<any>, offset: number) {
return offset < 0 ? array.length + offset : offset;
}
@@ -741,7 +763,7 @@ namespace ts {
* @param start The offset in `from` at which to start copying values.
* @param end The offset in `from` at which to stop copying values (non-inclusive).
*/
export function addRange<T>(to: T[] | undefined, from: T[] | undefined, start?: number, end?: number): T[] | undefined {
export function addRange<T>(to: T[] | undefined, from: ReadonlyArray<T> | undefined, start?: number, end?: number): T[] | undefined {
if (from === undefined) return to;
if (to === undefined) return from.slice(start, end);
start = start === undefined ? 0 : toOffset(from, start);
@@ -758,14 +780,14 @@ namespace ts {
/**
* Stable sort of an array. Elements equal to each other maintain their relative position in the array.
*/
export function stableSort<T>(array: T[], comparer: (x: T, y: T) => Comparison = compareValues) {
export function stableSort<T>(array: ReadonlyArray<T>, comparer: (x: T, y: T) => Comparison = compareValues) {
return array
.map((_, i) => i) // create array of indices
.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position
.map(i => array[i]); // get sorted array
}
export function rangeEquals<T>(array1: T[], array2: T[], pos: number, end: number) {
export function rangeEquals<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>, pos: number, end: number) {
while (pos < end) {
if (array1[pos] !== array2[pos]) {
return false;
@@ -779,7 +801,7 @@ namespace ts {
* Returns the element at a specific offset in an array if non-empty, `undefined` otherwise.
* A negative offset indicates the element should be retrieved from the end of the array.
*/
export function elementAt<T>(array: T[] | undefined, offset: number): T | undefined {
export function elementAt<T>(array: ReadonlyArray<T> | undefined, offset: number): T | undefined {
if (array) {
offset = toOffset(array, offset);
if (offset < array.length) {
@@ -792,21 +814,21 @@ namespace ts {
/**
* Returns the first element of an array if non-empty, `undefined` otherwise.
*/
export function firstOrUndefined<T>(array: T[]): T | undefined {
export function firstOrUndefined<T>(array: ReadonlyArray<T>): T | undefined {
return elementAt(array, 0);
}
/**
* Returns the last element of an array if non-empty, `undefined` otherwise.
*/
export function lastOrUndefined<T>(array: T[]): T | undefined {
export function lastOrUndefined<T>(array: ReadonlyArray<T>): T | undefined {
return elementAt(array, -1);
}
/**
* Returns the only element of an array if it contains only one element, `undefined` otherwise.
*/
export function singleOrUndefined<T>(array: T[]): T | undefined {
export function singleOrUndefined<T>(array: ReadonlyArray<T>): T | undefined {
return array && array.length === 1
? array[0]
: undefined;
@@ -816,13 +838,15 @@ namespace ts {
* Returns the only element of an array if it contains only one element; otheriwse, returns the
* array.
*/
export function singleOrMany<T>(array: T[]): T | T[];
export function singleOrMany<T>(array: ReadonlyArray<T>): T | ReadonlyArray<T>;
export function singleOrMany<T>(array: T[]): T | T[] {
return array && array.length === 1
? array[0]
: array;
}
export function replaceElement<T>(array: T[], index: number, value: T): T[] {
export function replaceElement<T>(array: ReadonlyArray<T>, index: number, value: T): T[] {
const result = array.slice(0);
result[index] = value;
return result;
@@ -835,7 +859,7 @@ namespace ts {
* @param array A sorted array whose first element must be no larger than number
* @param number The value to be searched for in the array.
*/
export function binarySearch<T>(array: T[], value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number {
export function binarySearch<T>(array: ReadonlyArray<T>, value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number {
if (!array || array.length === 0) {
return -1;
}
@@ -864,8 +888,8 @@ namespace ts {
return ~low;
}
export function reduceLeft<T, U>(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
export function reduceLeft<T>(array: T[], f: (memo: T, value: T, i: number) => T): T;
export function reduceLeft<T, U>(array: ReadonlyArray<T>, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
export function reduceLeft<T>(array: ReadonlyArray<T>, f: (memo: T, value: T, i: number) => T): T;
export function reduceLeft<T>(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T {
if (array && array.length > 0) {
const size = array.length;
@@ -890,8 +914,8 @@ namespace ts {
return initial;
}
export function reduceRight<T, U>(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
export function reduceRight<T>(array: T[], f: (memo: T, value: T, i: number) => T): T;
export function reduceRight<T, U>(array: ReadonlyArray<T>, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U;
export function reduceRight<T>(array: ReadonlyArray<T>, f: (memo: T, value: T, i: number) => T): T;
export function reduceRight<T>(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T {
if (array) {
const size = array.length;
@@ -1058,9 +1082,9 @@ namespace ts {
* the same key with the given 'makeKey' function, then the element with the higher
* index in the array will be the one associated with the produced key.
*/
export function arrayToMap<T>(array: T[], makeKey: (value: T) => string): Map<T>;
export function arrayToMap<T, U>(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map<U>;
export function arrayToMap<T, U>(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map<T | U> {
export function arrayToMap<T>(array: ReadonlyArray<T>, makeKey: (value: T) => string): Map<T>;
export function arrayToMap<T, U>(array: ReadonlyArray<T>, makeKey: (value: T) => string, makeValue: (value: T) => U): Map<U>;
export function arrayToMap<T, U>(array: ReadonlyArray<T>, makeKey: (value: T) => string, makeValue?: (value: T) => U): Map<T | U> {
const result = createMap<T | U>();
for (const value of array) {
result.set(makeKey(value), makeValue ? makeValue(value) : value);
@@ -1073,9 +1097,9 @@ namespace ts {
*
* @param array the array of input elements.
*/
export function arrayToSet(array: string[]): Map<true>;
export function arrayToSet<T>(array: T[], makeKey: (value: T) => string): Map<true>;
export function arrayToSet(array: any[], makeKey?: (value: any) => string): Map<true> {
export function arrayToSet(array: ReadonlyArray<string>): Map<true>;
export function arrayToSet<T>(array: ReadonlyArray<T>, makeKey: (value: T) => string): Map<true>;
export function arrayToSet(array: ReadonlyArray<any>, makeKey?: (value: any) => string): Map<true> {
return arrayToMap<any, true>(array, makeKey || (s => s), () => true);
}
@@ -1158,7 +1182,7 @@ namespace ts {
/**
* Tests whether a value is an array.
*/
export function isArray(value: any): value is any[] {
export function isArray(value: any): value is ReadonlyArray<any> {
return Array.isArray ? Array.isArray(value) : value instanceof Array;
}
@@ -1628,7 +1652,7 @@ namespace ts {
return getNormalizedPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory));
}
export function getNormalizedPathFromPathComponents(pathComponents: string[]) {
export function getNormalizedPathFromPathComponents(pathComponents: ReadonlyArray<string>) {
if (pathComponents && pathComponents.length) {
return pathComponents[0] + pathComponents.slice(1).join(directorySeparator);
}
@@ -1830,7 +1854,7 @@ namespace ts {
}
/* @internal */
export function fileExtensionIsOneOf(path: string, extensions: string[]): boolean {
export function fileExtensionIsOneOf(path: string, extensions: ReadonlyArray<string>): boolean {
for (const extension of extensions) {
if (fileExtensionIs(path, extension)) {
return true;
@@ -1855,7 +1879,7 @@ namespace ts {
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*";
const singleAsteriskRegexFragmentOther = "[^/]*";
export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude"): string | undefined {
export function getRegularExpressionForWildcard(specs: ReadonlyArray<string>, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined {
const patterns = getRegularExpressionsForWildcards(specs, basePath, usage);
if (!patterns || !patterns.length) {
return undefined;
@@ -1867,7 +1891,7 @@ namespace ts {
return `^(${pattern})${terminator}`;
}
function getRegularExpressionsForWildcards(specs: string[], basePath: string, usage: "files" | "directories" | "exclude"): string[] | undefined {
function getRegularExpressionsForWildcards(specs: ReadonlyArray<string>, basePath: string, usage: "files" | "directories" | "exclude"): string[] | undefined {
if (specs === undefined || specs.length === 0) {
return undefined;
}
@@ -1972,21 +1996,21 @@ namespace ts {
}
export interface FileSystemEntries {
files: string[];
directories: string[];
files: ReadonlyArray<string>;
directories: ReadonlyArray<string>;
}
export interface FileMatcherPatterns {
/** One pattern for each "include" spec. */
includeFilePatterns: string[];
includeFilePatterns: ReadonlyArray<string>;
/** One pattern matching one of any of the "include" specs. */
includeFilePattern: string;
includeDirectoryPattern: string;
excludePattern: string;
basePaths: string[];
basePaths: ReadonlyArray<string>;
}
export function getFileMatcherPatterns(path: string, excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns {
export function getFileMatcherPatterns(path: string, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns {
path = normalizePath(path);
currentDirectory = normalizePath(currentDirectory);
const absolutePath = combinePaths(currentDirectory, path);
@@ -2000,7 +2024,7 @@ namespace ts {
};
}
export function matchFiles(path: string, extensions: string[], excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries): string[] {
export function matchFiles(path: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries): string[] {
path = normalizePath(path);
currentDirectory = normalizePath(currentDirectory);
@@ -2020,7 +2044,7 @@ namespace ts {
visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth);
}
return flatten(results);
return flatten<string>(results);
function visitDirectory(path: string, absolutePath: string, depth: number | undefined) {
let { files, directories } = getFileSystemEntries(path);
@@ -2064,7 +2088,7 @@ namespace ts {
/**
* Computes the unique non-wildcard base paths amongst the provided include patterns.
*/
function getBasePaths(path: string, includes: string[], useCaseSensitiveFileNames: boolean) {
function getBasePaths(path: string, includes: ReadonlyArray<string>, useCaseSensitiveFileNames: boolean) {
// Storage for our results in the form of literal paths (e.g. the paths as written by the user).
const basePaths: string[] = [path];
@@ -2136,13 +2160,13 @@ namespace ts {
/**
* List of supported extensions in order of file resolution precedence.
*/
export const supportedTypeScriptExtensions = [Extension.Ts, Extension.Tsx, Extension.Dts];
export const supportedTypeScriptExtensions: ReadonlyArray<Extension> = [Extension.Ts, Extension.Tsx, Extension.Dts];
/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */
export const supportedTypescriptExtensionsForExtractExtension = [Extension.Dts, Extension.Ts, Extension.Tsx];
export const supportedJavascriptExtensions = [Extension.Js, Extension.Jsx];
const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions);
export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray<Extension> = [Extension.Dts, Extension.Ts, Extension.Tsx];
export const supportedJavascriptExtensions: ReadonlyArray<Extension> = [Extension.Js, Extension.Jsx];
const allSupportedExtensions = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions];
export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: JsFileExtensionInfo[]): string[] {
export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray<JsFileExtensionInfo>): ReadonlyArray<string> {
const needAllExtensions = options && options.allowJs;
if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) {
return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions;
@@ -2164,7 +2188,7 @@ namespace ts {
return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension));
}
export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: JsFileExtensionInfo[]) {
export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: ReadonlyArray<JsFileExtensionInfo>) {
if (!fileName) { return false; }
for (const extension of getSupportedExtensions(compilerOptions, extraFileExtensions)) {
@@ -2188,7 +2212,7 @@ namespace ts {
Lowest = DeclarationAndJavaScriptFiles,
}
export function getExtensionPriority(path: string, supportedExtensions: string[]): ExtensionPriority {
export function getExtensionPriority(path: string, supportedExtensions: ReadonlyArray<string>): ExtensionPriority {
for (let i = supportedExtensions.length - 1; i >= 0; i--) {
if (fileExtensionIs(path, supportedExtensions[i])) {
return adjustExtensionPriority(<ExtensionPriority>i, supportedExtensions);
@@ -2203,7 +2227,7 @@ namespace ts {
/**
* Adjusts an extension priority to be the highest priority within the same range.
*/
export function adjustExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: string[]): ExtensionPriority {
export function adjustExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: ReadonlyArray<string>): ExtensionPriority {
if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) {
return ExtensionPriority.TypeScriptFiles;
}
@@ -2212,12 +2236,13 @@ namespace ts {
}
else {
return supportedExtensions.length;
} }
}
}
/**
* Gets the next lowest extension priority for a given priority.
*/
export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: string[]): ExtensionPriority {
export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: ReadonlyArray<string>): ExtensionPriority {
if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) {
return ExtensionPriority.DeclarationAndJavaScriptFiles;
}
@@ -2406,7 +2431,7 @@ namespace ts {
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
*/
/* @internal */
export function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined {
export function matchPatternOrExact(patternStrings: ReadonlyArray<string>, candidate: string): string | Pattern | undefined {
const patterns: Pattern[] = [];
for (const patternString of patternStrings) {
const pattern = tryParsePattern(patternString);
@@ -2439,7 +2464,7 @@ namespace ts {
/** Return the object corresponding to the best pattern to match `candidate`. */
/* @internal */
export function findBestPatternMatch<T>(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined {
export function findBestPatternMatch<T>(values: ReadonlyArray<T>, getPattern: (value: T) => Pattern, candidate: string): T | undefined {
let matchedValue: T | undefined = undefined;
// use length of prefix as betterness criteria
let longestMatchPrefixLength = -1;
@@ -2495,7 +2520,7 @@ namespace ts {
Debug.fail(`File ${path} has unknown extension.`);
}
export function tryGetExtensionFromPath(path: string): Extension | undefined {
return find(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e));
return find<Extension>(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e));
}
export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) {
+1 -1
View File
@@ -2658,7 +2658,7 @@
"category": "Message",
"code": 6015
},
"Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": {
"Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": {
"category": "Message",
"code": 6016
},
+57 -41
View File
@@ -2,6 +2,15 @@
/// <reference path="scanner.ts"/>
namespace ts {
const enum SignatureFlags {
None = 0,
Yield = 1 << 0,
Await = 1 << 1,
Type = 1 << 2,
RequireCompleteParameterList = 1 << 3,
IgnoreMissingOpenBrace = 1 << 4,
}
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
@@ -2218,15 +2227,12 @@ namespace ts {
function fillSignature(
returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken,
yieldContext: boolean,
awaitContext: boolean,
requireCompleteParameterList: boolean,
flags: SignatureFlags,
signature: SignatureDeclaration): void {
signature.typeParameters = parseTypeParameters();
signature.parameters = parseParameterList(flags);
const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken;
signature.typeParameters = parseTypeParameters();
signature.parameters = parseParameterList(yieldContext, awaitContext, requireCompleteParameterList);
if (returnTokenRequired) {
parseExpected(returnToken);
signature.type = parseTypeOrTypePredicate();
@@ -2234,9 +2240,19 @@ namespace ts {
else if (parseOptional(returnToken)) {
signature.type = parseTypeOrTypePredicate();
}
else if (flags & SignatureFlags.Type) {
const start = scanner.getTokenPos();
const length = scanner.getTextPos() - start;
const backwardToken = parseOptional(returnToken === SyntaxKind.ColonToken ? SyntaxKind.EqualsGreaterThanToken : SyntaxKind.ColonToken);
if (backwardToken) {
// This is easy to get backward, especially in type contexts, so parse the type anyway
signature.type = parseTypeOrTypePredicate();
parseErrorAtPosition(start, length, Diagnostics._0_expected, tokenToString(returnToken));
}
}
}
function parseParameterList(yieldContext: boolean, awaitContext: boolean, requireCompleteParameterList: boolean) {
function parseParameterList(flags: SignatureFlags) {
// FormalParameters [Yield,Await]: (modified)
// [empty]
// FormalParameterList[?Yield,Await]
@@ -2254,15 +2270,15 @@ namespace ts {
const savedYieldContext = inYieldContext();
const savedAwaitContext = inAwaitContext();
setYieldContext(yieldContext);
setAwaitContext(awaitContext);
setYieldContext(!!(flags & SignatureFlags.Yield));
setAwaitContext(!!(flags & SignatureFlags.Await));
const result = parseDelimitedList(ParsingContext.Parameters, parseParameter);
setYieldContext(savedYieldContext);
setAwaitContext(savedAwaitContext);
if (!parseExpected(SyntaxKind.CloseParenToken) && requireCompleteParameterList) {
if (!parseExpected(SyntaxKind.CloseParenToken) && (flags & SignatureFlags.RequireCompleteParameterList)) {
// Caller insisted that we had to end with a ) We didn't. So just return
// undefined here.
return undefined;
@@ -2274,7 +2290,7 @@ namespace ts {
// We didn't even have an open paren. If the caller requires a complete parameter list,
// we definitely can't provide that. However, if they're ok with an incomplete one,
// then just return an empty set of parameters.
return requireCompleteParameterList ? undefined : createMissingList<ParameterDeclaration>();
return (flags & SignatureFlags.RequireCompleteParameterList) ? undefined : createMissingList<ParameterDeclaration>();
}
function parseTypeMemberSemicolon() {
@@ -2293,7 +2309,7 @@ namespace ts {
if (kind === SyntaxKind.ConstructSignature) {
parseExpected(SyntaxKind.NewKeyword);
}
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type, node);
parseTypeMemberSemicolon();
return addJSDocComment(finishNode(node));
}
@@ -2383,7 +2399,7 @@ namespace ts {
// Method signatures don't exist in expression contexts. So they have neither
// [Yield] nor [Await]
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, method);
fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type, method);
parseTypeMemberSemicolon();
return addJSDocComment(finishNode(method));
}
@@ -2527,7 +2543,7 @@ namespace ts {
if (kind === SyntaxKind.ConstructorType) {
parseExpected(SyntaxKind.NewKeyword);
}
fillSignature(SyntaxKind.EqualsGreaterThanToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node);
return finishNode(node);
}
@@ -3254,7 +3270,7 @@ namespace ts {
function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction {
const node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction);
node.modifiers = parseModifiersForArrowFunction();
const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async);
const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
// Arrow functions are never generators.
//
@@ -3263,7 +3279,7 @@ namespace ts {
// a => (b => c)
// And think that "(b =>" was actually a parenthesized arrow function with a missing
// close paren.
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ !allowAmbiguity, node);
fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? SignatureFlags.None : SignatureFlags.RequireCompleteParameterList), node);
// If we couldn't get parameters, we definitely could not parse out an arrow function.
if (!node.parameters) {
@@ -3288,7 +3304,7 @@ namespace ts {
function parseArrowFunctionExpressionBody(isAsync: boolean): Block | Expression {
if (token() === SyntaxKind.OpenBraceToken) {
return parseFunctionBlock(/*allowYield*/ false, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
return parseFunctionBlock(isAsync ? SignatureFlags.Await : SignatureFlags.None);
}
if (token() !== SyntaxKind.SemicolonToken &&
@@ -3309,8 +3325,8 @@ namespace ts {
// try to recover better. If we don't do this, then the next close curly we see may end
// up preemptively closing the containing construct.
//
// Note: even when 'ignoreMissingOpenBrace' is passed as true, parseBody will still error.
return parseFunctionBlock(/*allowYield*/ false, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ true);
// Note: even when 'IgnoreMissingOpenBrace' is passed, parseBody will still error.
return parseFunctionBlock(SignatureFlags.IgnoreMissingOpenBrace | (isAsync ? SignatureFlags.Await : SignatureFlags.None));
}
return isAsync
@@ -4386,16 +4402,16 @@ namespace ts {
parseExpected(SyntaxKind.FunctionKeyword);
node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken);
const isGenerator = !!node.asteriskToken;
const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async);
const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node);
node.body = parseFunctionBlock(isGenerator | isAsync);
if (saveDecoratorContext) {
setDecoratorContext(/*val*/ true);
@@ -4444,12 +4460,12 @@ namespace ts {
return finishNode(node);
}
function parseFunctionBlock(allowYield: boolean, allowAwait: boolean, ignoreMissingOpenBrace: boolean, diagnosticMessage?: DiagnosticMessage): Block {
function parseFunctionBlock(flags: SignatureFlags, diagnosticMessage?: DiagnosticMessage): Block {
const savedYieldContext = inYieldContext();
setYieldContext(allowYield);
setYieldContext(!!(flags & SignatureFlags.Yield));
const savedAwaitContext = inAwaitContext();
setAwaitContext(allowAwait);
setAwaitContext(!!(flags & SignatureFlags.Await));
// We may be in a [Decorator] context when parsing a function expression or
// arrow function. The body of the function is not in [Decorator] context.
@@ -4458,7 +4474,7 @@ namespace ts {
setDecoratorContext(/*val*/ false);
}
const block = parseBlock(ignoreMissingOpenBrace, diagnosticMessage);
const block = parseBlock(!!(flags & SignatureFlags.IgnoreMissingOpenBrace), diagnosticMessage);
if (saveDecoratorContext) {
setDecoratorContext(/*val*/ true);
@@ -5005,13 +5021,13 @@ namespace ts {
return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token() === SyntaxKind.StringLiteral);
}
function parseFunctionBlockOrSemicolon(isGenerator: boolean, isAsync: boolean, diagnosticMessage?: DiagnosticMessage): Block {
function parseFunctionBlockOrSemicolon(flags: SignatureFlags, diagnosticMessage?: DiagnosticMessage): Block {
if (token() !== SyntaxKind.OpenBraceToken && canParseSemicolon()) {
parseSemicolon();
return;
}
return parseFunctionBlock(isGenerator, isAsync, /*ignoreMissingOpenBrace*/ false, diagnosticMessage);
return parseFunctionBlock(flags, diagnosticMessage);
}
// DECLARATIONS
@@ -5146,10 +5162,10 @@ namespace ts {
parseExpected(SyntaxKind.FunctionKeyword);
node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken);
node.name = hasModifier(node, ModifierFlags.Default) ? parseOptionalIdentifier() : parseIdentifier();
const isGenerator = !!node.asteriskToken;
const isAsync = hasModifier(node, ModifierFlags.Async);
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, Diagnostics.or_expected);
const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node);
node.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, Diagnostics.or_expected);
return addJSDocComment(finishNode(node));
}
@@ -5158,8 +5174,8 @@ namespace ts {
node.decorators = decorators;
node.modifiers = modifiers;
parseExpected(SyntaxKind.ConstructorKeyword);
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false, Diagnostics.or_expected);
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected);
return addJSDocComment(finishNode(node));
}
@@ -5170,10 +5186,10 @@ namespace ts {
method.asteriskToken = asteriskToken;
method.name = name;
method.questionToken = questionToken;
const isGenerator = !!asteriskToken;
const isAsync = hasModifier(method, ModifierFlags.Async);
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, method);
method.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, diagnosticMessage);
const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = hasModifier(method, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, method);
method.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, diagnosticMessage);
return addJSDocComment(finishNode(method));
}
@@ -5226,8 +5242,8 @@ namespace ts {
node.decorators = decorators;
node.modifiers = modifiers;
node.name = parsePropertyName();
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false);
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None);
return addJSDocComment(finishNode(node));
}
+9 -4
View File
@@ -447,7 +447,7 @@ namespace ts {
let moduleResolutionCache: ModuleResolutionCache;
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[];
if (host.resolveModuleNames) {
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => {
resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(checkAllDefined(moduleNames), containingFile).map(resolved => {
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
return resolved as ResolvedModuleFull;
@@ -460,16 +460,16 @@ namespace ts {
else {
moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x));
const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache).resolvedModule;
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(moduleNames, containingFile, loader);
resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(checkAllDefined(moduleNames), containingFile, loader);
}
let resolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string) => ResolvedTypeReferenceDirective[];
if (host.resolveTypeReferenceDirectives) {
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile);
resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(checkAllDefined(typeDirectiveNames), containingFile);
}
else {
const loader = (typesRef: string, containingFile: string) => resolveTypeReferenceDirective(typesRef, containingFile, options, host).resolvedTypeReferenceDirective;
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(typeReferenceDirectiveNames, containingFile, loader);
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(checkAllDefined(typeReferenceDirectiveNames), containingFile, loader);
}
const filesByName = createMap<SourceFile | undefined>();
@@ -2127,4 +2127,9 @@ namespace ts {
return options.allowJs ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_allowJs_is_not_set;
}
}
function checkAllDefined(names: string[]): string[] {
Debug.assert(names.every(name => name !== undefined), "A name is undefined.", () => JSON.stringify(names));
return names;
}
}
+10 -3
View File
@@ -326,13 +326,20 @@ namespace ts {
}
export function getPositionOfLineAndCharacter(sourceFile: SourceFile, line: number, character: number): number {
return computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character);
return computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character, sourceFile.text);
}
/* @internal */
export function computePositionOfLineAndCharacter(lineStarts: number[], line: number, character: number): number {
export function computePositionOfLineAndCharacter(lineStarts: number[], line: number, character: number, debugText?: string): number {
Debug.assert(line >= 0 && line < lineStarts.length);
return lineStarts[line] + character;
const res = lineStarts[line] + character;
if (line < lineStarts.length - 1) {
Debug.assert(res < lineStarts[line + 1]);
}
else if (debugText !== undefined) {
Debug.assert(res < debugText.length);
}
return res;
}
/* @internal */
+3 -3
View File
@@ -39,7 +39,7 @@ namespace ts {
getExecutingFilePath(): string;
getCurrentDirectory(): string;
getDirectories(path: string): string[];
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[];
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
getModifiedTime?(path: string): Date;
/**
* This should be cryptographically secure.
@@ -100,7 +100,7 @@ namespace ts {
readFile(path: string): string;
writeFile(path: string, contents: string): void;
getDirectories(path: string): string[];
readDirectory(path: string, extensions?: string[], basePaths?: string[], excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[];
readDirectory(path: string, extensions?: ReadonlyArray<string>, basePaths?: ReadonlyArray<string>, excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[];
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
realpath(path: string): string;
@@ -287,7 +287,7 @@ namespace ts {
}
}
function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[], depth?: number): string[] {
function readDirectory(path: string, extensions?: ReadonlyArray<string>, excludes?: ReadonlyArray<string>, includes?: ReadonlyArray<string>, depth?: number): string[] {
return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries);
}
+1 -1
View File
@@ -3579,7 +3579,7 @@ namespace ts {
// Map spans of spread expressions into their expressions and spans of other
// expressions into an array literal.
const numElements = elements.length;
const segments = flatten(
const segments = flatten<Expression>(
spanMap(elements, partitionSpread, (partition, visitPartition, _start, end) =>
visitPartition(partition, multiLine, hasTrailingComma && end === numElements)
)
+1 -1
View File
@@ -88,7 +88,7 @@ namespace ts {
else {
// Map spans of JsxAttribute nodes into object literals and spans
// of JsxSpreadAttribute nodes into expressions.
const segments = flatten(
const segments = flatten<Expression | ObjectLiteralExpression>(
spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread
? map(attrs, transformJsxSpreadAttributeToExpression)
: createObjectLiteral(map(attrs, transformJsxAttributeToObjectLiteralElement))
+1 -1
View File
@@ -1377,7 +1377,7 @@ namespace ts {
const decoratorExpressions: Expression[] = [];
addRange(decoratorExpressions, map(allDecorators.decorators, transformDecorator));
addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter));
addRange(decoratorExpressions, flatMap<Decorator[], Expression>(allDecorators.parameters, transformDecoratorsOfParameter));
addTypeMetadata(node, container, decoratorExpressions);
return decoratorExpressions;
}
+7 -10
View File
@@ -2406,7 +2406,7 @@ namespace ts {
export interface ParseConfigHost {
useCaseSensitiveFileNames: boolean;
readDirectory(rootDir: string, extensions: string[], excludes: string[], includes: string[], depth: number): string[];
readDirectory(rootDir: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, depth: number): string[];
/**
* Gets a value indicating whether the specified path exists and is a file.
@@ -2890,13 +2890,11 @@ namespace ts {
TypeParameter = 1 << 18, // Type parameter
TypeAlias = 1 << 19, // Type alias
ExportValue = 1 << 20, // Exported value marker (see comment in declareModuleMember in binder)
ExportType = 1 << 21, // Exported type marker (see comment in declareModuleMember in binder)
ExportNamespace = 1 << 22, // Exported namespace marker (see comment in declareModuleMember in binder)
Alias = 1 << 23, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
Prototype = 1 << 24, // Prototype property (no source representation)
ExportStar = 1 << 25, // Export * declaration
Optional = 1 << 26, // Optional property
Transient = 1 << 27, // Transient symbol (created during type check)
Alias = 1 << 21, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
Prototype = 1 << 22, // Prototype property (no source representation)
ExportStar = 1 << 23, // Export * declaration
Optional = 1 << 24, // Optional property
Transient = 1 << 25, // Transient symbol (created during type check)
Enum = RegularEnum | ConstEnum,
Variable = FunctionScopedVariable | BlockScopedVariable,
@@ -2941,7 +2939,6 @@ namespace ts {
BlockScoped = BlockScopedVariable | Class | Enum,
PropertyOrAccessor = Property | Accessor,
Export = ExportNamespace | ExportType | ExportValue,
ClassMember = Method | Accessor | Property,
@@ -4354,7 +4351,7 @@ namespace ts {
*/
export type Visitor = (node: Node) => VisitResult<Node>;
export type VisitResult<T extends Node> = T | T[];
export type VisitResult<T extends Node> = T | T[] | undefined;
export interface Printer {
/**
+49 -34
View File
@@ -44,42 +44,47 @@ namespace ts {
string(): string;
}
// Pool writers to avoid needing to allocate them for every symbol we write.
const stringWriters: StringSymbolWriter[] = [];
export function getSingleLineStringWriter(): StringSymbolWriter {
if (stringWriters.length === 0) {
let str = "";
const stringWriter = createSingleLineStringWriter();
let stringWriterAcquired = false;
const writeText: (text: string) => void = text => str += text;
return {
string: () => str,
writeKeyword: writeText,
writeOperator: writeText,
writePunctuation: writeText,
writeSpace: writeText,
writeStringLiteral: writeText,
writeParameter: writeText,
writeProperty: writeText,
writeSymbol: writeText,
function createSingleLineStringWriter(): StringSymbolWriter {
let str = "";
// Completely ignore indentation for string writers. And map newlines to
// a single space.
writeLine: () => str += " ",
increaseIndent: noop,
decreaseIndent: noop,
clear: () => str = "",
trackSymbol: noop,
reportInaccessibleThisError: noop,
reportPrivateInBaseOfClassExpression: noop,
};
}
const writeText: (text: string) => void = text => str += text;
return {
string: () => str,
writeKeyword: writeText,
writeOperator: writeText,
writePunctuation: writeText,
writeSpace: writeText,
writeStringLiteral: writeText,
writeParameter: writeText,
writeProperty: writeText,
writeSymbol: writeText,
return stringWriters.pop();
// Completely ignore indentation for string writers. And map newlines to
// a single space.
writeLine: () => str += " ",
increaseIndent: noop,
decreaseIndent: noop,
clear: () => str = "",
trackSymbol: noop,
reportInaccessibleThisError: noop,
reportPrivateInBaseOfClassExpression: noop,
};
}
export function releaseStringWriter(writer: StringSymbolWriter) {
writer.clear();
stringWriters.push(writer);
export function usingSingleLineStringWriter(action: (writer: StringSymbolWriter) => void): string {
try {
Debug.assert(!stringWriterAcquired);
stringWriterAcquired = true;
action(stringWriter);
return stringWriter.string();
}
finally {
stringWriter.clear();
stringWriterAcquired = false;
}
}
export function getFullWidth(node: Node) {
@@ -1619,7 +1624,7 @@ namespace ts {
}
export function isRestParameter(node: ParameterDeclaration) {
if (node && (node.flags & NodeFlags.JavaScriptFile)) {
if (isInJavaScriptFile(node)) {
if (node.type && node.type.kind === SyntaxKind.JSDocVariadicType ||
forEach(getJSDocParameterTags(node),
t => t.typeExpression && t.typeExpression.type.kind === SyntaxKind.JSDocVariadicType)) {
@@ -2743,7 +2748,7 @@ namespace ts {
if (node.typeParameters) {
return node.typeParameters;
}
if (node.flags & NodeFlags.JavaScriptFile) {
if (isInJavaScriptFile(node)) {
const templateTag = getJSDocTemplateTag(node);
return templateTag && templateTag.typeParameters;
}
@@ -3334,7 +3339,7 @@ namespace ts {
}
}
return stableSort(result, (x, y) => compareValues(x[0], y[0]));
return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0]));
}
export function formatSyntaxKind(kind: SyntaxKind): string {
@@ -3598,6 +3603,15 @@ namespace ts {
}
return previous[previous.length - 1];
}
export function skipAlias(symbol: Symbol, checker: TypeChecker) {
return symbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
}
/** See comment on `declareModuleMember` in `binder.ts`. */
export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags {
return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags;
}
}
namespace ts {
@@ -5116,6 +5130,7 @@ namespace ts {
|| kind === SyntaxKind.ThisKeyword
|| kind === SyntaxKind.TrueKeyword
|| kind === SyntaxKind.SuperKeyword
|| kind === SyntaxKind.ImportKeyword
|| kind === SyntaxKind.NonNullExpression
|| kind === SyntaxKind.MetaProperty;
}
+47 -26
View File
@@ -219,7 +219,7 @@ namespace FourSlash {
// Add input file which has matched file name with the given reference-file path.
// This is necessary when resolveReference flag is specified
private addMatchedInputFile(referenceFilePath: string, extensions: string[]) {
private addMatchedInputFile(referenceFilePath: string, extensions: ReadonlyArray<string>) {
const inputFiles = this.inputFiles;
const languageServiceAdapterHost = this.languageServiceAdapterHost;
if (!extensions) {
@@ -605,7 +605,7 @@ namespace FourSlash {
this.verifyGoToXPlain(arg0, endMarkerNames, getDefs);
}
else if (ts.isArray(arg0)) {
const pairs: [string | string[], string | string[]][] = arg0;
const pairs: ReadonlyArray<[string | string[], string | string[]]> = arg0;
for (const [start, end] of pairs) {
this.verifyGoToXPlain(start, end, getDefs);
}
@@ -949,6 +949,22 @@ namespace FourSlash {
this.verifySymbol(symbol, declarationRanges);
}
public symbolsInScope(range: Range): ts.Symbol[] {
const node = this.goToAndGetNode(range);
return this.getChecker().getSymbolsInScope(node, ts.SymbolFlags.Value | ts.SymbolFlags.Type | ts.SymbolFlags.Namespace);
}
public verifyTypeOfSymbolAtLocation(range: Range, symbol: ts.Symbol, expected: string): void {
const node = this.goToAndGetNode(range);
const checker = this.getChecker();
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
const actual = checker.typeToString(type);
if (actual !== expected) {
this.raiseError(`Expected: '${expected}', actual: '${actual}'`);
}
}
private verifyReferencesAre(expectedReferences: Range[]) {
const actualReferences = this.getReferencesAtCaret() || [];
@@ -1790,7 +1806,14 @@ namespace FourSlash {
this.editScriptAndUpdateMarkers(fileName, offsetStart, offsetEnd, edit.newText);
const editDelta = edit.newText.length - edit.span.length;
if (offsetStart <= this.currentCaretPosition) {
this.currentCaretPosition += editDelta;
if (offsetEnd <= this.currentCaretPosition) {
// The entirety of the edit span falls before the caret position, shift the caret accordingly
this.currentCaretPosition += editDelta;
}
else {
// The span being replaced includes the caret position, place the caret at the beginning of the span
this.currentCaretPosition = offsetStart;
}
}
runningOffset += editDelta;
// TODO: Consider doing this at least some of the time for higher fidelity. Currently causes a failure (bug 707150)
@@ -2320,35 +2343,25 @@ namespace FourSlash {
* @param fileName Path to file where error should be retrieved from.
*/
private getCodeFixActions(fileName: string, errorCode?: number): ts.CodeAction[] {
const diagnosticsForCodeFix = this.getDiagnostics(fileName).map(diagnostic => {
return {
start: diagnostic.start,
length: diagnostic.length,
code: diagnostic.code
};
});
const dedupedDiagnositcs = ts.deduplicate(diagnosticsForCodeFix, ts.equalOwnProperties);
let actions: ts.CodeAction[] = undefined;
for (const diagnostic of dedupedDiagnositcs) {
const diagnosticsForCodeFix = this.getDiagnostics(fileName).map(diagnostic => ({
start: diagnostic.start,
length: diagnostic.length,
code: diagnostic.code
}));
return ts.flatMap(ts.deduplicate(diagnosticsForCodeFix, ts.equalOwnProperties), diagnostic => {
if (errorCode && errorCode !== diagnostic.code) {
continue;
return;
}
const newActions = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [diagnostic.code], this.formatCodeSettings);
if (newActions && newActions.length) {
actions = actions ? actions.concat(newActions) : newActions;
}
}
return actions;
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings);
});
}
private applyCodeActions(actions: ts.CodeAction[], index?: number): void {
if (index === undefined) {
if (!(actions && actions.length === 1)) {
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found.`);
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : "" }`);
}
index = 0;
}
@@ -2373,7 +2386,7 @@ namespace FourSlash {
const codeFixes = this.getCodeFixActions(this.activeFile.fileName, errorCode);
if (!codeFixes || codeFixes.length === 0) {
if (codeFixes.length === 0) {
if (expectedTextArray.length !== 0) {
this.raiseError("No codefixes returned.");
}
@@ -2702,11 +2715,11 @@ namespace FourSlash {
public verifyCodeFixAvailable(negative: boolean) {
const codeFix = this.getCodeFixActions(this.activeFile.fileName);
if (negative && codeFix) {
if (negative && codeFix.length) {
this.raiseError(`verifyCodeFixAvailable failed - expected no fixes but found one.`);
}
if (!(negative || codeFix)) {
if (!(negative || codeFix.length)) {
this.raiseError(`verifyCodeFixAvailable failed - expected code fixes but none found.`);
}
}
@@ -3426,6 +3439,10 @@ namespace FourSlashInterface {
public markerByName(s: string): FourSlash.Marker {
return this.state.getMarkerByName(s);
}
public symbolsInScope(range: FourSlash.Range): ts.Symbol[] {
return this.state.symbolsInScope(range);
}
}
export class GoTo {
@@ -3694,6 +3711,10 @@ namespace FourSlashInterface {
this.state.verifySymbolAtLocation(startRange, declarationRanges);
}
public typeOfSymbolAtLocation(range: FourSlash.Range, symbol: ts.Symbol, expected: string) {
this.state.verifyTypeOfSymbolAtLocation(range, symbol, expected);
}
public referencesOf(start: FourSlash.Range, references: FourSlash.Range[]) {
this.state.verifyReferencesOf(start, references);
}
+1 -1
View File
@@ -493,7 +493,7 @@ namespace Harness {
args(): string[];
getExecutingFilePath(): string;
exit(exitCode?: number): void;
readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[], depth?: number): string[];
readDirectory(path: string, extension?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
tryEnableSourceMapsForHost?(): void;
getEnvironmentVariable?(name: string): string;
}
+1 -1
View File
@@ -208,7 +208,7 @@ namespace Harness.LanguageService {
const script = this.getScriptSnapshot(fileName);
return script !== undefined;
}
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] {
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
return ts.matchFiles(path, extensions, exclude, include,
/*useCaseSensitiveFileNames*/ false,
this.getCurrentDirectory(),
+4 -4
View File
@@ -64,11 +64,11 @@ interface IOLog {
}[];
directoriesRead: {
path: string,
extensions: string[],
exclude: string[],
include: string[],
extensions: ReadonlyArray<string>,
exclude: ReadonlyArray<string>,
include: ReadonlyArray<string>,
depth: number,
result: string[]
result: ReadonlyArray<string>,
}[];
}
@@ -585,27 +585,25 @@ namespace ts.projectSystem {
return [];
}
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] {
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
return ts.matchFiles(this.toFullPath(path), extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => {
const result: FileSystemEntries = {
directories: [],
files: []
};
const directories: string[] = [];
const files: string[] = [];
const dirEntry = this.fs.get(this.toPath(dir));
if (isFolder(dirEntry)) {
dirEntry.entries.forEach((entry) => {
if (isFolder(entry)) {
result.directories.push(getBaseFileName(entry.fullPath));
directories.push(getBaseFileName(entry.fullPath));
}
else if (isFile(entry)) {
result.files.push(getBaseFileName(entry.fullPath));
files.push(getBaseFileName(entry.fullPath));
}
else {
Debug.fail("Unknown entry");
}
});
}
return result;
return { directories, files };
});
}
+36 -4
View File
@@ -44,6 +44,18 @@ namespace ts.projectSystem {
});
}
function trackingLogger(): { log(message: string): void, finish(): string[] } {
const logs: string[] = [];
return {
log(message) {
logs.push(message);
},
finish() {
return logs;
}
};
}
import typingsName = TI.typingsName;
describe("local module", () => {
@@ -1038,7 +1050,12 @@ namespace ts.projectSystem {
const cache = createMap<string>();
const host = createServerHost([app, jquery, chroma]);
const result = JsTyping.discoverTypings(host, [app.path, jquery.path, chroma.path], getDirectoryPath(<Path>app.path), /*safeListPath*/ undefined, cache, { enable: true }, []);
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [app.path, jquery.path, chroma.path], getDirectoryPath(<Path>app.path), /*safeListPath*/ undefined, cache, { enable: true }, []);
assert.deepEqual(logger.finish(), [
'Inferred typings from file names: ["jquery","chroma-js"]',
'Result: {"cachedTypingPaths":[],"newTypingNames":["jquery","chroma-js"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
]);
assert.deepEqual(result.newTypingNames, ["jquery", "chroma-js"]);
});
@@ -1051,7 +1068,12 @@ namespace ts.projectSystem {
const cache = createMap<string>();
for (const name of JsTyping.nodeCoreModuleList) {
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, [name, "somename"]);
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, [name, "somename"]);
assert.deepEqual(logger.finish(), [
'Inferred typings from unresolved imports: ["node","somename"]',
'Result: {"cachedTypingPaths":[],"newTypingNames":["node","somename"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
]);
assert.deepEqual(result.newTypingNames.sort(), ["node", "somename"]);
}
});
@@ -1067,7 +1089,12 @@ namespace ts.projectSystem {
};
const host = createServerHost([f, node]);
const cache = createMapFromTemplate<string>({ "node": node.path });
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, ["fs", "bar"]);
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, ["fs", "bar"]);
assert.deepEqual(logger.finish(), [
'Inferred typings from unresolved imports: ["node","bar"]',
'Result: {"cachedTypingPaths":["/a/b/node.d.ts"],"newTypingNames":["bar"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
]);
assert.deepEqual(result.cachedTypingPaths, [node.path]);
assert.deepEqual(result.newTypingNames, ["bar"]);
});
@@ -1087,7 +1114,12 @@ namespace ts.projectSystem {
};
const host = createServerHost([app, a, b]);
const cache = createMap<string>();
const result = JsTyping.discoverTypings(host, [app.path], getDirectoryPath(<Path>app.path), /*safeListPath*/ undefined, cache, { enable: true }, /*unresolvedImports*/ []);
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(<Path>app.path), /*safeListPath*/ undefined, cache, { enable: true }, /*unresolvedImports*/ []);
assert.deepEqual(logger.finish(), [
'Searching for typing names in /node_modules; all files: ["/node_modules/a/package.json"]',
'Result: {"cachedTypingPaths":[],"newTypingNames":["a"],"filesToWatch":["/bower_components","/node_modules"]}',
]);
assert.deepEqual(result, {
cachedTypingPaths: [],
newTypingNames: ["a"], // But not "b"
+4 -8
View File
@@ -7,8 +7,7 @@ namespace ts {
}
function lineColToPosition(lineIndex: server.LineIndex, line: number, col: number) {
const lineInfo = lineIndex.lineNumberToInfo(line);
return (lineInfo.offset + col - 1);
return lineIndex.absolutePositionOfStartOfLine(line) + (col - 1);
}
function validateEdit(lineIndex: server.LineIndex, sourceText: string, position: number, deleteLength: number, insertString: string): void {
@@ -298,20 +297,17 @@ and grew 1cm per day`;
it("Line/offset from pos", () => {
for (let i = 0; i < iterationCount; i++) {
const lp = lineIndex.charOffsetToLineNumberAndPos(rsa[i]);
const lp = lineIndex.positionToLineOffset(rsa[i]);
const lac = ts.computeLineAndCharacterOfPosition(lineMap, rsa[i]);
assert.equal(lac.line + 1, lp.line, "Line number mismatch " + (lac.line + 1) + " " + lp.line + " " + i);
assert.equal(lac.character, (lp.offset), "Charachter offset mismatch " + lac.character + " " + lp.offset + " " + i);
assert.equal(lac.character, lp.offset - 1, "Character offset mismatch " + lac.character + " " + (lp.offset - 1) + " " + i);
}
});
it("Start pos from line", () => {
for (let i = 0; i < iterationCount; i++) {
for (let j = 0; j < lines.length; j++) {
const lineInfo = lineIndex.lineNumberToInfo(j + 1);
const lineIndexOffset = lineInfo.offset;
const lineMapOffset = lineMap[j];
assert.equal(lineIndexOffset, lineMapOffset);
assert.equal(lineIndex.absolutePositionOfStartOfLine(j + 1), lineMap[j]);
}
}
});
+1 -1
View File
@@ -216,7 +216,7 @@ namespace Utils {
}
}
readDirectory(path: string, extensions: string[], excludes: string[], includes: string[], depth: number) {
readDirectory(path: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, depth: number) {
return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, depth, (path: string) => this.getAccessibleFileSystemEntries(path));
}
}
+1 -1
View File
@@ -277,7 +277,7 @@ namespace ts.server {
const referencedFilePaths = this.project.getReferencedFiles(fileInfo.scriptInfo.path);
if (referencedFilePaths.length > 0) {
return map(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos);
return map<Path, ModuleBuilderFileInfo>(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos);
}
return [];
}
+1 -1
View File
@@ -227,7 +227,7 @@ namespace ts.server {
}));
}
getFormattingEditsForRange(file: string, start: number, end: number, _options: ts.FormatCodeOptions): ts.TextChange[] {
getFormattingEditsForRange(file: string, start: number, end: number, _options: FormatCodeOptions): ts.TextChange[] {
const args: protocol.FormatRequestArgs = this.createFileLocationRequestArgsWithEndLineAndOffset(file, start, end);
+6 -6
View File
@@ -45,7 +45,7 @@ namespace ts.server {
* Any compiler options that might contain paths will be taken out.
* Enum compiler options will be converted to strings.
*/
readonly compilerOptions: ts.CompilerOptions;
readonly compilerOptions: CompilerOptions;
// "extends", "files", "include", or "exclude" will be undefined if an external config is used.
// Otherwise, we will use "true" if the property is present and "false" if it is missing.
readonly extends: boolean | undefined;
@@ -201,7 +201,7 @@ namespace ts.server {
* This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project.
*/
export function combineProjectOutput<T>(projects: Project[], action: (project: Project) => T[], comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) {
const result = projects.reduce<T[]>((previous, current) => concatenate(previous, action(current)), []).sort(comparer);
const result = flatMap(projects, action).sort(comparer);
return projects.length > 1 ? deduplicate(result, areEqual) : result;
}
@@ -226,7 +226,7 @@ namespace ts.server {
getFileName: x => x,
getScriptKind: _ => undefined,
hasMixedContent: (fileName, extraFileExtensions) => {
const mixedContentExtensions = ts.map(ts.filter(extraFileExtensions, item => item.isMixedContent), item => item.extension);
const mixedContentExtensions = map(filter(extraFileExtensions, item => item.isMixedContent), item => item.extension);
return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension));
}
};
@@ -663,7 +663,7 @@ namespace ts.server {
// If a change was made inside "folder/file", node will trigger the callback twice:
// one with the fileName being "folder/file", and the other one with "folder".
// We don't respond to the second one.
if (fileName && !ts.isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) {
if (fileName && !isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) {
return;
}
@@ -1120,7 +1120,7 @@ namespace ts.server {
configFileName: configFileName(),
projectType: project instanceof server.ExternalProject ? "external" : "configured",
languageServiceEnabled: project.languageServiceEnabled,
version: ts.version,
version,
};
this.eventHandler({ eventName: ProjectInfoTelemetryEvent, data });
@@ -1130,7 +1130,7 @@ namespace ts.server {
}
const configFilePath = project instanceof server.ConfiguredProject && project.getConfigFilePath();
const base = ts.getBaseFileName(configFilePath);
const base = getBaseFileName(configFilePath);
return base === "tsconfig.json" || base === "jsconfig.json" ? base : "other";
}
+28 -24
View File
@@ -3,8 +3,6 @@
/// <reference path="scriptInfo.ts" />
namespace ts.server {
type NameResolutionWithFailedLookupLocations = { failedLookupLocations: string[], isInvalidated?: boolean };
export class CachedServerHost implements ServerHost {
args: string[];
newLine: string;
@@ -32,7 +30,7 @@ namespace ts.server {
}
private getFileSystemEntries(rootDir: string) {
const path = ts.toPath(rootDir, this.currentDirectory, this.getCanonicalFileName);
const path = toPath(rootDir, this.currentDirectory, this.getCanonicalFileName);
const cachedResult = this.cachedReadDirectoryResult.get(path);
if (cachedResult) {
return cachedResult;
@@ -49,7 +47,7 @@ namespace ts.server {
private canWorkWithCacheForDir(rootDir: string) {
// Some of the hosts might not be able to handle read directory or getDirectories
const path = ts.toPath(rootDir, this.currentDirectory, this.getCanonicalFileName);
const path = toPath(rootDir, this.currentDirectory, this.getCanonicalFileName);
if (this.cachedReadDirectoryResult.get(path)) {
return true;
}
@@ -66,11 +64,11 @@ namespace ts.server {
}
writeFile(fileName: string, data: string, writeByteOrderMark?: boolean) {
const path = ts.toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
const path = toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
const result = this.cachedReadDirectoryResult.get(getDirectoryPath(path));
const baseFileName = getBaseFileName(toNormalizedPath(fileName));
if (result && !some(result.files, file => this.fileNameEqual(file, baseFileName))) {
result.files.push(baseFileName);
if (result) {
result.files = this.updateFileSystemEntry(result.files, baseFileName, /*isValid*/ true);
}
return this.host.writeFile(fileName, data, writeByteOrderMark);
}
@@ -102,7 +100,7 @@ namespace ts.server {
getDirectories(rootDir: string) {
if (this.canWorkWithCacheForDir(rootDir)) {
return this.getFileSystemEntries(rootDir).directories;
return this.getFileSystemEntries(rootDir).directories.slice();
}
return this.host.getDirectories(rootDir);
}
@@ -115,14 +113,14 @@ namespace ts.server {
}
fileExists(fileName: string): boolean {
const path = ts.toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
const path = toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
const result = this.cachedReadDirectoryResult.get(getDirectoryPath(path));
const baseName = getBaseFileName(toNormalizedPath(fileName));
return (result && some(result.files, file => this.fileNameEqual(file, baseName))) || this.host.fileExists(fileName);
return (result && this.hasEntry(result.files, baseName)) || this.host.fileExists(fileName);
}
directoryExists(dirPath: string) {
const path = ts.toPath(dirPath, this.currentDirectory, this.getCanonicalFileName);
const path = toPath(dirPath, this.currentDirectory, this.getCanonicalFileName);
return this.cachedReadDirectoryResult.has(path) || this.host.directoryExists(dirPath);
}
@@ -134,15 +132,20 @@ namespace ts.server {
return this.getCanonicalFileName(name1) === this.getCanonicalFileName(name2);
}
private updateFileSystemEntry(entries: string[], baseName: string, isValid: boolean) {
if (some(entries, entry => this.fileNameEqual(entry, baseName))) {
private hasEntry(entries: ReadonlyArray<string>, name: string) {
return some(entries, file => this.fileNameEqual(file, name));
}
private updateFileSystemEntry(entries: ReadonlyArray<string>, baseName: string, isValid: boolean) {
if (this.hasEntry(entries, baseName)) {
if (!isValid) {
filterMutate(entries, entry => !this.fileNameEqual(entry, baseName));
return filter(entries, entry => !this.fileNameEqual(entry, baseName));
}
}
else if (isValid) {
entries.push(baseName);
return entries.concat(baseName);
}
return entries;
}
addOrDeleteFileOrFolder(fileOrFolder: NormalizedPath) {
@@ -159,8 +162,8 @@ namespace ts.server {
if (parentResult) {
const baseName = getBaseFileName(fileOrFolder);
if (parentResult) {
this.updateFileSystemEntry(parentResult.files, baseName, this.host.fileExists(path));
this.updateFileSystemEntry(parentResult.directories, baseName, this.host.directoryExists(path));
parentResult.files = this.updateFileSystemEntry(parentResult.files, baseName, this.host.fileExists(path));
parentResult.directories = this.updateFileSystemEntry(parentResult.directories, baseName, this.host.directoryExists(path));
}
}
}
@@ -185,8 +188,9 @@ namespace ts.server {
}
export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost {
private compilationSettings: ts.CompilerOptions;
type NameResolutionWithFailedLookupLocations = { failedLookupLocations: string[], isInvalidated?: boolean };
export class LSHost implements LanguageServiceHost, ModuleResolutionHost {
private compilationSettings: CompilerOptions;
private readonly resolvedModuleNames = createMap<Map<ResolvedModuleWithFailedLookupLocations>>();
private readonly resolvedTypeReferenceDirectives = createMap<Map<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>>();
/* @internal */
@@ -203,7 +207,7 @@ namespace ts.server {
constructor(host: ServerHost, private project: Project, private readonly cancellationToken: HostCancellationToken) {
this.host = host;
this.cancellationToken = new ThrottledCancellationToken(cancellationToken, project.projectService.throttleWaitMilliseconds);
this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
this.getCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
if (host.trace) {
this.trace = s => host.trace(s);
@@ -285,7 +289,7 @@ namespace ts.server {
}
}
ts.Debug.assert(resolution !== undefined);
Debug.assert(resolution !== undefined);
resolvedModules.push(getResult(resolution));
}
@@ -363,7 +367,7 @@ namespace ts.server {
return combinePaths(nodeModuleBinDir, getDefaultLibFileName(this.compilationSettings));
}
getScriptSnapshot(filename: string): ts.IScriptSnapshot {
getScriptSnapshot(filename: string): IScriptSnapshot {
const scriptInfo = this.project.getScriptInfoLSHost(filename);
if (scriptInfo) {
return scriptInfo.getSnapshot();
@@ -411,7 +415,7 @@ namespace ts.server {
return this.host.directoryExists(path);
}
readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] {
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
return this.host.readDirectory(path, extensions, exclude, include, depth);
}
@@ -450,7 +454,7 @@ namespace ts.server {
});
}
setCompilationSettings(opt: ts.CompilerOptions) {
setCompilationSettings(opt: CompilerOptions) {
if (changesAffectModuleResolution(this.compilationSettings, opt)) {
this.resolvedModuleNames.clear();
this.resolvedTypeReferenceDirectives.clear();
+9 -9
View File
@@ -115,7 +115,7 @@ namespace ts.server {
export abstract class Project {
private rootFiles: ScriptInfo[] = [];
private rootFilesMap: Map<ProjectRoot> = createMap<ProjectRoot>();
private program: ts.Program;
private program: Program;
private externalFiles: SortedReadonlyArray<string>;
private missingFilesMap: Map<FileWatcher> = createMap<FileWatcher>();
@@ -191,7 +191,7 @@ namespace ts.server {
private readonly projectName: string,
readonly projectKind: ProjectKind,
readonly projectService: ProjectService,
private documentRegistry: ts.DocumentRegistry,
private documentRegistry: DocumentRegistry,
hasExplicitListOfFiles: boolean,
languageServiceEnabled: boolean,
private compilerOptions: CompilerOptions,
@@ -199,7 +199,7 @@ namespace ts.server {
host: ServerHost) {
if (!this.compilerOptions) {
this.compilerOptions = ts.getDefaultCompilerOptions();
this.compilerOptions = getDefaultCompilerOptions();
this.compilerOptions.allowNonTsExtensions = true;
this.compilerOptions.allowJs = true;
}
@@ -213,7 +213,7 @@ namespace ts.server {
this.lsHost = new LSHost(host, this, this.projectService.cancellationToken);
this.lsHost.setCompilationSettings(this.compilerOptions);
this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry);
this.languageService = createLanguageService(this.lsHost, this.documentRegistry);
if (!languageServiceEnabled) {
this.disableLanguageService();
@@ -729,7 +729,7 @@ namespace ts.server {
}
let strBuilder = "";
for (const file of this.program.getSourceFiles()) {
strBuilder += `${file.fileName}\n`;
strBuilder += `\t${file.fileName}\n`;
}
return strBuilder;
}
@@ -914,7 +914,7 @@ namespace ts.server {
// Used to keep track of what directories are watched for this project
directoriesWatchedForTsconfig: string[] = [];
constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry, compilerOptions: CompilerOptions) {
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions) {
super(InferredProject.newName(),
ProjectKind.Inferred,
projectService,
@@ -995,7 +995,7 @@ namespace ts.server {
constructor(configFileName: NormalizedPath,
projectService: ProjectService,
documentRegistry: ts.DocumentRegistry,
documentRegistry: DocumentRegistry,
hasExplicitListOfFiles: boolean,
compilerOptions: CompilerOptions,
languageServiceEnabled: boolean,
@@ -1232,7 +1232,7 @@ namespace ts.server {
}
getEffectiveTypeRoots() {
return ts.getEffectiveTypeRoots(this.getCompilerOptions(), this.lsHost.host) || [];
return getEffectiveTypeRoots(this.getCompilerOptions(), this.lsHost.host) || [];
}
}
@@ -1244,7 +1244,7 @@ namespace ts.server {
private typeAcquisition: TypeAcquisition;
constructor(public externalProjectName: string,
projectService: ProjectService,
documentRegistry: ts.DocumentRegistry,
documentRegistry: DocumentRegistry,
compilerOptions: CompilerOptions,
languageServiceEnabled: boolean,
public compileOnSaveEnabled: boolean,
+1 -1
View File
@@ -640,7 +640,7 @@ namespace ts.server.protocol {
}
/**
* Location in source code expressed as (one-based) line and character offset.
* Location in source code expressed as (one-based) line and (one-based) column offset.
*/
export interface Location {
line: number;
+12 -31
View File
@@ -61,7 +61,7 @@ namespace ts.server {
: ScriptSnapshot.fromString(this.getOrLoadText());
}
public getLineInfo(line: number) {
public getLineInfo(line: number): AbsolutePositionAndLineText {
return this.switchToScriptVersionCache().getSnapshot().index.lineNumberToInfo(line);
}
/**
@@ -72,19 +72,12 @@ namespace ts.server {
const lineMap = this.getLineMap();
const start = lineMap[line]; // -1 since line is 1-based
const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length;
return ts.createTextSpanFromBounds(start, end);
return createTextSpanFromBounds(start, end);
}
const index = this.svc.getSnapshot().index;
const lineInfo = index.lineNumberToInfo(line + 1);
let len: number;
if (lineInfo.leaf) {
len = lineInfo.leaf.text.length;
}
else {
const nextLineInfo = index.lineNumberToInfo(line + 2);
len = nextLineInfo.offset - lineInfo.offset;
}
return ts.createTextSpan(lineInfo.offset, len);
const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1);
const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition;
return createTextSpan(absolutePosition, len);
}
/**
@@ -93,27 +86,19 @@ namespace ts.server {
*/
lineOffsetToPosition(line: number, offset: number): number {
if (!this.svc) {
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1);
return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1, this.text);
}
const index = this.svc.getSnapshot().index;
const lineInfo = index.lineNumberToInfo(line);
// TODO: assert this offset is actually on the line
return (lineInfo.offset + offset - 1);
return this.svc.getSnapshot().index.absolutePositionOfStartOfLine(line) + (offset - 1);
}
/**
* @param line 1-based index
* @param offset 1-based index
*/
positionToLineOffset(position: number): ILineInfo {
positionToLineOffset(position: number): protocol.Location {
if (!this.svc) {
const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position);
return { line: line + 1, offset: character + 1 };
}
const index = this.svc.getSnapshot().index;
const lineOffset = index.charOffsetToLineNumberAndPos(position);
return { line: lineOffset.line, offset: lineOffset.offset + 1 };
return this.svc.getSnapshot().index.positionToLineOffset(position);
}
private getFileText(tempFileName?: string) {
@@ -162,7 +147,7 @@ namespace ts.server {
* All projects that include this file
*/
readonly containingProjects: Project[] = [];
private formatCodeSettings: ts.FormatCodeSettings;
private formatCodeSettings: FormatCodeSettings;
readonly path: Path;
private fileWatcher: FileWatcher;
@@ -343,7 +328,7 @@ namespace ts.server {
}
}
getLineInfo(line: number) {
getLineInfo(line: number): AbsolutePositionAndLineText {
return this.textStorage.getLineInfo(line);
}
@@ -373,11 +358,7 @@ namespace ts.server {
return this.textStorage.lineOffsetToPosition(line, offset);
}
/**
* @param line 1-based index
* @param offset 1-based index
*/
positionToLineOffset(position: number): ILineInfo {
positionToLineOffset(position: number): protocol.Location {
return this.textStorage.positionToLineOffset(position);
}
+105 -126
View File
@@ -8,15 +8,13 @@ namespace ts.server {
export interface LineCollection {
charCount(): number;
lineCount(): number;
isLeaf(): boolean;
isLeaf(): this is LineLeaf;
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker): void;
}
export interface ILineInfo {
line: number;
offset: number;
text?: string;
leaf?: LineLeaf;
export interface AbsolutePositionAndLineText {
absolutePosition: number;
lineText: string | undefined;
}
export enum CharRangeSection {
@@ -97,22 +95,20 @@ namespace ts.server {
}
// path at least length two (root and leaf)
let insertionNode = <LineNode>this.startPath[this.startPath.length - 2];
const leafNode = <LineLeaf>this.startPath[this.startPath.length - 1];
const len = lines.length;
if (len > 0) {
if (lines.length > 0) {
leafNode.text = lines[0];
if (len > 1) {
let insertedNodes = <LineCollection[]>new Array(len - 1);
if (lines.length > 1) {
let insertedNodes = <LineCollection[]>new Array(lines.length - 1);
let startNode = <LineCollection>leafNode;
for (let i = 1; i < lines.length; i++) {
insertedNodes[i - 1] = new LineLeaf(lines[i]);
}
let pathIndex = this.startPath.length - 2;
while (pathIndex >= 0) {
insertionNode = <LineNode>this.startPath[pathIndex];
const insertionNode = <LineNode>this.startPath[pathIndex];
insertedNodes = insertionNode.insertAt(startNode, insertedNodes);
pathIndex--;
startNode = insertionNode;
@@ -134,6 +130,7 @@ namespace ts.server {
}
}
else {
const insertionNode = <LineNode>this.startPath[this.startPath.length - 2];
// no content for leaf node, so delete it
insertionNode.remove(leafNode);
for (let j = this.startPath.length - 2; j >= 0; j--) {
@@ -251,7 +248,7 @@ namespace ts.server {
}
getTextChangeRange() {
return ts.createTextChangeRange(ts.createTextSpan(this.pos, this.deleteLen),
return createTextChangeRange(createTextSpan(this.pos, this.deleteLen),
this.insertedText ? this.insertedText.length : 0);
}
}
@@ -281,9 +278,9 @@ namespace ts.server {
// REVIEW: can optimize by coalescing simple edits
edit(pos: number, deleteLen: number, insertedText?: string) {
this.changes.push(new TextChange(pos, deleteLen, insertedText));
if ((this.changes.length > ScriptVersionCache.changeNumberThreshold) ||
(deleteLen > ScriptVersionCache.changeLengthThreshold) ||
(insertedText && (insertedText.length > ScriptVersionCache.changeLengthThreshold))) {
if (this.changes.length > ScriptVersionCache.changeNumberThreshold ||
deleteLen > ScriptVersionCache.changeLengthThreshold ||
insertedText && insertedText.length > ScriptVersionCache.changeLengthThreshold) {
this.getSnapshot();
}
}
@@ -340,21 +337,21 @@ namespace ts.server {
getTextChangesBetweenVersions(oldVersion: number, newVersion: number) {
if (oldVersion < newVersion) {
if (oldVersion >= this.minVersion) {
const textChangeRanges: ts.TextChangeRange[] = [];
const textChangeRanges: TextChangeRange[] = [];
for (let i = oldVersion + 1; i <= newVersion; i++) {
const snap = this.versions[this.versionToIndex(i)];
for (const textChange of snap.changesSincePreviousVersion) {
textChangeRanges.push(textChange.getTextChangeRange());
}
}
return ts.collapseTextChangeRangesAcrossMultipleVersions(textChangeRanges);
return collapseTextChangeRangesAcrossMultipleVersions(textChangeRanges);
}
else {
return undefined;
}
}
else {
return ts.unchangedTextChangeRange;
return unchangedTextChangeRange;
}
}
@@ -368,7 +365,7 @@ namespace ts.server {
}
}
export class LineIndexSnapshot implements ts.IScriptSnapshot {
export class LineIndexSnapshot implements IScriptSnapshot {
constructor(readonly version: number, readonly cache: ScriptVersionCache, readonly index: LineIndex, readonly changesSincePreviousVersion: ReadonlyArray<TextChange> = emptyArray) {
}
@@ -380,10 +377,10 @@ namespace ts.server {
return this.index.root.charCount();
}
getChangeRange(oldSnapshot: ts.IScriptSnapshot): ts.TextChangeRange {
getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange {
if (oldSnapshot instanceof LineIndexSnapshot && this.cache === oldSnapshot.cache) {
if (this.version <= oldSnapshot.version) {
return ts.unchangedTextChangeRange;
return unchangedTextChangeRange;
}
else {
return this.cache.getTextChangesBetweenVersions(oldSnapshot.version, this.version);
@@ -397,22 +394,27 @@ namespace ts.server {
// set this to true to check each edit for accuracy
checkEdits = false;
charOffsetToLineNumberAndPos(charOffset: number) {
return this.root.charOffsetToLineNumberAndPos(1, charOffset);
absolutePositionOfStartOfLine(oneBasedLine: number): number {
return this.lineNumberToInfo(oneBasedLine).absolutePosition;
}
lineNumberToInfo(lineNumber: number): ILineInfo {
positionToLineOffset(position: number): protocol.Location {
const { oneBasedLine, zeroBasedColumn } = this.root.charOffsetToLineInfo(1, position);
return { line: oneBasedLine, offset: zeroBasedColumn + 1 };
}
private positionToColumnAndLineText(position: number): { zeroBasedColumn: number, lineText: string } {
return this.root.charOffsetToLineInfo(1, position);
}
lineNumberToInfo(oneBasedLine: number): AbsolutePositionAndLineText {
const lineCount = this.root.lineCount();
if (lineNumber <= lineCount) {
const lineInfo = this.root.lineNumberToInfo(lineNumber, 0);
lineInfo.line = lineNumber;
return lineInfo;
if (oneBasedLine <= lineCount) {
const { position, leaf } = this.root.lineNumberToInfo(oneBasedLine, 0);
return { absolutePosition: position, lineText: leaf && leaf.text };
}
else {
return {
line: lineNumber,
offset: this.root.charCount()
};
return { absolutePosition: this.root.charCount(), lineText: undefined };
}
}
@@ -468,12 +470,9 @@ namespace ts.server {
return !walkFns.done;
}
edit(pos: number, deleteLength: number, newText?: string) {
function editFlat(source: string, s: number, dl: number, nt = "") {
return source.substring(0, s) + nt + source.substring(s + dl, source.length);
}
edit(pos: number, deleteLength: number, newText?: string): LineIndex {
if (this.root.charCount() === 0) {
// TODO: assert deleteLength === 0
Debug.assert(deleteLength === 0); // Can't delete from empty document
if (newText !== undefined) {
this.load(LineIndex.linesFromText(newText).lines);
return this;
@@ -482,7 +481,8 @@ namespace ts.server {
else {
let checkText: string;
if (this.checkEdits) {
checkText = editFlat(this.getText(0, this.root.charCount()), pos, deleteLength, newText);
const source = this.getText(0, this.root.charCount());
checkText = source.slice(0, pos) + newText + source.slice(pos + deleteLength);
}
const walker = new EditWalker();
let suppressTrailingText = false;
@@ -502,63 +502,44 @@ namespace ts.server {
else if (deleteLength > 0) {
// check whether last characters deleted are line break
const e = pos + deleteLength;
const lineInfo = this.charOffsetToLineNumberAndPos(e);
if ((lineInfo && (lineInfo.offset === 0))) {
const { zeroBasedColumn, lineText } = this.positionToColumnAndLineText(e);
if (zeroBasedColumn === 0) {
// move range end just past line that will merge with previous line
deleteLength += lineInfo.text.length;
deleteLength += lineText.length;
// store text by appending to end of insertedText
if (newText) {
newText = newText + lineInfo.text;
}
else {
newText = lineInfo.text;
}
newText = newText ? newText + lineText : lineText;
}
}
if (pos < this.root.charCount()) {
this.root.walk(pos, deleteLength, walker);
walker.insertLines(newText, suppressTrailingText);
}
this.root.walk(pos, deleteLength, walker);
walker.insertLines(newText, suppressTrailingText);
if (this.checkEdits) {
const updatedText = this.getText(0, this.root.charCount());
const updatedText = walker.lineIndex.getText(0, walker.lineIndex.getLength());
Debug.assert(checkText === updatedText, "buffer edit mismatch");
}
return walker.lineIndex;
}
}
static buildTreeFromBottom(nodes: LineCollection[]): LineNode {
const nodeCount = Math.ceil(nodes.length / lineCollectionCapacity);
const interiorNodes: LineNode[] = [];
private static buildTreeFromBottom(nodes: LineCollection[]): LineNode {
if (nodes.length < lineCollectionCapacity) {
return new LineNode(nodes);
}
const interiorNodes = new Array<LineNode>(Math.ceil(nodes.length / lineCollectionCapacity));
let nodeIndex = 0;
for (let i = 0; i < nodeCount; i++) {
interiorNodes[i] = new LineNode();
let charCount = 0;
let lineCount = 0;
for (let j = 0; j < lineCollectionCapacity; j++) {
if (nodeIndex < nodes.length) {
interiorNodes[i].add(nodes[nodeIndex]);
charCount += nodes[nodeIndex].charCount();
lineCount += nodes[nodeIndex].lineCount();
}
else {
break;
}
nodeIndex++;
}
interiorNodes[i].totalChars = charCount;
interiorNodes[i].totalLines = lineCount;
}
if (interiorNodes.length === 1) {
return interiorNodes[0];
}
else {
return this.buildTreeFromBottom(interiorNodes);
for (let i = 0; i < interiorNodes.length; i++) {
const end = Math.min(nodeIndex + lineCollectionCapacity, nodes.length);
interiorNodes[i] = new LineNode(nodes.slice(nodeIndex, end));
nodeIndex = end;
}
return this.buildTreeFromBottom(interiorNodes);
}
static linesFromText(text: string) {
const lineMap = ts.computeLineStarts(text);
const lineMap = computeLineStarts(text);
if (lineMap.length === 0) {
return { lines: <string[]>[], lineMap };
@@ -583,7 +564,10 @@ namespace ts.server {
export class LineNode implements LineCollection {
totalChars = 0;
totalLines = 0;
children: LineCollection[] = [];
constructor(private readonly children: LineCollection[] = []) {
if (children.length) this.updateCounts();
}
isLeaf() {
return false;
@@ -598,7 +582,7 @@ namespace ts.server {
}
}
execWalk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker, childIndex: number, nodeType: CharRangeSection) {
private execWalk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker, childIndex: number, nodeType: CharRangeSection) {
if (walkFns.pre) {
walkFns.pre(rangeStart, rangeLength, this.children[childIndex], this, nodeType);
}
@@ -614,7 +598,7 @@ namespace ts.server {
return walkFns.done;
}
skipChild(relativeStart: number, relativeLength: number, childIndex: number, walkFns: ILineIndexWalker, nodeType: CharRangeSection) {
private skipChild(relativeStart: number, relativeLength: number, childIndex: number, walkFns: ILineIndexWalker, nodeType: CharRangeSection) {
if (walkFns.pre && (!walkFns.done)) {
walkFns.pre(relativeStart, relativeLength, this.children[childIndex], this, nodeType);
walkFns.goSubtree = true;
@@ -624,16 +608,14 @@ namespace ts.server {
walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker) {
// assume (rangeStart < this.totalChars) && (rangeLength <= this.totalChars)
let childIndex = 0;
let child = this.children[0];
let childCharCount = child.charCount();
let childCharCount = this.children[childIndex].charCount();
// find sub-tree containing start
let adjustedStart = rangeStart;
while (adjustedStart >= childCharCount) {
this.skipChild(adjustedStart, rangeLength, childIndex, walkFns, CharRangeSection.PreStart);
adjustedStart -= childCharCount;
childIndex++;
child = this.children[childIndex];
childCharCount = child.charCount();
childCharCount = this.children[childIndex].charCount();
}
// Case I: both start and end of range in same subtree
if ((adjustedStart + rangeLength) <= childCharCount) {
@@ -648,7 +630,7 @@ namespace ts.server {
}
let adjustedLength = rangeLength - (childCharCount - adjustedStart);
childIndex++;
child = this.children[childIndex];
const child = this.children[childIndex];
childCharCount = child.charCount();
while (adjustedLength > childCharCount) {
if (this.execWalk(0, childCharCount, walkFns, childIndex, CharRangeSection.Mid)) {
@@ -656,8 +638,7 @@ namespace ts.server {
}
adjustedLength -= childCharCount;
childIndex++;
child = this.children[childIndex];
childCharCount = child.charCount();
childCharCount = this.children[childIndex].charCount();
}
if (adjustedLength > 0) {
if (this.execWalk(0, adjustedLength, walkFns, childIndex, CharRangeSection.End)) {
@@ -676,90 +657,88 @@ namespace ts.server {
}
}
charOffsetToLineNumberAndPos(lineNumber: number, charOffset: number): ILineInfo {
const childInfo = this.childFromCharOffset(lineNumber, charOffset);
// Input position is relative to the start of this node.
// Output line number is absolute.
charOffsetToLineInfo(lineNumberAccumulator: number, relativePosition: number): { oneBasedLine: number, zeroBasedColumn: number, lineText: string | undefined } {
const childInfo = this.childFromCharOffset(lineNumberAccumulator, relativePosition);
if (!childInfo.child) {
return {
line: lineNumber,
offset: charOffset,
oneBasedLine: lineNumberAccumulator,
zeroBasedColumn: relativePosition,
lineText: undefined,
};
}
else if (childInfo.childIndex < this.children.length) {
if (childInfo.child.isLeaf()) {
return {
line: childInfo.lineNumber,
offset: childInfo.charOffset,
text: (<LineLeaf>(childInfo.child)).text,
leaf: (<LineLeaf>(childInfo.child))
oneBasedLine: childInfo.lineNumberAccumulator,
zeroBasedColumn: childInfo.relativePosition,
lineText: childInfo.child.text,
};
}
else {
const lineNode = <LineNode>(childInfo.child);
return lineNode.charOffsetToLineNumberAndPos(childInfo.lineNumber, childInfo.charOffset);
return lineNode.charOffsetToLineInfo(childInfo.lineNumberAccumulator, childInfo.relativePosition);
}
}
else {
const lineInfo = this.lineNumberToInfo(this.lineCount(), 0);
return { line: this.lineCount(), offset: lineInfo.leaf.charCount() };
return { oneBasedLine: this.lineCount(), zeroBasedColumn: lineInfo.leaf.charCount(), lineText: undefined };
}
}
lineNumberToInfo(lineNumber: number, charOffset: number): ILineInfo {
const childInfo = this.childFromLineNumber(lineNumber, charOffset);
lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } {
const childInfo = this.childFromLineNumber(relativeOneBasedLine, positionAccumulator);
if (!childInfo.child) {
return {
line: lineNumber,
offset: charOffset
};
return { position: positionAccumulator, leaf: undefined };
}
else if (childInfo.child.isLeaf()) {
return {
line: lineNumber,
offset: childInfo.charOffset,
text: (<LineLeaf>(childInfo.child)).text,
leaf: (<LineLeaf>(childInfo.child))
};
return { position: childInfo.positionAccumulator, leaf: childInfo.child };
}
else {
const lineNode = <LineNode>(childInfo.child);
return lineNode.lineNumberToInfo(childInfo.relativeLineNumber, childInfo.charOffset);
return lineNode.lineNumberToInfo(childInfo.relativeOneBasedLine, childInfo.positionAccumulator);
}
}
childFromLineNumber(lineNumber: number, charOffset: number) {
/**
* Input line number is relative to the start of this node.
* Output line number is relative to the child.
* positionAccumulator will be an absolute position once relativeLineNumber reaches 0.
*/
private childFromLineNumber(relativeOneBasedLine: number, positionAccumulator: number): { child: LineCollection, relativeOneBasedLine: number, positionAccumulator: number } {
let child: LineCollection;
let relativeLineNumber = lineNumber;
let i: number;
let len: number;
for (i = 0, len = this.children.length; i < len; i++) {
for (i = 0; i < this.children.length; i++) {
child = this.children[i];
const childLineCount = child.lineCount();
if (childLineCount >= relativeLineNumber) {
if (childLineCount >= relativeOneBasedLine) {
break;
}
else {
relativeLineNumber -= childLineCount;
charOffset += child.charCount();
relativeOneBasedLine -= childLineCount;
positionAccumulator += child.charCount();
}
}
return { child, childIndex: i, relativeLineNumber, charOffset };
return { child, relativeOneBasedLine, positionAccumulator };
}
childFromCharOffset(lineNumber: number, charOffset: number) {
private childFromCharOffset(lineNumberAccumulator: number, relativePosition: number
): { child: LineCollection, childIndex: number, relativePosition: number, lineNumberAccumulator: number } {
let child: LineCollection;
let i: number;
let len: number;
for (i = 0, len = this.children.length; i < len; i++) {
child = this.children[i];
if (child.charCount() > charOffset) {
if (child.charCount() > relativePosition) {
break;
}
else {
charOffset -= child.charCount();
lineNumber += child.lineCount();
relativePosition -= child.charCount();
lineNumberAccumulator += child.lineCount();
}
}
return { child, childIndex: i, charOffset, lineNumber };
return { child, childIndex: i, relativePosition, lineNumberAccumulator };
}
private splitAfter(childIndex: number) {
+13 -7
View File
@@ -138,7 +138,7 @@ namespace ts.server {
terminal: false,
});
class Logger implements ts.server.Logger {
class Logger implements server.Logger {
private fd = -1;
private seq = 0;
private inGroup = false;
@@ -526,12 +526,18 @@ namespace ts.server {
if (err) {
watchedFile.callback(watchedFile.fileName, FileWatcherEventKind.Changed);
}
else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) {
watchedFile.mtime = stats.mtime;
const eventKind = watchedFile.mtime.getTime() === 0
? FileWatcherEventKind.Deleted
: FileWatcherEventKind.Changed;
watchedFile.callback(watchedFile.fileName, eventKind);
else {
const oldTime = watchedFile.mtime.getTime();
const newTime = stats.mtime.getTime();
if (oldTime !== newTime) {
watchedFile.mtime = stats.mtime;
const eventKind = oldTime === 0
? FileWatcherEventKind.Created
: newTime === 0
? FileWatcherEventKind.Deleted
: FileWatcherEventKind.Changed;
watchedFile.callback(watchedFile.fileName, eventKind);
}
}
});
}
+77 -89
View File
@@ -49,7 +49,7 @@ namespace ts.server {
interface FileStart {
file: string;
start: ILineInfo;
start: protocol.Location;
}
function compareNumber(a: number, b: number) {
@@ -72,28 +72,28 @@ namespace ts.server {
}
}
function formatDiag(fileName: NormalizedPath, project: Project, diag: ts.Diagnostic): protocol.Diagnostic {
function formatDiag(fileName: NormalizedPath, project: Project, diag: Diagnostic): protocol.Diagnostic {
const scriptInfo = project.getScriptInfoForNormalizedPath(fileName);
return {
start: scriptInfo.positionToLineOffset(diag.start),
end: scriptInfo.positionToLineOffset(diag.start + diag.length),
text: ts.flattenDiagnosticMessageText(diag.messageText, "\n"),
text: flattenDiagnosticMessageText(diag.messageText, "\n"),
code: diag.code,
category: DiagnosticCategory[diag.category].toLowerCase(),
source: diag.source
};
}
function convertToILineInfo(lineAndCharacter: LineAndCharacter): ILineInfo {
function convertToLocation(lineAndCharacter: LineAndCharacter): protocol.Location {
return { line: lineAndCharacter.line + 1, offset: lineAndCharacter.character + 1 };
}
function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: true): protocol.DiagnosticWithFileName;
function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: false): protocol.Diagnostic;
function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: boolean): protocol.Diagnostic | protocol.DiagnosticWithFileName {
const start = diag.file && convertToILineInfo(getLineAndCharacterOfPosition(diag.file, diag.start));
const end = diag.file && convertToILineInfo(getLineAndCharacterOfPosition(diag.file, diag.start + diag.length));
const text = ts.flattenDiagnosticMessageText(diag.messageText, "\n");
function formatConfigFileDiag(diag: Diagnostic, includeFileName: true): protocol.DiagnosticWithFileName;
function formatConfigFileDiag(diag: Diagnostic, includeFileName: false): protocol.Diagnostic;
function formatConfigFileDiag(diag: Diagnostic, includeFileName: boolean): protocol.Diagnostic | protocol.DiagnosticWithFileName {
const start = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start));
const end = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start + diag.length));
const text = flattenDiagnosticMessageText(diag.messageText, "\n");
const { code, source } = diag;
const category = DiagnosticCategory[diag.category].toLowerCase();
return includeFileName ? { start, end, text, code, category, source, fileName: diag.file && diag.file.fileName } :
@@ -389,8 +389,8 @@ namespace ts.server {
this.host.write(formatMessage(msg, this.logger, this.byteLength, this.host.newLine));
}
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ts.Diagnostic[]) {
const bakedDiags = ts.map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true));
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: Diagnostic[]) {
const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true));
const ev: protocol.ConfigFileDiagnosticEvent = {
seq: 0,
type: "event",
@@ -547,8 +547,8 @@ namespace ts.server {
length: d.length,
category: DiagnosticCategory[d.category].toLowerCase(),
code: d.code,
startLocation: d.file && convertToILineInfo(getLineAndCharacterOfPosition(d.file, d.start)),
endLocation: d.file && convertToILineInfo(getLineAndCharacterOfPosition(d.file, d.start + d.length))
startLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start)),
endLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start + d.length))
});
}
@@ -607,7 +607,7 @@ namespace ts.server {
return {
file: def.fileName,
start: defScriptInfo.positionToLineOffset(def.textSpan.start),
end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan))
end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan))
};
});
}
@@ -631,7 +631,7 @@ namespace ts.server {
return {
file: def.fileName,
start: defScriptInfo.positionToLineOffset(def.textSpan.start),
end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan))
end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan))
};
});
}
@@ -649,7 +649,7 @@ namespace ts.server {
return {
file: fileName,
start: scriptInfo.positionToLineOffset(textSpan.start),
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan))
end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan))
};
});
}
@@ -673,7 +673,7 @@ namespace ts.server {
const { fileName, isWriteAccess, textSpan, isInString } = occurrence;
const scriptInfo = project.getScriptInfo(fileName);
const start = scriptInfo.positionToLineOffset(textSpan.start);
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan));
const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan));
const result: protocol.OccurrencesResponseItem = {
start,
end,
@@ -723,7 +723,7 @@ namespace ts.server {
return documentHighlights;
}
function convertToDocumentHighlightsItem(documentHighlights: ts.DocumentHighlights): ts.server.protocol.DocumentHighlightsItem {
function convertToDocumentHighlightsItem(documentHighlights: DocumentHighlights): protocol.DocumentHighlightsItem {
const { fileName, highlightSpans } = documentHighlights;
const scriptInfo = project.getScriptInfo(fileName);
@@ -732,10 +732,10 @@ namespace ts.server {
highlightSpans: highlightSpans.map(convertHighlightSpan)
};
function convertHighlightSpan(highlightSpan: ts.HighlightSpan): ts.server.protocol.HighlightSpan {
function convertHighlightSpan(highlightSpan: HighlightSpan): protocol.HighlightSpan {
const { textSpan, kind } = highlightSpan;
const start = scriptInfo.positionToLineOffset(textSpan.start);
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan));
const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan));
return { start, end, kind };
}
}
@@ -778,7 +778,7 @@ namespace ts.server {
const scriptInfo = this.projectService.getScriptInfo(args.file);
projects = scriptInfo.containingProjects;
}
// ts.filter handles case when 'projects' is undefined
// filter handles case when 'projects' is undefined
projects = filter(projects, p => p.languageServiceEnabled);
if (!projects || !projects.length) {
return Errors.ThrowNoProject();
@@ -831,28 +831,29 @@ namespace ts.server {
return <protocol.FileSpan>{
file: location.fileName,
start: locationScriptInfo.positionToLineOffset(location.textSpan.start),
end: locationScriptInfo.positionToLineOffset(ts.textSpanEnd(location.textSpan)),
end: locationScriptInfo.positionToLineOffset(textSpanEnd(location.textSpan)),
};
});
},
compareRenameLocation,
(a, b) => a.file === b.file && a.start.line === b.start.line && a.start.offset === b.start.offset
);
const locs = fileSpans.reduce<protocol.SpanGroup[]>((accum, cur) => {
const locs: protocol.SpanGroup[] = [];
for (const cur of fileSpans) {
let curFileAccum: protocol.SpanGroup;
if (accum.length > 0) {
curFileAccum = accum[accum.length - 1];
if (locs.length > 0) {
curFileAccum = locs[locs.length - 1];
if (curFileAccum.file !== cur.file) {
curFileAccum = undefined;
}
}
if (!curFileAccum) {
curFileAccum = { file: cur.file, locs: [] };
accum.push(curFileAccum);
locs.push(curFileAccum);
}
curFileAccum.locs.push({ start: cur.start, end: cur.end });
return accum;
}, []);
}
return { info: renameInfo, locs };
}
@@ -912,10 +913,10 @@ namespace ts.server {
return undefined;
}
const displayString = ts.displayPartsToString(nameInfo.displayParts);
const displayString = displayPartsToString(nameInfo.displayParts);
const nameSpan = nameInfo.textSpan;
const nameColStart = scriptInfo.positionToLineOffset(nameSpan.start).offset;
const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, ts.textSpanEnd(nameSpan));
const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan));
const refs = combineProjectOutput<protocol.ReferencesResponseItem>(
projects,
(project: Project) => {
@@ -928,12 +929,12 @@ namespace ts.server {
const refScriptInfo = project.getScriptInfo(ref.fileName);
const start = refScriptInfo.positionToLineOffset(ref.textSpan.start);
const refLineSpan = refScriptInfo.lineToTextSpan(start.line - 1);
const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, textSpanEnd(refLineSpan)).replace(/\r|\n/g, "");
return {
file: ref.fileName,
start,
lineText,
end: refScriptInfo.positionToLineOffset(ts.textSpanEnd(ref.textSpan)),
end: refScriptInfo.positionToLineOffset(textSpanEnd(ref.textSpan)),
isWriteAccess: ref.isWriteAccess,
isDefinition: ref.isDefinition
};
@@ -1056,14 +1057,14 @@ namespace ts.server {
}
if (simplifiedResult) {
const displayString = ts.displayPartsToString(quickInfo.displayParts);
const docString = ts.displayPartsToString(quickInfo.documentation);
const displayString = displayPartsToString(quickInfo.displayParts);
const docString = displayPartsToString(quickInfo.documentation);
return {
kind: quickInfo.kind,
kindModifiers: quickInfo.kindModifiers,
start: scriptInfo.positionToLineOffset(quickInfo.textSpan.start),
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(quickInfo.textSpan)),
end: scriptInfo.positionToLineOffset(textSpanEnd(quickInfo.textSpan)),
displayString,
documentation: docString,
tags: quickInfo.tags || []
@@ -1123,32 +1124,29 @@ namespace ts.server {
// only to the previous line. If all this is true, then
// add edits necessary to properly indent the current line.
if ((args.key === "\n") && ((!edits) || (edits.length === 0) || allEditsBeforePos(edits, position))) {
const lineInfo = scriptInfo.getLineInfo(args.line);
if (lineInfo && (lineInfo.leaf) && (lineInfo.leaf.text)) {
const lineText = lineInfo.leaf.text;
if (lineText.search("\\S") < 0) {
const preferredIndent = project.getLanguageService(/*ensureSynchronized*/ false).getIndentationAtPosition(file, position, formatOptions);
let hasIndent = 0;
let i: number, len: number;
for (i = 0, len = lineText.length; i < len; i++) {
if (lineText.charAt(i) === " ") {
hasIndent++;
}
else if (lineText.charAt(i) === "\t") {
hasIndent += formatOptions.tabSize;
}
else {
break;
}
const { lineText, absolutePosition } = scriptInfo.getLineInfo(args.line);
if (lineText && lineText.search("\\S") < 0) {
const preferredIndent = project.getLanguageService(/*ensureSynchronized*/ false).getIndentationAtPosition(file, position, formatOptions);
let hasIndent = 0;
let i: number, len: number;
for (i = 0, len = lineText.length; i < len; i++) {
if (lineText.charAt(i) === " ") {
hasIndent++;
}
// i points to the first non whitespace character
if (preferredIndent !== hasIndent) {
const firstNoWhiteSpacePosition = lineInfo.offset + i;
edits.push({
span: ts.createTextSpanFromBounds(lineInfo.offset, firstNoWhiteSpacePosition),
newText: formatting.getIndentationString(preferredIndent, formatOptions)
});
else if (lineText.charAt(i) === "\t") {
hasIndent += formatOptions.tabSize;
}
else {
break;
}
}
// i points to the first non whitespace character
if (preferredIndent !== hasIndent) {
const firstNoWhiteSpacePosition = absolutePosition + i;
edits.push({
span: createTextSpanFromBounds(absolutePosition, firstNoWhiteSpacePosition),
newText: formatting.getIndentationString(preferredIndent, formatOptions)
});
}
}
}
@@ -1160,7 +1158,7 @@ namespace ts.server {
return edits.map((edit) => {
return {
start: scriptInfo.positionToLineOffset(edit.span.start),
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(edit.span)),
end: scriptInfo.positionToLineOffset(textSpanEnd(edit.span)),
newText: edit.newText ? edit.newText : ""
};
});
@@ -1178,15 +1176,13 @@ namespace ts.server {
return undefined;
}
if (simplifiedResult) {
return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => {
return mapDefined(completions.entries, entry => {
if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) {
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
const convertedSpan: protocol.TextSpan =
replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined;
result.push({ name, kind, kindModifiers, sortText, replacementSpan: convertedSpan });
const convertedSpan = replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined;
return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan };
}
return result;
}, []).sort((a, b) => ts.compareStrings(a.name, b.name));
}).sort((a, b) => compareStrings(a.name, b.name));
}
else {
return completions;
@@ -1198,13 +1194,8 @@ namespace ts.server {
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
return args.entryNames.reduce((accum: protocol.CompletionEntryDetails[], entryName: string) => {
const details = project.getLanguageService().getCompletionEntryDetails(file, position, entryName);
if (details) {
accum.push(details);
}
return accum;
}, []);
return mapDefined(args.entryNames, entryName =>
project.getLanguageService().getCompletionEntryDetails(file, position, entryName));
}
private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): protocol.CompileOnSaveAffectedFileListSingleProject[] {
@@ -1269,14 +1260,11 @@ namespace ts.server {
}
private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void {
const checkList = fileNames.reduce((accum: PendingErrorCheck[], uncheckedFileName: string) => {
const checkList = mapDefined(fileNames, uncheckedFileName => {
const fileName = toNormalizedPath(uncheckedFileName);
const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true);
if (project) {
accum.push({ fileName, project });
}
return accum;
}, []);
return project && { fileName, project };
});
if (checkList.length > 0) {
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay);
@@ -1321,11 +1309,11 @@ namespace ts.server {
if (!fileName) {
return;
}
const file = ts.normalizePath(fileName);
const file = normalizePath(fileName);
this.projectService.closeClientFile(file);
}
private decorateNavigationBarItems(items: ts.NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] {
private decorateNavigationBarItems(items: NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] {
return map(items, item => ({
text: item.text,
kind: item.kind,
@@ -1346,7 +1334,7 @@ namespace ts.server {
: items;
}
private decorateNavigationTree(tree: ts.NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree {
private decorateNavigationTree(tree: NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree {
return {
text: tree.text,
kind: tree.kind,
@@ -1359,7 +1347,7 @@ namespace ts.server {
private decorateSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan {
return {
start: scriptInfo.positionToLineOffset(span.start),
end: scriptInfo.positionToLineOffset(ts.textSpanEnd(span))
end: scriptInfo.positionToLineOffset(textSpanEnd(span))
};
}
@@ -1389,7 +1377,7 @@ namespace ts.server {
return navItems.map((navItem) => {
const scriptInfo = project.getScriptInfo(navItem.fileName);
const start = scriptInfo.positionToLineOffset(navItem.textSpan.start);
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(navItem.textSpan));
const end = scriptInfo.positionToLineOffset(textSpanEnd(navItem.textSpan));
const bakedItem: protocol.NavtoItem = {
name: navItem.name,
kind: navItem.kind,
@@ -1454,7 +1442,7 @@ namespace ts.server {
}
private getSupportedCodeFixes(): string[] {
return ts.getSupportedCodeFixes();
return getSupportedCodeFixes();
}
private isLocation(locationOrSpan: protocol.FileLocationOrRangeRequestArgs): locationOrSpan is protocol.FileLocationRequestArgs {
@@ -1485,7 +1473,7 @@ namespace ts.server {
return project.getLanguageService().getApplicableRefactors(file, position || textRange);
}
private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): ts.RefactorEditInfo | protocol.RefactorEditInfo {
private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo {
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const { position, textRange } = this.extractPositionAndRange(args, scriptInfo);
@@ -1506,7 +1494,7 @@ namespace ts.server {
if (simplifiedResult) {
const file = result.renameFilename;
let location: ILineInfo | undefined = undefined;
let location: protocol.Location | undefined;
if (file !== undefined && result.renameLocation !== undefined) {
const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(file));
location = renameScriptInfo.positionToLineOffset(result.renameLocation);
@@ -1654,7 +1642,7 @@ namespace ts.server {
getCanonicalFileName(fileName: string) {
const name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
return ts.normalizePath(name);
return normalizePath(name);
}
exit() {
+5 -5
View File
@@ -31,9 +31,9 @@ declare namespace ts.server {
export interface DiscoverTypings extends TypingInstallerRequest {
readonly fileNames: string[];
readonly projectRootPath: ts.Path;
readonly compilerOptions: ts.CompilerOptions;
readonly typeAcquisition: ts.TypeAcquisition;
readonly projectRootPath: Path;
readonly compilerOptions: CompilerOptions;
readonly typeAcquisition: TypeAcquisition;
readonly unresolvedImports: SortedReadonlyArray<string>;
readonly cachePath?: string;
readonly kind: "discover";
@@ -63,8 +63,8 @@ declare namespace ts.server {
}
export interface SetTypings extends ProjectResponse {
readonly typeAcquisition: ts.TypeAcquisition;
readonly compilerOptions: ts.CompilerOptions;
readonly typeAcquisition: TypeAcquisition;
readonly compilerOptions: CompilerOptions;
readonly typings: string[];
readonly unresolvedImports: SortedReadonlyArray<string>;
readonly kind: ActionSet;
@@ -145,6 +145,7 @@ namespace ts.server.typingsInstaller {
const discoverTypingsResult = JsTyping.discoverTypings(
this.installTypingHost,
this.log.isEnabled() ? this.log.writeLine : undefined,
req.fileNames,
req.projectRootPath,
this.safeListPath,
+2 -2
View File
@@ -77,7 +77,7 @@ namespace ts.server {
tabSize: 4,
newLineCharacter: host.newLine || "\n",
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
indentStyle: IndentStyle.Smart,
insertSpaceAfterConstructor: false,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
@@ -196,7 +196,7 @@ namespace ts.server {
}
export function enumerateInsertsAndDeletes<T>(a: SortedReadonlyArray<T>, b: SortedReadonlyArray<T>, inserted: (item: T) => void, deleted: (item: T) => void, compare?: (a: T, b: T) => Comparison) {
compare = compare || ts.compareValues;
compare = compare || compareValues;
let aIndex = 0;
let bIndex = 0;
const aLen = a.length;
+28 -24
View File
@@ -25,15 +25,15 @@ namespace ts.codefix {
return [deleteNode(token.parent)];
default:
return [deleteDefault()];
return deleteDefault();
}
function deleteDefault() {
function deleteDefault(): CodeAction[] | undefined {
if (isDeclarationName(token)) {
return deleteNode(token.parent);
return [deleteNode(token.parent)];
}
else if (isLiteralComputedPropertyDeclarationName(token)) {
return deleteNode(token.parent.parent);
return [deleteNode(token.parent.parent)];
}
else {
return undefined;
@@ -87,20 +87,16 @@ namespace ts.codefix {
case SyntaxKind.ImportSpecifier:
const namedImports = <NamedImports>parent.parent;
if (namedImports.elements.length === 1) {
// Only 1 import and it is unused. So the entire declaration should be removed.
const importSpec = getAncestor(identifier, SyntaxKind.ImportDeclaration);
return [deleteNode(importSpec)];
return deleteNamedImportBinding(namedImports);
}
else {
// delete import specifier
return [deleteNodeInList(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>parent;
if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'|
if (!importClause.namedBindings) { // |import d from './file'|
const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration);
return [deleteNode(importDecl)];
}
@@ -118,22 +114,30 @@ namespace ts.codefix {
}
case SyntaxKind.NamespaceImport:
const namespaceImport = <NamespaceImport>parent;
if (namespaceImport.name === identifier && !(<ImportClause>namespaceImport.parent).name) {
const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration);
return [deleteNode(importDecl)];
}
else {
const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1, /*includeJsDocComment*/ false);
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)];
}
return deleteNamedImportBinding(<NamespaceImport>parent);
default:
return [deleteDefault()];
return deleteDefault();
}
}
function deleteNamedImportBinding(namedBindings: NamedImportBindings): CodeAction[] | undefined {
if ((<ImportClause>namedBindings.parent).name) {
// Delete named imports while preserving the default import
// import d|, * as ns| from './file'
// import d|, { a }| from './file'
const previousToken = getTokenAtPosition(sourceFile, namedBindings.pos - 1, /*includeJsDocComment*/ false);
if (previousToken && previousToken.kind === SyntaxKind.CommaToken) {
return [deleteRange({ pos: previousToken.getStart(), end: namedBindings.end })];
}
return undefined;
}
else {
// Delete the entire import declaration
// |import * as ns from './file'|
// |import { a } from './file'|
const importDecl = getAncestor(namedBindings, SyntaxKind.ImportDeclaration);
return [deleteNode(importDecl)];
}
}
+1 -4
View File
@@ -222,10 +222,7 @@ namespace ts.codefix {
}
function getUniqueSymbolId(symbol: Symbol) {
if (symbol.flags & SymbolFlags.Alias) {
return getSymbolId(checker.getAliasedSymbol(symbol));
}
return getSymbolId(symbol);
return getSymbolId(skipAlias(symbol, checker));
}
function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) {
+91 -105
View File
@@ -57,11 +57,11 @@ namespace ts.Completions {
if (isSourceFileJavaScript(sourceFile)) {
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log);
addRange(entries, getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target));
getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries);
}
else {
if ((!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) {
return undefined;
return undefined;
}
getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log);
@@ -79,40 +79,41 @@ namespace ts.Completions {
return { isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, entries };
}
function getJavaScriptCompletionEntries(sourceFile: SourceFile, position: number, uniqueNames: Map<true>, target: ScriptTarget): CompletionEntry[] {
const entries: CompletionEntry[] = [];
const nameTable = getNameTable(sourceFile);
nameTable.forEach((pos, name) => {
function getJavaScriptCompletionEntries(
sourceFile: SourceFile,
position: number,
uniqueNames: Map<true>,
target: ScriptTarget,
entries: Push<CompletionEntry>): void {
getNameTable(sourceFile).forEach((pos, name) => {
// Skip identifiers produced only from the current location
if (pos === position) {
return;
}
const realName = unescapeLeadingUnderscores(name);
if (!uniqueNames.get(realName)) {
uniqueNames.set(realName, true);
const displayName = getCompletionEntryDisplayName(realName, target, /*performCharacterChecks*/ true);
if (displayName) {
const entry = {
name: displayName,
kind: ScriptElementKind.warning,
kindModifiers: "",
sortText: "1"
};
entries.push(entry);
}
if (uniqueNames.has(realName)) {
return;
}
uniqueNames.set(realName, true);
const displayName = getCompletionEntryDisplayName(realName, target, /*performCharacterChecks*/ true);
if (displayName) {
entries.push({
name: displayName,
kind: ScriptElementKind.warning,
kindModifiers: "",
sortText: "1"
});
}
});
return entries;
}
function createCompletionEntry(symbol: Symbol, location: Node, performCharacterChecks: boolean, typeChecker: TypeChecker, target: ScriptTarget): CompletionEntry {
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
// We would like to only show things that can be added after a dot, so for instance numeric properties can
// not be accessed with a dot (a.1 <- invalid)
const displayName = getCompletionEntryDisplayNameForSymbol(typeChecker, symbol, target, performCharacterChecks, location);
const displayName = getCompletionEntryDisplayNameForSymbol(symbol, target, performCharacterChecks);
if (!displayName) {
return undefined;
}
@@ -141,7 +142,7 @@ namespace ts.Completions {
const entry = createCompletionEntry(symbol, location, performCharacterChecks, typeChecker, target);
if (entry) {
const id = entry.name;
if (!uniqueNames.get(id)) {
if (!uniqueNames.has(id)) {
entries.push(entry);
uniqueNames.set(id, true);
}
@@ -307,7 +308,7 @@ namespace ts.Completions {
// We don't need to perform character checks here because we're only comparing the
// name against 'entryName' (which is known to be good), not building a new
// completion entry.
const symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(typeChecker, s, compilerOptions.target, /*performCharacterChecks*/ false, location) === entryName ? s : undefined);
const symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false) === entryName ? s : undefined);
if (symbol) {
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All);
@@ -341,20 +342,14 @@ namespace ts.Completions {
return undefined;
}
export function getCompletionEntrySymbol(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): Symbol {
export function getCompletionEntrySymbol(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): Symbol | undefined {
// Compute all the completion symbols again.
const completionData = getCompletionData(typeChecker, log, sourceFile, position);
if (completionData) {
const { symbols, location } = completionData;
// Find the symbol with the matching entry name.
// We don't need to perform character checks here because we're only comparing the
// name against 'entryName' (which is known to be good), not building a new
// completion entry.
return forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(typeChecker, s, compilerOptions.target, /*performCharacterChecks*/ false, location) === entryName ? s : undefined);
}
return undefined;
// Find the symbol with the matching entry name.
// We don't need to perform character checks here because we're only comparing the
// name against 'entryName' (which is known to be good), not building a new
// completion entry.
return completionData && forEach(completionData.symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false) === entryName ? s : undefined);
}
interface CompletionData {
@@ -369,7 +364,7 @@ namespace ts.Completions {
}
type Request = { kind: "JsDocTagName" } | { kind: "JsDocTag" } | { kind: "JsDocParameterName", tag: JSDocParameterTag };
function getCompletionData(typeChecker: TypeChecker, log: (message: string) => void, sourceFile: SourceFile, position: number): CompletionData {
function getCompletionData(typeChecker: TypeChecker, log: (message: string) => void, sourceFile: SourceFile, position: number): CompletionData | undefined {
const isJavaScriptFile = isSourceFileJavaScript(sourceFile);
let request: Request | undefined;
@@ -601,58 +596,60 @@ namespace ts.Completions {
isNewIdentifierLocation = false;
// Since this is qualified name check its a type node location
const isTypeLocation = isPartOfTypeNode(node.parent) || insideJsDocTagTypeExpression;
const isTypeLocation = insideJsDocTagTypeExpression || isPartOfTypeNode(node.parent);
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression) {
if (isEntityName(node)) {
let symbol = typeChecker.getSymbolAtLocation(node);
if (symbol) {
symbol = skipAlias(symbol, typeChecker);
// This is an alias, follow what it aliases
if (symbol && symbol.flags & SymbolFlags.Alias) {
symbol = typeChecker.getAliasedSymbol(symbol);
}
if (symbol && symbol.flags & SymbolFlags.HasExports) {
// Extract module or enum members
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName());
const isValidTypeAccess = (symbol: Symbol) => symbolCanbeReferencedAtTypeLocation(symbol);
const isValidAccess = isRhsOfImportDeclaration ?
// Any kind is allowed when dotting off namespace in internal import equals declaration
(symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
isTypeLocation ? isValidTypeAccess : isValidValueAccess;
forEach(exportedSymbols, symbol => {
if (isValidAccess(symbol)) {
symbols.push(symbol);
if (symbol.flags & (SymbolFlags.Module | SymbolFlags.Enum)) {
// Extract module or enum members
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName());
const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol);
const isValidAccess = isRhsOfImportDeclaration ?
// Any kind is allowed when dotting off namespace in internal import equals declaration
(symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) :
isTypeLocation ? isValidTypeAccess : isValidValueAccess;
for (const symbol of exportedSymbols) {
if (isValidAccess(symbol)) {
symbols.push(symbol);
}
}
});
// If the module is merged with a value, we must get the type of the class and add its propertes (for inherited static methods).
if (!isTypeLocation && symbol.declarations.some(d => d.kind !== SyntaxKind.SourceFile && d.kind !== SyntaxKind.ModuleDeclaration && d.kind !== SyntaxKind.EnumDeclaration)) {
addTypeProperties(typeChecker.getTypeOfSymbolAtLocation(symbol, node));
}
return;
}
}
}
if (!isTypeLocation) {
const type = typeChecker.getTypeAtLocation(node);
addTypeProperties(type);
addTypeProperties(typeChecker.getTypeAtLocation(node));
}
}
function addTypeProperties(type: Type) {
if (type) {
// Filter private properties
for (const symbol of type.getApparentProperties()) {
if (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName())) {
symbols.push(symbol);
}
// Filter private properties
for (const symbol of type.getApparentProperties()) {
if (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.getUnescapedName())) {
symbols.push(symbol);
}
}
if (isJavaScriptFile && type.flags & TypeFlags.Union) {
// In javascript files, for union types, we don't just get the members that
// the individual types have in common, we also include all the members that
// each individual type has. This is because we're going to add all identifiers
// anyways. So we might as well elevate the members that were at least part
// of the individual types to a higher status since we know what they are.
const unionType = <UnionType>type;
for (const elementType of unionType.types) {
addTypeProperties(elementType);
}
if (isJavaScriptFile && type.flags & TypeFlags.Union) {
// In javascript files, for union types, we don't just get the members that
// the individual types have in common, we also include all the members that
// each individual type has. This is because we're going to add all identifiers
// anyways. So we might as well elevate the members that were at least part
// of the individual types to a higher status since we know what they are.
const unionType = <UnionType>type;
for (const elementType of unionType.types) {
addTypeProperties(elementType);
}
}
}
@@ -777,12 +774,12 @@ namespace ts.Completions {
(!isContextTokenValueLocation(contextToken) &&
(isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken)))) {
// Its a type, but you can reach it by namespace.type as well
return symbolCanbeReferencedAtTypeLocation(symbol);
return symbolCanBeReferencedAtTypeLocation(symbol);
}
}
// expressions are value space (which includes the value namespaces)
return !!(symbol.flags & SymbolFlags.Value);
return !!(getCombinedLocalAndExportSymbolFlags(symbol) & SymbolFlags.Value);
});
}
@@ -812,21 +809,21 @@ namespace ts.Completions {
}
}
function symbolCanbeReferencedAtTypeLocation(symbol: Symbol): boolean {
function symbolCanBeReferencedAtTypeLocation(symbol: Symbol): boolean {
symbol = symbol.exportSymbol || symbol;
// This is an alias, follow what it aliases
if (symbol && symbol.flags & SymbolFlags.Alias) {
symbol = typeChecker.getAliasedSymbol(symbol);
}
symbol = skipAlias(symbol, typeChecker);
if (symbol.flags & SymbolFlags.Type) {
return true;
}
if (symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule)) {
if (symbol.flags & SymbolFlags.Module) {
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
// If the exported symbols contains type,
// symbol can be referenced at locations where type is allowed
return forEach(exportedSymbols, symbolCanbeReferencedAtTypeLocation);
return forEach(exportedSymbols, symbolCanBeReferencedAtTypeLocation);
}
}
@@ -1598,22 +1595,23 @@ namespace ts.Completions {
/**
* Get the name to be display in completion from a given symbol.
*
* @return undefined if the name is of external module otherwise a name with striped of any quote
* @return undefined if the name is of external module
*/
function getCompletionEntryDisplayNameForSymbol(typeChecker: TypeChecker, symbol: Symbol, target: ScriptTarget, performCharacterChecks: boolean, location: Node): string {
const displayName: string = getDeclaredName(typeChecker, symbol, location);
function getCompletionEntryDisplayNameForSymbol(symbol: Symbol, target: ScriptTarget, performCharacterChecks: boolean): string | undefined {
const name = symbol.getUnescapedName();
if (!name) return undefined;
if (displayName) {
const firstCharCode = displayName.charCodeAt(0);
// First check of the displayName is not external module; if it is an external module, it is not valid entry
if ((symbol.flags & SymbolFlags.Namespace) && (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote)) {
// First check of the displayName is not external module; if it is an external module, it is not valid entry
if (symbol.flags & SymbolFlags.Namespace) {
const firstCharCode = name.charCodeAt(0);
if (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote) {
// If the symbol is external module, don't show it in the completion list
// (i.e declare module "http" { const x; } | // <= request completion here, "http" should not be there)
return undefined;
}
}
return getCompletionEntryDisplayName(displayName, target, performCharacterChecks);
return getCompletionEntryDisplayName(name, target, performCharacterChecks);
}
/**
@@ -1621,24 +1619,12 @@ namespace ts.Completions {
* and checking whether the name is valid identifier name.
*/
function getCompletionEntryDisplayName(name: string, target: ScriptTarget, performCharacterChecks: boolean): string {
if (!name) {
return undefined;
}
name = stripQuotes(name);
if (!name) {
return undefined;
}
// If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an
// invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name.
// e.g "b a" is valid quoted name but when we strip off the quotes, it is invalid.
// We, thus, need to check if whatever was inside the quotes is actually a valid identifier name.
if (performCharacterChecks) {
if (!isIdentifierText(name, target)) {
return undefined;
}
if (performCharacterChecks && !isIdentifierText(name, target)) {
return undefined;
}
return name;
+2 -4
View File
@@ -440,7 +440,7 @@ namespace ts.FindAllReferences {
function getExport(): ExportedSymbol | ImportedSymbol | undefined {
const parent = node.parent!;
if (symbol.flags & SymbolFlags.Export) {
if (symbol.exportSymbol) {
if (parent.kind === SyntaxKind.PropertyAccessExpression) {
// When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use.
// So check that we are at the declaration.
@@ -449,9 +449,7 @@ namespace ts.FindAllReferences {
: undefined;
}
else {
const { exportSymbol } = symbol;
Debug.assert(!!exportSymbol);
return exportInfo(exportSymbol, getExportKindForDeclaration(parent));
return exportInfo(symbol.exportSymbol, getExportKindForDeclaration(parent));
}
}
else {
+18 -8
View File
@@ -12,7 +12,7 @@ namespace ts.JsTyping {
directoryExists: (path: string) => boolean;
fileExists: (fileName: string) => boolean;
readFile: (path: string, encoding?: string) => string;
readDirectory: (rootDir: string, extensions: string[], excludes: string[], includes: string[], depth?: number) => string[];
readDirectory: (rootDir: string, extensions: ReadonlyArray<string>, excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, depth?: number) => string[];
}
interface PackageJson {
@@ -51,6 +51,7 @@ namespace ts.JsTyping {
*/
export function discoverTypings(
host: TypingResolutionHost,
log: ((message: string) => void) | undefined,
fileNames: string[],
projectRootPath: Path,
safeListPath: Path,
@@ -107,8 +108,9 @@ namespace ts.JsTyping {
// add typings for unresolved imports
if (unresolvedImports) {
for (const moduleId of unresolvedImports) {
const typingName = nodeCoreModules.has(moduleId) ? "node" : moduleId;
const x = unresolvedImports.map(moduleId => nodeCoreModules.has(moduleId) ? "node" : moduleId);
if (x.length && log) log(`Inferred typings from unresolved imports: ${JSON.stringify(x)}`);
for (const typingName of x) {
if (!inferredTypings.has(typingName)) {
inferredTypings.set(typingName, undefined);
}
@@ -136,7 +138,9 @@ namespace ts.JsTyping {
newTypingNames.push(typing);
}
});
return { cachedTypingPaths, newTypingNames, filesToWatch };
const result = { cachedTypingPaths, newTypingNames, filesToWatch };
if (log) log(`Result: ${JSON.stringify(result)}`);
return result;
function addInferredTyping(typingName: string) {
if (!inferredTypings.has(typingName)) {
@@ -153,6 +157,7 @@ namespace ts.JsTyping {
}
filesToWatch.push(jsonPath);
if (log) log(`Searching for typing names in '${jsonPath}' dependencies`);
const jsonConfig: PackageJson = readConfigFile(jsonPath, (path: string) => host.readFile(path)).config;
addInferredTypingsFromKeys(jsonConfig.dependencies);
addInferredTypingsFromKeys(jsonConfig.devDependencies);
@@ -175,19 +180,23 @@ namespace ts.JsTyping {
* @param fileNames are the names for source files in the project
*/
function getTypingNamesFromSourceFileNames(fileNames: string[]) {
for (const j of fileNames) {
if (!hasJavaScriptFileExtension(j)) continue;
const fromFileNames = mapDefined(fileNames, j => {
if (!hasJavaScriptFileExtension(j)) return undefined;
const inferredTypingName = removeFileExtension(getBaseFileName(j.toLowerCase()));
const cleanedTypingName = inferredTypingName.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, "");
const safe = safeList.get(cleanedTypingName);
if (safe !== undefined) {
return safeList.get(cleanedTypingName);
});
if (fromFileNames.length) {
if (log) log(`Inferred typings from file names: ${JSON.stringify(fromFileNames)}`);
for (const safe of fromFileNames) {
addInferredTyping(safe);
}
}
const hasJsxFile = some(fileNames, f => fileExtensionIs(f, Extension.Jsx));
if (hasJsxFile) {
if (log) log(`Inferred 'react' typings due to presence of '.jsx' extension`);
addInferredTyping("react");
}
}
@@ -206,6 +215,7 @@ namespace ts.JsTyping {
// depth of 2, so we access `node_modules/foo` but not `node_modules/foo/bar`
const fileNames = host.readDirectory(packagesFolderPath, [".json"], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2);
if (log) log(`Searching for typing names in ${packagesFolderPath}; all files: ${JSON.stringify(fileNames)}`);
for (const fileName of fileNames) {
const normalizedFileName = normalizePath(fileName);
const baseFileName = getBaseFileName(normalizedFileName);
+6 -11
View File
@@ -40,19 +40,14 @@ namespace ts.Completions.PathCompletions {
rootDirs = map(rootDirs, rootDirectory => normalizePath(isRootedDiskPath(rootDirectory) ? rootDirectory : combinePaths(basePath, rootDirectory)));
// Determine the path to the directory containing the script relative to the root directory it is contained within
let relativeDirectory: string;
for (const rootDirectory of rootDirs) {
if (containsPath(rootDirectory, scriptPath, basePath, ignoreCase)) {
relativeDirectory = scriptPath.substr(rootDirectory.length);
break;
}
}
const relativeDirectory = forEach(rootDirs, rootDirectory =>
containsPath(rootDirectory, scriptPath, basePath, ignoreCase) ? scriptPath.substr(rootDirectory.length) : undefined);
// Now find a path for each potential directory that is to be merged with the one containing the script
return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory)));
}
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] {
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] {
const basePath = compilerOptions.project || host.getCurrentDirectory();
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase);
@@ -69,7 +64,7 @@ namespace ts.Completions.PathCompletions {
/**
* Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename.
*/
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] {
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] {
if (fragment === undefined) {
fragment = "";
}
@@ -190,7 +185,7 @@ namespace ts.Completions.PathCompletions {
return result;
}
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[], host: LanguageServiceHost): string[] {
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: ReadonlyArray<string>, host: LanguageServiceHost): string[] {
if (host.readDirectory) {
const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined;
if (parsed) {
@@ -505,7 +500,7 @@ namespace ts.Completions.PathCompletions {
return tryIOAndConsumeErrors(host, host.getDirectories, directoryName);
}
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] {
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>): string[] {
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include);
}
+3 -16
View File
@@ -33,22 +33,9 @@ namespace ts {
refactors.set(refactor.name, refactor);
}
export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
let results: ApplicableRefactorInfo[];
const refactorList: Refactor[] = [];
refactors.forEach(refactor => {
refactorList.push(refactor);
});
for (const refactor of refactorList) {
if (context.cancellationToken && context.cancellationToken.isCancellationRequested()) {
return results;
}
const infos = refactor.getAvailableActions(context);
if (infos && infos.length) {
(results || (results = [])).push(...infos);
}
}
return results;
export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] {
return flatMapIter(refactors.values(), refactor =>
context.cancellationToken && context.cancellationToken.isCancellationRequested() ? [] : refactor.getAvailableActions(context));
}
export function getEditsForRefactor(context: RefactorContext, refactorName: string, actionName: string): RefactorEditInfo | undefined {
+29 -45
View File
@@ -1760,21 +1760,14 @@ namespace ts {
function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: FormatCodeSettings): CodeAction[] {
synchronizeHostData();
const sourceFile = getValidSourceFile(fileName);
const span = { start, length: end - start };
const span = createTextSpanFromBounds(start, end);
const newLineCharacter = getNewLineOrDefaultFromHost(host);
const rulesProvider = getRuleProvider(formatOptions);
let allFixes: CodeAction[] = [];
forEach(deduplicate(errorCodes), errorCode => {
return flatMap(deduplicate(errorCodes), errorCode => {
cancellationToken.throwIfCancellationRequested();
const rulesProvider = getRuleProvider(formatOptions);
const fixes = codefix.getFixes({ errorCode, sourceFile, span, program, newLineCharacter, host, cancellationToken, rulesProvider });
if (fixes) {
allFixes = allFixes.concat(fixes);
}
return codefix.getFixes({ errorCode, sourceFile, span, program, newLineCharacter, host, cancellationToken, rulesProvider });
});
return allFixes;
}
function getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion {
@@ -2055,42 +2048,33 @@ namespace ts {
}
function initializeNameTable(sourceFile: SourceFile): void {
const nameTable = createUnderscoreEscapedMap<number>();
walk(sourceFile);
sourceFile.nameTable = nameTable;
function walk(node: Node) {
switch (node.kind) {
case SyntaxKind.Identifier:
setNameTable((<Identifier>node).text, node);
break;
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
// We want to store any numbers/strings if they were a name that could be
// related to a declaration. So, if we have 'import x = require("something")'
// then we want 'something' to be in the name table. Similarly, if we have
// "a['propname']" then we want to store "propname" in the name table.
if (isDeclarationName(node) ||
node.parent.kind === SyntaxKind.ExternalModuleReference ||
isArgumentOfElementAccessExpression(node) ||
isLiteralComputedPropertyDeclarationName(node)) {
setNameTable(getEscapedTextOfIdentifierOrLiteral((<LiteralExpression>node)), node);
}
break;
default:
forEachChild(node, walk);
if (node.jsDoc) {
for (const jsDoc of node.jsDoc) {
forEachChild(jsDoc, walk);
}
}
const nameTable = sourceFile.nameTable = createUnderscoreEscapedMap<number>();
sourceFile.forEachChild(function walk(node) {
if ((isIdentifier(node) || isStringOrNumericLiteral(node) && literalIsName(node)) && node.text) {
const text = getEscapedTextOfIdentifierOrLiteral(node);
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
}
}
function setNameTable(text: __String, node: ts.Node): void {
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
}
forEachChild(node, walk);
if (node.jsDoc) {
for (const jsDoc of node.jsDoc) {
forEachChild(jsDoc, walk);
}
}
});
}
/**
* We want to store any numbers/strings if they were a name that could be
* related to a declaration. So, if we have 'import x = require("something")'
* then we want 'something' to be in the name table. Similarly, if we have
* "a['propname']" then we want to store "propname" in the name table.
*/
function literalIsName(node: ts.StringLiteral | ts.NumericLiteral): boolean {
return isDeclarationName(node) ||
node.parent.kind === SyntaxKind.ExternalModuleReference ||
isArgumentOfElementAccessExpression(node) ||
isLiteralComputedPropertyDeclarationName(node);
}
function isObjectLiteralElement(node: Node): node is ObjectLiteralElement {
+3 -2
View File
@@ -444,7 +444,7 @@ namespace ts {
return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
}
public readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] {
public readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: string[], include?: string[], depth?: number): string[] {
const pattern = getFileMatcherPatterns(path, exclude, include,
this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory());
return JSON.parse(this.shimHost.readDirectory(
@@ -483,7 +483,7 @@ namespace ts {
}
}
public readDirectory(rootDir: string, extensions: string[], exclude: string[], include: string[], depth?: number): string[] {
public readDirectory(rootDir: string, extensions: ReadonlyArray<string>, exclude: ReadonlyArray<string>, include: ReadonlyArray<string>, depth?: number): string[] {
const pattern = getFileMatcherPatterns(rootDir, exclude, include,
this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory());
return JSON.parse(this.shimHost.readDirectory(
@@ -1116,6 +1116,7 @@ namespace ts {
const info = <DiscoverTypingsInfo>JSON.parse(discoverTypingsJson);
return ts.JsTyping.discoverTypings(
this.host,
msg => this.logger.log(msg),
info.fileNames,
toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName),
toPath(info.safeListPath, info.safeListPath, getCanonicalFileName),
+6 -6
View File
@@ -2,7 +2,7 @@
namespace ts.SymbolDisplay {
// TODO(drosen): use contextual SemanticMeaning.
export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind {
const { flags } = symbol;
const flags = getCombinedLocalAndExportSymbolFlags(symbol);
if (flags & SymbolFlags.Class) {
return getDeclarationOfKind(symbol, SyntaxKind.ClassExpression) ?
@@ -34,7 +34,7 @@ namespace ts.SymbolDisplay {
if (location.kind === SyntaxKind.ThisKeyword && isExpression(location)) {
return ScriptElementKind.parameterElement;
}
const { flags } = symbol;
const flags = getCombinedLocalAndExportSymbolFlags(symbol);
if (flags & SymbolFlags.Variable) {
if (isFirstDeclarationOfSymbolParameter(symbol)) {
return ScriptElementKind.parameterElement;
@@ -96,7 +96,7 @@ namespace ts.SymbolDisplay {
const displayParts: SymbolDisplayPart[] = [];
let documentation: SymbolDisplayPart[];
let tags: JSDocTagInfo[];
const symbolFlags = symbol.flags;
const symbolFlags = ts.getCombinedLocalAndExportSymbolFlags(symbol);
let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location);
let hasAddedSymbolInfo: boolean;
const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isExpression(location);
@@ -110,7 +110,7 @@ namespace ts.SymbolDisplay {
}
let signature: Signature;
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol, location);
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol.exportSymbol || symbol, location);
if (type) {
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
const right = (<PropertyAccessExpression>location.parent).name;
@@ -198,7 +198,7 @@ namespace ts.SymbolDisplay {
hasAddedSymbolInfo = true;
}
}
else if ((isNameOfFunctionDeclaration(location) && !(symbol.flags & SymbolFlags.Accessor)) || // name of function declaration
else if ((isNameOfFunctionDeclaration(location) && !(symbolFlags & SymbolFlags.Accessor)) || // name of function declaration
(location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration
// get the signature from the declaration and write it
const functionDeclaration = <FunctionLike>location.parent;
@@ -429,7 +429,7 @@ namespace ts.SymbolDisplay {
if (!documentation) {
documentation = symbol.getDocumentationComment();
tags = symbol.getJsDocTags();
if (documentation.length === 0 && symbol.flags & SymbolFlags.Property) {
if (documentation.length === 0 && symbolFlags & SymbolFlags.Property) {
// For some special property access expressions like `exports.foo = foo` or `module.exports.foo = foo`
// there documentation comments might be attached to the right hand side symbol of their declarations.
// The pattern of such special property access is that the parent symbol is the symbol of the file.
+1 -1
View File
@@ -164,7 +164,7 @@ namespace ts {
* LS host can optionally implement these methods to support completions for module specifiers.
* Without these methods, only completions for ambient modules will be provided.
*/
readDirectory?(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[];
readDirectory?(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
readFile?(path: string, encoding?: string): string;
fileExists?(path: string): boolean;
+7 -4
View File
@@ -1240,10 +1240,13 @@ namespace ts {
}
export function mapToDisplayParts(writeDisplayParts: (writer: DisplayPartsSymbolWriter) => void): SymbolDisplayPart[] {
writeDisplayParts(displayPartWriter);
const result = displayPartWriter.displayParts();
displayPartWriter.clear();
return result;
try {
writeDisplayParts(displayPartWriter);
return displayPartWriter.displayParts();
}
finally {
displayPartWriter.clear();
}
}
export function typeToDisplayParts(typechecker: TypeChecker, type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] {
@@ -28,7 +28,17 @@ x1(0);
/** @type {function (number): number} */
const x2 = (a) => a + 1;
x2(0);
x2(0);
/**
* @type {object}
*/
var props = {};
/**
* @type {Object}
*/
var props = {};
//// [0.js]
// @ts-check
@@ -54,3 +64,11 @@ x1(0);
/** @type {function (number): number} */
var x2 = function (a) { return a + 1; };
x2(0);
/**
* @type {object}
*/
var props = {};
/**
* @type {Object}
*/
var props = {};
@@ -58,3 +58,15 @@ const x2 = (a) => a + 1;
x2(0);
>x2 : Symbol(x2, Decl(0.js, 28, 5))
/**
* @type {object}
*/
var props = {};
>props : Symbol(props, Decl(0.js, 34, 3), Decl(0.js, 39, 3))
/**
* @type {Object}
*/
var props = {};
>props : Symbol(props, Decl(0.js, 34, 3), Decl(0.js, 39, 3))
@@ -86,3 +86,17 @@ x2(0);
>x2 : (arg0: number) => number
>0 : 0
/**
* @type {object}
*/
var props = {};
>props : any
>{} : {}
/**
* @type {Object}
*/
var props = {};
>props : any
>{} : {}
@@ -0,0 +1,18 @@
//// [importCallExpressionShouldNotGetParen.ts]
const localeName = "zh-CN";
import(`./locales/${localeName}.js`).then(bar => {
let x = bar;
});
import("./locales/" + localeName + ".js").then(bar => {
let x = bar;
});
//// [importCallExpressionShouldNotGetParen.js]
const localeName = "zh-CN";
import(`./locales/${localeName}.js`).then(bar => {
let x = bar;
});
import("./locales/" + localeName + ".js").then(bar => {
let x = bar;
});
@@ -0,0 +1,27 @@
=== tests/cases/conformance/dynamicImport/importCallExpressionShouldNotGetParen.ts ===
const localeName = "zh-CN";
>localeName : Symbol(localeName, Decl(importCallExpressionShouldNotGetParen.ts, 0, 5))
import(`./locales/${localeName}.js`).then(bar => {
>import(`./locales/${localeName}.js`).then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
>localeName : Symbol(localeName, Decl(importCallExpressionShouldNotGetParen.ts, 0, 5))
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
>bar : Symbol(bar, Decl(importCallExpressionShouldNotGetParen.ts, 1, 42))
let x = bar;
>x : Symbol(x, Decl(importCallExpressionShouldNotGetParen.ts, 2, 7))
>bar : Symbol(bar, Decl(importCallExpressionShouldNotGetParen.ts, 1, 42))
});
import("./locales/" + localeName + ".js").then(bar => {
>import("./locales/" + localeName + ".js").then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
>localeName : Symbol(localeName, Decl(importCallExpressionShouldNotGetParen.ts, 0, 5))
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
>bar : Symbol(bar, Decl(importCallExpressionShouldNotGetParen.ts, 5, 47))
let x = bar;
>x : Symbol(x, Decl(importCallExpressionShouldNotGetParen.ts, 6, 7))
>bar : Symbol(bar, Decl(importCallExpressionShouldNotGetParen.ts, 5, 47))
});
@@ -0,0 +1,39 @@
=== tests/cases/conformance/dynamicImport/importCallExpressionShouldNotGetParen.ts ===
const localeName = "zh-CN";
>localeName : "zh-CN"
>"zh-CN" : "zh-CN"
import(`./locales/${localeName}.js`).then(bar => {
>import(`./locales/${localeName}.js`).then(bar => { let x = bar;}) : Promise<void>
>import(`./locales/${localeName}.js`).then : <TResult1 = any, TResult2 = never>(onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
>import(`./locales/${localeName}.js`) : Promise<any>
>`./locales/${localeName}.js` : string
>localeName : "zh-CN"
>then : <TResult1 = any, TResult2 = never>(onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
>bar => { let x = bar;} : (bar: any) => void
>bar : any
let x = bar;
>x : any
>bar : any
});
import("./locales/" + localeName + ".js").then(bar => {
>import("./locales/" + localeName + ".js").then(bar => { let x = bar;}) : Promise<void>
>import("./locales/" + localeName + ".js").then : <TResult1 = any, TResult2 = never>(onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
>import("./locales/" + localeName + ".js") : Promise<any>
>"./locales/" + localeName + ".js" : string
>"./locales/" + localeName : string
>"./locales/" : "./locales/"
>localeName : "zh-CN"
>".js" : ".js"
>then : <TResult1 = any, TResult2 = never>(onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
>bar => { let x = bar;} : (bar: any) => void
>bar : any
let x = bar;
>x : any
>bar : any
});
@@ -18,5 +18,5 @@ function foo() { return "foo"; }
exports.foo = foo;
//// [1.js]
"use strict";
var p1 = (import)("./0"); // error
var p2 = (import)("./0"); // error
var p1 = Promise.resolve().then(function () { return require("./0"); }); // error
var p2 = Promise.resolve().then(function () { return require("./0"); }); // error
@@ -55,6 +55,9 @@ var nullable;
/** @type {Object} */
var Obj;
/** @type {object} */
var obj;
/** @type {Function} */
var Func;
@@ -77,6 +80,7 @@ var P: Promise<any>;
var p: Promise<any>;
var nullable: number | null;
var Obj: any;
var obj: any;
var Func: Function;
@@ -117,6 +121,8 @@ var p;
var nullable;
/** @type {Object} */
var Obj;
/** @type {object} */
var obj;
/** @type {Function} */
var Func;
//// [b.js]
@@ -138,4 +144,5 @@ var P;
var p;
var nullable;
var Obj;
var obj;
var Func;
@@ -71,9 +71,13 @@ var nullable;
var Obj;
>Obj : Symbol(Obj, Decl(a.js, 52, 3), Decl(b.ts, 17, 3))
/** @type {object} */
var obj;
>obj : Symbol(obj, Decl(a.js, 55, 3), Decl(b.ts, 18, 3))
/** @type {Function} */
var Func;
>Func : Symbol(Func, Decl(a.js, 55, 3), Decl(b.ts, 18, 3))
>Func : Symbol(Func, Decl(a.js, 58, 3), Decl(b.ts, 19, 3))
=== tests/cases/conformance/jsdoc/b.ts ===
var S: string;
@@ -132,7 +136,10 @@ var nullable: number | null;
var Obj: any;
>Obj : Symbol(Obj, Decl(a.js, 52, 3), Decl(b.ts, 17, 3))
var obj: any;
>obj : Symbol(obj, Decl(a.js, 55, 3), Decl(b.ts, 18, 3))
var Func: Function;
>Func : Symbol(Func, Decl(a.js, 55, 3), Decl(b.ts, 18, 3))
>Func : Symbol(Func, Decl(a.js, 58, 3), Decl(b.ts, 19, 3))
>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
@@ -71,6 +71,10 @@ var nullable;
var Obj;
>Obj : any
/** @type {object} */
var obj;
>obj : any
/** @type {Function} */
var Func;
>Func : Function
@@ -135,6 +139,9 @@ var nullable: number | null;
var Obj: any;
>Obj : any
var obj: any;
>obj : any
var Func: Function;
>Func : Function
>Function : Function
@@ -0,0 +1,44 @@
tests/cases/compiler/mergedDeclarationExports.ts(13,11): error TS2395: Individual declarations in merged declaration 'c' must be all exported or all local.
tests/cases/compiler/mergedDeclarationExports.ts(14,18): error TS2395: Individual declarations in merged declaration 'c' must be all exported or all local.
tests/cases/compiler/mergedDeclarationExports.ts(17,11): error TS2395: Individual declarations in merged declaration 'd' must be all exported or all local.
tests/cases/compiler/mergedDeclarationExports.ts(18,14): error TS2395: Individual declarations in merged declaration 'd' must be all exported or all local.
tests/cases/compiler/mergedDeclarationExports.ts(21,11): error TS2395: Individual declarations in merged declaration 'N' must be all exported or all local.
tests/cases/compiler/mergedDeclarationExports.ts(22,18): error TS2395: Individual declarations in merged declaration 'N' must be all exported or all local.
==== tests/cases/compiler/mergedDeclarationExports.ts (6 errors) ====
// OK -- one is type, one is value
interface b {}
export const b = 1;
// OK -- one is a type, one is a namespace, one is a value.
type t = 0;
namespace t { interface I {} }
export const t = 0;
// Should get errors if they have some meaning in common.
// both types
interface c {}
~
!!! error TS2395: Individual declarations in merged declaration 'c' must be all exported or all local.
export interface c {}
~
!!! error TS2395: Individual declarations in merged declaration 'c' must be all exported or all local.
// both types (class is also value, but that doesn't matter)
interface d {}
~
!!! error TS2395: Individual declarations in merged declaration 'd' must be all exported or all local.
export class d {}
~
!!! error TS2395: Individual declarations in merged declaration 'd' must be all exported or all local.
// both namespaces
namespace N { }
~
!!! error TS2395: Individual declarations in merged declaration 'N' must be all exported or all local.
export namespace N {}
~
!!! error TS2395: Individual declarations in merged declaration 'N' must be all exported or all local.
@@ -0,0 +1,36 @@
//// [mergedDeclarationExports.ts]
// OK -- one is type, one is value
interface b {}
export const b = 1;
// OK -- one is a type, one is a namespace, one is a value.
type t = 0;
namespace t { interface I {} }
export const t = 0;
// Should get errors if they have some meaning in common.
// both types
interface c {}
export interface c {}
// both types (class is also value, but that doesn't matter)
interface d {}
export class d {}
// both namespaces
namespace N { }
export namespace N {}
//// [mergedDeclarationExports.js]
"use strict";
exports.__esModule = true;
exports.b = 1;
exports.t = 0;
var d = (function () {
function d() {
}
return d;
}());
exports.d = d;
@@ -0,0 +1,37 @@
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(2,17): error TS1005: ':' expected.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(4,22): error TS1005: '=>' expected.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(4,24): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(9,18): error TS1005: '{' expected.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(9,21): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(9,28): error TS1005: ';' expected.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(12,1): error TS1128: Declaration or statement expected.
==== tests/cases/compiler/parseErrorIncorrectReturnToken.ts (7 errors) ====
type F1 = {
(n: number) => string; // should be : not =>
~~
!!! error TS1005: ':' expected.
}
type F2 = (n: number): string; // should be => not :
~
!!! error TS1005: '=>' expected.
~~~~~~
!!! error TS2693: 'string' only refers to a type, but is being used as a value here.
// doesn't work in non-type contexts, where the return type is optional
let f = (n: number) => string => n.toString();
let o = {
m(n: number) => string {
~~
!!! error TS1005: '{' expected.
~~~~~~
!!! error TS2693: 'string' only refers to a type, but is being used as a value here.
~
!!! error TS1005: ';' expected.
return n.toString();
}
};
~
!!! error TS1128: Declaration or statement expected.
@@ -0,0 +1,27 @@
//// [parseErrorIncorrectReturnToken.ts]
type F1 = {
(n: number) => string; // should be : not =>
}
type F2 = (n: number): string; // should be => not :
// doesn't work in non-type contexts, where the return type is optional
let f = (n: number) => string => n.toString();
let o = {
m(n: number) => string {
return n.toString();
}
};
//// [parseErrorIncorrectReturnToken.js]
string; // should be => not :
// doesn't work in non-type contexts, where the return type is optional
var f = function (n) { return function (string) { return n.toString(); }; };
var o = {
m: function (n) { }
};
string;
{
return n.toString();
}
;
@@ -2,7 +2,7 @@
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
@@ -2,7 +2,7 @@
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
@@ -2,7 +2,7 @@
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
@@ -2,7 +2,7 @@
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
@@ -2,7 +2,7 @@
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es5","es2015.promise"], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
@@ -2,7 +2,7 @@
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
@@ -2,7 +2,7 @@
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es5","es2015.core"], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
@@ -2,7 +2,7 @@
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
@@ -0,0 +1,22 @@
// OK -- one is type, one is value
interface b {}
export const b = 1;
// OK -- one is a type, one is a namespace, one is a value.
type t = 0;
namespace t { interface I {} }
export const t = 0;
// Should get errors if they have some meaning in common.
// both types
interface c {}
export interface c {}
// both types (class is also value, but that doesn't matter)
interface d {}
export class d {}
// both namespaces
namespace N { }
export namespace N {}
@@ -0,0 +1,13 @@
type F1 = {
(n: number) => string; // should be : not =>
}
type F2 = (n: number): string; // should be => not :
// doesn't work in non-type contexts, where the return type is optional
let f = (n: number) => string => n.toString();
let o = {
m(n: number) => string {
return n.toString();
}
};
@@ -0,0 +1,11 @@
// @module: esnext
// @target: es6
// @noImplicitAny: true
const localeName = "zh-CN";
import(`./locales/${localeName}.js`).then(bar => {
let x = bar;
});
import("./locales/" + localeName + ".js").then(bar => {
let x = bar;
});
@@ -31,4 +31,14 @@ x1(0);
/** @type {function (number): number} */
const x2 = (a) => a + 1;
x2(0);
x2(0);
/**
* @type {object}
*/
var props = {};
/**
* @type {Object}
*/
var props = {};
@@ -57,6 +57,9 @@ var nullable;
/** @type {Object} */
var Obj;
/** @type {object} */
var obj;
/** @type {Function} */
var Func;
@@ -79,4 +82,5 @@ var P: Promise<any>;
var p: Promise<any>;
var nullable: number | null;
var Obj: any;
var obj: any;
var Func: Function;
@@ -35,7 +35,7 @@ goTo.file("commentsExternalModules_file0.ts");
verify.quickInfoAt("1", "namespace m1", "Namespace comment");
goTo.marker('2');
verify.completionListContains("b", "var m1.b: number", "b's comment");
verify.completionListContains("b", "var b: number", "b's comment");
verify.completionListContains("foo", "function foo(): number", "foo's comment");
goTo.marker('3');
+1 -1
View File
@@ -99,7 +99,7 @@
verify.quickInfoAt("1", "namespace m1", "Namespace comment");
goTo.marker('2');
verify.completionListContains("b", "var m1.b: number", "b's comment");
verify.completionListContains("b", "var b: number", "b's comment");
verify.completionListContains("foo", "function foo(): number", "foo's comment");
goTo.marker('3');
@@ -6,8 +6,8 @@
/////*3*/
goTo.marker("0");
verify.not.completionListContains("B");
verify.not.completionListContains("\u0042");
verify.completionListContains("B");
verify.completionListContains("\u0042");
goTo.marker("2");
verify.not.completionListContains("C");
@@ -18,10 +18,9 @@ verify.not.completionListContains("A");
verify.not.completionListContains("\u0041");
goTo.marker("3");
verify.not.completionListContains("B");
verify.not.completionListContains("\u0042");
verify.not.completionListContains("A");
verify.not.completionListContains("\u0041");
verify.not.completionListContains("C");
verify.not.completionListContains("\u0043");
verify.completionListContains("B");
verify.completionListContains("\u0042");
verify.completionListContains("A");
verify.completionListContains("\u0041");
verify.completionListContains("C");
verify.completionListContains("\u0043");
@@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
// @Filename: /a.ts
////import * as self from "./a";
////
////declare module "a" {
//// export const a: number;
////}
////
/////**/
goTo.marker();
verify.not.completionListContains("a");
@@ -19,6 +19,7 @@ verify.completionListContains("bar");
verify.completionListContains("break");
verify.completionListContains("any");
verify.completionListContains("$");
verify.completionListContains("b");
// Nothing else should show up
verify.completionListCount(4);
verify.completionListCount(5);
@@ -41,19 +41,16 @@ function getVerify(isTypeLocation?: boolean) {
verifyValueOrType: verify
};
}
function typeLocationVerify(valueMarker: string, verify: (typeMarker: string) => void) {
verify(valueMarker + "Type");
return valueMarker;
}
function verifyModuleM(marker: string) {
const isTypeLocation = marker.indexOf("Type") !== -1;
const { verifyValue, verifyType, verifyValueOrType } = getVerify(isTypeLocation);
if (!isTypeLocation) {
marker = typeLocationVerify(marker, verifyModuleM);
}
verifyModuleMWorker(marker, /*isTypeLocation*/ false);
verifyModuleMWorker(`${marker}Type`, /*isTypeLocation*/ true);
}
function verifyModuleMWorker(marker: string, isTypeLocation: boolean): void {
goTo.marker(marker);
const { verifyValue, verifyType, verifyValueOrType } = getVerify(isTypeLocation);
verifyType.completionListContains("I");
verifyValueOrType.completionListContains("C");
verifyValueOrType.completionListContains("E");
@@ -63,8 +60,9 @@ function verifyModuleM(marker: string) {
verifyValueOrType.completionListContains("A");
}
// Module m
goTo.marker("1");
verify.completionListContains("A");
verifyModuleM("1");
// Class C
@@ -232,6 +232,7 @@
interface GotoMarkVerifyOptions {
isClassScope?: boolean;
isTypeLocation?: boolean;
insideMod1?: boolean;
}
function getVerify(isTypeLocation: boolean) {
@@ -243,30 +244,30 @@ function getVerify(isTypeLocation: boolean) {
};
}
function goToMarkAndGeneralVerify(marker: string, { isClassScope, isTypeLocation }: GotoMarkVerifyOptions = {})
{
function goToMarkAndGeneralVerify(marker: string, { isClassScope, isTypeLocation, insideMod1 }: GotoMarkVerifyOptions = {}) {
goTo.marker(marker);
const mod1Dot = insideMod1 ? "" : "mod1.";
const verifyValueInModule = isClassScope || isTypeLocation ? verify.not : verify;
const verifyValueOrTypeInModule = isClassScope ? verify.not : verify;
const verifyTypeInModule = isTypeLocation ? verify : verify.not;
verifyValueInModule.completionListContains('mod1var', 'var mod1var: number');
verifyValueInModule.completionListContains('mod1fn', 'function mod1fn(): void');
verifyValueInModule.completionListContains('mod1evar', 'var mod1.mod1evar: number');
verifyValueInModule.completionListContains('mod1efn', 'function mod1.mod1efn(): void');
verifyValueInModule.completionListContains('mod1eexvar', 'var mod1.mod1eexvar: number');
verifyValueInModule.completionListContains('mod1evar', `var ${mod1Dot}mod1evar: number`);
verifyValueInModule.completionListContains('mod1efn', `function ${mod1Dot}mod1efn(): void`);
verifyValueInModule.completionListContains('mod1eexvar', `var mod1.mod1eexvar: number`);
verifyValueInModule.completionListContains('mod3', 'namespace mod3');
verifyValueInModule.completionListContains('shwvar', 'var shwvar: number');
verifyValueInModule.completionListContains('shwfn', 'function shwfn(): void');
verifyTypeInModule.completionListContains('mod1int', 'interface mod1int');
verifyTypeInModule.completionListContains('mod1eint', 'interface mod1.mod1eint');
verifyTypeInModule.completionListContains('mod1eint', `interface ${mod1Dot}mod1eint`);
verifyTypeInModule.completionListContains('shwint', 'interface shwint');
verifyValueOrTypeInModule.completionListContains('mod1cls', 'class mod1cls');
verifyValueOrTypeInModule.completionListContains('mod1mod', 'namespace mod1mod');
verifyValueOrTypeInModule.completionListContains('mod1ecls', 'class mod1.mod1ecls');
verifyValueOrTypeInModule.completionListContains('mod1emod', 'namespace mod1.mod1emod');
verifyValueOrTypeInModule.completionListContains('mod1ecls', `class ${mod1Dot}mod1ecls`);
verifyValueOrTypeInModule.completionListContains('mod1emod', `namespace ${mod1Dot}mod1emod`);
verifyValueOrTypeInModule.completionListContains('mod2', 'namespace mod2');
verifyValueOrTypeInModule.completionListContains('shwcls', 'class shwcls');
@@ -295,12 +296,12 @@ function goToMarkAndGeneralVerify(marker: string, { isClassScope, isTypeLocation
}
// from mod1
goToMarkAndGeneralVerify('mod1');
goToMarkAndGeneralVerify('mod1', { insideMod1: true });
// from mod1 in type position
goToMarkAndGeneralVerify('mod1Type', { isTypeLocation: true });
goToMarkAndGeneralVerify('mod1Type', { isTypeLocation: true, insideMod1: true });
// from function in mod1
goToMarkAndGeneralVerify('function');
goToMarkAndGeneralVerify('function', { insideMod1: true });
verify.completionListContains('bar', '(local var) bar: number');
verify.completionListContains('foob', '(local function) foob(): void');
@@ -310,34 +311,34 @@ goToMarkAndGeneralVerify('class', { isClassScope: true });
//verify.not.completionListContains('ceVar');
// from interface in mod1
goToMarkAndGeneralVerify('interface');
goToMarkAndGeneralVerify('interface', { insideMod1: true });
// from namespace in mod1
verifyNamespaceInMod1('namespace');
verifyNamespaceInMod1('namespaceType', /*isTypeLocation*/ true);
function verifyNamespaceInMod1(marker: string, isTypeLocation?: boolean) {
goToMarkAndGeneralVerify(marker, { isTypeLocation });
goToMarkAndGeneralVerify(marker, { isTypeLocation, insideMod1: true });
const { verifyValue, verifyType, verifyValueOrType, verifyNotValueOrType } = getVerify(isTypeLocation);
verifyValue.completionListContains('m1X', 'var m1X: number');
verifyValue.completionListContains('m1Func', 'function m1Func(): void');
verifyValue.completionListContains('m1eX', 'var mod1mod.m1eX: number');
verifyValue.completionListContains('m1eFunc', 'function mod1mod.m1eFunc(): void');
verifyValue.completionListContains('m1eX', 'var m1eX: number');
verifyValue.completionListContains('m1eFunc', 'function m1eFunc(): void');
verifyType.completionListContains('m1Int', 'interface m1Int');
verifyType.completionListContains('m1eInt', 'interface mod1mod.m1eInt');
verifyType.completionListContains('m1eInt', 'interface m1eInt');
verifyValueOrType.completionListContains('m1Class', 'class m1Class');
verifyValueOrType.completionListContains('m1eClass', 'class mod1mod.m1eClass');
verifyValueOrType.completionListContains('m1eClass', 'class m1eClass');
verifyNotValueOrType.completionListContains('m1Mod', 'namespace m1Mod');
verifyNotValueOrType.completionListContains('m1eMod', 'namespace mod1mod.m1eMod');
verifyNotValueOrType.completionListContains('m1eMod', 'namespace m1eMod');
}
// from exported function in mod1
goToMarkAndGeneralVerify('exportedFunction');
goToMarkAndGeneralVerify('exportedFunction', { insideMod1: true });
verify.completionListContains('bar', '(local var) bar: number');
verify.completionListContains('foob', '(local function) foob(): void');
@@ -347,27 +348,27 @@ verify.not.completionListContains('ceFunc');
verify.not.completionListContains('ceVar');
// from exported interface in mod1
goToMarkAndGeneralVerify('exportedInterface');
goToMarkAndGeneralVerify('exportedInterface', { insideMod1: true });
// from exported namespace in mod1
verifyExportedNamespace('exportedNamespace');
verifyExportedNamespace('exportedNamespaceType', /*isTypeLocation*/ true);
function verifyExportedNamespace(marker: string, isTypeLocation?: boolean) {
goToMarkAndGeneralVerify(marker, { isTypeLocation });
goToMarkAndGeneralVerify(marker, { isTypeLocation, insideMod1: true });
const { verifyValue, verifyType, verifyValueOrType, verifyNotValueOrType } = getVerify(isTypeLocation);
verifyValue.completionListContains('mX', 'var mX: number');
verifyValue.completionListContains('mFunc', 'function mFunc(): void');
verifyValue.completionListContains('meX', 'var mod1.mod1emod.meX: number');
verifyValue.completionListContains('meFunc', 'function mod1.mod1emod.meFunc(): void');
verifyValue.completionListContains('meX', 'var meX: number');
verifyValue.completionListContains('meFunc', 'function meFunc(): void');
verifyType.completionListContains('mInt', 'interface mInt');
verifyType.completionListContains('meInt', 'interface mod1.mod1emod.meInt');
verifyType.completionListContains('meInt', 'interface meInt');
verifyValueOrType.completionListContains('mClass', 'class mClass');
verifyValueOrType.completionListContains('meClass', 'class mod1.mod1emod.meClass');
verifyValueOrType.completionListContains('meClass', 'class meClass');
verifyNotValueOrType.completionListContains('mMod', 'namespace mMod');
verifyNotValueOrType.completionListContains('meMod', 'namespace mod1.mod1emod.meMod');
verifyNotValueOrType.completionListContains('meMod', 'namespace meMod');
}
// from extended namespace
@@ -380,7 +381,7 @@ function verifyExtendedNamespace(marker: string, isTypeLocation?: boolean) {
verifyValue.completionListContains('mod1evar', 'var mod1.mod1evar: number');
verifyValue.completionListContains('mod1efn', 'function mod1.mod1efn(): void');
verifyValue.completionListContains('mod1eexvar', 'var mod1.mod1eexvar: number');
verifyValue.completionListContains('mod1eexvar', 'var mod1eexvar: number');
verifyValue.completionListContains('mod3', 'namespace mod3');
verifyValue.completionListContains('shwvar', 'var shwvar: number');
verifyValue.completionListContains('shwfn', 'function shwfn(): void');
@@ -415,4 +416,4 @@ function verifyExtendedNamespace(marker: string, isTypeLocation?: boolean) {
verify.not.completionListContains('sivar');
verify.not.completionListContains('sifn');
verify.not.completionListContains('mod2eexvar');
}
}
@@ -0,0 +1,11 @@
/// <reference path="fourslash.ts" />
// @Filename: /a.ts
////export default function f() {}
// @Filename: /b.ts
////import * as a from "./a";
////a./**/;
goTo.marker();
verify.completionListContains("default", "function f(): void");
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
////class C {
//// static m() { }
////}
////
////class D extends C {}
////namespace D {
//// export type T = number;
////}
////
////let x: D./*type*/;
////D./*value*/
goTo.marker("type");
verify.completionListContains("T");
verify.not.completionListContains("m");
goTo.marker("value");
verify.not.completionListContains("T");
verify.completionListContains("m");
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts'/>
////namespace N {
//// export type T = number;
////}
////const N = { m() {} };
////let x: N./*type*/;
////N./*value*/;
goTo.marker("type");
verify.completionListContains("T");
verify.not.completionListContains("m");
goTo.marker("value");
verify.not.completionListContains("T");
verify.completionListContains("m");
@@ -9,4 +9,4 @@ goTo.marker('1');
verify.completionListContains("func", "function func(): void", /*documentation*/ undefined, "function");
goTo.marker('2');
verify.completionListContains("func", "function func(): void", /*documentation*/ undefined, "function");
verify.completionListContains("func", "function func(): void", /*documentation*/ undefined, "function");
@@ -15,8 +15,9 @@
const ranges = test.ranges();
const [r0, r1, r2, r3, r4] = ranges;
const fnRanges = [r0, r1, r2, r3];
verify.singleReferenceGroup("function DefaultExportedFunction(): () => typeof DefaultExportedFunction", fnRanges);
const fn = "function DefaultExportedFunction(): () => typeof DefaultExportedFunction";
verify.singleReferenceGroup(fn, fnRanges);
// The namespace and function do not merge,
// so the namespace should be all alone.
verify.singleReferenceGroup("namespace DefaultExportedFunction", [r4]);
verify.singleReferenceGroup(`namespace DefaultExportedFunction\n${fn}`, [r4]);
@@ -10,6 +10,8 @@
////namespace [|{| "isWriteAccess": true, "isDefinition": true |}DefaultExportedClass|] {
////}
verify.noErrors();
// The namespace and class do not merge,
// so the namespace should be all alone.
verify.singleReferenceGroup("namespace DefaultExportedClass");
verify.singleReferenceGroup("class DefaultExportedClass\nnamespace DefaultExportedClass");
+5 -1
View File
@@ -6,4 +6,8 @@
goTo.marker();
edit.insertLine("");
verify.currentLineContentIs('class bar {');
verify.currentFileContentIs(
`class foo { }
class bar {
}
// new line here`);
+2
View File
@@ -116,6 +116,7 @@ declare namespace FourSlashInterface {
ranges(): Range[];
rangesByText(): ts.Map<Range[]>;
markerByName(s: string): Marker;
symbolsInScope(range: Range): any[];
}
class goTo {
marker(name?: string | Marker): void;
@@ -192,6 +193,7 @@ declare namespace FourSlashInterface {
verifyGetEmitOutputContentsForCurrentFile(expected: ts.OutputFile[]): void;
noReferences(markerNameOrRange?: string | Range): void;
symbolAtLocation(startRange: Range, ...declarationRanges: Range[]): void;
typeOfSymbolAtLocation(range: Range, symbol: any, expected: string): void;
/**
* @deprecated, prefer 'referenceGroups'
* Like `referencesAre`, but goes to `start` first.
@@ -7,7 +7,7 @@
//// var num: number;
//// var str: string;
//// if (typeof /*1*/nonExportedStrOrNum === "number") {
//// num = /*2*/nonExportedStrOrNum;
//// num = /*2*/nonExportedStrOrNum;
//// }
//// else {
//// str = /*3*/nonExportedStrOrNum.length;
@@ -40,15 +40,15 @@ verify.completionListContains("nonExportedStrOrNum", "var nonExportedStrOrNum: s
goTo.marker('4');
verify.quickInfoIs('var m.exportedStrOrNum: string | number');
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
verify.completionListContains("exportedStrOrNum", "var exportedStrOrNum: string | number");
goTo.marker('5');
verify.quickInfoIs('var m.exportedStrOrNum: number');
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: number");
verify.completionListContains("exportedStrOrNum", "var exportedStrOrNum: number");
goTo.marker('6');
verify.quickInfoIs('var m.exportedStrOrNum: string');
verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string");
verify.completionListContains("exportedStrOrNum", "var exportedStrOrNum: string");
goTo.marker('7');
verify.quickInfoIs('var m.exportedStrOrNum: string | number');
@@ -0,0 +1,12 @@
/// <reference path='fourslash.ts'/>
////export function f() {}
////[|1|];
const ranges = test.ranges();
const symbolsInScope = test.symbolsInScope(ranges[0]);
const f = symbolsInScope.find(s => s.name === "f");
if (f === undefined) throw new Error("'f' not in scope");
if (f.exportSymbol === undefined) throw new Error("Expected to get the local symbol");
verify.typeOfSymbolAtLocation(ranges[0], f, "() => void");
@@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
// @noUnusedLocals: true
// @Filename: file2.ts
//// [| import A, { x } from './a'; |]
//// console.log(A);
// @Filename: file1.ts
//// export default 10;
//// export var x = 10;
verify.rangeAfterCodeFix("import A from './a';");
@@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
// @noUnusedLocals: true
// @Filename: file2.ts
//// [| import /* 1 */ A /* 2 */, /* 3 */ { /* 4 */ x /* 5 */ } /* 6 */ from './a'; |]
//// console.log(A);
// @Filename: file1.ts
//// export default 10;
//// export var x = 10;
// It's ambiguous which token comment /* 6 */ applies to or whether it should be removed.
// In the current implementation the comment is left behind, but this behavior isn't a requirement.
verify.rangeAfterCodeFix("import /* 1 */ A /* 2 */ /* 6 */ from './a';");