mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into referencesDogfood_1
This commit is contained in:
+19
-7
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4161,5 +4161,9 @@
|
||||
"Convert all constructor functions to classes": {
|
||||
"category": "Message",
|
||||
"code": 95045
|
||||
},
|
||||
"Generate 'get' and 'set' accessors": {
|
||||
"category": "Message",
|
||||
"code": 95046
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface UserPreferences {
|
||||
readonly disableSuggestions?: boolean;
|
||||
readonly quotePreference?: "double" | "single";
|
||||
readonly includeCompletionsForModuleExports?: boolean;
|
||||
readonly includeCompletionsWithInsertText?: boolean;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user