Merge branch 'master' into referencesDogfood_1

This commit is contained in:
Ryan Cavanaugh
2018-04-11 16:17:38 -07:00
120 changed files with 3082 additions and 1867 deletions
+19 -7
View File
@@ -4170,14 +4170,17 @@ namespace ts {
else {
// Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form)
const name = declaration.propertyName || <Identifier>declaration.name;
if (isComputedNonLiteralName(name)) {
// computed properties with non-literal names are treated as 'any'
const isLate = isLateBindableName(name);
const isWellKnown = isComputedPropertyName(name) && isWellKnownSymbolSyntactically(name.expression);
if (!isLate && !isWellKnown && isComputedNonLiteralName(name)) {
return anyType;
}
// Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature,
// or otherwise the type of the string index signature.
const text = getTextOfPropertyName(name);
const text = isLate ? getLateBoundNameFromType(checkComputedPropertyName(name as ComputedPropertyName) as LiteralType | UniqueESSymbolType) :
isWellKnown ? getPropertyNameForKnownSymbolName(idText(((name as ComputedPropertyName).expression as PropertyAccessExpression).name)) :
getTextOfPropertyName(name);
// Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation
if (strictNullChecks && declaration.flags & NodeFlags.Ambient && isParameterDeclaration(declaration)) {
@@ -4363,7 +4366,7 @@ namespace ts {
for (const declaration of symbol.declarations) {
let declarationInConstructor = false;
const expression = declaration.kind === SyntaxKind.BinaryExpression ? <BinaryExpression>declaration :
declaration.kind === SyntaxKind.PropertyAccessExpression ? <BinaryExpression>getAncestor(declaration, SyntaxKind.BinaryExpression) :
declaration.kind === SyntaxKind.PropertyAccessExpression ? cast(declaration.parent, isBinaryExpression) :
undefined;
if (!expression) {
@@ -13875,7 +13878,8 @@ namespace ts {
const assignmentKind = getAssignmentTargetKind(node);
if (assignmentKind) {
if (!(localOrExportSymbol.flags & SymbolFlags.Variable)) {
if (!(localOrExportSymbol.flags & SymbolFlags.Variable) &&
!(isInJavaScriptFile(node) && localOrExportSymbol.flags & SymbolFlags.ValueModule)) {
error(node, Diagnostics.Cannot_assign_to_0_because_it_is_not_a_variable, symbolToString(symbol));
return unknownType;
}
@@ -19086,6 +19090,9 @@ namespace ts {
const links = getNodeLinks(node);
const type = getTypeOfSymbol(node.symbol);
if (isTypeAny(type)) {
return type;
}
// Check if function expression is contextually typed and assign parameter types if so.
if (!(links.flags & NodeCheckFlags.ContextChecked)) {
@@ -19848,8 +19855,9 @@ namespace ts {
// VarExpr = ValueExpr
// requires VarExpr to be classified as a reference
// A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1)
// and the type of the non - compound operation to be assignable to the type of VarExpr.
if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)) {
// and the type of the non-compound operation to be assignable to the type of VarExpr.
if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)
&& (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) {
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
checkTypeAssignableTo(valueType, leftType, left, /*headMessage*/ undefined);
}
@@ -21867,6 +21875,10 @@ namespace ts {
// and give a better error message when the host function mentions `arguments`
// but the tag doesn't have an array type
if (decl) {
const i = getJSDocTags(decl).filter(isJSDocParameterTag).indexOf(node);
if (i > -1 && i < decl.parameters.length && isBindingPattern(decl.parameters[i].name)) {
return;
}
if (!containsArgumentsReference(decl)) {
error(node.name,
Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name,
+7 -6
View File
@@ -1265,10 +1265,7 @@ namespace ts {
});
}
export function assign<T1 extends MapLike<{}>, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3;
export function assign<T1 extends MapLike<{}>, T2>(t: T1, arg1: T2): T1 & T2;
export function assign<T1 extends MapLike<{}>>(t: T1, ...args: any[]): any;
export function assign<T1 extends MapLike<{}>>(t: T1, ...args: any[]) {
export function assign<T extends object>(t: T, ...args: T[]) {
for (const arg of args) {
for (const p in arg) {
if (hasProperty(arg, p)) {
@@ -1724,6 +1721,10 @@ namespace ts {
return compareComparableValues(a, b);
}
export function min<T>(a: T, b: T, compare: Comparer<T>): T {
return compare(a, b) === Comparison.LessThan ? a : b;
}
/**
* Compare two strings using a case-insensitive ordinal comparison.
*
@@ -3119,8 +3120,8 @@ namespace ts {
return (arg: T) => f(arg) && g(arg);
}
export function or<T>(f: (arg: T) => boolean, g: (arg: T) => boolean) {
return (arg: T) => f(arg) || g(arg);
export function or<T>(f: (arg: T) => boolean, g: (arg: T) => boolean): (arg: T) => boolean {
return arg => f(arg) || g(arg);
}
export function assertTypeIsNever(_: never): void { } // tslint:disable-line no-empty
+4
View File
@@ -4161,5 +4161,9 @@
"Convert all constructor functions to classes": {
"category": "Message",
"code": 95045
},
"Generate 'get' and 'set' accessors": {
"category": "Message",
"code": 95046
}
}
+8 -6
View File
@@ -7600,7 +7600,7 @@ namespace ts {
const tripleSlashXMLCommentStartRegEx = /^\/\/\/\s*<(\S+)\s.*?\/>/im;
const singleLinePragmaRegEx = /^\/\/\/?\s*@(\S+)\s*(.*)\s*$/im;
function extractPragmas(pragmas: PragmaPsuedoMapEntry[], range: CommentRange, text: string) {
const tripleSlash = tripleSlashXMLCommentStartRegEx.exec(text);
const tripleSlash = range.kind === SyntaxKind.SingleLineCommentTrivia && tripleSlashXMLCommentStartRegEx.exec(text);
if (tripleSlash) {
const name = tripleSlash[1].toLowerCase() as keyof PragmaPsuedoMap; // Technically unsafe cast, but we do it so the below check to make it safe typechecks
const pragma = commentPragmas[name] as PragmaDefinition;
@@ -7637,15 +7637,17 @@ namespace ts {
return;
}
const singleLine = singleLinePragmaRegEx.exec(text);
const singleLine = range.kind === SyntaxKind.SingleLineCommentTrivia && singleLinePragmaRegEx.exec(text);
if (singleLine) {
return addPragmaForMatch(pragmas, range, PragmaKindFlags.SingleLine, singleLine);
}
const multiLinePragmaRegEx = /\s*@(\S+)\s*(.*)\s*$/gim; // Defined inline since it uses the "g" flag, which keeps a persistent index (for iterating)
let multiLineMatch: RegExpExecArray;
while (multiLineMatch = multiLinePragmaRegEx.exec(text)) {
addPragmaForMatch(pragmas, range, PragmaKindFlags.MultiLine, multiLineMatch);
if (range.kind === SyntaxKind.MultiLineCommentTrivia) {
const multiLinePragmaRegEx = /\s*@(\S+)\s*(.*)\s*$/gim; // Defined inline since it uses the "g" flag, which keeps a persistent index (for iterating)
let multiLineMatch: RegExpExecArray;
while (multiLineMatch = multiLinePragmaRegEx.exec(text)) {
addPragmaForMatch(pragmas, range, PragmaKindFlags.MultiLine, multiLineMatch);
}
}
}
-1
View File
@@ -573,7 +573,6 @@ namespace ts {
const packageIdToSourceFile = createMap<SourceFile>();
// Maps from a SourceFile's `.path` to the name of the package it was imported with.
let sourceFileToPackageName = createMap<string>();
// See `sourceFileIsRedirectedTo`.
let redirectTargetsSet = createMap<true>();
const filesByName = createMap<SourceFile | undefined>();
+3 -3
View File
@@ -18,7 +18,7 @@ namespace ts {
}
let reportDiagnostic = createDiagnosticReporter(sys);
function udpateReportDiagnostic(options: CompilerOptions) {
function updateReportDiagnostic(options: CompilerOptions) {
if (options.pretty) {
reportDiagnostic = createDiagnosticReporter(sys, /*pretty*/ true);
}
@@ -107,7 +107,7 @@ namespace ts {
const commandLineOptions = commandLine.options;
if (configFileName) {
const configParseResult = parseConfigFileWithSystem(configFileName, commandLineOptions, sys, reportDiagnostic);
udpateReportDiagnostic(configParseResult.options);
updateReportDiagnostic(configParseResult.options);
if (isWatchSet(configParseResult.options)) {
reportWatchModeWithoutSysSupport();
createWatchOfConfigFile(configParseResult, commandLineOptions);
@@ -117,7 +117,7 @@ namespace ts {
}
}
else {
udpateReportDiagnostic(commandLineOptions);
updateReportDiagnostic(commandLineOptions);
if (isWatchSet(commandLineOptions)) {
reportWatchModeWithoutSysSupport();
createWatchOfFilesAndCompilerOptions(commandLine.fileNames, commandLineOptions);
+1 -1
View File
@@ -2527,7 +2527,7 @@ namespace ts {
/**
* If two source files are for the same version of the same package, one will redirect to the other.
* (See `createRedirectSourceFile` in program.ts.)
* The redirect will have this set. The other will not have anything set, but see Program#sourceFileIsRedirectedTo.
* The redirect will have this set. The redirected-to source file will be in `redirectTargetsSet`.
*/
/* @internal */ redirectInfo?: RedirectInfo | undefined;
+15 -5
View File
@@ -4271,7 +4271,7 @@ namespace ts {
}
}
export function isParameterPropertyDeclaration(node: Node): boolean {
export function isParameterPropertyDeclaration(node: Node): node is ParameterDeclaration {
return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
}
@@ -4630,11 +4630,21 @@ namespace ts {
* parameters by name and binding patterns do not have a name.
*/
export function getJSDocParameterTags(param: ParameterDeclaration): ReadonlyArray<JSDocParameterTag> {
if (param.name && isIdentifier(param.name)) {
const name = param.name.escapedText;
return getJSDocTags(param.parent).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.escapedText === name);
if (param.name) {
if (isIdentifier(param.name)) {
const name = param.name.escapedText;
return getJSDocTags(param.parent).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.escapedText === name);
}
else {
const i = param.parent.parameters.indexOf(param);
Debug.assert(i > -1, "Parameters should always be in their parents' parameter list");
const paramTags = getJSDocTags(param.parent).filter(isJSDocParameterTag);
if (i < paramTags.length) {
return [paramTags[i]];
}
}
}
// a binding pattern doesn't have a name, so it's not possible to match it a JSDoc parameter, which is identified by name
// return empty array for: out-of-order binding patterns and JSDoc function syntax, which has un-named parameters
return emptyArray;
}
+17 -1
View File
@@ -1081,8 +1081,20 @@ namespace FourSlash {
}
}
private verifyDocumentHighlightsRespectFilesList(files: ReadonlyArray<string>): void {
const startFile = this.activeFile.fileName;
for (const fileName of files) {
const searchFileNames = startFile === fileName ? [startFile] : [startFile, fileName];
const highlights = this.getDocumentHighlightsAtCurrentPosition(searchFileNames);
if (!highlights.every(dh => ts.contains(searchFileNames, dh.fileName))) {
this.raiseError(`When asking for document highlights only in files ${searchFileNames}, got document highlights in ${unique(highlights, dh => dh.fileName)}`);
}
}
}
public verifyReferencesOf(range: Range, references: Range[]) {
this.goToRangeStart(range);
this.verifyDocumentHighlightsRespectFilesList(unique(references, e => e.fileName));
this.verifyReferencesAre(references);
}
@@ -1094,7 +1106,7 @@ namespace FourSlash {
}
}
public verifyReferenceGroups(starts: string | string[] | Range | Range[], parts: FourSlashInterface.ReferenceGroup[]): void {
public verifyReferenceGroups(starts: string | string[] | Range | Range[], parts: FourSlashInterface.ReferenceGroup[] | undefined): void {
interface ReferenceGroupJson {
definition: string | { text: string, range: ts.TextSpan };
references: ts.ReferenceEntry[];
@@ -1128,6 +1140,10 @@ namespace FourSlash {
};
});
this.assertObjectsEqual(fullActual, fullExpected);
if (parts) {
this.verifyDocumentHighlightsRespectFilesList(unique(ts.flatMap(parts, p => p.ranges), r => r.fileName));
}
}
}
+70 -212
View File
@@ -95,376 +95,245 @@ describe("PatternMatcher", () => {
describe("SingleWordPattern", () => {
it("PreferCaseSensitiveExact", () => {
const match = getFirstMatch("Foo", "Foo");
assert.equal(ts.PatternMatchKind.exact, match.kind);
assert.equal(true, match.isCaseSensitive);
assertSegmentMatch("Foo", "Foo", { kind: ts.PatternMatchKind.exact, isCaseSensitive: true });
});
it("PreferCaseSensitiveExactInsensitive", () => {
const match = getFirstMatch("foo", "Foo");
assert.equal(ts.PatternMatchKind.exact, match.kind);
assert.equal(false, match.isCaseSensitive);
assertSegmentMatch("foo", "Foo", { kind: ts.PatternMatchKind.exact, isCaseSensitive: false });
});
it("PreferCaseSensitivePrefix", () => {
const match = getFirstMatch("Foo", "Fo");
assert.equal(ts.PatternMatchKind.prefix, match.kind);
assert.equal(true, match.isCaseSensitive);
assertSegmentMatch("Foo", "Fo", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
});
it("PreferCaseSensitivePrefixCaseInsensitive", () => {
const match = getFirstMatch("Foo", "fo");
assert.equal(ts.PatternMatchKind.prefix, match.kind);
assert.equal(false, match.isCaseSensitive);
assertSegmentMatch("Foo", "fo", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: false });
});
it("PreferCaseSensitiveCamelCaseMatchSimple", () => {
const match = getFirstMatch("FogBar", "FB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
assertSegmentMatch("FogBar", "FB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
});
it("PreferCaseSensitiveCamelCaseMatchPartialPattern", () => {
const match = getFirstMatch("FogBar", "FoB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
assertSegmentMatch("FogBar", "FoB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
});
it("PreferCaseSensitiveCamelCaseMatchToLongPattern1", () => {
const match = getFirstMatch("FogBar", "FBB");
assert.isTrue(match === undefined);
assertSegmentMatch("FogBar", "FBB", undefined);
});
it("PreferCaseSensitiveCamelCaseMatchToLongPattern2", () => {
const match = getFirstMatch("FogBar", "FoooB");
assert.isTrue(match === undefined);
assertSegmentMatch("FogBar", "FoooB", undefined);
});
it("CamelCaseMatchPartiallyUnmatched", () => {
const match = getFirstMatch("FogBarBaz", "FZ");
assert.isTrue(match === undefined);
assertSegmentMatch("FogBarBaz", "FZ", undefined);
});
it("CamelCaseMatchCompletelyUnmatched", () => {
const match = getFirstMatch("FogBarBaz", "ZZ");
assert.isTrue(match === undefined);
assertSegmentMatch("FogBarBaz", "ZZ", undefined);
});
it("TwoUppercaseCharacters", () => {
const match = getFirstMatch("SimpleUIElement", "SiUI");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
assertSegmentMatch("SimpleUIElement", "SiUI", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
});
it("PreferCaseSensitiveLowercasePattern", () => {
const match = getFirstMatch("FogBar", "b");
assert.equal(ts.PatternMatchKind.substring, match.kind);
assert.equal(false, match.isCaseSensitive);
assertSegmentMatch("FogBar", "b", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
});
it("PreferCaseSensitiveLowercasePattern2", () => {
const match = getFirstMatch("FogBar", "fB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(false, match.isCaseSensitive);
assertSegmentMatch("FogBar", "fB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: false });
});
it("PreferCaseSensitiveTryUnderscoredName", () => {
const match = getFirstMatch("_fogBar", "_fB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
assertSegmentMatch("_fogBar", "_fB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
});
it("PreferCaseSensitiveTryUnderscoredName2", () => {
const match = getFirstMatch("_fogBar", "fB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
assertSegmentMatch("_fogBar", "fB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
});
it("PreferCaseSensitiveTryUnderscoredNameInsensitive", () => {
const match = getFirstMatch("_FogBar", "_fB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(false, match.isCaseSensitive);
assertSegmentMatch("_FogBar", "_fB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: false });
});
it("PreferCaseSensitiveMiddleUnderscore", () => {
const match = getFirstMatch("Fog_Bar", "FB");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
assertSegmentMatch("Fog_Bar", "FB", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
});
it("PreferCaseSensitiveMiddleUnderscore2", () => {
const match = getFirstMatch("Fog_Bar", "F_B");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(true, match.isCaseSensitive);
assertSegmentMatch("Fog_Bar", "F_B", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
});
it("PreferCaseSensitiveMiddleUnderscore3", () => {
const match = getFirstMatch("Fog_Bar", "F__B");
assert.isTrue(undefined === match);
assertSegmentMatch("Fog_Bar", "F__B", undefined);
});
it("PreferCaseSensitiveMiddleUnderscore4", () => {
const match = getFirstMatch("Fog_Bar", "f_B");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(false, match.isCaseSensitive);
assertSegmentMatch("Fog_Bar", "f_B", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: false });
});
it("PreferCaseSensitiveMiddleUnderscore5", () => {
const match = getFirstMatch("Fog_Bar", "F_b");
assert.equal(ts.PatternMatchKind.camelCase, match.kind);
assert.equal(false, match.isCaseSensitive);
assertSegmentMatch("Fog_Bar", "F_b", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: false });
});
it("AllLowerPattern1", () => {
const match = getFirstMatch("FogBarChangedEventArgs", "changedeventargs");
assert.isTrue(undefined !== match);
assertSegmentMatch("FogBarChangedEventArgs", "changedeventargs", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
});
it("AllLowerPattern2", () => {
const match = getFirstMatch("FogBarChangedEventArgs", "changedeventarrrgh");
assert.isTrue(undefined === match);
assertSegmentMatch("FogBarChangedEventArgs", "changedeventarrrgh", undefined);
});
it("AllLowerPattern3", () => {
const match = getFirstMatch("ABCDEFGH", "bcd");
assert.isTrue(undefined !== match);
assertSegmentMatch("ABCDEFGH", "bcd", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
});
it("AllLowerPattern4", () => {
const match = getFirstMatch("AbcdefghijEfgHij", "efghij");
assert.isTrue(undefined === match);
assertSegmentMatch("AbcdefghijEfgHij", "efghij", undefined);
});
});
describe("MultiWordPattern", () => {
it("ExactWithLowercase", () => {
const matches = getAllMatches("AddMetadataReference", "addmetadatareference");
assertContainsKind(ts.PatternMatchKind.exact, matches);
assertSegmentMatch("AddMetadataReference", "addmetadatareference", { kind: ts.PatternMatchKind.exact, isCaseSensitive: false });
});
it("SingleLowercasedSearchWord1", () => {
const matches = getAllMatches("AddMetadataReference", "add");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertSegmentMatch("AddMetadataReference", "add", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: false });
});
it("SingleLowercasedSearchWord2", () => {
const matches = getAllMatches("AddMetadataReference", "metadata");
assertContainsKind(ts.PatternMatchKind.substring, matches);
assertSegmentMatch("AddMetadataReference", "metadata", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
});
it("SingleUppercaseSearchWord1", () => {
const matches = getAllMatches("AddMetadataReference", "Add");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertSegmentMatch("AddMetadataReference", "Add", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
});
it("SingleUppercaseSearchWord2", () => {
const matches = getAllMatches("AddMetadataReference", "Metadata");
assertContainsKind(ts.PatternMatchKind.substring, matches);
assertSegmentMatch("AddMetadataReference", "Metadata", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
});
it("SingleUppercaseSearchLetter1", () => {
const matches = getAllMatches("AddMetadataReference", "A");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertSegmentMatch("AddMetadataReference", "A", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
});
it("SingleUppercaseSearchLetter2", () => {
const matches = getAllMatches("AddMetadataReference", "M");
assertContainsKind(ts.PatternMatchKind.substring, matches);
assertSegmentMatch("AddMetadataReference", "M", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "add metadata");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
it("TwoLowercaseWords0", () => {
assertSegmentMatch("AddMetadataReference", "add metadata", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: false });
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "A M");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
it("TwoLowercaseWords1", () => {
assertSegmentMatch("AddMetadataReference", "A M", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "AM");
assertContainsKind(ts.PatternMatchKind.camelCase, matches);
it("TwoLowercaseWords2", () => {
assertSegmentMatch("AddMetadataReference", "AM", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "ref Metadata");
assertArrayEquals(ts.map(matches, m => m.kind), [ts.PatternMatchKind.substring, ts.PatternMatchKind.substring]);
it("TwoLowercaseWords3", () => {
assertSegmentMatch("AddMetadataReference", "ref Metadata", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
});
it("TwoLowercaseWords", () => {
const matches = getAllMatches("AddMetadataReference", "ref M");
assertArrayEquals(ts.map(matches, m => m.kind), [ts.PatternMatchKind.substring, ts.PatternMatchKind.substring]);
it("TwoLowercaseWords4", () => {
assertSegmentMatch("AddMetadataReference", "ref M", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
});
it("MixedCamelCase", () => {
const matches = getAllMatches("AddMetadataReference", "AMRe");
assertContainsKind(ts.PatternMatchKind.camelCase, matches);
assertSegmentMatch("AddMetadataReference", "AMRe", { kind: ts.PatternMatchKind.camelCase, isCaseSensitive: true });
});
it("BlankPattern", () => {
const matches = getAllMatches("AddMetadataReference", "");
assert.isTrue(matches === undefined);
assertInvalidPattern("");
});
it("WhitespaceOnlyPattern", () => {
const matches = getAllMatches("AddMetadataReference", " ");
assert.isTrue(matches === undefined);
assertInvalidPattern(" ");
});
it("EachWordSeparately1", () => {
const matches = getAllMatches("AddMetadataReference", "add Meta");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
assertSegmentMatch("AddMetadataReference", "add Meta", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: false });
});
it("EachWordSeparately2", () => {
const matches = getAllMatches("AddMetadataReference", "Add meta");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
assertSegmentMatch("AddMetadataReference", "Add meta", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
});
it("EachWordSeparately3", () => {
const matches = getAllMatches("AddMetadataReference", "Add Meta");
assertContainsKind(ts.PatternMatchKind.prefix, matches);
assertContainsKind(ts.PatternMatchKind.substring, matches);
assertSegmentMatch("AddMetadataReference", "Add Meta", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
});
it("MixedCasing", () => {
const matches = getAllMatches("AddMetadataReference", "mEta");
assert.isTrue(matches === undefined);
assertSegmentMatch("AddMetadataReference", "mEta", undefined);
});
it("MixedCasing2", () => {
const matches = getAllMatches("AddMetadataReference", "Data");
assert.isTrue(matches === undefined);
assertSegmentMatch("AddMetadataReference", "Data", undefined);
});
it("AsteriskSplit", () => {
const matches = getAllMatches("GetKeyWord", "K*W");
assertArrayEquals(ts.map(matches, m => m.kind), [ts.PatternMatchKind.substring, ts.PatternMatchKind.substring]);
assertSegmentMatch("GetKeyWord", "K*W", { kind: ts.PatternMatchKind.substring, isCaseSensitive: true });
});
it("LowercaseSubstring1", () => {
const matches = getAllMatches("Operator", "a");
assert.isTrue(matches === undefined);
assertSegmentMatch("Operator", "a", undefined);
});
it("LowercaseSubstring2", () => {
const matches = getAllMatches("FooAttribute", "a");
assertContainsKind(ts.PatternMatchKind.substring, matches);
assert.isFalse(matches[0].isCaseSensitive);
assertSegmentMatch("FooAttribute", "a", { kind: ts.PatternMatchKind.substring, isCaseSensitive: false });
});
});
describe("DottedPattern", () => {
it("DottedPattern1", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "B.Q");
assert.equal(ts.PatternMatchKind.prefix, match.kind);
assert.equal(true, match.isCaseSensitive);
assertFullMatch("Foo.Bar.Baz", "Quux", "B.Q", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
});
it("DottedPattern2", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "C.Q");
assert.isTrue(match === undefined);
assertFullMatch("Foo.Bar.Baz", "Quux", "C.Q", undefined);
});
it("DottedPattern3", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "B.B.Q");
assert.equal(ts.PatternMatchKind.prefix, match.kind);
assert.equal(true, match.isCaseSensitive);
assertFullMatch("Foo.Bar.Baz", "Quux", "B.B.Q", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
});
it("DottedPattern4", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "Baz.Quux");
assert.equal(ts.PatternMatchKind.exact, match.kind);
assert.equal(true, match.isCaseSensitive);
assertFullMatch("Foo.Bar.Baz", "Quux", "Baz.Quux", { kind: ts.PatternMatchKind.exact, isCaseSensitive: true });
});
it("DottedPattern5", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "F.B.B.Quux");
assert.equal(ts.PatternMatchKind.exact, match.kind);
assert.equal(true, match.isCaseSensitive);
assertFullMatch("Foo.Bar.Baz", "Quux", "F.B.B.Quux", { kind: ts.PatternMatchKind.prefix, isCaseSensitive: true });
});
it("DottedPattern6", () => {
const match = getFirstMatchForDottedPattern("Foo.Bar.Baz", "Quux", "F.F.B.B.Quux");
assert.isTrue(match === undefined);
assertFullMatch("Foo.Bar.Baz", "Quux", "F.F.B.B.Quux", undefined);
});
it("DottedPattern7", () => {
let match = getFirstMatch("UIElement", "UIElement");
match = getFirstMatch("GetKeyword", "UIElement");
assert.isTrue(match === undefined);
assertSegmentMatch("UIElement", "UIElement", { kind: ts.PatternMatchKind.exact, isCaseSensitive: true });
assertSegmentMatch("GetKeyword", "UIElement", undefined);
});
});
function getFirstMatch(candidate: string, pattern: string): ts.PatternMatch {
const matches = ts.createPatternMatcher(pattern).getMatchesForLastSegmentOfPattern(candidate);
return matches ? matches[0] : undefined;
function assertSegmentMatch(candidate: string, pattern: string, expected: ts.PatternMatch | undefined): void {
assert.deepEqual(ts.createPatternMatcher(pattern).getMatchForLastSegmentOfPattern(candidate), expected);
}
function getAllMatches(candidate: string, pattern: string): ts.PatternMatch[] {
return ts.createPatternMatcher(pattern).getMatchesForLastSegmentOfPattern(candidate);
function assertInvalidPattern(pattern: string) {
assert.equal(ts.createPatternMatcher(pattern), undefined);
}
function getFirstMatchForDottedPattern(dottedContainer: string, candidate: string, pattern: string): ts.PatternMatch {
const matches = ts.createPatternMatcher(pattern).getMatches(dottedContainer.split("."), candidate);
return matches ? matches[0] : undefined;
function assertFullMatch(dottedContainer: string, candidate: string, pattern: string, expected: ts.PatternMatch | undefined): void {
assert.deepEqual(ts.createPatternMatcher(pattern).getFullMatch(dottedContainer.split("."), candidate), expected);
}
function spanListToSubstrings(identifier: string, spans: ts.TextSpan[]) {
return ts.map(spans, s => identifier.substr(s.start, s.length));
return spans.map(s => identifier.substr(s.start, s.length));
}
function breakIntoCharacterSpans(identifier: string) {
@@ -474,23 +343,12 @@ describe("PatternMatcher", () => {
function breakIntoWordSpans(identifier: string) {
return spanListToSubstrings(identifier, ts.breakIntoWordSpans(identifier));
}
function assertArrayEquals<T>(array1: T[], array2: T[]) {
assert.equal(array1.length, array2.length);
for (let i = 0; i < array1.length; i++) {
assert.equal(array1[i], array2[i]);
}
}
function verifyBreakIntoCharacterSpans(original: string, ...parts: string[]): void {
assertArrayEquals(parts, breakIntoCharacterSpans(original));
assert.deepEqual(parts, breakIntoCharacterSpans(original));
}
function verifyBreakIntoWordSpans(original: string, ...parts: string[]): void {
assertArrayEquals(parts, breakIntoWordSpans(original));
}
function assertContainsKind(kind: ts.PatternMatchKind, results: ts.PatternMatch[]) {
assert.isTrue(ts.forEach(results, r => r.kind === kind));
assert.deepEqual(parts, breakIntoWordSpans(original));
}
});
@@ -4184,6 +4184,57 @@ namespace ts.projectSystem {
session.clearMessages();
});
it("disable suggestion diagnostics", () => {
const file: FileOrFolder = {
path: "/a.js",
content: 'require("b")',
};
const host = createServerHost([file]);
const session = createSession(host, { canUseEvents: true });
const service = session.getProjectService();
session.executeCommandSeq<protocol.OpenRequest>({
command: server.CommandNames.Open,
arguments: { file: file.path, fileContent: file.content },
});
session.executeCommandSeq<protocol.ConfigureRequest>({
command: server.CommandNames.Configure,
arguments: {
preferences: { disableSuggestions: true }
},
});
checkNumberOfProjects(service, { inferredProjects: 1 });
session.clearMessages();
const expectedSequenceId = session.getNextSeq();
host.checkTimeoutQueueLengthAndRun(2);
checkProjectUpdatedInBackgroundEvent(session, [file.path]);
session.clearMessages();
session.executeCommandSeq<protocol.GeterrRequest>({
command: server.CommandNames.Geterr,
arguments: {
delay: 0,
files: [file.path],
}
});
host.checkTimeoutQueueLengthAndRun(1);
checkErrorMessage(session, "syntaxDiag", { file: file.path, diagnostics: [] }, /*isMostRecent*/ true);
session.clearMessages();
host.runQueuedImmediateCallbacks(1);
checkErrorMessage(session, "semanticDiag", { file: file.path, diagnostics: [] });
// No suggestion event, we're done.
checkCompleteEvent(session, 2, expectedSequenceId);
session.clearMessages();
});
it("suppressed diagnostic events", () => {
const file: FileOrFolder = {
path: "/a.ts",
@@ -3780,6 +3780,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_0_errors_6194" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found {0} errors.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Našel se tento počet chyb: {0}.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_1_error_6193" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 1 error.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Našla se 1 chyba.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_package_json_at_0_6099" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 'package.json' at '{0}'.]]></Val>
@@ -5997,6 +6015,12 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await_2570" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you forget to use 'await'?]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_mean_2_2551" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?]]></Val>
@@ -3768,6 +3768,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_0_errors_6194" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found {0} errors.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[{0} Fehler gefunden.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_1_error_6193" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 1 error.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[1 Fehler gefunden.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_package_json_at_0_6099" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 'package.json' at '{0}'.]]></Val>
@@ -5982,6 +6000,12 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await_2570" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you forget to use 'await'?]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_mean_2_2551" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?]]></Val>
@@ -3780,6 +3780,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_0_errors_6194" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found {0} errors.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Se encontró {0} errores.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_1_error_6193" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 1 error.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Se encontró 1 error.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_package_json_at_0_6099" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 'package.json' at '{0}'.]]></Val>
@@ -5997,6 +6015,12 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await_2570" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you forget to use 'await'?]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_mean_2_2551" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?]]></Val>
@@ -3780,6 +3780,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_0_errors_6194" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found {0} errors.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[{0} erreurs trouvées.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_1_error_6193" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 1 error.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[1 erreur trouvée.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_package_json_at_0_6099" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 'package.json' at '{0}'.]]></Val>
@@ -5997,6 +6015,12 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await_2570" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you forget to use 'await'?]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_mean_2_2551" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?]]></Val>
@@ -3771,6 +3771,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_0_errors_6194" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found {0} errors.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[{0} 件のエラーが見つかりました。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_1_error_6193" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 1 error.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[1 件のエラーが見つかりました。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_package_json_at_0_6099" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 'package.json' at '{0}'.]]></Val>
@@ -5988,6 +6006,12 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await_2570" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you forget to use 'await'?]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_mean_2_2551" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?]]></Val>
@@ -3771,6 +3771,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_0_errors_6194" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found {0} errors.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[{0}개 오류가 발견되었습니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_1_error_6193" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 1 error.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[1개 오류가 발견되었습니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_package_json_at_0_6099" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 'package.json' at '{0}'.]]></Val>
@@ -5988,6 +6006,12 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await_2570" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you forget to use 'await'?]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_mean_2_2551" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?]]></Val>
@@ -3761,6 +3761,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_0_errors_6194" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found {0} errors.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Encontrados {0} erros.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_1_error_6193" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 1 error.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Encontrado 1 erro.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_package_json_at_0_6099" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 'package.json' at '{0}'.]]></Val>
@@ -5975,6 +5993,12 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await_2570" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you forget to use 'await'?]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_mean_2_2551" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?]]></Val>
@@ -3770,6 +3770,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_0_errors_6194" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found {0} errors.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Найдено ошибок: {0}.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_1_error_6193" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 1 error.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Найдено ошибок: 1.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Found_package_json_at_0_6099" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Found 'package.json' at '{0}'.]]></Val>
@@ -5987,6 +6005,12 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await_2570" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you forget to use 'await'?]]></Val>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Property_0_does_not_exist_on_type_1_Did_you_mean_2_2551" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?]]></Val>
+2 -2
View File
@@ -1834,11 +1834,11 @@ namespace ts.server {
this.logger.info(`Host information ${args.hostInfo}`);
}
if (args.formatOptions) {
mergeMapLikes(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions));
this.hostConfiguration.formatCodeOptions = { ...this.hostConfiguration.formatCodeOptions, ...convertFormatOptions(args.formatOptions) };
this.logger.info("Format host information updated");
}
if (args.preferences) {
mergeMapLikes(this.hostConfiguration.preferences, args.preferences);
this.hostConfiguration.preferences = { ...this.hostConfiguration.preferences, ...args.preferences };
}
if (args.extraFileExtensions) {
this.hostConfiguration.extraFileExtensions = args.extraFileExtensions;
+1
View File
@@ -2640,6 +2640,7 @@ namespace ts.server.protocol {
}
export interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
/**
* If enabled, TypeScript will search through all external modules' exports and add them to the completions list.
+6 -3
View File
@@ -395,15 +395,18 @@ namespace ts.server {
if (formatSettings) {
if (!this.formatSettings) {
this.formatSettings = getDefaultFormatCodeSettings(this.host);
assign(this.formatSettings, formatSettings);
}
else {
this.formatSettings = { ...this.formatSettings, ...formatSettings };
}
mergeMapLikes(this.formatSettings, formatSettings);
}
if (preferences) {
if (!this.preferences) {
this.preferences = clone(defaultPreferences);
this.preferences = defaultPreferences;
}
mergeMapLikes(this.preferences, preferences);
this.preferences = { ...this.preferences, ...preferences };
}
}
+11 -3
View File
@@ -523,12 +523,20 @@ namespace ts.server {
return;
}
next.immediate(() => {
this.suggestionCheck(fileName, project);
const goNext = () => {
if (checkList.length > index) {
next.delay(followMs, checkOne);
}
});
};
if (this.getPreferences(fileName).disableSuggestions) {
goNext();
}
else {
next.immediate(() => {
this.suggestionCheck(fileName, project);
goNext();
});
}
});
};
-8
View File
@@ -80,14 +80,6 @@ namespace ts.server {
};
}
export function mergeMapLikes<T extends object>(target: T, source: Partial<T>): void {
for (const key in source) {
if (hasProperty(source, key)) {
target[key] = source[key];
}
}
}
export type NormalizedPath = string & { __normalizedPathTag: any };
export function toNormalizedPath(fileName: string): NormalizedPath {
+12 -2
View File
@@ -22,10 +22,20 @@ namespace ts.DocumentHighlights {
}
function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: ReadonlyArray<SourceFile>): DocumentHighlights[] | undefined {
const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken);
const sourceFilesSet = arrayToSet(sourceFilesToSearch, f => f.fileName);
const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken, /*options*/ undefined, sourceFilesSet);
if (!referenceEntries) return undefined;
const map = arrayToMultiMap(referenceEntries.map(FindAllReferences.toHighlightSpan), e => e.fileName, e => e.span);
return arrayFrom(map.entries(), ([fileName, highlightSpans]) => ({ fileName, highlightSpans }));
return arrayFrom(map.entries(), ([fileName, highlightSpans]) => {
if (!sourceFilesSet.has(fileName)) {
Debug.assert(program.redirectTargetsSet.has(fileName));
const redirectTarget = program.getSourceFile(fileName);
const redirect = find(sourceFilesToSearch, f => f.redirectInfo && f.redirectInfo.redirectTarget === redirectTarget)!;
fileName = redirect.fileName;
Debug.assert(sourceFilesSet.has(fileName));
}
return { fileName, highlightSpans };
});
}
function getSyntacticDocumentHighlights(node: Node, sourceFile: SourceFile): DocumentHighlights[] {
+16 -16
View File
@@ -41,7 +41,7 @@ namespace ts.FindAllReferences {
export function findReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined {
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, /*options*/ {});
const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken);
const checker = program.getTypeChecker();
return !referencedSymbols || !referencedSymbols.length ? undefined : mapDefined<SymbolAndEntries, ReferencedSymbol>(referencedSymbols, ({ definition, references }) =>
// Only include referenced symbols that have a valid definition.
@@ -86,8 +86,8 @@ namespace ts.FindAllReferences {
return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options)), toReferenceEntry);
}
export function getReferenceEntriesForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}): Entry[] | undefined {
return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options));
export function getReferenceEntriesForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlyMap<true> = arrayToSet(sourceFiles, f => f.fileName)): Entry[] | undefined {
return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options, sourceFilesSet));
}
function flattenEntries(referenceSymbols: SymbolAndEntries[]): Entry[] {
@@ -229,10 +229,10 @@ namespace ts.FindAllReferences {
/* @internal */
namespace ts.FindAllReferences.Core {
/** Core find-all-references algorithm. Handles special cases before delegating to `getReferencedSymbolsForSymbol`. */
export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}): SymbolAndEntries[] | undefined {
export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlyMap<true> = arrayToSet(sourceFiles, f => f.fileName)): SymbolAndEntries[] | undefined {
if (isSourceFile(node)) {
const reference = GoToDefinition.getReferenceAtPosition(node, position, program);
return reference && getReferencedSymbolsForModule(program, program.getTypeChecker().getMergedSymbol(reference.file.symbol), sourceFiles);
return reference && getReferencedSymbolsForModule(program, program.getTypeChecker().getMergedSymbol(reference.file.symbol), sourceFiles, sourceFilesSet);
}
if (!options.implementations) {
@@ -252,10 +252,10 @@ namespace ts.FindAllReferences.Core {
}
if (symbol.flags & SymbolFlags.Module && isModuleReferenceLocation(node)) {
return getReferencedSymbolsForModule(program, symbol, sourceFiles);
return getReferencedSymbolsForModule(program, symbol, sourceFiles, sourceFilesSet);
}
return getReferencedSymbolsForSymbol(symbol, node, sourceFiles, checker, cancellationToken, options);
return getReferencedSymbolsForSymbol(symbol, node, sourceFiles, sourceFilesSet, checker, cancellationToken, options);
}
function isModuleReferenceLocation(node: Node): boolean {
@@ -275,7 +275,7 @@ namespace ts.FindAllReferences.Core {
}
}
function getReferencedSymbolsForModule(program: Program, symbol: Symbol, sourceFiles: ReadonlyArray<SourceFile>): SymbolAndEntries[] {
function getReferencedSymbolsForModule(program: Program, symbol: Symbol, sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>): SymbolAndEntries[] {
Debug.assert(!!symbol.valueDeclaration);
const references = findModuleReferences(program, sourceFiles, symbol).map<Entry>(reference => {
@@ -297,7 +297,9 @@ namespace ts.FindAllReferences.Core {
// Don't include the source file itself. (This may not be ideal behavior, but awkward to include an entire file as a reference.)
break;
case SyntaxKind.ModuleDeclaration:
references.push({ type: "node", node: (decl as ModuleDeclaration).name });
if (sourceFilesSet.has(decl.getSourceFile().fileName)) {
references.push({ type: "node", node: (decl as ModuleDeclaration).name });
}
break;
default:
Debug.fail("Expected a module symbol to be declared by a SourceFile or ModuleDeclaration.");
@@ -337,14 +339,14 @@ namespace ts.FindAllReferences.Core {
}
/** Core find-all-references algorithm for a normal symbol. */
function getReferencedSymbolsForSymbol(symbol: Symbol, node: Node, sourceFiles: ReadonlyArray<SourceFile>, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
function getReferencedSymbolsForSymbol(symbol: Symbol, node: Node, sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
symbol = skipPastExportOrImportSpecifierOrUnion(symbol, node, checker) || symbol;
// Compute the meaning from the location and the symbol it references
const searchMeaning = getIntersectingMeaningFromDeclarations(node, symbol);
const result: SymbolAndEntries[] = [];
const state = new State(sourceFiles, getSpecialSearchKind(node), checker, cancellationToken, searchMeaning, options, result);
const state = new State(sourceFiles, sourceFilesSet, getSpecialSearchKind(node), checker, cancellationToken, searchMeaning, options, result);
if (node.kind === SyntaxKind.DefaultKeyword) {
addReference(node, symbol, state);
@@ -467,10 +469,9 @@ namespace ts.FindAllReferences.Core {
*/
readonly markSeenReExportRHS = nodeSeenTracker();
private readonly includedSourceFiles: Map<true>;
constructor(
readonly sourceFiles: ReadonlyArray<SourceFile>,
readonly sourceFilesSet: ReadonlyMap<true>,
/** True if we're searching for constructor references. */
readonly specialSearchKind: SpecialSearchKind,
readonly checker: TypeChecker,
@@ -478,17 +479,16 @@ namespace ts.FindAllReferences.Core {
readonly searchMeaning: SemanticMeaning,
readonly options: Options,
private readonly result: Push<SymbolAndEntries>) {
this.includedSourceFiles = arrayToSet(sourceFiles, s => s.fileName);
}
includesSourceFile(sourceFile: SourceFile): boolean {
return this.includedSourceFiles.has(sourceFile.fileName);
return this.sourceFilesSet.has(sourceFile.fileName);
}
private importTracker: ImportTracker | undefined;
/** Gets every place to look for references of an exported symbols. See `ImportsResult` in `importTracker.ts` for more documentation. */
getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult {
if (!this.importTracker) this.importTracker = createImportTracker(this.sourceFiles, this.checker, this.cancellationToken);
if (!this.importTracker) this.importTracker = createImportTracker(this.sourceFiles, this.sourceFilesSet, this.checker, this.cancellationToken);
return this.importTracker(exportSymbol, exportInfo, this.options.isForRename);
}
+4 -3
View File
@@ -12,10 +12,10 @@ namespace ts.FindAllReferences {
export type ImportTracker = (exportSymbol: Symbol, exportInfo: ExportInfo, isForRename: boolean) => ImportsResult;
/** Creates the imports map and returns an ImportTracker that uses it. Call this lazily to avoid calling `getDirectImportsMap` unnecessarily. */
export function createImportTracker(sourceFiles: ReadonlyArray<SourceFile>, checker: TypeChecker, cancellationToken: CancellationToken): ImportTracker {
export function createImportTracker(sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>, checker: TypeChecker, cancellationToken: CancellationToken): ImportTracker {
const allDirectImports = getDirectImportsMap(sourceFiles, checker, cancellationToken);
return (exportSymbol, exportInfo, isForRename) => {
const { directImports, indirectUsers } = getImportersForExport(sourceFiles, allDirectImports, exportInfo, checker, cancellationToken);
const { directImports, indirectUsers } = getImportersForExport(sourceFiles, sourceFilesSet, allDirectImports, exportInfo, checker, cancellationToken);
return { indirectUsers, ...getSearchesFromDirectImports(directImports, exportSymbol, exportInfo.exportKind, checker, isForRename) };
};
}
@@ -39,6 +39,7 @@ namespace ts.FindAllReferences {
/** Returns import statements that directly reference the exporting module, and a list of files that may access the module through a namespace. */
function getImportersForExport(
sourceFiles: ReadonlyArray<SourceFile>,
sourceFilesSet: ReadonlyMap<true>,
allDirectImports: Map<ImporterOrCallExpression[]>,
{ exportingModuleSymbol, exportKind }: ExportInfo,
checker: TypeChecker,
@@ -62,7 +63,7 @@ namespace ts.FindAllReferences {
// Module augmentations may use this module's exports without importing it.
for (const decl of exportingModuleSymbol.declarations) {
if (isExternalModuleAugmentation(decl)) {
if (isExternalModuleAugmentation(decl) && sourceFilesSet.has(decl.getSourceFile().fileName)) {
addIndirectUser(decl);
}
}
+11 -14
View File
@@ -10,6 +10,7 @@ namespace ts.NavigateTo {
export function getNavigateToItems(sourceFiles: ReadonlyArray<SourceFile>, checker: TypeChecker, cancellationToken: CancellationToken, searchValue: string, maxResultCount: number, excludeDtsFiles: boolean): NavigateToItem[] {
const patternMatcher = createPatternMatcher(searchValue);
if (!patternMatcher) return emptyArray;
let rawItems: RawNavigateToItem[] = [];
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
@@ -35,28 +36,24 @@ namespace ts.NavigateTo {
function getItemsFromNamedDeclaration(patternMatcher: PatternMatcher, name: string, declarations: ReadonlyArray<Declaration>, checker: TypeChecker, fileName: string, rawItems: Push<RawNavigateToItem>): void {
// First do a quick check to see if the name of the declaration matches the
// last portion of the (possibly) dotted name they're searching for.
const matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);
if (!matches) {
const match = patternMatcher.getMatchForLastSegmentOfPattern(name);
if (!match) {
return; // continue to next named declarations
}
for (const declaration of declarations) {
if (!shouldKeepItem(declaration, checker)) {
continue;
}
if (!shouldKeepItem(declaration, checker)) continue;
// It was a match! If the pattern has dots in it, then also see if the
// declaration container matches as well.
let containerMatches = matches;
if (patternMatcher.patternContainsDots) {
containerMatches = patternMatcher.getMatches(getContainers(declaration), name);
if (!containerMatches) {
continue;
const fullMatch = patternMatcher.getFullMatch(getContainers(declaration), name);
if (fullMatch) {
rawItems.push({ name, fileName, matchKind: fullMatch.kind, isCaseSensitive: fullMatch.isCaseSensitive, declaration });
}
}
rawItems.push({ name, fileName, matchKind: Math.min(...matches.map(m => m.kind)), isCaseSensitive: matches.every(m => m.isCaseSensitive), declaration });
else {
// If the pattern has dots in it, then also see if the declaration container matches as well.
rawItems.push({ name, fileName, matchKind: match.kind, isCaseSensitive: match.isCaseSensitive, declaration });
}
}
}
+24 -54
View File
@@ -33,12 +33,12 @@ namespace ts {
// this will return a successful match, having only tested "SK" against "SyntaxKind". At
// that point a call can be made to 'getMatches("SyntaxKind", "ts.compiler")', with the
// work to create 'ts.compiler' only being done once the first match succeeded.
getMatchesForLastSegmentOfPattern(candidate: string): PatternMatch[];
getMatchForLastSegmentOfPattern(candidate: string): PatternMatch | undefined;
// Fully checks a candidate, with an dotted container, against the search pattern.
// The candidate must match the last part of the search pattern, and the dotted container
// must match the preceding segments of the pattern.
getMatches(candidateContainers: string[], candidate: string): PatternMatch[] | undefined;
getFullMatch(candidateContainers: ReadonlyArray<string>, candidate: string): PatternMatch | undefined;
// Whether or not the pattern contained dots or not. Clients can use this to determine
// If they should call getMatches, or if getMatchesForLastSegmentOfPattern is sufficient.
@@ -97,31 +97,25 @@ namespace ts {
};
}
export function createPatternMatcher(pattern: string): PatternMatcher {
export function createPatternMatcher(pattern: string): PatternMatcher | undefined {
// We'll often see the same candidate string many times when searching (For example, when
// we see the name of a module that is used everywhere, or the name of an overload). As
// such, we cache the information we compute about the candidate for the life of this
// pattern matcher so we don't have to compute it multiple times.
const stringToWordSpans = createMap<TextSpan[]>();
pattern = pattern.trim();
const dotSeparatedSegments = pattern.split(".").map(p => createSegment(p.trim()));
const invalidPattern = dotSeparatedSegments.length === 0 || forEach(dotSeparatedSegments, segmentIsInvalid);
const dotSeparatedSegments = pattern.trim().split(".").map(p => createSegment(p.trim()));
// A segment is considered invalid if we couldn't find any words in it.
if (dotSeparatedSegments.some(segment => !segment.subWordTextChunks.length)) return undefined;
return {
getMatches: (containers, candidate) => skipMatch(candidate) ? undefined : getMatches(containers, candidate, dotSeparatedSegments, stringToWordSpans),
getMatchesForLastSegmentOfPattern: candidate => skipMatch(candidate) ? undefined : matchSegment(candidate, lastOrUndefined(dotSeparatedSegments), stringToWordSpans),
getFullMatch: (containers, candidate) => getFullMatch(containers, candidate, dotSeparatedSegments, stringToWordSpans),
getMatchForLastSegmentOfPattern: candidate => matchSegment(candidate, last(dotSeparatedSegments), stringToWordSpans),
patternContainsDots: dotSeparatedSegments.length > 1
};
// Quick checks so we can bail out when asked to match a candidate.
function skipMatch(candidate: string) {
return invalidPattern || !candidate;
}
}
function getMatches(candidateContainers: ReadonlyArray<string>, candidate: string, dotSeparatedSegments: ReadonlyArray<Segment>, stringToWordSpans: Map<TextSpan[]>): PatternMatch[] | undefined {
function getFullMatch(candidateContainers: ReadonlyArray<string>, candidate: string, dotSeparatedSegments: ReadonlyArray<Segment>, stringToWordSpans: Map<TextSpan[]>): PatternMatch | undefined {
// First, check that the last part of the dot separated pattern matches the name of the
// candidate. If not, then there's no point in proceeding and doing the more
// expensive work.
@@ -140,29 +134,13 @@ namespace ts {
return undefined;
}
// So far so good. Now break up the container for the candidate and check if all
// the dotted parts match up correctly.
const totalMatch = candidateMatch;
let bestMatch: PatternMatch | undefined;
for (let i = dotSeparatedSegments.length - 2, j = candidateContainers.length - 1;
i >= 0;
i -= 1, j -= 1) {
const segment = dotSeparatedSegments[i];
const containerName = candidateContainers[j];
const containerMatch = matchSegment(containerName, segment, stringToWordSpans);
if (!containerMatch) {
// This container didn't match the pattern piece. So there's no match at all.
return undefined;
}
addRange(totalMatch, containerMatch);
bestMatch = betterMatch(bestMatch, matchSegment(candidateContainers[j], dotSeparatedSegments[i], stringToWordSpans));
}
// Success, this symbol's full name matched against the dotted name the user was asking
// about.
return totalMatch;
return bestMatch;
}
function getWordSpans(word: string, stringToWordSpans: Map<TextSpan[]>): TextSpan[] {
@@ -225,7 +203,7 @@ namespace ts {
}
}
function matchSegment(candidate: string, segment: Segment, stringToWordSpans: Map<TextSpan[]>): PatternMatch[] {
function matchSegment(candidate: string, segment: Segment, stringToWordSpans: Map<TextSpan[]>): PatternMatch {
// First check if the segment matches as is. This is also useful if the segment contains
// characters we would normally strip when splitting into parts that we also may want to
// match in the candidate. For example if the segment is "@int" and the candidate is
@@ -235,9 +213,7 @@ namespace ts {
// multi-word segment.
if (every(segment.totalTextChunk.text, ch => ch !== CharacterCodes.space && ch !== CharacterCodes.asterisk)) {
const match = matchTextChunk(candidate, segment.totalTextChunk, stringToWordSpans);
if (match) {
return [match];
}
if (match) return match;
}
// The logic for pattern matching is now as follows:
@@ -277,20 +253,19 @@ namespace ts {
// Only if all words have some sort of match is the pattern considered matched.
const subWordTextChunks = segment.subWordTextChunks;
let matches: PatternMatch[];
let bestMatch: PatternMatch | undefined;
for (const subWordTextChunk of subWordTextChunks) {
// Try to match the candidate with this word
const result = matchTextChunk(candidate, subWordTextChunk, stringToWordSpans);
if (!result) {
return undefined;
}
matches = matches || [];
matches.push(result);
bestMatch = betterMatch(bestMatch, matchTextChunk(candidate, subWordTextChunk, stringToWordSpans));
}
return bestMatch;
}
return matches;
function betterMatch(a: PatternMatch | undefined, b: PatternMatch | undefined): PatternMatch {
return min(a, b, compareMatches);
}
function compareMatches(a: PatternMatch | undefined, b: PatternMatch | undefined): Comparison {
return a === undefined ? Comparison.GreaterThan : b === undefined ? Comparison.LessThan
: compareValues(a.kind, b.kind) || compareBooleans(!a.isCaseSensitive, !b.isCaseSensitive);
}
function partStartsWith(candidate: string, candidateSpan: TextSpan, pattern: string, ignoreCase: boolean, patternSpan: TextSpan = { start: 0, length: pattern.length }): boolean {
@@ -381,11 +356,6 @@ namespace ts {
};
}
// A segment is considered invalid if we couldn't find any words in it.
function segmentIsInvalid(segment: Segment) {
return segment.subWordTextChunks.length === 0;
}
function isUpperCaseLetter(ch: number) {
// Fast check for the ascii range.
if (ch >= CharacterCodes.A && ch <= CharacterCodes.Z) {
-39
View File
@@ -698,14 +698,6 @@ namespace ts.refactor.extractSymbol {
Global,
}
function getUniqueName(baseName: string, fileText: string): string {
let nameText = baseName;
for (let i = 1; stringContains(fileText, nameText); i++) {
nameText = `${baseName}_${i}`;
}
return nameText;
}
/**
* Result of 'extractRange' operation for a specific scope.
* Stores either a list of changes that should be applied to extract a range or a list of errors
@@ -1126,37 +1118,6 @@ namespace ts.refactor.extractSymbol {
}
}
/**
* @return The index of the (only) reference to the extracted symbol. We want the cursor
* to be on the reference, rather than the declaration, because it's closer to where the
* user was before extracting it.
*/
function getRenameLocation(edits: ReadonlyArray<FileTextChanges>, renameFilename: string, functionNameText: string, isDeclaredBeforeUse: boolean): number {
let delta = 0;
let lastPos = -1;
for (const { fileName, textChanges } of edits) {
Debug.assert(fileName === renameFilename);
for (const change of textChanges) {
const { span, newText } = change;
const index = newText.indexOf(functionNameText);
if (index !== -1) {
lastPos = span.start + delta + index;
// If the reference comes first, return immediately.
if (!isDeclaredBeforeUse) {
return lastPos;
}
}
delta += newText.length - span.length;
}
}
// If the declaration comes first, return the position of the last occurrence.
Debug.assert(isDeclaredBeforeUse);
Debug.assert(lastPos >= 0);
return lastPos;
}
function getFirstDeclaration(type: Type): Declaration | undefined {
let firstDeclaration;
@@ -0,0 +1,256 @@
/* @internal */
namespace ts.refactor.generateGetAccessorAndSetAccessor {
const actionName = "Generate 'get' and 'set' accessors";
const actionDescription = Diagnostics.Generate_get_and_set_accessors.message;
registerRefactor(actionName, { getEditsForAction, getAvailableActions });
type AcceptedDeclaration = ParameterDeclaration | PropertyDeclaration | PropertyAssignment;
type AcceptedNameType = Identifier | StringLiteral;
type ContainerDeclaration = ClassLikeDeclaration | ObjectLiteralExpression;
interface DeclarationInfo {
container: ContainerDeclaration;
isStatic: boolean;
type: TypeNode | undefined;
}
interface Info extends DeclarationInfo {
declaration: AcceptedDeclaration;
fieldName: AcceptedNameType;
accessorName: AcceptedNameType;
}
function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
const { file, startPosition } = context;
if (!getConvertibleFieldAtPosition(file, startPosition)) return undefined;
return [{
name: actionName,
description: actionDescription,
actions: [
{
name: actionName,
description: actionDescription
}
]
}];
}
function getEditsForAction(context: RefactorContext, _actionName: string): RefactorEditInfo | undefined {
const { file, startPosition } = context;
const fieldInfo = getConvertibleFieldAtPosition(file, startPosition);
if (!fieldInfo) return undefined;
const isJS = isSourceFileJavaScript(file);
const changeTracker = textChanges.ChangeTracker.fromContext(context);
const { isStatic, fieldName, accessorName, type, container, declaration } = fieldInfo;
const isInClassLike = isClassLike(container);
const accessorModifiers = getAccessorModifiers(isJS, declaration, isStatic, isInClassLike);
const fieldModifiers = getFieldModifiers(isJS, isStatic, isInClassLike);
updateFieldDeclaration(changeTracker, file, declaration, fieldName, fieldModifiers, container);
const getAccessor = generateGetAccessor(fieldName, accessorName, type, accessorModifiers, isStatic, container);
const setAccessor = generateSetAccessor(fieldName, accessorName, type, accessorModifiers, isStatic, container);
insertAccessor(changeTracker, file, getAccessor, declaration, container);
insertAccessor(changeTracker, file, setAccessor, declaration, container);
const edits = changeTracker.getChanges();
const renameFilename = file.fileName;
const renameLocationOffset = isIdentifier(fieldName) ? 0 : -1;
const renameLocation = renameLocationOffset + getRenameLocation(edits, renameFilename, fieldName.text, /*isDeclaredBeforeUse*/ false);
return { renameFilename, renameLocation, edits };
}
function isConvertableName (name: DeclarationName): name is AcceptedNameType {
return isIdentifier(name) || isStringLiteral(name);
}
function isAcceptedDeclaration(node: Node): node is AcceptedDeclaration {
return isParameterPropertyDeclaration(node) || isPropertyDeclaration(node) || isPropertyAssignment(node);
}
function createPropertyName (name: string, originalName: AcceptedNameType) {
return isIdentifier(originalName) ? createIdentifier(name) : createLiteral(name);
}
function createAccessorAccessExpression (fieldName: AcceptedNameType, isStatic: boolean, container: ContainerDeclaration) {
const leftHead = isStatic ? (<ClassLikeDeclaration>container).name : createThis();
return isIdentifier(fieldName) ? createPropertyAccess(leftHead, fieldName) : createElementAccess(leftHead, createLiteral(fieldName));
}
function getAccessorModifiers(isJS: boolean, declaration: AcceptedDeclaration, isStatic: boolean, isClassLike: boolean): NodeArray<Modifier> | undefined {
if (!isClassLike) return undefined;
if (!declaration.modifiers || getModifierFlags(declaration) & ModifierFlags.Private) {
const modifiers = append<Modifier>(
!isJS ? [createToken(SyntaxKind.PublicKeyword)] : undefined,
isStatic ? createToken(SyntaxKind.StaticKeyword) : undefined
);
return modifiers && createNodeArray(modifiers);
}
return declaration.modifiers;
}
function getFieldModifiers(isJS: boolean, isStatic: boolean, isClassLike: boolean): NodeArray<Modifier> | undefined {
if (!isClassLike) return undefined;
const modifiers = append<Modifier>(
!isJS ? [createToken(SyntaxKind.PrivateKeyword)] : undefined,
isStatic ? createToken(SyntaxKind.StaticKeyword) : undefined
);
return modifiers && createNodeArray(modifiers);
}
function getPropertyDeclarationInfo(propertyDeclaration: PropertyDeclaration): DeclarationInfo | undefined {
if (!isClassLike(propertyDeclaration.parent) || !propertyDeclaration.parent.members) return undefined;
return {
isStatic: hasStaticModifier(propertyDeclaration),
type: propertyDeclaration.type,
container: propertyDeclaration.parent
};
}
function getParameterPropertyDeclarationInfo(parameterDeclaration: ParameterDeclaration): DeclarationInfo | undefined {
if (!isClassLike(parameterDeclaration.parent.parent) || !parameterDeclaration.parent.parent.members) return undefined;
return {
isStatic: false,
type: parameterDeclaration.type,
container: parameterDeclaration.parent.parent
};
}
function getPropertyAssignmentDeclarationInfo(propertyAssignment: PropertyAssignment): DeclarationInfo | undefined {
return {
isStatic: false,
type: undefined,
container: propertyAssignment.parent
};
}
function getDeclarationInfo(declaration: AcceptedDeclaration) {
if (isPropertyDeclaration(declaration)) {
return getPropertyDeclarationInfo(declaration);
}
else if (isPropertyAssignment(declaration)) {
return getPropertyAssignmentDeclarationInfo(declaration);
}
else {
return getParameterPropertyDeclarationInfo(declaration);
}
}
function getConvertibleFieldAtPosition(file: SourceFile, startPosition: number): Info | undefined {
const node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false);
const declaration = findAncestor(node.parent, isAcceptedDeclaration);
// make sure propertyDeclaration have AccessibilityModifier or Static Modifier
const meaning = ModifierFlags.AccessibilityModifier | ModifierFlags.Static;
if (!declaration || !isConvertableName(declaration.name) || (getModifierFlags(declaration) | meaning) !== meaning) return undefined;
const info = getDeclarationInfo(declaration);
const fieldName = createPropertyName(getUniqueName(`_${declaration.name.text}`, file.text), declaration.name);
suppressLeadingAndTrailingTrivia(fieldName);
suppressLeadingAndTrailingTrivia(declaration);
return {
...info,
declaration,
fieldName,
accessorName: createPropertyName(declaration.name.text, declaration.name)
};
}
function generateGetAccessor(fieldName: AcceptedNameType, accessorName: AcceptedNameType, type: TypeNode, modifiers: ModifiersArray | undefined, isStatic: boolean, container: ContainerDeclaration) {
return createGetAccessor(
/*decorators*/ undefined,
modifiers,
accessorName,
/*parameters*/ undefined,
type,
createBlock([
createReturn(
createAccessorAccessExpression(fieldName, isStatic, container)
)
], /*multiLine*/ true)
);
}
function generateSetAccessor(fieldName: AcceptedNameType, accessorName: AcceptedNameType, type: TypeNode, modifiers: ModifiersArray | undefined, isStatic: boolean, container: ContainerDeclaration) {
return createSetAccessor(
/*decorators*/ undefined,
modifiers,
accessorName,
[createParameter(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
createIdentifier("value"),
/*questionToken*/ undefined,
type
)],
createBlock([
createStatement(
createAssignment(
createAccessorAccessExpression(fieldName, isStatic, container),
createIdentifier("value")
)
)
], /*multiLine*/ true)
);
}
function updatePropertyDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: PropertyDeclaration, fieldName: AcceptedNameType, modifiers: ModifiersArray | undefined) {
const property = updateProperty(
declaration,
declaration.decorators,
modifiers,
fieldName,
declaration.questionToken || declaration.exclamationToken,
declaration.type,
declaration.initializer
);
changeTracker.replaceNode(file, declaration, property);
}
function updateParameterPropertyDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: ParameterDeclaration, fieldName: AcceptedNameType, modifiers: ModifiersArray | undefined, classLikeContainer: ClassLikeDeclaration) {
const property = createProperty(
declaration.decorators,
modifiers,
fieldName,
declaration.questionToken,
declaration.type,
declaration.initializer
);
changeTracker.insertNodeAtClassStart(file, classLikeContainer, property);
changeTracker.deleteNodeInList(file, declaration);
}
function updatePropertyAssignmentDeclaration (changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: PropertyAssignment, fieldName: AcceptedNameType) {
const assignment = updatePropertyAssignment(declaration, fieldName, declaration.initializer);
changeTracker.replacePropertyAssignment(file, declaration, assignment);
}
function updateFieldDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: AcceptedDeclaration, fieldName: AcceptedNameType, modifiers: ModifiersArray | undefined, container: ContainerDeclaration) {
if (isPropertyDeclaration(declaration)) {
updatePropertyDeclaration(changeTracker, file, declaration, fieldName, modifiers);
}
else if (isPropertyAssignment(declaration)) {
updatePropertyAssignmentDeclaration(changeTracker, file, declaration, fieldName);
}
else {
updateParameterPropertyDeclaration(changeTracker, file, declaration, fieldName, modifiers, <ClassLikeDeclaration>container);
}
}
function insertAccessor(changeTracker: textChanges.ChangeTracker, file: SourceFile, accessor: AccessorDeclaration, declaration: AcceptedDeclaration, container: ContainerDeclaration) {
isParameterPropertyDeclaration(declaration)
? changeTracker.insertNodeAtClassStart(file, <ClassLikeDeclaration>container, accessor)
: changeTracker.insertNodeAfter(file, declaration, accessor);
}
}
+8 -11
View File
@@ -1663,20 +1663,17 @@ namespace ts {
/// References and Occurrences
function getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
const canonicalFileName = getCanonicalFileName(normalizeSlashes(fileName));
return flatMap(getDocumentHighlights(fileName, position, [fileName]), entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => {
Debug.assert(getCanonicalFileName(normalizeSlashes(entry.fileName)) === canonicalFileName); // Get occurrences only supports reporting occurrences for the file queried.
return {
fileName: entry.fileName,
textSpan: highlightSpan.textSpan,
isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
isDefinition: false,
isInString: highlightSpan.isInString,
};
}));
return flatMap(getDocumentHighlights(fileName, position, [fileName]), entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => ({
fileName: entry.fileName,
textSpan: highlightSpan.textSpan,
isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
isDefinition: false,
isInString: highlightSpan.isInString,
})));
}
function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray<string>): DocumentHighlights[] {
Debug.assert(contains(filesToSearch, fileName));
synchronizeHostData();
const sourceFilesToSearch = map(filesToSearch, f => Debug.assertDefined(program.getSourceFile(f)));
const sourceFile = getValidSourceFile(fileName);
+9
View File
@@ -316,6 +316,12 @@ namespace ts.textChanges {
return this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, startNode, endNode, options), newNodes, options);
}
public replacePropertyAssignment(sourceFile: SourceFile, oldNode: PropertyAssignment, newNode: PropertyAssignment) {
return this.replaceNode(sourceFile, oldNode, newNode, {
suffix: "," + this.newLineCharacter
});
}
private insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) {
this.replaceRange(sourceFile, createTextRange(pos), newNode, options);
}
@@ -468,6 +474,9 @@ namespace ts.textChanges {
else if (isVariableDeclaration(node)) {
return { prefix: ", " };
}
else if (isPropertyAssignment(node)) {
return { suffix: "," + this.newLineCharacter };
}
else if (isParameter(node)) {
return {};
}
+1
View File
@@ -228,6 +228,7 @@ namespace ts {
}
export interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
+41
View File
@@ -1526,4 +1526,45 @@ namespace ts {
function getFirstChild(node: Node): Node | undefined {
return node.forEachChild(child => child);
}
/* @internal */
export function getUniqueName(baseName: string, fileText: string): string {
let nameText = baseName;
for (let i = 1; stringContains(fileText, nameText); i++) {
nameText = `${baseName}_${i}`;
}
return nameText;
}
/**
* @return The index of the (only) reference to the extracted symbol. We want the cursor
* to be on the reference, rather than the declaration, because it's closer to where the
* user was before extracting it.
*/
/* @internal */
export function getRenameLocation(edits: ReadonlyArray<FileTextChanges>, renameFilename: string, name: string, isDeclaredBeforeUse: boolean): number {
let delta = 0;
let lastPos = -1;
for (const { fileName, textChanges } of edits) {
Debug.assert(fileName === renameFilename);
for (const change of textChanges) {
const { span, newText } = change;
const index = newText.indexOf(name);
if (index !== -1) {
lastPos = span.start + delta + index;
// If the reference comes first, return immediately.
if (!isDeclaredBeforeUse) {
return lastPos;
}
}
delta += newText.length - span.length;
}
}
// If the declaration comes first, return the position of the last occurrence.
Debug.assert(isDeclaredBeforeUse);
Debug.assert(lastPos >= 0);
return lastPos;
}
}