Merge branch 'master' of https://github.com/Microsoft/TypeScript into feature/eslint

This commit is contained in:
Alexander T
2019-08-06 09:07:48 +03:00
82 changed files with 1374 additions and 7824 deletions
+47 -24
View File
@@ -2377,7 +2377,7 @@ namespace ts {
return links.target;
}
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportAssignment | ExportSpecifier) {
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportSpecifier) {
const symbol = getSymbolOfNode(node);
const target = resolveAlias(symbol);
if (target) {
@@ -2399,15 +2399,10 @@ namespace ts {
links.referenced = true;
const node = getDeclarationOfAliasSymbol(symbol);
if (!node) return Debug.fail();
if (node.kind === SyntaxKind.ExportAssignment) {
// export default <symbol>
checkExpressionCached((<ExportAssignment>node).expression);
}
else if (node.kind === SyntaxKind.ExportSpecifier) {
// export { <symbol> } or export { <symbol> as foo }
checkExpressionCached((<ExportSpecifier>node).propertyName || (<ExportSpecifier>node).name);
}
else if (isInternalModuleImportEqualsDeclaration(node)) {
// We defer checking of the reference of an `import =` until the import itself is referenced,
// This way a chain of imports can be elided if ultimately the final input is only used in a type
// position.
if (isInternalModuleImportEqualsDeclaration(node)) {
// import foo = <symbol>
checkExpressionCached(<Expression>node.moduleReference);
}
@@ -17829,8 +17824,12 @@ namespace ts {
return type;
}
function isExportOrExportExpression(location: Node) {
return !!findAncestor(location, e => e.parent && isExportAssignment(e.parent) && e.parent.expression === e && isEntityNameExpression(e));
}
function markAliasReferenced(symbol: Symbol, location: Node) {
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) {
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && ((compilerOptions.preserveConstEnums && isExportOrExportExpression(location)) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol)))) {
markAliasSymbolAsReferenced(symbol);
}
}
@@ -18812,6 +18811,17 @@ namespace ts {
return false;
}
function getContextualIterationType(kind: IterationTypeKind, functionDecl: SignatureDeclaration): Type | undefined {
const isAsync = !!(getFunctionFlags(functionDecl) & FunctionFlags.Async);
const contextualReturnType = getContextualReturnType(functionDecl);
if (contextualReturnType) {
return getIterationTypeOfGeneratorFunctionReturnType(kind, contextualReturnType, isAsync)
|| undefined;
}
return undefined;
}
function getContextualReturnType(functionDecl: SignatureDeclaration): Type | undefined {
// If the containing function has a return type annotation, is a constructor, or is a get accessor whose
// corresponding set accessor has a type annotation, return statements in the function are contextually typed
@@ -20341,8 +20351,8 @@ namespace ts {
// if jsx emit was not react as there wont be error being emitted
reactSym.isReferenced = SymbolFlags.All;
// If react symbol is alias, mark it as referenced
if (reactSym.flags & SymbolFlags.Alias && !isConstEnumOrConstEnumOnlyModule(resolveAlias(reactSym))) {
// If react symbol is alias, mark it as refereced
if (reactSym.flags & SymbolFlags.Alias) {
markAliasSymbolAsReferenced(reactSym);
}
}
@@ -23477,7 +23487,11 @@ namespace ts {
}
if (isGenerator) {
return createGeneratorReturnType(yieldType || neverType, returnType || fallbackReturnType, nextType || unknownType, isAsync);
return createGeneratorReturnType(
yieldType || neverType,
returnType || fallbackReturnType,
nextType || getContextualIterationType(IterationTypeKind.Next, func) || unknownType,
isAsync);
}
else {
// From within an async function you can return either a non-promise value or a promise. Any
@@ -24839,13 +24853,7 @@ namespace ts {
|| anyType;
}
const contextualReturnType = getContextualReturnType(func);
if (contextualReturnType) {
return getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, contextualReturnType, isAsync)
|| anyType;
}
return anyType;
return getContextualIterationType(IterationTypeKind.Next, func) || anyType;
}
function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type {
@@ -24894,7 +24902,7 @@ namespace ts {
return result;
}
function checkExpressionCached(node: Expression, checkMode?: CheckMode): Type {
function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
if (checkMode && checkMode !== CheckMode.Normal) {
@@ -25222,7 +25230,8 @@ namespace ts {
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).expression === node) ||
(node.parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>node.parent).expression === node) ||
((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && isInRightSideOfImportOrExportAssignment(<Identifier>node) ||
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node));
(node.parent.kind === SyntaxKind.TypeQuery && (<TypeQueryNode>node.parent).exprName === node)) ||
(node.parent.kind === SyntaxKind.ExportSpecifier && (compilerOptions.preserveConstEnums || node.flags & NodeFlags.Ambient)); // We allow reexporting const enums
if (!ok) {
error(node, Diagnostics.const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment_or_type_query);
@@ -30152,6 +30161,10 @@ namespace ts {
}
else {
markExportAsReferenced(node);
const target = symbol && (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
if (!target || target === unknownSymbol || target.flags & SymbolFlags.Value) {
checkExpressionCached(node.propertyName || node.name);
}
}
}
}
@@ -30178,7 +30191,17 @@ namespace ts {
grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers);
}
if (node.expression.kind === SyntaxKind.Identifier) {
markExportAsReferenced(node);
const id = node.expression as Identifier;
const sym = resolveEntityName(id, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, node);
if (sym) {
markAliasReferenced(sym, id);
// If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`)
const target = sym.flags & SymbolFlags.Alias ? resolveAlias(sym) : sym;
if (target === unknownSymbol || target.flags & SymbolFlags.Value) {
// However if it is a value, we need to check it's being used correctly
checkExpressionCached(node.expression);
}
}
if (getEmitDeclarations(compilerOptions)) {
collectLinkedAliases(node.expression as Identifier, /*setVisibility*/ true);
+13 -3
View File
@@ -1743,6 +1743,16 @@ namespace ts {
return false;
}
/** @internal */
export interface TSConfig {
compilerOptions: CompilerOptions;
compileOnSave: boolean | undefined;
exclude?: ReadonlyArray<string>;
files: ReadonlyArray<string> | undefined;
include?: ReadonlyArray<string>;
references: ReadonlyArray<ProjectReference> | undefined;
}
/**
* Generate an uncommented, complete tsconfig for use with "--showConfig"
* @param configParseResult options to be generated into tsconfig.json
@@ -1750,7 +1760,7 @@ namespace ts {
* @param host provides current directory and case sensitivity services
*/
/** @internal */
export function convertToTSConfig(configParseResult: ParsedCommandLine, configFileName: string, host: { getCurrentDirectory(): string, useCaseSensitiveFileNames: boolean }): object {
export function convertToTSConfig(configParseResult: ParsedCommandLine, configFileName: string, host: { getCurrentDirectory(): string, useCaseSensitiveFileNames: boolean }): TSConfig {
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
const files = map(
filter(
@@ -1778,13 +1788,13 @@ namespace ts {
build: undefined,
version: undefined,
},
references: map(configParseResult.projectReferences, r => ({ ...r, path: r.originalPath, originalPath: undefined })),
references: map(configParseResult.projectReferences, r => ({ ...r, path: r.originalPath ? r.originalPath : "", originalPath: undefined })),
files: length(files) ? files : undefined,
...(configParseResult.configFileSpecs ? {
include: filterSameAsDefaultInclude(configParseResult.configFileSpecs.validatedIncludeSpecs),
exclude: configParseResult.configFileSpecs.validatedExcludeSpecs
} : {}),
compilerOnSave: !!configParseResult.compileOnSave ? true : undefined
compileOnSave: !!configParseResult.compileOnSave ? true : undefined
};
return config;
}
+29 -1
View File
@@ -22,6 +22,7 @@ namespace ts {
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;
let exportedVariableStatement = false;
let enabledSubstitutions: ESNextSubstitutionFlags;
let enclosingFunctionFlags: FunctionFlags;
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
@@ -40,6 +41,7 @@ namespace ts {
return node;
}
exportedVariableStatement = false;
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
return visited;
@@ -79,6 +81,8 @@ namespace ts {
return visitBinaryExpression(node as BinaryExpression, noDestructuringValue);
case SyntaxKind.CatchClause:
return visitCatchClause(node as CatchClause);
case SyntaxKind.VariableStatement:
return visitVariableStatement(node as VariableStatement);
case SyntaxKind.VariableDeclaration:
return visitVariableDeclaration(node as VariableDeclaration);
case SyntaxKind.ForOfStatement:
@@ -321,19 +325,43 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
function visitVariableStatement(node: VariableStatement): VisitResult<VariableStatement> {
if (hasModifier(node, ModifierFlags.Export)) {
const savedExportedVariableStatement = exportedVariableStatement;
exportedVariableStatement = true;
const visited = visitEachChild(node, visitor, context);
exportedVariableStatement = savedExportedVariableStatement;
return visited;
}
return visitEachChild(node, visitor, context);
}
/**
* Visits a VariableDeclaration node with a binding pattern.
*
* @param node A VariableDeclaration node.
*/
function visitVariableDeclaration(node: VariableDeclaration): VisitResult<VariableDeclaration> {
if (exportedVariableStatement) {
const savedExportedVariableStatement = exportedVariableStatement;
exportedVariableStatement = false;
const visited = visitVariableDeclarationWorker(node, /*exportedVariableStatement*/ true);
exportedVariableStatement = savedExportedVariableStatement;
return visited;
}
return visitVariableDeclarationWorker(node, /*exportedVariableStatement*/ false);
}
function visitVariableDeclarationWorker(node: VariableDeclaration, exportedVariableStatement: boolean): VisitResult<VariableDeclaration> {
// If we are here it is because the name contains a binding pattern with a rest somewhere in it.
if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
return flattenDestructuringBinding(
node,
visitor,
context,
FlattenLevel.ObjectRest
FlattenLevel.ObjectRest,
/*rval*/ undefined,
exportedVariableStatement
);
}
return visitEachChild(node, visitor, context);
+12 -1
View File
@@ -567,6 +567,8 @@ namespace ts {
return emitNode && emitNode.flags || 0;
}
const escapeNoSubstitutionTemplateLiteralText = compose(escapeString, escapeTemplateSubstitution);
const escapeNonAsciiNoSubstitutionTemplateLiteralText = compose(escapeNonAsciiString, escapeTemplateSubstitution);
export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile, neverAsciiEscape: boolean | undefined) {
// If we don't need to downlevel and we can reach the original source text using
// the node's parent reference, then simply get the text as it was originally written.
@@ -577,7 +579,11 @@ namespace ts {
return getSourceTextOfNodeFromSourceFile(sourceFile, node);
}
const escapeText = neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? escapeString : escapeNonAsciiString;
// If a NoSubstitutionTemplateLiteral appears to have a substitution in it, the original text
// had to include a backslash: `not \${a} substitution`.
const escapeText = neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ?
node.kind === SyntaxKind.NoSubstitutionTemplateLiteral ? escapeNoSubstitutionTemplateLiteralText : escapeString :
node.kind === SyntaxKind.NoSubstitutionTemplateLiteral ? escapeNonAsciiNoSubstitutionTemplateLiteralText : escapeNonAsciiString;
// If we can't reach the original source text, use the canonical form if it's a number,
// or a (possibly escaped) quoted form of the original text if it's string-like.
@@ -3118,6 +3124,11 @@ namespace ts {
}
}
const templateSubstitutionRegExp = /\$\{/g;
function escapeTemplateSubstitution(str: string): string {
return str.replace(templateSubstitutionRegExp, "\\${");
}
// This consists of the first 19 unprintable ASCII characters, canonical escapes, lineSeparator,
// paragraphSeparator, and nextLine. The latter three are just desirable to suppress new lines in
// the language service. These characters should be escaped when printing, and if any characters are added,
+2
View File
@@ -730,6 +730,7 @@ namespace Harness {
includeBuiltFile?: string;
baselineFile?: string;
libFiles?: string;
noTypesAndSymbols?: boolean;
}
// Additional options not already in ts.optionDeclarations
@@ -746,6 +747,7 @@ namespace Harness {
{ name: "currentDirectory", type: "string" },
{ name: "symlink", type: "string" },
{ name: "link", type: "string" },
{ name: "noTypesAndSymbols", type: "boolean" },
// Emitted js baseline will print full paths for every output file
{ name: "fullEmitPaths", type: "boolean" }
];
+4 -4
View File
@@ -577,10 +577,10 @@ namespace ts.FindAllReferences {
// If a reference is a class expression, the exported node would be its parent.
// If a reference is a variable declaration, the exported node would be the variable statement.
function getExportNode(parent: Node, node: Node): Node | undefined {
if (parent.kind === SyntaxKind.VariableDeclaration) {
const p = parent as VariableDeclaration;
return p.name !== node ? undefined :
p.parent.kind === SyntaxKind.CatchClause ? undefined : p.parent.parent.kind === SyntaxKind.VariableStatement ? p.parent.parent : undefined;
const declaration = isVariableDeclaration(parent) ? parent : isBindingElement(parent) ? walkUpBindingElementsAndPatterns(parent) : undefined;
if (declaration) {
return (parent as VariableDeclaration | BindingElement).name !== node ? undefined :
isCatchClause(declaration.parent) ? undefined : isVariableStatement(declaration.parent.parent) ? declaration.parent.parent : undefined;
}
else {
return parent;
+39 -10
View File
@@ -58,16 +58,18 @@ class CompilerBaselineRunner extends RunnerBase {
}
public checkTestCodeOutput(fileName: string, test?: CompilerFileBasedTest) {
if (test && test.configurations) {
if (test && ts.some(test.configurations)) {
test.configurations.forEach(configuration => {
describe(`${this.testSuiteName} tests for ${fileName}${configuration ? ` (${Harness.getFileBasedTestConfigurationDescription(configuration)})` : ``}`, () => {
this.runSuite(fileName, test, configuration);
});
});
}
describe(`${this.testSuiteName} tests for ${fileName}`, () => {
this.runSuite(fileName, test);
});
else {
describe(`${this.testSuiteName} tests for ${fileName}`, () => {
this.runSuite(fileName, test);
});
}
}
private runSuite(fileName: string, test?: CompilerFileBasedTest, configuration?: Harness.FileBasedTestConfiguration) {
@@ -112,6 +114,7 @@ class CompilerBaselineRunner extends RunnerBase {
class CompilerTest {
private fileName: string;
private justName: string;
private configuredName: string;
private lastUnit: Harness.TestCaseParser.TestUnitData;
private harnessSettings: Harness.TestCaseParser.CompilerSettings;
private hasNonDtsFiles: boolean;
@@ -126,6 +129,25 @@ class CompilerTest {
constructor(fileName: string, testCaseContent?: Harness.TestCaseParser.TestCaseContent, configurationOverrides?: Harness.TestCaseParser.CompilerSettings) {
this.fileName = fileName;
this.justName = vpath.basename(fileName);
this.configuredName = this.justName;
if (configurationOverrides) {
let configuredName = "";
const keys = Object
.keys(configurationOverrides)
.map(k => k.toLowerCase())
.sort();
for (const key of keys) {
if (configuredName) {
configuredName += ",";
}
configuredName += `${key}=${configurationOverrides[key].toLowerCase()}`;
}
if (configuredName) {
const extname = vpath.extname(this.justName);
const basename = vpath.basename(this.justName, extname, /*ignoreCase*/ true);
this.configuredName = `${basename}(${configuredName})${extname}`;
}
}
const rootDir = fileName.indexOf("conformance") === -1 ? "tests/cases/compiler/" : ts.getDirectoryPath(fileName) + "/";
@@ -205,7 +227,7 @@ class CompilerTest {
public verifyDiagnostics() {
// check errors
Harness.Compiler.doErrorBaseline(
this.justName,
this.configuredName,
this.tsConfigFiles.concat(this.toBeCompiled, this.otherFiles),
this.result.diagnostics,
!!this.options.pretty);
@@ -213,7 +235,7 @@ class CompilerTest {
public verifyModuleResolution() {
if (this.options.traceResolution) {
Harness.Baseline.runBaseline(this.justName.replace(/\.tsx?$/, ".trace.json"),
Harness.Baseline.runBaseline(this.configuredName.replace(/\.tsx?$/, ".trace.json"),
JSON.stringify(this.result.traces.map(utils.sanitizeTraceResolutionLogEntry), undefined, 4));
}
}
@@ -225,14 +247,14 @@ class CompilerTest {
// Because of the noEmitOnError option no files are created. We need to return null because baselining isn't required.
? null // eslint-disable-line no-null/no-null
: record;
Harness.Baseline.runBaseline(this.justName.replace(/\.tsx?$/, ".sourcemap.txt"), baseline);
Harness.Baseline.runBaseline(this.configuredName.replace(/\.tsx?$/, ".sourcemap.txt"), baseline);
}
}
public verifyJavaScriptOutput() {
if (this.hasNonDtsFiles) {
Harness.Compiler.doJsEmitBaseline(
this.justName,
this.configuredName,
this.fileName,
this.options,
this.result,
@@ -245,7 +267,7 @@ class CompilerTest {
public verifySourceMapOutput() {
Harness.Compiler.doSourcemapBaseline(
this.justName,
this.configuredName,
this.options,
this.result,
this.harnessSettings);
@@ -256,8 +278,15 @@ class CompilerTest {
return;
}
const noTypesAndSymbols =
this.harnessSettings.noTypesAndSymbols &&
this.harnessSettings.noTypesAndSymbols.toLowerCase() === "true";
if (noTypesAndSymbols) {
return;
}
Harness.Compiler.doTypeAndSymbolBaseline(
this.justName,
this.configuredName,
this.result.program!,
this.toBeCompiled.concat(this.otherFiles).filter(file => !!this.result.program!.getSourceFile(file.unitName)),
/*opts*/ undefined,
+1
View File
@@ -131,6 +131,7 @@
"unittests/tsserver/formatSettings.ts",
"unittests/tsserver/getApplicableRefactors.ts",
"unittests/tsserver/getEditsForFileRename.ts",
"unittests/tsserver/getExportReferences.ts",
"unittests/tsserver/importHelpers.ts",
"unittests/tsserver/inferredProjects.ts",
"unittests/tsserver/languageService.ts",
@@ -53,6 +53,26 @@ namespace ts {
showTSConfigCorrectly("Show TSConfig with advanced options", ["--showConfig", "--declaration", "--declarationDir", "lib", "--skipLibCheck", "--noErrorTruncation"]);
showTSConfigCorrectly("Show TSConfig with compileOnSave and more", ["-p", "tsconfig.json"], {
compilerOptions: {
esModuleInterop: true,
target: "es5",
module: "commonjs",
strict: true,
},
compileOnSave: true,
exclude: [
"dist"
],
files: [],
include: [
"src/*"
],
references: [
{ path: "./test" }
],
});
// Regression test for https://github.com/Microsoft/TypeScript/issues/28836
showTSConfigCorrectly("Show TSConfig with paths and more", ["-p", "tsconfig.json"], {
compilerOptions: {
@@ -1,11 +1,4 @@
namespace ts.projectSystem {
interface DocumentSpanFromSubstring {
file: File;
text: string;
options?: SpanFromSubstringOptions;
contextText?: string;
contextOptions?: SpanFromSubstringOptions;
}
function documentSpanFromSubstring({ file, text, contextText, options, contextOptions }: DocumentSpanFromSubstring): DocumentSpan {
const contextSpan = contextText !== undefined ? documentSpanFromSubstring({ file, text: contextText, options: contextOptions }) : undefined;
return {
@@ -19,19 +12,6 @@ namespace ts.projectSystem {
return documentSpanFromSubstring(input);
}
interface MakeReferenceItem extends DocumentSpanFromSubstring {
isDefinition: boolean;
lineText: string;
}
function makeReferenceItem({ isDefinition, lineText, ...rest }: MakeReferenceItem): protocol.ReferencesResponseItem {
return {
...protocolFileSpanWithContextFromSubstring(rest),
isDefinition,
isWriteAccess: isDefinition,
lineText,
};
}
interface MakeReferenceEntry extends DocumentSpanFromSubstring {
isDefinition: boolean;
}
@@ -0,0 +1,185 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: getExportReferences", () => {
const exportVariable = "export const value = 0;";
const exportArrayDestructured = "export const [valueA, valueB] = [0, 1];";
const exportObjectDestructured = "export const { valueC, valueD: renamedD } = { valueC: 0, valueD: 1 };";
const exportNestedObject = "export const { nest: [valueE, { valueF }] } = { nest: [0, { valueF: 1 }] };";
const mainTs: File = {
path: "/main.ts",
content: 'import { value, valueA, valueB, valueC, renamedD, valueE, valueF } from "./mod";',
};
const modTs: File = {
path: "/mod.ts",
content: `${exportVariable}
${exportArrayDestructured}
${exportObjectDestructured}
${exportNestedObject}
`,
};
const tsconfig: File = {
path: "/tsconfig.json",
content: "{}",
};
function makeSampleSession() {
const host = createServerHost([mainTs, modTs, tsconfig]);
const session = createSession(host);
openFilesForSession([mainTs, modTs], session);
return session;
}
const referenceMainTs = (mainTs: File, text: string): protocol.ReferencesResponseItem =>
makeReferenceItem({
file: mainTs,
isDefinition: true,
lineText: mainTs.content,
contextText: mainTs.content,
text,
});
const referenceModTs = (
texts: { text: string; lineText: string; contextText?: string },
override: Partial<MakeReferenceItem> = {},
): protocol.ReferencesResponseItem =>
makeReferenceItem({
file: modTs,
isDefinition: true,
...texts,
...override,
});
it("should get const variable declaration references", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "value"),
);
const expectResponse = {
refs: [
referenceModTs({ text: "value", lineText: exportVariable, contextText: exportVariable }),
referenceMainTs(mainTs, "value"),
],
symbolDisplayString: "const value: 0",
symbolName: "value",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "value").offset,
};
assert.deepEqual(response, expectResponse);
});
it("should get array destructuring declaration references", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "valueA"),
);
const expectResponse = {
refs: [
referenceModTs({
text: "valueA",
lineText: exportArrayDestructured,
contextText: exportArrayDestructured,
}),
referenceMainTs(mainTs, "valueA"),
],
symbolDisplayString: "const valueA: number",
symbolName: "valueA",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "valueA").offset,
};
assert.deepEqual(response, expectResponse);
});
it("should get object destructuring declaration references", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "valueC"),
);
const expectResponse = {
refs: [
referenceModTs({
text: "valueC",
lineText: exportObjectDestructured,
contextText: exportObjectDestructured,
}),
referenceMainTs(mainTs, "valueC"),
referenceModTs(
{ text: "valueC", lineText: exportObjectDestructured, contextText: "valueC: 0" },
{ options: { index: 1 } },
),
],
symbolDisplayString: "const valueC: number",
symbolName: "valueC",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "valueC").offset,
};
assert.deepEqual(response, expectResponse);
});
it("should get object declaration references that renames destructured property", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "renamedD"),
);
const expectResponse = {
refs: [
referenceModTs({
text: "renamedD",
lineText: exportObjectDestructured,
contextText: exportObjectDestructured,
}),
referenceMainTs(mainTs, "renamedD"),
],
symbolDisplayString: "const renamedD: number",
symbolName: "renamedD",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "renamedD").offset,
};
assert.deepEqual(response, expectResponse);
});
it("should get nested object declaration references", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "valueF"),
);
const expectResponse = {
refs: [
referenceModTs({
text: "valueF",
lineText: exportNestedObject,
contextText: exportNestedObject,
}),
referenceMainTs(mainTs, "valueF"),
referenceModTs(
{
text: "valueF",
lineText: exportNestedObject,
contextText: "valueF: 1",
},
{ options: { index: 1 } },
),
],
symbolDisplayString: "const valueF: number",
symbolName: "valueF",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "valueF").offset,
};
assert.deepEqual(response, expectResponse);
});
});
}
@@ -519,6 +519,8 @@ namespace ts.projectSystem {
file: File;
text: string;
options?: SpanFromSubstringOptions;
contextText?: string;
contextOptions?: SpanFromSubstringOptions;
}
export function protocolFileSpanFromSubstring({ file, text, options }: DocumentSpanFromSubstring): protocol.FileSpan {
return { file: file.path, ...protocolTextSpanFromSubstring(file.content, text, options) };
@@ -727,4 +729,18 @@ namespace ts.projectSystem {
assert.strictEqual(outputs.length, index + 1, JSON.stringify(outputs));
}
}
export interface MakeReferenceItem extends DocumentSpanFromSubstring {
isDefinition: boolean;
lineText: string;
}
export function makeReferenceItem({ isDefinition, lineText, ...rest }: MakeReferenceItem): protocol.ReferencesResponseItem {
return {
...protocolFileSpanWithContextFromSubstring(rest),
isDefinition,
isWriteAccess: isDefinition,
lineText,
};
}
}