mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-09 20:51:43 -06:00
Merge branch 'master' into createTypeNode
This commit is contained in:
commit
7b93a75e41
@ -330,7 +330,7 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts
|
||||
options += " --lib " + opts.lib
|
||||
}
|
||||
else {
|
||||
options += " --lib es5,scripthost"
|
||||
options += " --lib es5"
|
||||
}
|
||||
options += " --noUnusedLocals --noUnusedParameters";
|
||||
|
||||
@ -584,13 +584,13 @@ var cancellationTokenFile = path.join(builtLocalDirectory, "cancellationToken.js
|
||||
compileFile(cancellationTokenFile, cancellationTokenSources, [builtLocalDirectory].concat(cancellationTokenSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], outDir: builtLocalDirectory, noOutFile: true, lib: "es6" });
|
||||
|
||||
var typingsInstallerFile = path.join(builtLocalDirectory, "typingsInstaller.js");
|
||||
compileFile(typingsInstallerFile, typingsInstallerSources, [builtLocalDirectory].concat(typingsInstallerSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], outDir: builtLocalDirectory, noOutFile: false, lib: "es6,scripthost" });
|
||||
compileFile(typingsInstallerFile, typingsInstallerSources, [builtLocalDirectory].concat(typingsInstallerSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], outDir: builtLocalDirectory, noOutFile: false, lib: "es6" });
|
||||
|
||||
var watchGuardFile = path.join(builtLocalDirectory, "watchGuard.js");
|
||||
compileFile(watchGuardFile, watchGuardSources, [builtLocalDirectory].concat(watchGuardSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], outDir: builtLocalDirectory, noOutFile: false, lib: "es6" });
|
||||
|
||||
var serverFile = path.join(builtLocalDirectory, "tsserver.js");
|
||||
compileFile(serverFile, serverSources, [builtLocalDirectory, copyright, cancellationTokenFile, typingsInstallerFile, watchGuardFile].concat(serverSources).concat(servicesSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], preserveConstEnums: true, lib: "es6,scripthost" });
|
||||
compileFile(serverFile, serverSources, [builtLocalDirectory, copyright, cancellationTokenFile, typingsInstallerFile, watchGuardFile].concat(serverSources).concat(servicesSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { types: ["node"], preserveConstEnums: true, lib: "es6" });
|
||||
var tsserverLibraryFile = path.join(builtLocalDirectory, "tsserverlibrary.js");
|
||||
var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibrary.d.ts");
|
||||
compileFile(
|
||||
@ -714,7 +714,7 @@ compileFile(
|
||||
/*prereqs*/[builtLocalDirectory, tscFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources),
|
||||
/*prefixes*/[],
|
||||
/*useBuiltCompiler:*/ true,
|
||||
/*opts*/ { inlineSourceMap: true, types: ["node", "mocha", "chai"], lib: "es6,scripthost" });
|
||||
/*opts*/ { inlineSourceMap: true, types: ["node", "mocha", "chai"], lib: "es6" });
|
||||
|
||||
var internalTests = "internal/";
|
||||
|
||||
|
||||
@ -2413,10 +2413,6 @@
|
||||
"category": "Error",
|
||||
"code": 5012
|
||||
},
|
||||
"Unsupported file encoding.": {
|
||||
"category": "Error",
|
||||
"code": 5013
|
||||
},
|
||||
"Failed to parse file '{0}': {1}.": {
|
||||
"category": "Error",
|
||||
"code": 5014
|
||||
|
||||
@ -87,9 +87,6 @@ namespace ts {
|
||||
return sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
}
|
||||
|
||||
// returned by CScript sys environment
|
||||
const unsupportedFileEncodingErrorCode = -2147024809;
|
||||
|
||||
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile {
|
||||
let text: string;
|
||||
try {
|
||||
@ -100,9 +97,7 @@ namespace ts {
|
||||
}
|
||||
catch (e) {
|
||||
if (onError) {
|
||||
onError(e.number === unsupportedFileEncodingErrorCode
|
||||
? createCompilerDiagnostic(Diagnostics.Unsupported_file_encoding).messageText
|
||||
: e.message);
|
||||
onError(e.message);
|
||||
}
|
||||
text = "";
|
||||
}
|
||||
|
||||
@ -74,13 +74,6 @@ namespace ts {
|
||||
return parseInt(version.substring(1, dot));
|
||||
}
|
||||
|
||||
declare class Enumerator {
|
||||
public atEnd(): boolean;
|
||||
public moveNext(): boolean;
|
||||
public item(): any;
|
||||
constructor(o: any);
|
||||
}
|
||||
|
||||
declare var ChakraHost: {
|
||||
args: string[];
|
||||
currentDirectory: string;
|
||||
@ -104,152 +97,6 @@ namespace ts {
|
||||
};
|
||||
|
||||
export let sys: System = (function() {
|
||||
|
||||
function getWScriptSystem(): System {
|
||||
|
||||
const fso = new ActiveXObject("Scripting.FileSystemObject");
|
||||
const shell = new ActiveXObject("WScript.Shell");
|
||||
|
||||
const fileStream = new ActiveXObject("ADODB.Stream");
|
||||
fileStream.Type = 2 /*text*/;
|
||||
|
||||
const binaryStream = new ActiveXObject("ADODB.Stream");
|
||||
binaryStream.Type = 1 /*binary*/;
|
||||
|
||||
const args: string[] = [];
|
||||
for (let i = 0; i < WScript.Arguments.length; i++) {
|
||||
args[i] = WScript.Arguments.Item(i);
|
||||
}
|
||||
|
||||
function readFile(fileName: string, encoding?: string): string {
|
||||
if (!fso.FileExists(fileName)) {
|
||||
return undefined;
|
||||
}
|
||||
fileStream.Open();
|
||||
try {
|
||||
if (encoding) {
|
||||
fileStream.Charset = encoding;
|
||||
fileStream.LoadFromFile(fileName);
|
||||
}
|
||||
else {
|
||||
// Load file and read the first two bytes into a string with no interpretation
|
||||
fileStream.Charset = "x-ansi";
|
||||
fileStream.LoadFromFile(fileName);
|
||||
const bom = fileStream.ReadText(2) || "";
|
||||
// Position must be at 0 before encoding can be changed
|
||||
fileStream.Position = 0;
|
||||
// [0xFF,0xFE] and [0xFE,0xFF] mean utf-16 (little or big endian), otherwise default to utf-8
|
||||
fileStream.Charset = bom.length >= 2 && (bom.charCodeAt(0) === 0xFF && bom.charCodeAt(1) === 0xFE || bom.charCodeAt(0) === 0xFE && bom.charCodeAt(1) === 0xFF) ? "unicode" : "utf-8";
|
||||
}
|
||||
// ReadText method always strips byte order mark from resulting string
|
||||
return fileStream.ReadText();
|
||||
}
|
||||
catch (e) {
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
fileStream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void {
|
||||
fileStream.Open();
|
||||
binaryStream.Open();
|
||||
try {
|
||||
// Write characters in UTF-8 encoding
|
||||
fileStream.Charset = "utf-8";
|
||||
fileStream.WriteText(data);
|
||||
// If we don't want the BOM, then skip it by setting the starting location to 3 (size of BOM).
|
||||
// If not, start from position 0, as the BOM will be added automatically when charset==utf8.
|
||||
if (writeByteOrderMark) {
|
||||
fileStream.Position = 0;
|
||||
}
|
||||
else {
|
||||
fileStream.Position = 3;
|
||||
}
|
||||
fileStream.CopyTo(binaryStream);
|
||||
binaryStream.SaveToFile(fileName, 2 /*overwrite*/);
|
||||
}
|
||||
finally {
|
||||
binaryStream.Close();
|
||||
fileStream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
function getNames(collection: any): string[] {
|
||||
const result: string[] = [];
|
||||
for (const e = new Enumerator(collection); !e.atEnd(); e.moveNext()) {
|
||||
result.push(e.item().Name);
|
||||
}
|
||||
return result.sort();
|
||||
}
|
||||
|
||||
function getDirectories(path: string): string[] {
|
||||
const folder = fso.GetFolder(path);
|
||||
return getNames(folder.subfolders);
|
||||
}
|
||||
|
||||
function getAccessibleFileSystemEntries(path: string): FileSystemEntries {
|
||||
try {
|
||||
const folder = fso.GetFolder(path || ".");
|
||||
const files = getNames(folder.files);
|
||||
const directories = getNames(folder.subfolders);
|
||||
return { files, directories };
|
||||
}
|
||||
catch (e) {
|
||||
return { files: [], directories: [] };
|
||||
}
|
||||
}
|
||||
|
||||
function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[]): string[] {
|
||||
return matchFiles(path, extensions, excludes, includes, /*useCaseSensitiveFileNames*/ false, shell.CurrentDirectory, getAccessibleFileSystemEntries);
|
||||
}
|
||||
|
||||
const wscriptSystem: System = {
|
||||
args,
|
||||
newLine: "\r\n",
|
||||
useCaseSensitiveFileNames: false,
|
||||
write(s: string): void {
|
||||
WScript.StdOut.Write(s);
|
||||
},
|
||||
readFile,
|
||||
writeFile,
|
||||
resolvePath(path: string): string {
|
||||
return fso.GetAbsolutePathName(path);
|
||||
},
|
||||
fileExists(path: string): boolean {
|
||||
return fso.FileExists(path);
|
||||
},
|
||||
directoryExists(path: string) {
|
||||
return fso.FolderExists(path);
|
||||
},
|
||||
createDirectory(directoryName: string) {
|
||||
if (!wscriptSystem.directoryExists(directoryName)) {
|
||||
fso.CreateFolder(directoryName);
|
||||
}
|
||||
},
|
||||
getExecutingFilePath() {
|
||||
return WScript.ScriptFullName;
|
||||
},
|
||||
getCurrentDirectory() {
|
||||
return shell.CurrentDirectory;
|
||||
},
|
||||
getDirectories,
|
||||
getEnvironmentVariable(name: string) {
|
||||
return new ActiveXObject("WScript.Shell").ExpandEnvironmentStrings(`%${name}%`);
|
||||
},
|
||||
readDirectory,
|
||||
exit(exitCode?: number): void {
|
||||
try {
|
||||
WScript.Quit(exitCode);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
return wscriptSystem;
|
||||
}
|
||||
|
||||
function getNodeSystem(): System {
|
||||
const _fs = require("fs");
|
||||
const _path = require("path");
|
||||
@ -646,9 +493,6 @@ namespace ts {
|
||||
if (typeof ChakraHost !== "undefined") {
|
||||
sys = getChakraSystem();
|
||||
}
|
||||
else if (typeof WScript !== "undefined" && typeof ActiveXObject === "function") {
|
||||
sys = getWScriptSystem();
|
||||
}
|
||||
else if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof require !== "undefined") {
|
||||
// process and process.nextTick checks if current environment is node-like
|
||||
// process.browser check excludes webpack and browserify
|
||||
|
||||
@ -928,9 +928,13 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
function rangeToReferenceEntry(r: Range) {
|
||||
let { isWriteAccess, isDefinition } = (r.marker && r.marker.data) || { isWriteAccess: false, isDefinition: false };
|
||||
let { isWriteAccess, isDefinition, isInString } = (r.marker && r.marker.data) || { isWriteAccess: false, isDefinition: false, isInString: undefined };
|
||||
isWriteAccess = !!isWriteAccess; isDefinition = !!isDefinition;
|
||||
return { fileName: r.fileName, textSpan: { start: r.start, length: r.end - r.start }, isWriteAccess, isDefinition };
|
||||
const result: any = { fileName: r.fileName, textSpan: { start: r.start, length: r.end - r.start }, isWriteAccess, isDefinition };
|
||||
if (isInString !== undefined) {
|
||||
result.isInString = isInString;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1855,17 +1859,41 @@ namespace FourSlash {
|
||||
|
||||
const unsatisfiedRanges: Range[] = [];
|
||||
|
||||
const delayedErrors: string[] = [];
|
||||
for (const range of ranges) {
|
||||
const length = range.end - range.start;
|
||||
const matchingImpl = ts.find(implementations, impl =>
|
||||
range.fileName === impl.fileName && range.start === impl.textSpan.start && length === impl.textSpan.length);
|
||||
if (matchingImpl) {
|
||||
if (range.marker && range.marker.data) {
|
||||
const expected = <{ displayParts?: ts.SymbolDisplayPart[], parts: string[], kind?: string }>range.marker.data;
|
||||
if (expected.displayParts) {
|
||||
if (!ts.arrayIsEqualTo(expected.displayParts, matchingImpl.displayParts, displayPartIsEqualTo)) {
|
||||
delayedErrors.push(`Mismatched display parts: expected ${JSON.stringify(expected.displayParts)}, actual ${JSON.stringify(matchingImpl.displayParts)}`);
|
||||
}
|
||||
}
|
||||
else if (expected.parts) {
|
||||
const actualParts = matchingImpl.displayParts.map(p => p.text);
|
||||
if (!ts.arrayIsEqualTo(expected.parts, actualParts)) {
|
||||
delayedErrors.push(`Mismatched non-tagged display parts: expected ${JSON.stringify(expected.parts)}, actual ${JSON.stringify(actualParts)}`);
|
||||
}
|
||||
}
|
||||
if (expected.kind !== undefined) {
|
||||
if (expected.kind !== matchingImpl.kind) {
|
||||
delayedErrors.push(`Mismatched kind: expected ${JSON.stringify(expected.kind)}, actual ${JSON.stringify(matchingImpl.kind)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matchingImpl.matched = true;
|
||||
}
|
||||
else {
|
||||
unsatisfiedRanges.push(range);
|
||||
}
|
||||
}
|
||||
if (delayedErrors.length) {
|
||||
this.raiseError(delayedErrors.join("\n"));
|
||||
}
|
||||
|
||||
const unmatchedImplementations = implementations.filter(impl => !impl.matched);
|
||||
if (unmatchedImplementations.length || unsatisfiedRanges.length) {
|
||||
@ -1890,6 +1918,10 @@ namespace FourSlash {
|
||||
function implementationsAreEqual(a: ImplementationLocationInformation, b: ImplementationLocationInformation) {
|
||||
return a.fileName === b.fileName && TestState.textSpansEqual(a.textSpan, b.textSpan);
|
||||
}
|
||||
|
||||
function displayPartIsEqualTo(a: ts.SymbolDisplayPart, b: ts.SymbolDisplayPart): boolean {
|
||||
return a.kind === b.kind && a.text === b.text;
|
||||
}
|
||||
}
|
||||
|
||||
public getMarkers(): Marker[] {
|
||||
|
||||
@ -34,12 +34,6 @@ var _chai: typeof chai = require("chai");
|
||||
var assert: typeof _chai.assert = _chai.assert;
|
||||
declare var __dirname: string; // Node-specific
|
||||
var global: NodeJS.Global = <any>Function("return this").call(undefined);
|
||||
declare namespace NodeJS {
|
||||
export interface Global {
|
||||
WScript: typeof WScript;
|
||||
ActiveXObject: typeof ActiveXObject;
|
||||
}
|
||||
}
|
||||
|
||||
declare var window: {};
|
||||
declare var XMLHttpRequest: {
|
||||
@ -60,14 +54,10 @@ namespace Utils {
|
||||
export const enum ExecutionEnvironment {
|
||||
Node,
|
||||
Browser,
|
||||
CScript
|
||||
}
|
||||
|
||||
export function getExecutionEnvironment() {
|
||||
if (typeof WScript !== "undefined" && typeof ActiveXObject === "function") {
|
||||
return ExecutionEnvironment.CScript;
|
||||
}
|
||||
else if (typeof window !== "undefined") {
|
||||
if (typeof window !== "undefined") {
|
||||
return ExecutionEnvironment.Browser;
|
||||
}
|
||||
else {
|
||||
@ -93,7 +83,6 @@ namespace Utils {
|
||||
export function evalFile(fileContents: string, fileName: string, nodeContext?: any) {
|
||||
const environment = getExecutionEnvironment();
|
||||
switch (environment) {
|
||||
case ExecutionEnvironment.CScript:
|
||||
case ExecutionEnvironment.Browser:
|
||||
eval(fileContents);
|
||||
break;
|
||||
@ -516,83 +505,6 @@ namespace Harness {
|
||||
export const virtualFileSystemRoot = "/";
|
||||
|
||||
namespace IOImpl {
|
||||
declare class Enumerator {
|
||||
public atEnd(): boolean;
|
||||
public moveNext(): boolean;
|
||||
public item(): any;
|
||||
constructor(o: any);
|
||||
}
|
||||
|
||||
export namespace CScript {
|
||||
let fso: any;
|
||||
if (global.ActiveXObject) {
|
||||
fso = new global.ActiveXObject("Scripting.FileSystemObject");
|
||||
}
|
||||
else {
|
||||
fso = {};
|
||||
}
|
||||
|
||||
export const args = () => ts.sys.args;
|
||||
export const getExecutingFilePath = () => ts.sys.getExecutingFilePath();
|
||||
export const exit = (exitCode: number) => ts.sys.exit(exitCode);
|
||||
export const resolvePath = (path: string) => ts.sys.resolvePath(path);
|
||||
export const getCurrentDirectory = () => ts.sys.getCurrentDirectory();
|
||||
export const newLine = () => harnessNewLine;
|
||||
export const useCaseSensitiveFileNames = () => ts.sys.useCaseSensitiveFileNames;
|
||||
|
||||
export const readFile: typeof IO.readFile = path => ts.sys.readFile(path);
|
||||
export const writeFile: typeof IO.writeFile = (path, content) => ts.sys.writeFile(path, content);
|
||||
export const directoryName: typeof IO.directoryName = fso.GetParentFolderName;
|
||||
export const getDirectories: typeof IO.getDirectories = dir => ts.sys.getDirectories(dir);
|
||||
export const directoryExists: typeof IO.directoryExists = fso.FolderExists;
|
||||
export const fileExists: typeof IO.fileExists = fso.FileExists;
|
||||
export const log: typeof IO.log = global.WScript && global.WScript.StdOut.WriteLine;
|
||||
export const getEnvironmentVariable: typeof IO.getEnvironmentVariable = name => ts.sys.getEnvironmentVariable(name);
|
||||
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude, include) => ts.sys.readDirectory(path, extension, exclude, include);
|
||||
|
||||
export function createDirectory(path: string) {
|
||||
if (directoryExists(path)) {
|
||||
fso.CreateFolder(path);
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteFile(path: string) {
|
||||
if (fileExists(path)) {
|
||||
fso.DeleteFile(path, true); // true: delete read-only files
|
||||
}
|
||||
}
|
||||
|
||||
export let listFiles: typeof IO.listFiles = (path, spec?, options?) => {
|
||||
options = options || <{ recursive?: boolean; }>{};
|
||||
function filesInFolder(folder: any, root: string): string[] {
|
||||
let paths: string[] = [];
|
||||
let fc: any;
|
||||
|
||||
if (options.recursive) {
|
||||
fc = new Enumerator(folder.subfolders);
|
||||
|
||||
for (; !fc.atEnd(); fc.moveNext()) {
|
||||
paths = paths.concat(filesInFolder(fc.item(), root + "\\" + fc.item().Name));
|
||||
}
|
||||
}
|
||||
|
||||
fc = new Enumerator(folder.files);
|
||||
|
||||
for (; !fc.atEnd(); fc.moveNext()) {
|
||||
if (!spec || fc.item().Name.match(spec)) {
|
||||
paths.push(root + "\\" + fc.item().Name);
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
const folder: any = fso.GetFolder(path);
|
||||
|
||||
return filesInFolder(folder, path);
|
||||
};
|
||||
}
|
||||
|
||||
export namespace Node {
|
||||
declare const require: any;
|
||||
let fs: any, pathModule: any;
|
||||
@ -840,16 +752,16 @@ namespace Harness {
|
||||
}
|
||||
}
|
||||
|
||||
switch (Utils.getExecutionEnvironment()) {
|
||||
case Utils.ExecutionEnvironment.CScript:
|
||||
IO = IOImpl.CScript;
|
||||
break;
|
||||
const environment = Utils.getExecutionEnvironment();
|
||||
switch (environment) {
|
||||
case Utils.ExecutionEnvironment.Node:
|
||||
IO = IOImpl.Node;
|
||||
break;
|
||||
case Utils.ExecutionEnvironment.Browser:
|
||||
IO = IOImpl.Network;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown value '${environment}' for ExecutionEnvironment.`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,7 +785,7 @@ namespace Harness {
|
||||
/** Aggregate various writes into a single array of lines. Useful for passing to the
|
||||
* TypeScript compiler to fill with source code or errors.
|
||||
*/
|
||||
export class WriterAggregator implements ITextWriter {
|
||||
export class WriterAggregator {
|
||||
public lines: string[] = [];
|
||||
public currentLine = <string>undefined;
|
||||
|
||||
|
||||
@ -3489,6 +3489,52 @@ namespace ts.projectSystem {
|
||||
});
|
||||
});
|
||||
|
||||
describe("occurence highlight on string", () => {
|
||||
it("should be marked if only on string values", () => {
|
||||
const file1: FileOrFolder = {
|
||||
path: "/a/b/file1.ts",
|
||||
content: `let t1 = "div";\nlet t2 = "div";\nlet t3 = { "div": 123 };\nlet t4 = t3["div"];`
|
||||
};
|
||||
|
||||
const host = createServerHost([file1]);
|
||||
const session = createSession(host);
|
||||
const projectService = session.getProjectService();
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
{
|
||||
const highlightRequest = makeSessionRequest<protocol.FileLocationRequestArgs>(
|
||||
CommandNames.Occurrences,
|
||||
{ file: file1.path, line: 1, offset: 11 }
|
||||
);
|
||||
const highlightResponse = session.executeCommand(highlightRequest).response as protocol.OccurrencesResponseItem[];
|
||||
const firstOccurence = highlightResponse[0];
|
||||
assert.isTrue(firstOccurence.isInString, "Highlights should be marked with isInString");
|
||||
}
|
||||
|
||||
{
|
||||
const highlightRequest = makeSessionRequest<protocol.FileLocationRequestArgs>(
|
||||
CommandNames.Occurrences,
|
||||
{ file: file1.path, line: 3, offset: 13 }
|
||||
);
|
||||
const highlightResponse = session.executeCommand(highlightRequest).response as protocol.OccurrencesResponseItem[];
|
||||
assert.isTrue(highlightResponse.length === 2);
|
||||
const firstOccurence = highlightResponse[0];
|
||||
assert.isUndefined(firstOccurence.isInString, "Highlights should not be marked with isInString if on property name");
|
||||
}
|
||||
|
||||
{
|
||||
const highlightRequest = makeSessionRequest<protocol.FileLocationRequestArgs>(
|
||||
CommandNames.Occurrences,
|
||||
{ file: file1.path, line: 4, offset: 14 }
|
||||
);
|
||||
const highlightResponse = session.executeCommand(highlightRequest).response as protocol.OccurrencesResponseItem[];
|
||||
assert.isTrue(highlightResponse.length === 2);
|
||||
const firstOccurence = highlightResponse[0];
|
||||
assert.isUndefined(firstOccurence.isInString, "Highlights should not be marked with isInString if on indexer");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("maxNodeModuleJsDepth for inferred projects", () => {
|
||||
it("should be set to 2 if the project has js root files", () => {
|
||||
const file1: FileOrFolder = {
|
||||
|
||||
@ -382,7 +382,9 @@ namespace ts.server {
|
||||
const end = this.lineOffsetToPosition(fileName, entry.end);
|
||||
return {
|
||||
fileName,
|
||||
textSpan: ts.createTextSpanFromBounds(start, end)
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
kind: ScriptElementKind.unknown,
|
||||
displayParts: []
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -621,6 +621,11 @@ namespace ts.server.protocol {
|
||||
* True if the occurrence is a write location, false otherwise.
|
||||
*/
|
||||
isWriteAccess: boolean;
|
||||
|
||||
/**
|
||||
* True if the occurrence is in a string, undefined otherwise;
|
||||
*/
|
||||
isInString?: true;
|
||||
}
|
||||
|
||||
export interface OccurrencesResponse extends Response {
|
||||
|
||||
@ -651,16 +651,21 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
return occurrences.map(occurrence => {
|
||||
const { fileName, isWriteAccess, textSpan } = occurrence;
|
||||
const { fileName, isWriteAccess, textSpan, isInString } = occurrence;
|
||||
const scriptInfo = project.getScriptInfo(fileName);
|
||||
const start = scriptInfo.positionToLineOffset(textSpan.start);
|
||||
const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan));
|
||||
return {
|
||||
const result: protocol.OccurrencesResponseItem = {
|
||||
start,
|
||||
end,
|
||||
file: fileName,
|
||||
isWriteAccess,
|
||||
};
|
||||
// no need to serialize the property if it is not true
|
||||
if (isInString) {
|
||||
result.isInString = isInString;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,8 @@ namespace ts.DocumentHighlights {
|
||||
}
|
||||
|
||||
function getSemanticDocumentHighlights(node: Node, typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] {
|
||||
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFilesToSearch);
|
||||
const context = new FindAllReferences.DefaultFindReferencesContext(typeChecker, cancellationToken);
|
||||
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(context, node, sourceFilesToSearch);
|
||||
return referencedSymbols && convertReferencedSymbols(referencedSymbols);
|
||||
}
|
||||
|
||||
@ -37,7 +38,8 @@ namespace ts.DocumentHighlights {
|
||||
|
||||
documentHighlights.highlightSpans.push({
|
||||
textSpan: referenceEntry.textSpan,
|
||||
kind: referenceEntry.isWriteAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference
|
||||
kind: referenceEntry.isWriteAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
|
||||
isInString: referenceEntry.isInString
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,21 +1,75 @@
|
||||
/* @internal */
|
||||
namespace ts.FindAllReferences {
|
||||
|
||||
export interface FindReferencesContext<T extends DocumentSpan> {
|
||||
readonly typeChecker: TypeChecker;
|
||||
readonly cancellationToken: CancellationToken;
|
||||
getReferenceEntryFromNode(node: Node, isInString?: boolean): T;
|
||||
getReferenceEntryForSpanInFile(fileName: string, span: TextSpan): T;
|
||||
}
|
||||
|
||||
export class DefaultFindReferencesContext implements FindReferencesContext<ReferenceEntry> {
|
||||
constructor(readonly typeChecker: TypeChecker, readonly cancellationToken: CancellationToken) {
|
||||
}
|
||||
getReferenceEntryFromNode(node: Node, isInString?: boolean): ReferenceEntry {
|
||||
const reference = getReferenceEntryFromNode(node);
|
||||
if (isInString) {
|
||||
reference.isInString = true;
|
||||
}
|
||||
return reference;
|
||||
}
|
||||
getReferenceEntryForSpanInFile(fileName: string, textSpan: TextSpan): ReferenceEntry {
|
||||
return { textSpan, fileName, isWriteAccess: false, isDefinition: false };
|
||||
}
|
||||
}
|
||||
|
||||
export class FindImplementationsContext implements FindReferencesContext<ImplementationLocation> {
|
||||
constructor(readonly typeChecker: TypeChecker, readonly cancellationToken: CancellationToken) {
|
||||
}
|
||||
getReferenceEntryFromNode(node: Node): ImplementationLocation {
|
||||
const entry = <ImplementationLocation><any>getReferenceEntryFromNode(node);
|
||||
const symbol = this.typeChecker.getSymbolAtLocation(isDeclaration(node) && node.name ? node.name : node);
|
||||
if (symbol) {
|
||||
const def = getDefinition(symbol, node, this.typeChecker);
|
||||
entry.kind = def.kind;
|
||||
entry.displayParts = def.displayParts;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
entry.kind = ScriptElementKind.interfaceElement;
|
||||
entry.displayParts = [punctuationPart(SyntaxKind.OpenParenToken), textPart("object literal"), punctuationPart(SyntaxKind.CloseParenToken)]
|
||||
}
|
||||
else if (node.kind === SyntaxKind.ClassExpression) {
|
||||
entry.kind = ScriptElementKind.localClassElement;
|
||||
entry.displayParts = [punctuationPart(SyntaxKind.OpenParenToken), textPart("anonymous local class"), punctuationPart(SyntaxKind.CloseParenToken)]
|
||||
}
|
||||
else {
|
||||
entry.kind = getNodeKind(node);
|
||||
entry.displayParts = [];
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
getReferenceEntryForSpanInFile(fileName: string, textSpan: TextSpan): ImplementationLocation {
|
||||
return { textSpan, fileName, kind: ScriptElementKind.unknown, displayParts: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export function findReferencedSymbols(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number, findInStrings: boolean, findInComments: boolean, isForRename: boolean): ReferencedSymbol[] | undefined {
|
||||
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
return getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFiles, findInStrings, findInComments, isForRename);
|
||||
return getReferencedSymbolsForNode(new DefaultFindReferencesContext(typeChecker, cancellationToken), node, sourceFiles, findInStrings, findInComments, isForRename);
|
||||
}
|
||||
|
||||
export function convertReferences(referenceSymbols: ReferencedSymbol[]): ReferenceEntry[] {
|
||||
return referenceSymbols && flatMap(referenceSymbols, r => r.references);
|
||||
}
|
||||
|
||||
export function getReferencedSymbolsForNode(typeChecker: TypeChecker, cancellationToken: CancellationToken, node: Node, sourceFiles: SourceFile[], findInStrings?: boolean, findInComments?: boolean, isForRename?: boolean, implementations?: boolean): ReferencedSymbol[] | undefined {
|
||||
export function getReferencedSymbolsForNode<T extends DocumentSpan>(context: FindReferencesContext<T>, node: Node, sourceFiles: SourceFile[], findInStrings?: boolean, findInComments?: boolean, isForRename?: boolean, implementations?: boolean): ReferencedSymbolOf<T>[] | undefined {
|
||||
if (!implementations) {
|
||||
const special = getReferencedSymbolsSpecial(node, sourceFiles, typeChecker, cancellationToken);
|
||||
const special = getReferencedSymbolsSpecial(node, sourceFiles, context);
|
||||
if (special) {
|
||||
return special;
|
||||
}
|
||||
}
|
||||
const { typeChecker, cancellationToken } = context;
|
||||
|
||||
// `getSymbolAtLocation` normally returns the symbol of the class when given the constructor keyword,
|
||||
// so we have to specify that we want the constructor symbol.
|
||||
@ -24,7 +78,7 @@ namespace ts.FindAllReferences {
|
||||
// Could not find a symbol e.g. unknown identifier
|
||||
if (!symbol) {
|
||||
if (!implementations && node.kind === SyntaxKind.StringLiteral) {
|
||||
return getReferencesForStringLiteral(<StringLiteral>node, sourceFiles, typeChecker, cancellationToken);
|
||||
return getReferencesForStringLiteral(<StringLiteral>node, sourceFiles, context);
|
||||
}
|
||||
// Can't have references to something that we have no symbol for.
|
||||
return undefined;
|
||||
@ -49,7 +103,7 @@ namespace ts.FindAllReferences {
|
||||
// Compute the meaning from the location and the symbol it references
|
||||
const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations);
|
||||
|
||||
const result: ReferencedSymbol[] = [];
|
||||
const result: ReferencedSymbolOf<T>[] = [];
|
||||
// Maps from a symbol ID to the ReferencedSymbol entry in 'result'.
|
||||
const symbolToIndex: number[] = [];
|
||||
const inheritsFromCache: Map<boolean> = createMap<boolean>();
|
||||
@ -81,14 +135,14 @@ namespace ts.FindAllReferences {
|
||||
|
||||
function getRefs(scope: ts.Node, searchName: string): void {
|
||||
getReferencesInNode(scope, symbol, searchName, node, searchMeaning, findInStrings, findInComments, result,
|
||||
symbolToIndex, implementations, typeChecker, cancellationToken, searchSymbols, inheritsFromCache);
|
||||
symbolToIndex, implementations, context, searchSymbols, inheritsFromCache);
|
||||
}
|
||||
}
|
||||
|
||||
/** getReferencedSymbols for special node kinds. */
|
||||
function getReferencedSymbolsSpecial(node: Node, sourceFiles: SourceFile[], typeChecker: TypeChecker, cancellationToken: CancellationToken): ReferencedSymbol[] | undefined {
|
||||
function getReferencedSymbolsSpecial<T extends DocumentSpan>(node: Node, sourceFiles: SourceFile[], context: FindReferencesContext<T>): ReferencedSymbolOf<T>[] | undefined {
|
||||
if (isTypeKeyword(node.kind)) {
|
||||
return getAllReferencesForKeyword(sourceFiles, node.kind, cancellationToken);
|
||||
return getAllReferencesForKeyword(context, sourceFiles, node.kind);
|
||||
}
|
||||
|
||||
// Labels
|
||||
@ -97,20 +151,20 @@ namespace ts.FindAllReferences {
|
||||
const labelDefinition = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
|
||||
// if we have a label definition, look within its statement for references, if not, then
|
||||
// the label is undefined and we have no results..
|
||||
return labelDefinition && getLabelReferencesInNode(labelDefinition.parent, labelDefinition, cancellationToken);
|
||||
return labelDefinition && getLabelReferencesInNode(labelDefinition.parent, labelDefinition, context);
|
||||
}
|
||||
else {
|
||||
// it is a label definition and not a target, search within the parent labeledStatement
|
||||
return getLabelReferencesInNode(node.parent, <Identifier>node, cancellationToken);
|
||||
return getLabelReferencesInNode(node.parent, <Identifier>node, context);
|
||||
}
|
||||
}
|
||||
|
||||
if (isThis(node)) {
|
||||
return getReferencesForThisKeyword(node, sourceFiles, typeChecker, cancellationToken);
|
||||
return getReferencesForThisKeyword(node, sourceFiles, context);
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.SuperKeyword) {
|
||||
return getReferencesForSuperKeyword(node, typeChecker, cancellationToken);
|
||||
return getReferencesForSuperKeyword(node, context);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@ -352,10 +406,11 @@ namespace ts.FindAllReferences {
|
||||
return positions;
|
||||
}
|
||||
|
||||
function getLabelReferencesInNode(container: Node, targetLabel: Identifier, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
const references: ReferenceEntry[] = [];
|
||||
function getLabelReferencesInNode<T extends DocumentSpan>(container: Node, targetLabel: Identifier, context: FindReferencesContext<T>): ReferencedSymbolOf<T>[] {
|
||||
const references: T[] = [];
|
||||
const sourceFile = container.getSourceFile();
|
||||
const labelName = targetLabel.text;
|
||||
const { cancellationToken } = context;
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container.getStart(), container.getEnd(), cancellationToken);
|
||||
forEach(possiblePositions, position => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
@ -368,7 +423,7 @@ namespace ts.FindAllReferences {
|
||||
// Only pick labels that are either the target label, or have a target that is the target label
|
||||
if (node === targetLabel ||
|
||||
(isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel)) {
|
||||
references.push(getReferenceEntryFromNode(node));
|
||||
references.push(context.getReferenceEntryFromNode(node));
|
||||
}
|
||||
});
|
||||
|
||||
@ -404,12 +459,13 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
function getAllReferencesForKeyword(sourceFiles: SourceFile[], keywordKind: ts.SyntaxKind, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
function getAllReferencesForKeyword<T extends DocumentSpan>(context: FindReferencesContext<T>, sourceFiles: SourceFile[], keywordKind: ts.SyntaxKind): ReferencedSymbolOf<T>[] {
|
||||
const { cancellationToken } = context;
|
||||
const name = tokenToString(keywordKind);
|
||||
const references: ReferenceEntry[] = [];
|
||||
const references: T[] = [];
|
||||
for (const sourceFile of sourceFiles) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
addReferencesForKeywordInFile(sourceFile, keywordKind, name, cancellationToken, references);
|
||||
addReferencesForKeywordInFile(sourceFile, keywordKind, name, context, references);
|
||||
}
|
||||
|
||||
if (!references.length) return undefined;
|
||||
@ -427,18 +483,14 @@ namespace ts.FindAllReferences {
|
||||
return [{ definition, references }];
|
||||
}
|
||||
|
||||
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, cancellationToken: CancellationToken, references: Push<ReferenceEntry>): void {
|
||||
function addReferencesForKeywordInFile<T extends DocumentSpan>(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, context: FindReferencesContext<T>, references: Push<T>): void {
|
||||
const { cancellationToken } = context;
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile.getStart(), sourceFile.getEnd(), cancellationToken);
|
||||
for (const position of possiblePositions) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position);
|
||||
if (referenceLocation.kind === kind) {
|
||||
references.push({
|
||||
textSpan: createTextSpanFromNode(referenceLocation),
|
||||
fileName: sourceFile.fileName,
|
||||
isWriteAccess: false,
|
||||
isDefinition: false,
|
||||
});
|
||||
references.push(context.getReferenceEntryForSpanInFile(sourceFile.fileName, createTextSpanFromNode(referenceLocation)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -447,22 +499,22 @@ namespace ts.FindAllReferences {
|
||||
* tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
|
||||
* searchLocation: a node where the search value
|
||||
*/
|
||||
function getReferencesInNode(container: Node,
|
||||
function getReferencesInNode<T extends DocumentSpan>(container: Node,
|
||||
searchSymbol: Symbol,
|
||||
searchText: string,
|
||||
searchLocation: Node,
|
||||
searchMeaning: SemanticMeaning,
|
||||
findInStrings: boolean,
|
||||
findInComments: boolean,
|
||||
result: ReferencedSymbol[],
|
||||
result: ReferencedSymbolOf<T>[],
|
||||
symbolToIndex: number[],
|
||||
implementations: boolean,
|
||||
typeChecker: TypeChecker,
|
||||
cancellationToken: CancellationToken,
|
||||
context: FindReferencesContext<T>,
|
||||
searchSymbols: Symbol[],
|
||||
inheritsFromCache: Map<boolean>): void {
|
||||
|
||||
const sourceFile = container.getSourceFile();
|
||||
const { typeChecker, cancellationToken } = context;
|
||||
|
||||
const start = findInComments ? container.getFullStart() : container.getStart();
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, start, container.getEnd(), cancellationToken);
|
||||
@ -486,12 +538,7 @@ namespace ts.FindAllReferences {
|
||||
// 'FindReferences' will just filter out these results.
|
||||
result.push({
|
||||
definition: undefined,
|
||||
references: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textSpan: createTextSpan(position, searchText.length),
|
||||
isWriteAccess: false,
|
||||
isDefinition: false
|
||||
}]
|
||||
references: [context.getReferenceEntryForSpanInFile(sourceFile.fileName, createTextSpan(position, searchText.length))]
|
||||
});
|
||||
}
|
||||
continue;
|
||||
@ -571,11 +618,11 @@ namespace ts.FindAllReferences {
|
||||
function addReferences(references: Node[]): void {
|
||||
if (references.length) {
|
||||
const referencedSymbol = getReferencedSymbol(searchSymbol);
|
||||
addRange(referencedSymbol.references, map(references, getReferenceEntryFromNode));
|
||||
addRange(referencedSymbol.references, map(references, n => context.getReferenceEntryFromNode(n)));
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencedSymbol(symbol: Symbol): ReferencedSymbol {
|
||||
function getReferencedSymbol(symbol: Symbol): ReferencedSymbolOf<T> {
|
||||
const symbolId = getSymbolId(symbol);
|
||||
let index = symbolToIndex[symbolId];
|
||||
if (index === undefined) {
|
||||
@ -594,10 +641,10 @@ namespace ts.FindAllReferences {
|
||||
function addReferenceToRelatedSymbol(node: Node, relatedSymbol: Symbol) {
|
||||
const references = getReferencedSymbol(relatedSymbol).references;
|
||||
if (implementations) {
|
||||
getImplementationReferenceEntryForNode(node, references, typeChecker);
|
||||
getImplementationReferenceEntryForNode(node, references, context);
|
||||
}
|
||||
else {
|
||||
references.push(getReferenceEntryFromNode(node));
|
||||
references.push(context.getReferenceEntryFromNode(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -658,21 +705,21 @@ namespace ts.FindAllReferences {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getImplementationReferenceEntryForNode(refNode: Node, result: ReferenceEntry[], typeChecker: TypeChecker): void {
|
||||
function getImplementationReferenceEntryForNode<T extends DocumentSpan>(refNode: Node, result: T[], context: FindReferencesContext<T>): void {
|
||||
// Check if we found a function/propertyAssignment/method with an implementation or initializer
|
||||
if (isDeclarationName(refNode) && isImplementation(refNode.parent)) {
|
||||
result.push(getReferenceEntryFromNode(refNode.parent));
|
||||
result.push(context.getReferenceEntryFromNode(refNode.parent));
|
||||
}
|
||||
else if (refNode.kind === SyntaxKind.Identifier) {
|
||||
if (refNode.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
// Go ahead and dereference the shorthand assignment by going to its definition
|
||||
getReferenceEntriesForShorthandPropertyAssignment(refNode, typeChecker, result);
|
||||
getReferenceEntriesForShorthandPropertyAssignment(refNode, context, result);
|
||||
}
|
||||
|
||||
// Check if the node is within an extends or implements clause
|
||||
const containingClass = getContainingClassIfInHeritageClause(refNode);
|
||||
if (containingClass) {
|
||||
result.push(getReferenceEntryFromNode(containingClass));
|
||||
result.push(context.getReferenceEntryFromNode(containingClass));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -681,22 +728,22 @@ namespace ts.FindAllReferences {
|
||||
if (containingTypeReference) {
|
||||
const parent = containingTypeReference.parent;
|
||||
if (isVariableLike(parent) && parent.type === containingTypeReference && parent.initializer && isImplementationExpression(parent.initializer)) {
|
||||
maybeAdd(getReferenceEntryFromNode(parent.initializer));
|
||||
maybeAdd(context.getReferenceEntryFromNode(parent.initializer));
|
||||
}
|
||||
else if (isFunctionLike(parent) && parent.type === containingTypeReference && parent.body) {
|
||||
if (parent.body.kind === SyntaxKind.Block) {
|
||||
forEachReturnStatement(<Block>parent.body, returnStatement => {
|
||||
if (returnStatement.expression && isImplementationExpression(returnStatement.expression)) {
|
||||
maybeAdd(getReferenceEntryFromNode(returnStatement.expression));
|
||||
maybeAdd(context.getReferenceEntryFromNode(returnStatement.expression));
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (isImplementationExpression(<Expression>parent.body)) {
|
||||
maybeAdd(getReferenceEntryFromNode(parent.body));
|
||||
maybeAdd(context.getReferenceEntryFromNode(parent.body));
|
||||
}
|
||||
}
|
||||
else if (isAssertionExpression(parent) && isImplementationExpression(parent.expression)) {
|
||||
maybeAdd(getReferenceEntryFromNode(parent.expression));
|
||||
maybeAdd(context.getReferenceEntryFromNode(parent.expression));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -706,7 +753,7 @@ namespace ts.FindAllReferences {
|
||||
// Because we are returning the implementation locations and not the identifier locations,
|
||||
// duplicate entries would be returned here as each of the type references is part of
|
||||
// the same implementation. For that reason, check before we add a new entry
|
||||
function maybeAdd(a: ReferenceEntry) {
|
||||
function maybeAdd(a: T) {
|
||||
if (!forEach(result, b => a.fileName === b.fileName && a.textSpan.start === b.textSpan.start && a.textSpan.length === b.textSpan.length)) {
|
||||
result.push(a);
|
||||
}
|
||||
@ -845,7 +892,7 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencesForSuperKeyword(superKeyword: Node, typeChecker: TypeChecker, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
function getReferencesForSuperKeyword<T extends DocumentSpan>(superKeyword: Node, context: FindReferencesContext<T>): ReferencedSymbolOf<T>[] {
|
||||
let searchSpaceNode = getSuperContainer(superKeyword, /*stopOnFunctions*/ false);
|
||||
if (!searchSpaceNode) {
|
||||
return undefined;
|
||||
@ -868,7 +915,8 @@ namespace ts.FindAllReferences {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const references: ReferenceEntry[] = [];
|
||||
const references: T[] = [];
|
||||
const { typeChecker, cancellationToken } = context;
|
||||
|
||||
const sourceFile = searchSpaceNode.getSourceFile();
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode.getStart(), searchSpaceNode.getEnd(), cancellationToken);
|
||||
@ -887,7 +935,7 @@ namespace ts.FindAllReferences {
|
||||
// Now make sure the owning class is the same as the search-space
|
||||
// and has the same static qualifier as the original 'super's owner.
|
||||
if (container && (ModifierFlags.Static & getModifierFlags(container)) === staticFlag && container.parent.symbol === searchSpaceNode.symbol) {
|
||||
references.push(getReferenceEntryFromNode(node));
|
||||
references.push(context.getReferenceEntryFromNode(node));
|
||||
}
|
||||
}
|
||||
|
||||
@ -895,8 +943,9 @@ namespace ts.FindAllReferences {
|
||||
return [{ definition, references }];
|
||||
}
|
||||
|
||||
function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: SourceFile[], typeChecker: TypeChecker, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
function getReferencesForThisKeyword<T extends DocumentSpan>(thisOrSuperKeyword: Node, sourceFiles: SourceFile[], context: FindReferencesContext<T>): ReferencedSymbolOf<T>[] {
|
||||
let searchSpaceNode = getThisContainer(thisOrSuperKeyword, /* includeArrowFunctions */ false);
|
||||
const { typeChecker, cancellationToken } = context;
|
||||
|
||||
// Whether 'this' occurs in a static context within a class.
|
||||
let staticFlag = ModifierFlags.Static;
|
||||
@ -930,7 +979,7 @@ namespace ts.FindAllReferences {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const references: ReferenceEntry[] = [];
|
||||
const references: T[] = [];
|
||||
|
||||
let possiblePositions: number[];
|
||||
if (searchSpaceNode.kind === SyntaxKind.SourceFile) {
|
||||
@ -963,7 +1012,7 @@ namespace ts.FindAllReferences {
|
||||
references: references
|
||||
}];
|
||||
|
||||
function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: number[], result: ReferenceEntry[]): void {
|
||||
function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: number[], result: T[]): void {
|
||||
forEach(possiblePositions, position => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
@ -978,13 +1027,13 @@ namespace ts.FindAllReferences {
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
if (searchSpaceNode.symbol === container.symbol) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
result.push(context.getReferenceEntryFromNode(node));
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
if (isObjectLiteralMethod(searchSpaceNode) && searchSpaceNode.symbol === container.symbol) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
result.push(context.getReferenceEntryFromNode(node));
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ClassExpression:
|
||||
@ -992,12 +1041,12 @@ namespace ts.FindAllReferences {
|
||||
// Make sure the container belongs to the same class
|
||||
// and has the appropriate static modifier from the original container.
|
||||
if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (getModifierFlags(container) & ModifierFlags.Static) === staticFlag) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
result.push(context.getReferenceEntryFromNode(node));
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.SourceFile:
|
||||
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
result.push(context.getReferenceEntryFromNode(node));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1005,7 +1054,8 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencesForStringLiteral(node: StringLiteral, sourceFiles: SourceFile[], typeChecker: TypeChecker, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
function getReferencesForStringLiteral<T extends DocumentSpan>(node: StringLiteral, sourceFiles: SourceFile[], context: FindReferencesContext<T>): ReferencedSymbolOf<T>[] {
|
||||
const { typeChecker, cancellationToken } = context;
|
||||
const type = getStringLiteralTypeForNode(node, typeChecker);
|
||||
|
||||
if (!type) {
|
||||
@ -1013,7 +1063,7 @@ namespace ts.FindAllReferences {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const references: ReferenceEntry[] = [];
|
||||
const references: T[] = [];
|
||||
|
||||
for (const sourceFile of sourceFiles) {
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, type.text, sourceFile.getStart(), sourceFile.getEnd(), cancellationToken);
|
||||
@ -1033,7 +1083,7 @@ namespace ts.FindAllReferences {
|
||||
references: references
|
||||
}];
|
||||
|
||||
function getReferencesForStringLiteralInFile(sourceFile: SourceFile, searchType: Type, possiblePositions: number[], references: ReferenceEntry[]): void {
|
||||
function getReferencesForStringLiteralInFile(sourceFile: SourceFile, searchType: Type, possiblePositions: number[], references: T[]): void {
|
||||
for (const position of possiblePositions) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
@ -1044,7 +1094,7 @@ namespace ts.FindAllReferences {
|
||||
|
||||
const type = getStringLiteralTypeForNode(<StringLiteral>node, typeChecker);
|
||||
if (type === searchType) {
|
||||
references.push(getReferenceEntryFromNode(node));
|
||||
references.push(context.getReferenceEntryFromNode(node, /*isInString*/ true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1357,14 +1407,15 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
export function getReferenceEntriesForShorthandPropertyAssignment(node: Node, typeChecker: TypeChecker, result: ReferenceEntry[]): void {
|
||||
export function getReferenceEntriesForShorthandPropertyAssignment<T extends DocumentSpan>(node: Node, context: FindReferencesContext<T>, result: T[]): void {
|
||||
const { typeChecker } = context;
|
||||
const refSymbol = typeChecker.getSymbolAtLocation(node);
|
||||
const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(refSymbol.valueDeclaration);
|
||||
|
||||
if (shorthandSymbol) {
|
||||
for (const declaration of shorthandSymbol.getDeclarations()) {
|
||||
if (getMeaningFromDeclaration(declaration) & SemanticMeaning.Value) {
|
||||
result.push(getReferenceEntryFromNode(declaration));
|
||||
result.push(context.getReferenceEntryFromNode(declaration));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,25 +1,25 @@
|
||||
/* @internal */
|
||||
namespace ts.GoToImplementation {
|
||||
export function getImplementationAtPosition(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], node: Node): ImplementationLocation[] {
|
||||
const context = new FindAllReferences.FindImplementationsContext(typeChecker, cancellationToken);
|
||||
// If invoked directly on a shorthand property assignment, then return
|
||||
// the declaration of the symbol being assigned (not the symbol being assigned to).
|
||||
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const result: ReferenceEntry[] = [];
|
||||
FindAllReferences.getReferenceEntriesForShorthandPropertyAssignment(node, typeChecker, result);
|
||||
const result: ImplementationLocation[] = [];
|
||||
FindAllReferences.getReferenceEntriesForShorthandPropertyAssignment(node, context, result);
|
||||
return result.length > 0 ? result : undefined;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.SuperKeyword || isSuperProperty(node.parent)) {
|
||||
// References to and accesses on the super keyword only have one possible implementation, so no
|
||||
// need to "Find all References"
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
return symbol.valueDeclaration && [FindAllReferences.getReferenceEntryFromNode(symbol.valueDeclaration)];
|
||||
return symbol.valueDeclaration && [context.getReferenceEntryFromNode(symbol.valueDeclaration)];
|
||||
}
|
||||
else {
|
||||
// Perform "Find all References" and retrieve only those that are implementations
|
||||
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken,
|
||||
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(context,
|
||||
node, sourceFiles, /*findInStrings*/false, /*findInComments*/false, /*isForRename*/false, /*implementations*/true);
|
||||
const result = flatMap(referencedSymbols, symbol =>
|
||||
map(symbol.references, ({ textSpan, fileName }) => ({ textSpan, fileName })));
|
||||
const result = flatMap(referencedSymbols, symbol => symbol.references);
|
||||
|
||||
return result && result.length > 0 ? result : undefined;
|
||||
}
|
||||
|
||||
@ -1396,7 +1396,8 @@ namespace ts {
|
||||
fileName: entry.fileName,
|
||||
textSpan: highlightSpan.textSpan,
|
||||
isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
|
||||
isDefinition: false
|
||||
isDefinition: false,
|
||||
isInString: highlightSpan.isInString,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,21 +354,23 @@ namespace ts {
|
||||
caretOffset: number;
|
||||
}
|
||||
|
||||
export interface RenameLocation {
|
||||
export interface DocumentSpan {
|
||||
textSpan: TextSpan;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export interface ReferenceEntry {
|
||||
textSpan: TextSpan;
|
||||
fileName: string;
|
||||
export interface RenameLocation extends DocumentSpan {
|
||||
}
|
||||
|
||||
export interface ReferenceEntry extends DocumentSpan {
|
||||
isWriteAccess: boolean;
|
||||
isDefinition: boolean;
|
||||
isInString?: true;
|
||||
}
|
||||
|
||||
export interface ImplementationLocation {
|
||||
textSpan: TextSpan;
|
||||
fileName: string;
|
||||
export interface ImplementationLocation extends DocumentSpan {
|
||||
kind: string;
|
||||
displayParts: SymbolDisplayPart[];
|
||||
}
|
||||
|
||||
export interface DocumentHighlights {
|
||||
@ -385,6 +387,7 @@ namespace ts {
|
||||
|
||||
export interface HighlightSpan {
|
||||
fileName?: string;
|
||||
isInString?: true;
|
||||
textSpan: TextSpan;
|
||||
kind: string;
|
||||
}
|
||||
@ -476,9 +479,12 @@ namespace ts {
|
||||
displayParts: SymbolDisplayPart[];
|
||||
}
|
||||
|
||||
export interface ReferencedSymbol {
|
||||
export interface ReferencedSymbolOf<T extends DocumentSpan> {
|
||||
definition: ReferencedSymbolDefinitionInfo;
|
||||
references: ReferenceEntry[];
|
||||
references: T[];
|
||||
}
|
||||
|
||||
export interface ReferencedSymbol extends ReferencedSymbolOf<ReferenceEntry> {
|
||||
}
|
||||
|
||||
export enum SymbolDisplayPartKind {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["es5", "scripthost"],
|
||||
"lib": ["es5"],
|
||||
"noEmitOnError": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
@ -13,4 +13,4 @@
|
||||
"target": "es5",
|
||||
"types": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////type Options = "[|option 1|]" | "option 2";
|
||||
////let myOption: Options = "[|option 1|]";
|
||||
////type Options = "[|{| "isInString": true |}option 1|]" | "option 2";
|
||||
////let myOption: Options = "[|{| "isInString": true |}option 1|]";
|
||||
|
||||
verify.singleReferenceGroup('"option 1"');
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// Should handle calls made on members declared in a class
|
||||
|
||||
//// class Bar {
|
||||
//// [|hello() {}|]
|
||||
//// [|{|"parts": ["(","method",")"," ","Bar",".","hello","(",")",":"," ","void"], "kind": "method"|}hello() {}|]
|
||||
//// }
|
||||
////
|
||||
//// new Bar().hel/*reference*/lo;
|
||||
|
||||
@ -8,12 +8,12 @@
|
||||
////
|
||||
//// interface Baz extends Foo {}
|
||||
////
|
||||
//// var bar: Foo = [|{ hello: helloImpl /**0*/ }|];
|
||||
//// var bar: Foo = [|{|"parts": ["(","object literal",")"], "kind": "interface"|}{ hello: helloImpl /**0*/ }|];
|
||||
//// var baz: Foo[] = [|[{ hello: helloImpl /**4*/ }]|];
|
||||
////
|
||||
//// function helloImpl () {}
|
||||
////
|
||||
//// function whatever(x: Foo = [|{ hello() {/**1*/} }|] ) {
|
||||
//// function whatever(x: Foo = [|{|"parts": ["(","object literal",")"], "kind": "interface"|}{ hello() {/**1*/} }|] ) {
|
||||
//// }
|
||||
////
|
||||
//// class Bar {
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
//// let x7: (new() => Foo) = [|class { hello () { /**constructorType*/} }|];
|
||||
//// let x8: Foo[] = [|[{ hello () { /**arrayType*/} }]|];
|
||||
//// let x9: { y: Foo } = [|{ y: { hello () { /**typeLiteral*/} } }|];
|
||||
//// let x10 = [|{|"parts": ["(","anonymous local class",")"], "kind": "local class"|}class implements Foo { hello() {} }|]
|
||||
//// let x11 = [|{|"parts": ["(","local class",")"," ","C"], "kind": "local class"|}class C implements Foo { hello() {} }|]
|
||||
////
|
||||
//// // Should not do anything for type predicates
|
||||
//// function isFoo(a: any): a is Foo {
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
// References to a unknown index property
|
||||
|
||||
////var a;
|
||||
////a["[|blah|]"];
|
||||
////a["[|{| "isInString": true |}blah|]"];
|
||||
|
||||
verify.singleReferenceGroup('"blah"');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user