Add support for Call Hierarchies in language server (#35176)

* Add support for Call Hierarchies in language server

* Use baselines for callHierarchy tests

* Clean up commented code

* Support multiple hierarchy items when an implementation can't be found

* Use optional chaining in a few places

* Use getFileAndProject
This commit is contained in:
Ron Buckton 2019-12-22 13:25:09 -08:00 committed by GitHub
parent 114dad7f56
commit 6c413e0bbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 2712 additions and 55 deletions

View File

@ -630,10 +630,18 @@ namespace ts {
return [...array1, ...array2];
}
function selectIndex(_: unknown, i: number) {
return i;
}
export function indicesOf(array: readonly unknown[]): number[] {
return array.map(selectIndex);
}
function deduplicateRelational<T>(array: readonly T[], equalityComparer: EqualityComparer<T>, comparer: Comparer<T>) {
// Perform a stable sort of the array. This ensures the first entry in a list of
// duplicates remains the first entry in the result.
const indices = array.map((_, i) => i);
const indices = indicesOf(array);
stableSortIndices(array, indices, comparer);
let last = array[indices[0]];
@ -939,7 +947,7 @@ namespace ts {
* Stable sort of an array. Elements equal to each other maintain their relative position in the array.
*/
export function stableSort<T>(array: readonly T[], comparer: Comparer<T>): SortedReadonlyArray<T> {
const indices = array.map((_, i) => i);
const indices = indicesOf(array);
stableSortIndices(array, indices, comparer);
return indices.map(i => array[i]) as SortedArray<T> as SortedReadonlyArray<T>;
}
@ -1245,8 +1253,10 @@ namespace ts {
return result;
}
export function group<T>(values: readonly T[], getGroupId: (value: T) => string): readonly (readonly T[])[] {
return arrayFrom(arrayToMultiMap(values, getGroupId).values());
export function group<T>(values: readonly T[], getGroupId: (value: T) => string): readonly (readonly T[])[];
export function group<T, R>(values: readonly T[], getGroupId: (value: T) => string, resultSelector: (values: readonly T[]) => R): R[];
export function group<T>(values: readonly T[], getGroupId: (value: T) => string, resultSelector: (values: readonly T[]) => readonly T[] = identity): readonly (readonly T[])[] {
return arrayFrom(arrayToMultiMap(values, getGroupId).values(), resultSelector);
}
export function clone<T>(object: T): T {

View File

@ -743,6 +743,51 @@ namespace ts.server {
return notImplemented();
}
private convertCallHierarchyItem(item: protocol.CallHierarchyItem): CallHierarchyItem {
return {
file: item.file,
name: item.name,
kind: item.kind,
span: this.decodeSpan(item.span, item.file),
selectionSpan: this.decodeSpan(item.selectionSpan, item.file)
};
}
prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined {
const args = this.createFileLocationRequestArgs(fileName, position);
const request = this.processRequest<protocol.PrepareCallHierarchyRequest>(CommandNames.PrepareCallHierarchy, args);
const response = this.processResponse<protocol.PrepareCallHierarchyResponse>(request);
return response.body && mapOneOrMany(response.body, item => this.convertCallHierarchyItem(item));
}
private convertCallHierarchyIncomingCall(item: protocol.CallHierarchyIncomingCall): CallHierarchyIncomingCall {
return {
from: this.convertCallHierarchyItem(item.from),
fromSpans: item.fromSpans.map(span => this.decodeSpan(span, item.from.file))
};
}
provideCallHierarchyIncomingCalls(fileName: string, position: number) {
const args = this.createFileLocationRequestArgs(fileName, position);
const request = this.processRequest<protocol.ProvideCallHierarchyIncomingCallsRequest>(CommandNames.PrepareCallHierarchy, args);
const response = this.processResponse<protocol.ProvideCallHierarchyIncomingCallsResponse>(request);
return response.body.map(item => this.convertCallHierarchyIncomingCall(item));
}
private convertCallHierarchyOutgoingCall(file: string, item: protocol.CallHierarchyOutgoingCall): CallHierarchyOutgoingCall {
return {
to: this.convertCallHierarchyItem(item.to),
fromSpans: item.fromSpans.map(span => this.decodeSpan(span, file))
};
}
provideCallHierarchyOutgoingCalls(fileName: string, position: number) {
const args = this.createFileLocationRequestArgs(fileName, position);
const request = this.processRequest<protocol.ProvideCallHierarchyOutgoingCallsRequest>(CommandNames.PrepareCallHierarchy, args);
const response = this.processResponse<protocol.ProvideCallHierarchyOutgoingCallsResponse>(request);
return response.body.map(item => this.convertCallHierarchyOutgoingCall(fileName, item));
}
getProgram(): Program {
throw new Error("SourceFile objects are not serializable through the server protocol.");
}

View File

@ -147,6 +147,12 @@ namespace FourSlash {
return ts.ScriptSnapshot.fromString(sourceText);
}
const enum CallHierarchyItemDirection {
Root,
Incoming,
Outgoing
}
export class TestState {
// Language service instance
private languageServiceAdapterHost: Harness.LanguageService.LanguageServiceAdapterHost;
@ -734,11 +740,8 @@ namespace FourSlash {
if (!range) {
this.raiseError(`goToDefinitionsAndBoundSpan failed - found a TextSpan ${JSON.stringify(defs.textSpan)} when it wasn't expected.`);
}
else if (defs.textSpan.start !== range.pos || defs.textSpan.length !== range.end - range.pos) {
const expected: ts.TextSpan = {
start: range.pos, length: range.end - range.pos
};
this.raiseError(`goToDefinitionsAndBoundSpan failed - expected to find TextSpan ${JSON.stringify(expected)} but got ${JSON.stringify(defs.textSpan)}`);
else {
this.assertTextSpanEqualsRange(defs.textSpan, range, "goToDefinitionsAndBoundSpan failed");
}
}
@ -1411,18 +1414,91 @@ namespace FourSlash {
private alignmentForExtraInfo = 50;
private spanInfoToString(spanInfo: ts.TextSpan, prefixString: string) {
private spanLines(file: FourSlashFile, spanInfo: ts.TextSpan, { selection = false, fullLines = false, lineNumbers = false } = {}) {
if (selection) {
fullLines = true;
}
let contextStartPos = spanInfo.start;
let contextEndPos = contextStartPos + spanInfo.length;
if (fullLines) {
if (contextStartPos > 0) {
while (contextStartPos > 1) {
const ch = file.content.charCodeAt(contextStartPos - 1);
if (ch === ts.CharacterCodes.lineFeed || ch === ts.CharacterCodes.carriageReturn) {
break;
}
contextStartPos--;
}
}
if (contextEndPos < file.content.length) {
while (contextEndPos < file.content.length - 1) {
const ch = file.content.charCodeAt(contextEndPos);
if (ch === ts.CharacterCodes.lineFeed || ch === ts.CharacterCodes.carriageReturn) {
break;
}
contextEndPos++;
}
}
}
let contextString: string;
let contextLineMap: number[];
let contextStart: ts.LineAndCharacter;
let contextEnd: ts.LineAndCharacter;
let selectionStart: ts.LineAndCharacter;
let selectionEnd: ts.LineAndCharacter;
let lineNumberPrefixLength: number;
if (lineNumbers) {
contextString = file.content;
contextLineMap = ts.computeLineStarts(contextString);
contextStart = ts.computeLineAndCharacterOfPosition(contextLineMap, contextStartPos);
contextEnd = ts.computeLineAndCharacterOfPosition(contextLineMap, contextEndPos);
selectionStart = ts.computeLineAndCharacterOfPosition(contextLineMap, spanInfo.start);
selectionEnd = ts.computeLineAndCharacterOfPosition(contextLineMap, ts.textSpanEnd(spanInfo));
lineNumberPrefixLength = (contextEnd.line + 1).toString().length + 2;
}
else {
contextString = file.content.substring(contextStartPos, contextEndPos);
contextLineMap = ts.computeLineStarts(contextString);
contextStart = { line: 0, character: 0 };
contextEnd = { line: contextLineMap.length - 1, character: 0 };
selectionStart = selection ? ts.computeLineAndCharacterOfPosition(contextLineMap, spanInfo.start - contextStartPos) : contextStart;
selectionEnd = selection ? ts.computeLineAndCharacterOfPosition(contextLineMap, ts.textSpanEnd(spanInfo) - contextStartPos) : contextEnd;
lineNumberPrefixLength = 0;
}
const output: string[] = [];
for (let lineNumber = contextStart.line; lineNumber <= contextEnd.line; lineNumber++) {
const spanLine = contextString.substring(contextLineMap[lineNumber], contextLineMap[lineNumber + 1]);
output.push(lineNumbers ? `${`${lineNumber + 1}: `.padStart(lineNumberPrefixLength, " ")}${spanLine}` : spanLine);
if (selection) {
if (lineNumber < selectionStart.line || lineNumber > selectionEnd.line) {
continue;
}
const isEmpty = selectionStart.line === selectionEnd.line && selectionStart.character === selectionEnd.character;
const selectionPadLength = lineNumber === selectionStart.line ? selectionStart.character : 0;
const selectionPad = " ".repeat(selectionPadLength + lineNumberPrefixLength);
const selectionLength = isEmpty ? 0 : Math.max(lineNumber < selectionEnd.line ? spanLine.trimRight().length - selectionPadLength : selectionEnd.character - selectionPadLength, 1);
const selectionLine = isEmpty ? "<" : "^".repeat(selectionLength);
output.push(`${selectionPad}${selectionLine}`);
}
}
return output;
}
private spanInfoToString(spanInfo: ts.TextSpan, prefixString: string, file: FourSlashFile = this.activeFile) {
let resultString = "SpanInfo: " + JSON.stringify(spanInfo);
if (spanInfo) {
const spanString = this.activeFile.content.substr(spanInfo.start, spanInfo.length);
const spanLineMap = ts.computeLineStarts(spanString);
for (let i = 0; i < spanLineMap.length; i++) {
const spanLines = this.spanLines(file, spanInfo);
for (let i = 0; i < spanLines.length; i++) {
if (!i) {
resultString += "\n";
}
resultString += prefixString + spanString.substring(spanLineMap[i], spanLineMap[i + 1]);
resultString += prefixString + spanLines[i];
}
resultString += "\n" + prefixString + ":=> (" + this.getLineColStringAtPosition(spanInfo.start) + ") to (" + this.getLineColStringAtPosition(ts.textSpanEnd(spanInfo)) + ")";
resultString += "\n" + prefixString + ":=> (" + this.getLineColStringAtPosition(spanInfo.start, file) + ") to (" + this.getLineColStringAtPosition(ts.textSpanEnd(spanInfo), file) + ")";
}
return resultString;
@ -1701,13 +1777,13 @@ namespace FourSlash {
Harness.IO.log(stringify(help.items[help.selectedItemIndex]));
}
private getBaselineFileNameForInternalFourslashFile() {
private getBaselineFileNameForInternalFourslashFile(ext = ".baseline") {
return this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ext);
}
private getBaselineFileNameForContainingTestFile() {
return ts.getBaseFileName(this.originalInputFileName).replace(ts.Extension.Ts, ".baseline");
private getBaselineFileNameForContainingTestFile(ext = ".baseline") {
return ts.getBaseFileName(this.originalInputFileName).replace(ts.Extension.Ts, ext);
}
private getSignatureHelp({ triggerReason }: FourSlashInterface.VerifySignatureHelpOptions): ts.SignatureHelpItems | undefined {
@ -3164,6 +3240,131 @@ namespace FourSlash {
Harness.IO.log(stringify(codeFixes));
}
private formatCallHierarchyItemSpan(file: FourSlashFile, span: ts.TextSpan, prefix: string, trailingPrefix = prefix) {
const startLc = this.languageServiceAdapterHost.positionToLineAndCharacter(file.fileName, span.start);
const endLc = this.languageServiceAdapterHost.positionToLineAndCharacter(file.fileName, ts.textSpanEnd(span));
const lines = this.spanLines(file, span, { fullLines: true, lineNumbers: true, selection: true });
let text = "";
text += `${prefix}${file.fileName}:${startLc.line + 1}:${startLc.character + 1}-${endLc.line + 1}:${endLc.character + 1}\n`;
for (const line of lines) {
text += `${prefix}${line.trimRight()}\n`;
}
text += `${trailingPrefix}\n`;
return text;
}
private formatCallHierarchyItemSpans(file: FourSlashFile, spans: ts.TextSpan[], prefix: string, trailingPrefix = prefix) {
let text = "";
for (let i = 0; i < spans.length; i++) {
text += this.formatCallHierarchyItemSpan(file, spans[i], prefix, i < spans.length - 1 ? prefix : trailingPrefix);
}
return text;
}
private formatCallHierarchyItem(file: FourSlashFile, callHierarchyItem: ts.CallHierarchyItem, direction: CallHierarchyItemDirection, seen: ts.Map<boolean>, prefix: string, trailingPrefix: string = prefix) {
const key = `${callHierarchyItem.file}|${JSON.stringify(callHierarchyItem.span)}|${direction}`;
const alreadySeen = seen.has(key);
seen.set(key, true);
const incomingCalls =
direction === CallHierarchyItemDirection.Outgoing ? { result: "skip" } as const :
alreadySeen ? { result: "seen" } as const :
{ result: "show", values: this.languageService.provideCallHierarchyIncomingCalls(callHierarchyItem.file, callHierarchyItem.selectionSpan.start) } as const;
const outgoingCalls =
direction === CallHierarchyItemDirection.Incoming ? { result: "skip" } as const :
alreadySeen ? { result: "seen" } as const :
{ result: "show", values: this.languageService.provideCallHierarchyOutgoingCalls(callHierarchyItem.file, callHierarchyItem.selectionSpan.start) } as const;
let text = "";
text += `${prefix}╭ name: ${callHierarchyItem.name}\n`;
text += `${prefix}├ kind: ${callHierarchyItem.kind}\n`;
text += `${prefix}├ file: ${callHierarchyItem.file}\n`;
text += `${prefix}├ span:\n`;
text += this.formatCallHierarchyItemSpan(file, callHierarchyItem.span, `${prefix}`);
text += `${prefix}├ selectionSpan:\n`;
text += this.formatCallHierarchyItemSpan(file, callHierarchyItem.selectionSpan, `${prefix}`,
incomingCalls.result !== "skip" || outgoingCalls.result !== "skip" ? `${prefix}` :
`${trailingPrefix}`);
if (incomingCalls.result === "seen") {
if (outgoingCalls.result === "skip") {
text += `${trailingPrefix}╰ incoming: ...\n`;
}
else {
text += `${prefix}├ incoming: ...\n`;
}
}
else if (incomingCalls.result === "show") {
if (!ts.some(incomingCalls.values)) {
if (outgoingCalls.result === "skip") {
text += `${trailingPrefix}╰ incoming: none\n`;
}
else {
text += `${prefix}├ incoming: none\n`;
}
}
else {
text += `${prefix}├ incoming:\n`;
for (let i = 0; i < incomingCalls.values.length; i++) {
const incomingCall = incomingCalls.values[i];
const file = this.findFile(incomingCall.from.file);
text += `${prefix}│ ╭ from:\n`;
text += this.formatCallHierarchyItem(file, incomingCall.from, CallHierarchyItemDirection.Incoming, seen, `${prefix}│ │ `);
text += `${prefix}│ ├ fromSpans:\n`;
text += this.formatCallHierarchyItemSpans(file, incomingCall.fromSpans, `${prefix}│ │ `,
i < incomingCalls.values.length - 1 ? `${prefix}│ ╰ ` :
outgoingCalls.result !== "skip" ? `${prefix}│ ╰ ` :
`${trailingPrefix}╰ ╰ `);
}
}
}
if (outgoingCalls.result === "seen") {
text += `${trailingPrefix}╰ outgoing: ...\n`;
}
else if (outgoingCalls.result === "show") {
if (!ts.some(outgoingCalls.values)) {
text += `${trailingPrefix}╰ outgoing: none\n`;
}
else {
text += `${prefix}├ outgoing:\n`;
for (let i = 0; i < outgoingCalls.values.length; i++) {
const outgoingCall = outgoingCalls.values[i];
text += `${prefix}│ ╭ to:\n`;
text += this.formatCallHierarchyItem(this.findFile(outgoingCall.to.file), outgoingCall.to, CallHierarchyItemDirection.Outgoing, seen, `${prefix}│ │ `);
text += `${prefix}│ ├ fromSpans:\n`;
text += this.formatCallHierarchyItemSpans(file, outgoingCall.fromSpans, `${prefix}│ │ `,
i < outgoingCalls.values.length - 1 ? `${prefix}│ ╰ ` :
`${trailingPrefix}╰ ╰ `);
}
}
}
return text;
}
private formatCallHierarchy(callHierarchyItem: ts.CallHierarchyItem | undefined) {
let text = "";
if (callHierarchyItem) {
const file = this.findFile(callHierarchyItem.file);
text += this.formatCallHierarchyItem(file, callHierarchyItem, CallHierarchyItemDirection.Root, ts.createMap(), "");
}
return text;
}
public baselineCallHierarchy() {
const baselineFile = this.getBaselineFileNameForContainingTestFile(".callHierarchy.txt");
const callHierarchyItem = this.languageService.prepareCallHierarchy(this.activeFile.fileName, this.currentCaretPosition);
const text = callHierarchyItem ? ts.mapOneOrMany(callHierarchyItem, item => this.formatCallHierarchy(item), result => result.join("")) : "none";
Harness.Baseline.runBaseline(baselineFile, text);
}
private assertTextSpanEqualsRange(span: ts.TextSpan, range: Range, message?: string) {
if (!textSpanEqualsRange(span, range)) {
this.raiseError(`${prefixMessage(message)}Expected to find TextSpan ${JSON.stringify({ start: range.pos, length: range.end - range.pos })} but got ${JSON.stringify(span)} instead.`);
}
}
private getLineContent(index: number) {
const text = this.getFileContent(this.activeFile.fileName);
const pos = this.languageServiceAdapterHost.lineAndCharacterToPosition(this.activeFile.fileName, { line: index, character: 0 });
@ -3243,8 +3444,8 @@ namespace FourSlash {
return this.tryFindFileWorker(name).file !== undefined;
}
private getLineColStringAtPosition(position: number) {
const pos = this.languageServiceAdapterHost.positionToLineAndCharacter(this.activeFile.fileName, position);
private getLineColStringAtPosition(position: number, file: FourSlashFile = this.activeFile) {
const pos = this.languageServiceAdapterHost.positionToLineAndCharacter(file.fileName, position);
return `line ${(pos.line + 1)}, col ${pos.character}`;
}
@ -3297,6 +3498,14 @@ namespace FourSlash {
}
}
function prefixMessage(message: string | undefined) {
return message ? `${message} - ` : "";
}
function textSpanEqualsRange(span: ts.TextSpan, range: Range) {
return span.start === range.pos && span.length === range.end - range.pos;
}
function updateTextRangeForTextChanges({ pos, end }: ts.TextRange, textChanges: readonly ts.TextChange[]): ts.TextRange {
forEachTextChange(textChanges, change => {
const update = (p: number): number => updatePosition(p, change.span.start, ts.textSpanEnd(change.span), change.newText);

View File

@ -541,9 +541,14 @@ namespace FourSlashInterface {
this.state.getEditsForFileRename(options);
}
public baselineCallHierarchy() {
this.state.baselineCallHierarchy();
}
public moveToNewFile(options: MoveToNewFileOptions): void {
this.state.moveToNewFile(options);
}
public noMoveToNewFile(): void {
this.state.noMoveToNewFile();
}

View File

@ -574,6 +574,15 @@ namespace Harness.LanguageService {
getEditsForFileRename(): readonly ts.FileTextChanges[] {
throw new Error("Not supported on the shim.");
}
prepareCallHierarchy(fileName: string, position: number) {
return unwrapJSONCallResult(this.shim.prepareCallHierarchy(fileName, position));
}
provideCallHierarchyIncomingCalls(fileName: string, position: number) {
return unwrapJSONCallResult(this.shim.provideCallHierarchyIncomingCalls(fileName, position));
}
provideCallHierarchyOutgoingCalls(fileName: string, position: number) {
return unwrapJSONCallResult(this.shim.provideCallHierarchyOutgoingCalls(fileName, position));
}
getEmitOutput(fileName: string): ts.EmitOutput {
return unwrapJSONCallResult(this.shim.getEmitOutput(fileName));
}

View File

@ -4,7 +4,7 @@
* Declaration module describing the TypeScript Server protocol
*/
namespace ts.server.protocol {
// NOTE: If updating this, be sure to also update `allCommandNames` in `harness/unittests/session.ts`.
// NOTE: If updating this, be sure to also update `allCommandNames` in `testRunner/unittests/tsserver/session.ts`.
export const enum CommandTypes {
JsxClosingTag = "jsxClosingTag",
Brace = "brace",
@ -137,7 +137,11 @@ namespace ts.server.protocol {
/* @internal */
SelectionRangeFull = "selectionRange-full",
// NOTE: If updating this, be sure to also update `allCommandNames` in `harness/unittests/session.ts`.
PrepareCallHierarchy = "prepareCallHierarchy",
ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls",
ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls",
// NOTE: If updating this, be sure to also update `allCommandNames` in `testRunner/unittests/tsserver/session.ts`.
}
/**
@ -2983,6 +2987,48 @@ namespace ts.server.protocol {
body?: NavigationTree;
}
export interface CallHierarchyItem {
name: string;
kind: ScriptElementKind;
file: string;
span: TextSpan;
selectionSpan: TextSpan;
}
export interface CallHierarchyIncomingCall {
from: CallHierarchyItem;
fromSpans: TextSpan[];
}
export interface CallHierarchyOutgoingCall {
to: CallHierarchyItem;
fromSpans: TextSpan[];
}
export interface PrepareCallHierarchyRequest extends FileLocationRequest {
command: CommandTypes.PrepareCallHierarchy;
}
export interface PrepareCallHierarchyResponse extends Response {
readonly body: CallHierarchyItem | CallHierarchyItem[];
}
export interface ProvideCallHierarchyIncomingCallsRequest extends FileLocationRequest {
command: CommandTypes.ProvideCallHierarchyIncomingCalls;
}
export interface ProvideCallHierarchyIncomingCallsResponse extends Response {
readonly body: CallHierarchyIncomingCall[];
}
export interface ProvideCallHierarchyOutgoingCallsRequest extends FileLocationRequest {
command: CommandTypes.ProvideCallHierarchyOutgoingCalls;
}
export interface ProvideCallHierarchyOutgoingCallsResponse extends Response {
readonly body: CallHierarchyOutgoingCall[];
}
export const enum IndentStyle {
None = "None",
Block = "Block",

View File

@ -1060,7 +1060,7 @@ namespace ts.server {
if (simplifiedResult) {
return {
definitions: this.mapDefinitionInfo(definitions, project),
textSpan: toProcolTextSpan(textSpan, scriptInfo)
textSpan: toProtocolTextSpan(textSpan, scriptInfo)
};
}
@ -1315,7 +1315,7 @@ namespace ts.server {
if (info.canRename) {
const { canRename, fileToRename, displayName, fullDisplayName, kind, kindModifiers, triggerSpan } = info;
return identity<protocol.RenameInfoSuccess>(
{ canRename, fileToRename, displayName, fullDisplayName, kind, kindModifiers, triggerSpan: toProcolTextSpan(triggerSpan, scriptInfo) });
{ canRename, fileToRename, displayName, fullDisplayName, kind, kindModifiers, triggerSpan: toProtocolTextSpan(triggerSpan, scriptInfo) });
}
else {
return info;
@ -1415,8 +1415,8 @@ namespace ts.server {
if (simplifiedResult) {
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
return spans.map(s => ({
textSpan: toProcolTextSpan(s.textSpan, scriptInfo),
hintSpan: toProcolTextSpan(s.hintSpan, scriptInfo),
textSpan: toProtocolTextSpan(s.textSpan, scriptInfo),
hintSpan: toProtocolTextSpan(s.hintSpan, scriptInfo),
bannerText: s.bannerText,
autoCollapse: s.autoCollapse,
kind: s.kind
@ -1605,7 +1605,7 @@ namespace ts.server {
const entries = mapDefined<CompletionEntry, protocol.CompletionEntry>(completions.entries, entry => {
if (completions.isMemberCompletion || startsWith(entry.name.toLowerCase(), prefix.toLowerCase())) {
const { name, kind, kindModifiers, sortText, insertText, replacementSpan, hasAction, source, isRecommended } = entry;
const convertedSpan = replacementSpan ? toProcolTextSpan(replacementSpan, scriptInfo) : undefined;
const convertedSpan = replacementSpan ? toProtocolTextSpan(replacementSpan, scriptInfo) : undefined;
// Use `hasAction || undefined` to avoid serializing `false`.
return { name, kind, kindModifiers, sortText, insertText, replacementSpan: convertedSpan, hasAction: hasAction || undefined, source, isRecommended };
}
@ -1775,7 +1775,7 @@ namespace ts.server {
text: item.text,
kind: item.kind,
kindModifiers: item.kindModifiers,
spans: item.spans.map(span => toProcolTextSpan(span, scriptInfo)),
spans: item.spans.map(span => toProtocolTextSpan(span, scriptInfo)),
childItems: this.mapLocationNavigationBarItems(item.childItems, scriptInfo),
indent: item.indent
}));
@ -1796,8 +1796,8 @@ namespace ts.server {
text: tree.text,
kind: tree.kind,
kindModifiers: tree.kindModifiers,
spans: tree.spans.map(span => toProcolTextSpan(span, scriptInfo)),
nameSpan: tree.nameSpan && toProcolTextSpan(tree.nameSpan, scriptInfo),
spans: tree.spans.map(span => toProtocolTextSpan(span, scriptInfo)),
nameSpan: tree.nameSpan && toProtocolTextSpan(tree.nameSpan, scriptInfo),
childItems: map(tree.childItems, item => this.toLocationNavigationTree(item, scriptInfo))
};
}
@ -2059,7 +2059,7 @@ namespace ts.server {
return !spans
? undefined
: simplifiedResult
? spans.map(span => toProcolTextSpan(span, scriptInfo))
? spans.map(span => toProtocolTextSpan(span, scriptInfo))
: spans;
}
@ -2131,7 +2131,7 @@ namespace ts.server {
private mapSelectionRange(selectionRange: SelectionRange, scriptInfo: ScriptInfo): protocol.SelectionRange {
const result: protocol.SelectionRange = {
textSpan: toProcolTextSpan(selectionRange.textSpan, scriptInfo),
textSpan: toProtocolTextSpan(selectionRange.textSpan, scriptInfo),
};
if (selectionRange.parent) {
result.parent = this.mapSelectionRange(selectionRange.parent, scriptInfo);
@ -2139,6 +2139,71 @@ namespace ts.server {
return result;
}
private toProtocolCallHierarchyItem(item: CallHierarchyItem, scriptInfo?: ScriptInfo): protocol.CallHierarchyItem {
if (!scriptInfo) {
const file = toNormalizedPath(item.file);
scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file);
if (!scriptInfo) {
this.projectService.logErrorForScriptInfoNotFound(file);
return Errors.ThrowNoProject();
}
}
return {
name: item.name,
kind: item.kind,
file: item.file,
span: toProtocolTextSpan(item.span, scriptInfo),
selectionSpan: toProtocolTextSpan(item.selectionSpan, scriptInfo)
};
}
private toProtocolCallHierarchyIncomingCall(incomingCall: CallHierarchyIncomingCall, scriptInfo: ScriptInfo): protocol.CallHierarchyIncomingCall {
return {
from: this.toProtocolCallHierarchyItem(incomingCall.from),
fromSpans: incomingCall.fromSpans.map(fromSpan => toProtocolTextSpan(fromSpan, scriptInfo))
};
}
private toProtocolCallHierarchyOutgoingCall(outgoingCall: CallHierarchyOutgoingCall, scriptInfo: ScriptInfo): protocol.CallHierarchyOutgoingCall {
return {
to: this.toProtocolCallHierarchyItem(outgoingCall.to),
fromSpans: outgoingCall.fromSpans.map(fromSpan => toProtocolTextSpan(fromSpan, scriptInfo))
};
}
private prepareCallHierarchy(args: protocol.FileLocationRequestArgs): protocol.CallHierarchyItem | protocol.CallHierarchyItem[] | undefined {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file);
if (scriptInfo) {
const position = this.getPosition(args, scriptInfo);
const result = project.getLanguageService().prepareCallHierarchy(file, position);
return result && mapOneOrMany(result, item => this.toProtocolCallHierarchyItem(item, scriptInfo));
}
return undefined;
}
private provideCallHierarchyIncomingCalls(args: protocol.FileLocationRequestArgs): protocol.CallHierarchyIncomingCall[] {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file);
if (!scriptInfo) {
this.projectService.logErrorForScriptInfoNotFound(file);
return Errors.ThrowNoProject();
}
const incomingCalls = project.getLanguageService().provideCallHierarchyIncomingCalls(file, this.getPosition(args, scriptInfo));
return incomingCalls.map(call => this.toProtocolCallHierarchyIncomingCall(call, scriptInfo));
}
private provideCallHierarchyOutgoingCalls(args: protocol.FileLocationRequestArgs): protocol.CallHierarchyOutgoingCall[] {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file);
if (!scriptInfo) {
this.projectService.logErrorForScriptInfoNotFound(file);
return Errors.ThrowNoProject();
}
const outgoingCalls = project.getLanguageService().provideCallHierarchyOutgoingCalls(file, this.getPosition(args, scriptInfo));
return outgoingCalls.map(call => this.toProtocolCallHierarchyOutgoingCall(call, scriptInfo));
}
getCanonicalFileName(fileName: string) {
const name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
return normalizePath(name);
@ -2504,6 +2569,15 @@ namespace ts.server {
[CommandNames.SelectionRangeFull]: (request: protocol.SelectionRangeRequest) => {
return this.requiredResponse(this.getSmartSelectionRange(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.PrepareCallHierarchy]: (request: protocol.PrepareCallHierarchyRequest) => {
return this.requiredResponse(this.prepareCallHierarchy(request.arguments));
},
[CommandNames.ProvideCallHierarchyIncomingCalls]: (request: protocol.ProvideCallHierarchyIncomingCallsRequest) => {
return this.requiredResponse(this.provideCallHierarchyIncomingCalls(request.arguments));
},
[CommandNames.ProvideCallHierarchyOutgoingCalls]: (request: protocol.ProvideCallHierarchyOutgoingCallsRequest) => {
return this.requiredResponse(this.provideCallHierarchyOutgoingCalls(request.arguments));
},
});
public addProtocolHandler(command: string, handler: (request: protocol.Request) => HandlerResponse) {
@ -2627,7 +2701,7 @@ namespace ts.server {
readonly project: Project;
}
function toProcolTextSpan(textSpan: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan {
function toProtocolTextSpan(textSpan: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan {
return {
start: scriptInfo.positionToLineOffset(textSpan.start),
end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan))
@ -2635,8 +2709,8 @@ namespace ts.server {
}
function toProtocolTextSpanWithContext(span: TextSpan, contextSpan: TextSpan | undefined, scriptInfo: ScriptInfo): protocol.TextSpanWithContext {
const textSpan = toProcolTextSpan(span, scriptInfo);
const contextTextSpan = contextSpan && toProcolTextSpan(contextSpan, scriptInfo);
const textSpan = toProtocolTextSpan(span, scriptInfo);
const contextTextSpan = contextSpan && toProtocolTextSpan(contextSpan, scriptInfo);
return contextTextSpan ?
{ ...textSpan, contextStart: contextTextSpan.start, contextEnd: contextTextSpan.end } :
textSpan;

View File

@ -0,0 +1,485 @@
/* @internal */
namespace ts.CallHierarchy {
export type NamedExpression =
| ClassExpression & { name: Identifier }
| FunctionExpression & { name: Identifier }
;
/** Indictates whether a node is named function or class expression. */
function isNamedExpression(node: Node): node is NamedExpression {
return (isFunctionExpression(node) || isClassExpression(node)) && isNamedDeclaration(node);
}
export type ConstNamedExpression =
| ClassExpression & { name: undefined, parent: VariableDeclaration & { name: Identifier } }
| FunctionExpression & { name: undefined, parent: VariableDeclaration & { name: Identifier } }
| ArrowFunction & { name: undefined, parent: VariableDeclaration & { name: Identifier } }
;
/** Indicates whether a node is a function, arrow, or class expression assigned to a constant variable. */
function isConstNamedExpression(node: Node): node is ConstNamedExpression {
return (isFunctionExpression(node) || isArrowFunction(node) || isClassExpression(node))
&& isVariableDeclaration(node.parent)
&& node === node.parent.initializer
&& isIdentifier(node.parent.name)
&& !!(getCombinedNodeFlags(node.parent) & NodeFlags.Const);
}
export type CallHierarchyDeclaration =
| SourceFile
| ModuleDeclaration & { name: Identifier }
| FunctionDeclaration
| ClassDeclaration
| MethodDeclaration
| GetAccessorDeclaration
| SetAccessorDeclaration
| NamedExpression
| ConstNamedExpression
;
/**
* Indicates whether a node could possibly be a call hierarchy declaration.
*
* See `resolveCallHierarchyDeclaration` for the specific rules.
*/
function isPossibleCallHierarchyDeclaration(node: Node) {
return isSourceFile(node)
|| isModuleDeclaration(node)
|| isFunctionDeclaration(node)
|| isFunctionExpression(node)
|| isClassDeclaration(node)
|| isClassExpression(node)
|| isMethodDeclaration(node)
|| isMethodSignature(node)
|| isGetAccessorDeclaration(node)
|| isSetAccessorDeclaration(node);
}
/**
* Indicates whether a node is a valid a call hierarchy declaration.
*
* See `resolveCallHierarchyDeclaration` for the specific rules.
*/
function isValidCallHierarchyDeclaration(node: Node): node is CallHierarchyDeclaration {
return isSourceFile(node)
|| isModuleDeclaration(node) && isIdentifier(node.name)
|| isFunctionDeclaration(node)
|| isClassDeclaration(node)
|| isMethodDeclaration(node)
|| isMethodSignature(node)
|| isGetAccessorDeclaration(node)
|| isSetAccessorDeclaration(node)
|| isNamedExpression(node)
|| isConstNamedExpression(node);
}
/** Gets the node that can be used as a reference to a call hierarchy declaration. */
function getCallHierarchyDeclarationReferenceNode(node: CallHierarchyDeclaration) {
if (isSourceFile(node)) return node;
if (isNamedDeclaration(node)) return node.name;
if (isConstNamedExpression(node)) return node.parent.name;
return Debug.assertDefined(node.modifiers && find(node.modifiers, isDefaultModifier));
}
function isDefaultModifier(node: Node) {
return node.kind === SyntaxKind.DefaultKeyword;
}
/** Gets the symbol for a call hierarchy declaration. */
function getSymbolOfCallHierarchyDeclaration(typeChecker: TypeChecker, node: CallHierarchyDeclaration) {
const location = getCallHierarchyDeclarationReferenceNode(node);
return location && typeChecker.getSymbolAtLocation(location);
}
/** Gets the text and range for the name of a call hierarchy declaration. */
function getCallHierarchyItemName(program: Program, node: CallHierarchyDeclaration): { text: string, pos: number, end: number } {
if (isSourceFile(node)) {
return { text: node.fileName, pos: 0, end: 0 };
}
if ((isFunctionDeclaration(node) || isClassDeclaration(node)) && !isNamedDeclaration(node)) {
const defaultModifier = node.modifiers && find(node.modifiers, isDefaultModifier);
if (defaultModifier) {
return { text: "default", pos: defaultModifier.getStart(), end: defaultModifier.getEnd() };
}
}
const declName = isConstNamedExpression(node) ? node.parent.name :
Debug.assertDefined(getNameOfDeclaration(node), "Expected call hierarchy item to have a name");
let text =
isIdentifier(declName) ? idText(declName) :
isStringOrNumericLiteralLike(declName) ? declName.text :
isComputedPropertyName(declName) ?
isStringOrNumericLiteralLike(declName.expression) ? declName.expression.text :
undefined :
undefined;
if (text === undefined) {
const typeChecker = program.getTypeChecker();
const symbol = typeChecker.getSymbolAtLocation(declName);
if (symbol) {
text = typeChecker.symbolToString(symbol, node);
}
}
if (text === undefined) {
// get the text from printing the node on a single line without comments...
const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true });
text = usingSingleLineStringWriter(writer => printer.writeNode(EmitHint.Unspecified, node, node.getSourceFile(), writer));
}
return { text, pos: declName.getStart(), end: declName.getEnd() };
}
/** Finds the implementation of a function-like declaration, if one exists. */
function findImplementation(typeChecker: TypeChecker, node: Extract<CallHierarchyDeclaration, FunctionLikeDeclaration>): Extract<CallHierarchyDeclaration, FunctionLikeDeclaration> | undefined;
function findImplementation(typeChecker: TypeChecker, node: FunctionLikeDeclaration): FunctionLikeDeclaration | undefined;
function findImplementation(typeChecker: TypeChecker, node: FunctionLikeDeclaration): FunctionLikeDeclaration | undefined {
if (node.body) {
return node;
}
if (isConstructorDeclaration(node)) {
return getFirstConstructorWithBody(node.parent);
}
if (isFunctionDeclaration(node) || isMethodDeclaration(node)) {
const symbol = getSymbolOfCallHierarchyDeclaration(typeChecker, node);
if (symbol && symbol.valueDeclaration && isFunctionLikeDeclaration(symbol.valueDeclaration) && symbol.valueDeclaration.body) {
return symbol.valueDeclaration;
}
return undefined;
}
return node;
}
function findAllInitialDeclarations(typeChecker: TypeChecker, node: CallHierarchyDeclaration) {
const symbol = getSymbolOfCallHierarchyDeclaration(typeChecker, node);
let declarations: CallHierarchyDeclaration[] | undefined;
if (symbol && symbol.declarations) {
const indices = indicesOf(symbol.declarations);
const keys = map(symbol.declarations, decl => ({ file: decl.getSourceFile().fileName, pos: decl.pos }));
indices.sort((a, b) => compareStringsCaseSensitive(keys[a].file, keys[b].file) || keys[a].pos - keys[b].pos);
const sortedDeclarations = map(indices, i => symbol.declarations[i]);
let lastDecl: CallHierarchyDeclaration | undefined;
for (const decl of sortedDeclarations) {
if (isValidCallHierarchyDeclaration(decl)) {
if (!lastDecl || lastDecl.parent !== decl.parent || lastDecl.end !== decl.pos) {
declarations = append(declarations, decl);
}
lastDecl = decl;
}
}
}
return declarations;
}
/** Find the implementation or the first declaration for a call hierarchy declaration. */
function findImplementationOrAllInitialDeclarations(typeChecker: TypeChecker, node: CallHierarchyDeclaration): CallHierarchyDeclaration | CallHierarchyDeclaration[] {
if (isFunctionLikeDeclaration(node)) {
return findImplementation(typeChecker, node) ??
findAllInitialDeclarations(typeChecker, node) ??
node;
}
return findAllInitialDeclarations(typeChecker, node) ?? node;
}
/** Resolves the call hierarchy declaration for a node. */
export function resolveCallHierarchyDeclaration(program: Program, location: Node): CallHierarchyDeclaration | CallHierarchyDeclaration[] | undefined {
// A call hierarchy item must refer to either a SourceFile, Module Declaration, or something intrinsically callable that has a name:
// - Class Declarations
// - Class Expressions (with a name)
// - Function Declarations
// - Function Expressions (with a name or assigned to a const variable)
// - Arrow Functions (assigned to a const variable)
// - Constructors
// - Methods
// - Accessors
//
// If a call is contained in a non-named callable Node (function expression, arrow function, etc.), then
// its containing `CallHierarchyItem` is a containing function or SourceFile that matches the above list.
const typeChecker = program.getTypeChecker();
let followingSymbol = false;
while (true) {
if (isValidCallHierarchyDeclaration(location)) {
return findImplementationOrAllInitialDeclarations(typeChecker, location);
}
if (isPossibleCallHierarchyDeclaration(location)) {
const ancestor = findAncestor(location, isValidCallHierarchyDeclaration);
return ancestor && findImplementationOrAllInitialDeclarations(typeChecker, ancestor);
}
if (isDeclarationName(location)) {
if (isValidCallHierarchyDeclaration(location.parent)) {
return findImplementationOrAllInitialDeclarations(typeChecker, location.parent);
}
if (isPossibleCallHierarchyDeclaration(location.parent)) {
const ancestor = findAncestor(location.parent, isValidCallHierarchyDeclaration);
return ancestor && findImplementationOrAllInitialDeclarations(typeChecker, ancestor);
}
if (isVariableDeclaration(location.parent) && location.parent.initializer && isConstNamedExpression(location.parent.initializer)) {
return location.parent.initializer;
}
return undefined;
}
if (isConstructorDeclaration(location)) {
if (isValidCallHierarchyDeclaration(location.parent)) {
return location.parent;
}
return undefined;
}
if (!followingSymbol) {
let symbol = typeChecker.getSymbolAtLocation(location);
if (symbol) {
if (symbol.flags & SymbolFlags.Alias) {
symbol = typeChecker.getAliasedSymbol(symbol);
}
if (symbol.valueDeclaration) {
followingSymbol = true;
location = symbol.valueDeclaration;
continue;
}
}
}
return undefined;
}
}
/** Creates a `CallHierarchyItem` for a call hierarchy declaration. */
export function createCallHierarchyItem(program: Program, node: CallHierarchyDeclaration): CallHierarchyItem {
const sourceFile = node.getSourceFile();
const name = getCallHierarchyItemName(program, node);
const kind = getNodeKind(node);
const span = createTextSpanFromBounds(skipTrivia(sourceFile.text, node.getFullStart(), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true), node.getEnd());
const selectionSpan = createTextSpanFromBounds(name.pos, name.end);
return { file: sourceFile.fileName, kind, name: name.text, span, selectionSpan };
}
function isDefined<T>(x: T): x is NonNullable<T> {
return x !== undefined;
}
interface CallSite {
declaration: CallHierarchyDeclaration;
range: TextRange;
}
function convertEntryToCallSite(entry: FindAllReferences.Entry, _originalNode: Node, typeChecker: TypeChecker): CallSite | undefined {
if (entry.kind === FindAllReferences.EntryKind.Node) {
if (isCallOrNewExpressionTarget(entry.node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true)
|| isTaggedTemplateTag(entry.node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true)
|| isDecoratorTarget(entry.node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true)
|| isJsxOpeningLikeElementTagName(entry.node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true)
|| isRightSideOfPropertyAccess(entry.node)
|| isArgumentExpressionOfElementAccess(entry.node)) {
const ancestor = findAncestor(entry.node, isValidCallHierarchyDeclaration) || entry.node.getSourceFile();
return { declaration: firstOrOnly(findImplementationOrAllInitialDeclarations(typeChecker, ancestor)), range: createTextRangeFromNode(entry.node, entry.node.getSourceFile()) };
}
}
}
function getCallSiteGroupKey(entry: CallSite) {
return "" + getNodeId(entry.declaration);
}
function createCallHierarchyIncomingCall(from: CallHierarchyItem, fromSpans: TextSpan[]): CallHierarchyIncomingCall {
return { from, fromSpans };
}
function convertCallSiteGroupToIncomingCall(program: Program, entries: readonly CallSite[]) {
return createCallHierarchyIncomingCall(createCallHierarchyItem(program, entries[0].declaration), map(entries, entry => createTextSpanFromRange(entry.range)));
}
/** Gets the call sites that call into the provided call hierarchy declaration. */
export function getIncomingCalls(program: Program, declaration: CallHierarchyDeclaration, cancellationToken: CancellationToken): CallHierarchyIncomingCall[] {
// Source files and modules have no incoming calls.
if (isSourceFile(declaration) || isModuleDeclaration(declaration)) {
return [];
}
const location = getCallHierarchyDeclarationReferenceNode(declaration);
const calls = filter(FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, program.getSourceFiles(), location, /*position*/ 0, /*options*/ undefined, convertEntryToCallSite), isDefined);
return calls ? group(calls, getCallSiteGroupKey, entries => convertCallSiteGroupToIncomingCall(program, entries)) : [];
}
function createCallSiteCollector(program: Program, callSites: CallSite[]): (node: Node | undefined) => void {
function recordCallSite(node: CallExpression | NewExpression | TaggedTemplateExpression | PropertyAccessExpression | ElementAccessExpression | Decorator | JsxOpeningLikeElement) {
const target =
isTaggedTemplateExpression(node) ? node.tag :
isJsxOpeningLikeElement(node) ? node.tagName :
isAccessExpression(node) ? node :
node.expression;
const declaration = resolveCallHierarchyDeclaration(program, target);
if (declaration) {
const range = createTextRangeFromNode(target, node.getSourceFile());
if (isArray(declaration)) {
for (const decl of declaration) {
callSites.push({ declaration: decl, range });
}
}
else {
callSites.push({ declaration, range });
}
}
}
function collect(node: Node | undefined) {
if (!node) return;
if (node.flags & NodeFlags.Ambient) {
// do not descend into ambient nodes.
return;
}
if (isValidCallHierarchyDeclaration(node)) {
// do not descend into other call site declarations, other than class member names
if (isClassLike(node)) {
for (const member of node.members) {
if (member.name && isComputedPropertyName(member.name)) {
collect(member.name.expression);
}
}
}
return;
}
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
// do not descend into nodes that cannot contain callable nodes
return;
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.AsExpression:
// do not descend into the type side of an assertion
collect((node as TypeAssertion | AsExpression).expression);
return;
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
// do not descend into the type of a variable or parameter declaration
collect((node as VariableDeclaration | ParameterDeclaration).name);
collect((node as VariableDeclaration | ParameterDeclaration).initializer);
return;
case SyntaxKind.CallExpression:
// do not descend into the type arguments of a call expression
recordCallSite(node as CallExpression);
collect((node as CallExpression).expression);
forEach((node as CallExpression).arguments, collect);
return;
case SyntaxKind.NewExpression:
// do not descend into the type arguments of a new expression
recordCallSite(node as NewExpression);
collect((node as NewExpression).expression);
forEach((node as NewExpression).arguments, collect);
return;
case SyntaxKind.TaggedTemplateExpression:
// do not descend into the type arguments of a tagged template expression
recordCallSite(node as TaggedTemplateExpression);
collect((node as TaggedTemplateExpression).tag);
collect((node as TaggedTemplateExpression).template);
return;
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxSelfClosingElement:
// do not descend into the type arguments of a JsxOpeningLikeElement
recordCallSite(node as JsxOpeningLikeElement);
collect((node as JsxOpeningLikeElement).tagName);
collect((node as JsxOpeningLikeElement).attributes);
return;
case SyntaxKind.Decorator:
recordCallSite(node as Decorator);
collect((node as Decorator).expression);
return;
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
recordCallSite(node as AccessExpression);
forEachChild(node, collect);
break;
}
if (isPartOfTypeNode(node)) {
// do not descend into types
return;
}
forEachChild(node, collect);
}
return collect;
}
function collectCallSitesOfSourceFile(node: SourceFile, collect: (node: Node | undefined) => void) {
forEach(node.statements, collect);
}
function collectCallSitesOfModuleDeclaration(node: ModuleDeclaration, collect: (node: Node | undefined) => void) {
if (!hasModifier(node, ModifierFlags.Ambient) && node.body && isModuleBlock(node.body)) {
forEach(node.body.statements, collect);
}
}
function collectCallSitesOfFunctionLikeDeclaration(typeChecker: TypeChecker, node: FunctionLikeDeclaration, collect: (node: Node | undefined) => void) {
const implementation = findImplementation(typeChecker, node);
if (implementation) {
forEach(implementation.parameters, collect);
collect(implementation.body);
}
}
function collectCallSitesOfClassLikeDeclaration(node: ClassLikeDeclaration, collect: (node: Node | undefined) => void) {
forEach(node.decorators, collect);
const heritage = getClassExtendsHeritageElement(node);
if (heritage) {
collect(heritage.expression);
}
for (const member of node.members) {
forEach(member.decorators, collect);
if (isPropertyDeclaration(member)) {
collect(member.initializer);
}
else if (isConstructorDeclaration(member) && member.body) {
forEach(member.parameters, collect);
collect(member.body);
}
}
}
function collectCallSites(program: Program, node: CallHierarchyDeclaration) {
const callSites: CallSite[] = [];
const collect = createCallSiteCollector(program, callSites);
switch (node.kind) {
case SyntaxKind.SourceFile:
collectCallSitesOfSourceFile(node, collect);
break;
case SyntaxKind.ModuleDeclaration:
collectCallSitesOfModuleDeclaration(node, collect);
break;
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
collectCallSitesOfFunctionLikeDeclaration(program.getTypeChecker(), node, collect);
break;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
collectCallSitesOfClassLikeDeclaration(node, collect);
break;
default:
Debug.assertNever(node);
}
return callSites;
}
function createCallHierarchyOutgoingCall(to: CallHierarchyItem, fromSpans: TextSpan[]): CallHierarchyOutgoingCall {
return { to, fromSpans };
}
function convertCallSiteGroupToOutgoingCall(program: Program, entries: readonly CallSite[]) {
return createCallHierarchyOutgoingCall(createCallHierarchyItem(program, entries[0].declaration), map(entries, entry => createTextSpanFromRange(entry.range)));
}
/** Gets the call sites that call out of the provided call hierarchy declaration. */
export function getOutgoingCalls(program: Program, declaration: CallHierarchyDeclaration): CallHierarchyOutgoingCall[] {
if (declaration.flags & NodeFlags.Ambient || isMethodSignature(declaration)) {
return [];
}
return group(collectCallSites(program, declaration), getCallSiteGroupKey, entries => convertCallSiteGroupToOutgoingCall(program, entries));
}
}

View File

@ -2137,6 +2137,26 @@ namespace ts {
return refactor.getEditsForRefactor(getRefactorContext(file, positionOrRange, preferences, formatOptions), refactorName, actionName);
}
function prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined {
synchronizeHostData();
const declarations = CallHierarchy.resolveCallHierarchyDeclaration(program, getTouchingPropertyName(getValidSourceFile(fileName), position));
return declarations && mapOneOrMany(declarations, declaration => CallHierarchy.createCallHierarchyItem(program, declaration));
}
function provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[] {
synchronizeHostData();
const sourceFile = getValidSourceFile(fileName);
const declaration = firstOrOnly(CallHierarchy.resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position)));
return declaration ? CallHierarchy.getIncomingCalls(program, declaration, cancellationToken) : [];
}
function provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[] {
synchronizeHostData();
const sourceFile = getValidSourceFile(fileName);
const declaration = firstOrOnly(CallHierarchy.resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position)));
return declaration ? CallHierarchy.getOutgoingCalls(program, declaration) : [];
}
return {
dispose,
cleanupSemanticCache,
@ -2192,6 +2212,9 @@ namespace ts {
getEditsForRefactor,
toLineColumnOffset: sourceMapper.toLineColumnOffset,
getSourceMapper: () => sourceMapper,
prepareCallHierarchy,
provideCallHierarchyIncomingCalls,
provideCallHierarchyOutgoingCalls
};
}

View File

@ -271,6 +271,10 @@ namespace ts {
*/
getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): string;
prepareCallHierarchy(fileName: string, position: number): string;
provideCallHierarchyIncomingCalls(fileName: string, position: number): string;
provideCallHierarchyOutgoingCalls(fileName: string, position: number): string;
getEmitOutput(fileName: string): string;
getEmitOutputObject(fileName: string): EmitOutput;
}
@ -1020,6 +1024,29 @@ namespace ts {
);
}
/// CALL HIERARCHY
public prepareCallHierarchy(fileName: string, position: number): string {
return this.forwardJSONCall(
`prepareCallHierarchy('${fileName}', ${position})`,
() => this.languageService.prepareCallHierarchy(fileName, position)
);
}
public provideCallHierarchyIncomingCalls(fileName: string, position: number): string {
return this.forwardJSONCall(
`provideCallHierarchyIncomingCalls('${fileName}', ${position})`,
() => this.languageService.provideCallHierarchyIncomingCalls(fileName, position)
);
}
public provideCallHierarchyOutgoingCalls(fileName: string, position: number): string {
return this.forwardJSONCall(
`provideCallHierarchyOutgoingCalls('${fileName}', ${position})`,
() => this.languageService.provideCallHierarchyOutgoingCalls(fileName, position)
);
}
/// Emit
public getEmitOutput(fileName: string): string {
return this.forwardJSONCall(

View File

@ -18,6 +18,7 @@
"documentRegistry.ts",
"importTracker.ts",
"findAllReferences.ts",
"callHierarchy.ts",
"getEditsForFileRename.ts",
"goToDefinition.ts",
"jsDoc.ts",

View File

@ -351,6 +351,10 @@ namespace ts {
getNavigationBarItems(fileName: string): NavigationBarItem[];
getNavigationTree(fileName: string): NavigationTree;
prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined;
provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[];
provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[];
@ -522,6 +526,24 @@ namespace ts {
childItems?: NavigationTree[];
}
export interface CallHierarchyItem {
name: string;
kind: ScriptElementKind;
file: string;
span: TextSpan;
selectionSpan: TextSpan;
}
export interface CallHierarchyIncomingCall {
from: CallHierarchyItem;
fromSpans: TextSpan[];
}
export interface CallHierarchyOutgoingCall {
to: CallHierarchyItem;
fromSpans: TextSpan[];
}
export interface TodoCommentDescriptor {
text: string;
priority: number;

View File

@ -196,27 +196,58 @@ namespace ts {
return false;
}
export function isCallExpressionTarget(node: Node): boolean {
return isCallOrNewExpressionTargetWorker(node, isCallExpression);
export function isCallExpressionTarget(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
return isCalleeWorker(node, isCallExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions);
}
export function isNewExpressionTarget(node: Node): boolean {
return isCallOrNewExpressionTargetWorker(node, isNewExpression);
export function isNewExpressionTarget(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
return isCalleeWorker(node, isNewExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions);
}
export function isCallOrNewExpressionTarget(node: Node): boolean {
return isCallOrNewExpressionTargetWorker(node, isCallOrNewExpression);
export function isCallOrNewExpressionTarget(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
return isCalleeWorker(node, isCallOrNewExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions);
}
function isCallOrNewExpressionTargetWorker<T extends CallExpression | NewExpression>(node: Node, pred: (node: Node) => node is T): boolean {
const target = climbPastPropertyAccess(node);
return !!target && !!target.parent && pred(target.parent) && target.parent.expression === target;
export function isTaggedTemplateTag(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
return isCalleeWorker(node, isTaggedTemplateExpression, selectTagOfTaggedTemplateExpression, includeElementAccess, skipPastOuterExpressions);
}
export function isDecoratorTarget(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
return isCalleeWorker(node, isDecorator, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions);
}
export function isJsxOpeningLikeElementTagName(node: Node, includeElementAccess = false, skipPastOuterExpressions = false): boolean {
return isCalleeWorker(node, isJsxOpeningLikeElement, selectTagNameOfJsxOpeningLikeElement, includeElementAccess, skipPastOuterExpressions);
}
function selectExpressionOfCallOrNewExpressionOrDecorator(node: CallExpression | NewExpression | Decorator) {
return node.expression;
}
function selectTagOfTaggedTemplateExpression(node: TaggedTemplateExpression) {
return node.tag;
}
function selectTagNameOfJsxOpeningLikeElement(node: JsxOpeningLikeElement) {
return node.tagName;
}
function isCalleeWorker<T extends CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement>(node: Node, pred: (node: Node) => node is T, calleeSelector: (node: T) => Expression, includeElementAccess: boolean, skipPastOuterExpressions: boolean) {
let target = includeElementAccess ? climbPastPropertyOrElementAccess(node) : climbPastPropertyAccess(node);
if (skipPastOuterExpressions) {
target = skipOuterExpressions(target);
}
return !!target && !!target.parent && pred(target.parent) && calleeSelector(target.parent) === target;
}
export function climbPastPropertyAccess(node: Node) {
return isRightSideOfPropertyAccess(node) ? node.parent : node;
}
export function climbPastPropertyOrElementAccess(node: Node) {
return isRightSideOfPropertyAccess(node) || isArgumentExpressionOfElementAccess(node) ? node.parent : node;
}
export function getTargetLabel(referenceNode: Node, labelName: string): Identifier | undefined {
while (referenceNode) {
if (referenceNode.kind === SyntaxKind.LabeledStatement && (<LabeledStatement>referenceNode).label.escapedText === labelName) {
@ -236,11 +267,11 @@ namespace ts {
}
export function isJumpStatementTarget(node: Node): node is Identifier & { parent: BreakOrContinueStatement } {
return node.kind === SyntaxKind.Identifier && isBreakOrContinueStatement(node.parent) && node.parent.label === node;
return isIdentifier(node) && tryCast(node.parent, isBreakOrContinueStatement)?.label === node;
}
export function isLabelOfLabeledStatement(node: Node): node is Identifier {
return node.kind === SyntaxKind.Identifier && isLabeledStatement(node.parent) && node.parent.label === node;
return isIdentifier(node) && tryCast(node.parent, isLabeledStatement)?.label === node;
}
export function isLabelName(node: Node): boolean {
@ -248,24 +279,27 @@ namespace ts {
}
export function isTagName(node: Node): boolean {
return isJSDocTag(node.parent) && node.parent.tagName === node;
return tryCast(node.parent, isJSDocTag)?.tagName === node;
}
export function isRightSideOfQualifiedName(node: Node) {
return node.parent.kind === SyntaxKind.QualifiedName && (<QualifiedName>node.parent).right === node;
return tryCast(node.parent, isQualifiedName)?.right === node;
}
export function isRightSideOfPropertyAccess(node: Node) {
return node && node.parent && node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node;
return tryCast(node.parent, isPropertyAccessExpression)?.name === node;
}
export function isArgumentExpressionOfElementAccess(node: Node) {
return tryCast(node.parent, isElementAccessExpression)?.argumentExpression === node;
}
export function isNameOfModuleDeclaration(node: Node) {
return node.parent.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>node.parent).name === node;
return tryCast(node.parent, isModuleDeclaration)?.name === node;
}
export function isNameOfFunctionDeclaration(node: Node): boolean {
return node.kind === SyntaxKind.Identifier &&
isFunctionLike(node.parent) && (<FunctionLikeDeclaration>node.parent).name === node;
return isIdentifier(node) && tryCast(node.parent, isFunctionLike)?.name === node;
}
export function isLiteralNameOfPropertyDeclarationOrIndexAccess(node: StringLiteral | NumericLiteral | NoSubstitutionTemplateLiteral): boolean {
@ -2298,4 +2332,23 @@ namespace ts {
export function getRefactorContextSpan({ startPosition, endPosition }: RefactorContext): TextSpan {
return createTextSpanFromBounds(startPosition, endPosition === undefined ? startPosition : endPosition);
}
}
/**
* If the provided value is an array, the mapping function is applied to each element; otherwise, the mapping function is applied
* to the provided value itself.
*/
export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[], f: (x: T, i: number) => U): U | U[];
export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[] | undefined, f: (x: T, i: number) => U): U | U[] | undefined;
export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[], f: (x: T, i: number) => U, resultSelector: (x: U[]) => U): U;
export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[] | undefined, f: (x: T, i: number) => U, resultSelector: (x: U[]) => U): U | undefined;
export function mapOneOrMany<T, U>(valueOrArray: T | readonly T[] | undefined, f: (x: T, i: number) => U, resultSelector: (x: U[]) => U | U[] = identity): U | U[] | undefined {
return valueOrArray ? isArray(valueOrArray) ? resultSelector(map(valueOrArray, f)) : f(valueOrArray, 0) : undefined;
}
/**
* If the provided value is an array, the first element of the array is returned; otherwise, the provided value is returned instead.
*/
export function firstOrOnly<T>(valueOrArray: T | readonly T[]): T {
return isArray(valueOrArray) ? first(valueOrArray) : valueOrArray;
}
}

View File

@ -267,6 +267,9 @@ namespace ts.server {
CommandNames.GetEditsForFileRename,
CommandNames.GetEditsForFileRenameFull,
CommandNames.SelectionRange,
CommandNames.PrepareCallHierarchy,
CommandNames.ProvideCallHierarchyIncomingCalls,
CommandNames.ProvideCallHierarchyOutgoingCalls,
];
it("should not throw when commands are executed with invalid arguments", () => {

View File

@ -5113,6 +5113,9 @@ declare namespace ts {
getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles?: boolean): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
getNavigationTree(fileName: string): NavigationTree;
prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined;
provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[];
provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[];
@ -5252,6 +5255,21 @@ declare namespace ts {
/** Present if non-empty */
childItems?: NavigationTree[];
}
interface CallHierarchyItem {
name: string;
kind: ScriptElementKind;
file: string;
span: TextSpan;
selectionSpan: TextSpan;
}
interface CallHierarchyIncomingCall {
from: CallHierarchyItem;
fromSpans: TextSpan[];
}
interface CallHierarchyOutgoingCall {
to: CallHierarchyItem;
fromSpans: TextSpan[];
}
interface TodoCommentDescriptor {
text: string;
priority: number;
@ -6113,6 +6131,9 @@ declare namespace ts.server.protocol {
GetEditsForFileRename = "getEditsForFileRename",
ConfigurePlugin = "configurePlugin",
SelectionRange = "selectionRange",
PrepareCallHierarchy = "prepareCallHierarchy",
ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls",
ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls"
}
/**
* A TypeScript Server message
@ -8342,6 +8363,39 @@ declare namespace ts.server.protocol {
interface NavTreeResponse extends Response {
body?: NavigationTree;
}
interface CallHierarchyItem {
name: string;
kind: ScriptElementKind;
file: string;
span: TextSpan;
selectionSpan: TextSpan;
}
interface CallHierarchyIncomingCall {
from: CallHierarchyItem;
fromSpans: TextSpan[];
}
interface CallHierarchyOutgoingCall {
to: CallHierarchyItem;
fromSpans: TextSpan[];
}
interface PrepareCallHierarchyRequest extends FileLocationRequest {
command: CommandTypes.PrepareCallHierarchy;
}
interface PrepareCallHierarchyResponse extends Response {
readonly body: CallHierarchyItem | CallHierarchyItem[];
}
interface ProvideCallHierarchyIncomingCallsRequest extends FileLocationRequest {
command: CommandTypes.ProvideCallHierarchyIncomingCalls;
}
interface ProvideCallHierarchyIncomingCallsResponse extends Response {
readonly body: CallHierarchyIncomingCall[];
}
interface ProvideCallHierarchyOutgoingCallsRequest extends FileLocationRequest {
command: CommandTypes.ProvideCallHierarchyOutgoingCalls;
}
interface ProvideCallHierarchyOutgoingCallsResponse extends Response {
readonly body: CallHierarchyOutgoingCall[];
}
enum IndentStyle {
None = "None",
Block = "Block",
@ -9433,6 +9487,12 @@ declare namespace ts.server {
private configurePlugin;
private getSmartSelectionRange;
private mapSelectionRange;
private toProtocolCallHierarchyItem;
private toProtocolCallHierarchyIncomingCall;
private toProtocolCallHierarchyOutgoingCall;
private prepareCallHierarchy;
private provideCallHierarchyIncomingCalls;
private provideCallHierarchyOutgoingCalls;
getCanonicalFileName(fileName: string): string;
exit(): void;
private notRequired;

View File

@ -5113,6 +5113,9 @@ declare namespace ts {
getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles?: boolean): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
getNavigationTree(fileName: string): NavigationTree;
prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined;
provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[];
provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[];
@ -5252,6 +5255,21 @@ declare namespace ts {
/** Present if non-empty */
childItems?: NavigationTree[];
}
interface CallHierarchyItem {
name: string;
kind: ScriptElementKind;
file: string;
span: TextSpan;
selectionSpan: TextSpan;
}
interface CallHierarchyIncomingCall {
from: CallHierarchyItem;
fromSpans: TextSpan[];
}
interface CallHierarchyOutgoingCall {
to: CallHierarchyItem;
fromSpans: TextSpan[];
}
interface TodoCommentDescriptor {
text: string;
priority: number;

View File

@ -0,0 +1,65 @@
╭ name: bar
├ kind: getter
├ file: /tests/cases/fourslash/callHierarchyAccessor.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyAccessor.ts:6:5-8:6
│ │ 6: get bar() {
│ │ ^^^^^^^^^^^
│ │ 7: return baz();
│ │ ^^^^^^^^^^^^^^^^^^^^^
│ │ 8: }
│ │ ^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyAccessor.ts:6:9-6:12
│ │ 6: get bar() {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyAccessor.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyAccessor.ts:1:1-3:2
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: new C().bar;
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyAccessor.ts:1:10-1:13
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyAccessor.ts:2:13-2:16
│ │ │ 2: new C().bar;
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyAccessor.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyAccessor.ts:11:1-12:2
│ │ │ │ 11: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 12: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyAccessor.ts:11:10-11:13
│ │ │ │ 11: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyAccessor.ts:7:16-7:19
│ │ │ 7: return baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,65 @@
╭ name: bar
├ kind: function
├ file: /tests/cases/fourslash/callHierarchyClass.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyClass.ts:5:1-7:2
│ │ 5: function bar() {
│ │ ^^^^^^^^^^^^^^^^
│ │ 6: new Baz();
│ │ ^^^^^^^^^^^^^^
│ │ 7: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyClass.ts:5:10-5:13
│ │ 5: function bar() {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyClass.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyClass.ts:1:1-3:2
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: bar();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyClass.ts:1:10-1:13
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyClass.ts:2:5-2:8
│ │ │ 2: bar();
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: Baz
│ │ ├ kind: class
│ │ ├ file: /tests/cases/fourslash/callHierarchyClass.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyClass.ts:9:1-10:2
│ │ │ │ 9: class Baz {
│ │ │ │ ^^^^^^^^^^^
│ │ │ │ 10: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyClass.ts:9:7-9:10
│ │ │ │ 9: class Baz {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyClass.ts:6:9-6:12
│ │ │ 6: new Baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,65 @@
╭ name: bar
├ kind: function
├ file: /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts:5:13-7:2
│ │ 5: const bar = () => {
│ │ ^^^^^^^
│ │ 6: baz();
│ │ ^^^^^^^^^^
│ │ 7: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts:5:7-5:10
│ │ 5: const bar = () => {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts:1:1-3:2
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: bar();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts:1:10-1:13
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts:2:5-2:8
│ │ │ 2: bar();
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts:9:1-10:2
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 10: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts:9:10-9:13
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedArrowFunction.ts:6:5-6:8
│ │ │ 6: baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,69 @@
╭ name: Bar
├ kind: class
├ file: /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts:5:13-9:2
│ │ 5: const Bar = class {
│ │ ^^^^^^^
│ │ 6: constructor() {
│ │ ^^^^^^^^^^^^^^^^^^^
│ │ 7: baz();
│ │ ^^^^^^^^^^^^^^
│ │ 8: }
│ │ ^^^^^
│ │ 9: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts:5:7-5:10
│ │ 5: const Bar = class {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts:1:1-3:2
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: new Bar();
│ │ │ │ ^^^^^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts:1:10-1:13
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts:2:9-2:12
│ │ │ 2: new Bar();
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts:11:1-12:2
│ │ │ │ 11: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 12: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts:11:10-11:13
│ │ │ │ 11: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedClassExpression.ts:7:9-7:12
│ │ │ 7: baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,65 @@
╭ name: bar
├ kind: function
├ file: /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts:5:13-7:2
│ │ 5: const bar = function () {
│ │ ^^^^^^^^^^^^^
│ │ 6: baz();
│ │ ^^^^^^^^^^
│ │ 7: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts:5:7-5:10
│ │ 5: const bar = function () {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts:1:1-3:2
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: bar();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts:1:10-1:13
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts:2:5-2:8
│ │ │ 2: bar();
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts:9:1-10:2
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 10: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts:9:10-9:13
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyConstNamedFunctionExpression.ts:6:5-6:8
│ │ │ 6: baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,65 @@
╭ name: bar
├ kind: function
├ file: /tests/cases/fourslash/callHierarchyDecorator.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyDecorator.ts:5:1-7:2
│ │ 5: function bar() {
│ │ ^^^^^^^^^^^^^^^^
│ │ 6: baz();
│ │ ^^^^^^^^^^
│ │ 7: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyDecorator.ts:5:10-5:13
│ │ 5: function bar() {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: Foo
│ │ ├ kind: class
│ │ ├ file: /tests/cases/fourslash/callHierarchyDecorator.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyDecorator.ts:1:1-3:2
│ │ │ │ 1: @bar
│ │ │ │ ^^^^
│ │ │ │ 2: class Foo {
│ │ │ │ ^^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyDecorator.ts:2:7-2:10
│ │ │ │ 2: class Foo {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyDecorator.ts:1:2-1:5
│ │ │ 1: @bar
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyDecorator.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyDecorator.ts:9:1-10:2
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 10: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyDecorator.ts:9:10-9:13
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyDecorator.ts:6:5-6:8
│ │ │ 6: baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,69 @@
╭ name: default
├ kind: class
├ file: /tests/cases/fourslash/other.ts
├ span:
│ ╭ /tests/cases/fourslash/other.ts:1:1-5:2
│ │ 1: export default class {
│ │ ^^^^^^^^^^^^^^^^^^^^^^
│ │ 2: constructor() {
│ │ ^^^^^^^^^^^^^^^^^^^
│ │ 3: baz();
│ │ ^^^^^^^^^^^^^^
│ │ 4: }
│ │ ^^^^^
│ │ 5: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/other.ts:1:8-1:15
│ │ 1: export default class {
│ │ ^^^^^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:3:1-5:2
│ │ │ │ 3: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 4: new Bar();
│ │ │ │ ^^^^^^^^^^^^^^
│ │ │ │ 5: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:3:10-3:13
│ │ │ │ 3: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:4:9-4:12
│ │ │ 4: new Bar();
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/other.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/other.ts:7:1-8:2
│ │ │ │ 7: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 8: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/other.ts:7:10-7:13
│ │ │ │ 7: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/other.ts:3:9-3:12
│ │ │ 3: baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,65 @@
╭ name: default
├ kind: function
├ file: /tests/cases/fourslash/other.ts
├ span:
│ ╭ /tests/cases/fourslash/other.ts:1:1-3:2
│ │ 1: export default function () {
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ │ 2: baz();
│ │ ^^^^^^^^^^
│ │ 3: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/other.ts:1:8-1:15
│ │ 1: export default function () {
│ │ ^^^^^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:3:1-5:2
│ │ │ │ 3: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 4: bar();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 5: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:3:10-3:13
│ │ │ │ 3: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:4:5-4:8
│ │ │ 4: bar();
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/other.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/other.ts:5:1-6:2
│ │ │ │ 5: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 6: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/other.ts:5:10-5:13
│ │ │ │ 5: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/other.ts:2:5-2:8
│ │ │ 2: baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,47 @@
╭ name: /tests/cases/fourslash/other.ts
├ kind: module
├ file: /tests/cases/fourslash/other.ts
├ span:
│ ╭ /tests/cases/fourslash/other.ts:1:1-6:2
│ │ 1: export = function () {
│ │ ^^^^^^^^^^^^^^^^^^^^^^
│ │ 2: baz();
│ │ ^^^^^^^^^^
│ │ 3: }
│ │ ^
│ │ 4:
│ │ ^
│ │ 5: function baz() {
│ │ ^^^^^^^^^^^^^^^^
│ │ 6: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/other.ts:1:1-1:1
│ │ 1: export = function () {
│ │ <
│ ╰
├ incoming: none
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/other.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/other.ts:5:1-6:2
│ │ │ │ 5: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 6: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/other.ts:5:10-5:13
│ │ │ │ 5: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/other.ts:2:5-2:8
│ │ │ 2: baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,41 @@
╭ name: foo
├ kind: function
├ file: /tests/cases/fourslash/callHierarchyFile.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyFile.ts:2:1-3:2
│ │ 2: function foo() {
│ │ ^^^^^^^^^^^^^^^^
│ │ 3: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyFile.ts:2:10-2:13
│ │ 2: function foo() {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: /tests/cases/fourslash/callHierarchyFile.ts
│ │ ├ kind: script
│ │ ├ file: /tests/cases/fourslash/callHierarchyFile.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyFile.ts:1:1-3:2
│ │ │ │ 1: foo();
│ │ │ │ ^^^^^^
│ │ │ │ 2: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyFile.ts:1:1-1:1
│ │ │ │ 1: foo();
│ │ │ │ <
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyFile.ts:1:1-1:4
│ │ │ 1: foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none

View File

@ -0,0 +1,95 @@
╭ name: bar
├ kind: function
├ file: /tests/cases/fourslash/callHierarchyFunction.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:5:1-9:2
│ │ 5: function bar() {
│ │ ^^^^^^^^^^^^^^^^
│ │ 6: baz();
│ │ ^^^^^^^^^^
│ │ 7: quxx();
│ │ ^^^^^^^^^^^
│ │ 8: baz();
│ │ ^^^^^^^^^^
│ │ 9: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:5:10-5:13
│ │ 5: function bar() {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyFunction.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:1:1-3:2
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: bar();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:1:10-1:13
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:2:5-2:8
│ │ │ 2: bar();
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyFunction.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:11:1-12:2
│ │ │ │ 11: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 12: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:11:10-11:13
│ │ │ │ 11: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:6:5-6:8
│ │ │ 6: baz();
│ │ │ ^^^
│ │ ╰
│ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:8:5-8:8
│ │ │ 8: baz();
│ │ │ ^^^
│ ╰ ╰
│ ╭ to:
│ │ ╭ name: quxx
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyFunction.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:14:1-15:2
│ │ │ │ 14: function quxx() {
│ │ │ │ ^^^^^^^^^^^^^^^^^
│ │ │ │ 15: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:14:10-14:14
│ │ │ │ 14: function quxx() {
│ │ │ │ ^^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyFunction.ts:7:5-7:9
│ │ │ 7: quxx();
│ │ │ ^^^^
╰ ╰ ╰

View File

@ -0,0 +1,78 @@
╭ name: foo
├ kind: function
├ file: /tests/cases/fourslash/a.d.ts
├ span:
│ ╭ /tests/cases/fourslash/a.d.ts:1:1-1:40
│ │ 1: declare function foo(x?: number): void;
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/a.d.ts:1:18-1:21
│ │ 1: declare function foo(x?: number): void;
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: bar
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:1-3:2
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: foo();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:10-1:13
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none
╭ name: foo
├ kind: function
├ file: /tests/cases/fourslash/b.d.ts
├ span:
│ ╭ /tests/cases/fourslash/b.d.ts:1:1-1:40
│ │ 1: declare function foo(x?: string): void;
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/b.d.ts:1:18-1:21
│ │ 1: declare function foo(x?: string): void;
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: bar
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:1-3:2
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: foo();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:10-1:13
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none

View File

@ -0,0 +1,78 @@
╭ name: foo
├ kind: function
├ file: /tests/cases/fourslash/a.d.ts
├ span:
│ ╭ /tests/cases/fourslash/a.d.ts:1:1-1:40
│ │ 1: declare function foo(x?: number): void;
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/a.d.ts:1:18-1:21
│ │ 1: declare function foo(x?: number): void;
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: bar
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:1-3:2
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: foo();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:10-1:13
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none
╭ name: foo
├ kind: function
├ file: /tests/cases/fourslash/b.d.ts
├ span:
│ ╭ /tests/cases/fourslash/b.d.ts:1:1-1:40
│ │ 1: declare function foo(x?: string): void;
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/b.d.ts:1:18-1:21
│ │ 1: declare function foo(x?: string): void;
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: bar
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:1-3:2
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: foo();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:10-1:13
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none

View File

@ -0,0 +1,78 @@
╭ name: foo
├ kind: function
├ file: /tests/cases/fourslash/a.d.ts
├ span:
│ ╭ /tests/cases/fourslash/a.d.ts:1:1-1:40
│ │ 1: declare function foo(x?: number): void;
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/a.d.ts:1:18-1:21
│ │ 1: declare function foo(x?: number): void;
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: bar
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:1-3:2
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: foo();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:10-1:13
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none
╭ name: foo
├ kind: function
├ file: /tests/cases/fourslash/b.d.ts
├ span:
│ ╭ /tests/cases/fourslash/b.d.ts:1:1-1:40
│ │ 1: declare function foo(x?: string): void;
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/b.d.ts:1:18-1:21
│ │ 1: declare function foo(x?: string): void;
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: bar
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:1-3:2
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: foo();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:10-1:13
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none

View File

@ -0,0 +1,78 @@
╭ name: foo
├ kind: function
├ file: /tests/cases/fourslash/a.d.ts
├ span:
│ ╭ /tests/cases/fourslash/a.d.ts:1:1-1:40
│ │ 1: declare function foo(x?: number): void;
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/a.d.ts:1:18-1:21
│ │ 1: declare function foo(x?: number): void;
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: bar
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:1-3:2
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: foo();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:10-1:13
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none
╭ name: foo
├ kind: function
├ file: /tests/cases/fourslash/b.d.ts
├ span:
│ ╭ /tests/cases/fourslash/b.d.ts:1:1-1:40
│ │ 1: declare function foo(x?: string): void;
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/b.d.ts:1:18-1:21
│ │ 1: declare function foo(x?: string): void;
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: bar
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:1-3:2
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: foo();
│ │ │ │ ^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.ts:1:10-1:13
│ │ │ │ 1: function bar() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none

View File

@ -0,0 +1,59 @@
╭ name: bar
├ kind: function
├ file: /tests/cases/fourslash/main.ts
├ span:
│ ╭ /tests/cases/fourslash/main.ts:1:1-3:2
│ │ 1: function bar() {
│ │ ^^^^^^^^^^^^^^^^
│ │ 2: foo();
│ │ ^^^^^^^^^^
│ │ 3: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/main.ts:1:10-1:13
│ │ 1: function bar() {
│ │ ^^^
│ ╰
├ incoming: none
├ outgoing:
│ ╭ to:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/a.d.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/a.d.ts:1:1-1:40
│ │ │ │ 1: declare function foo(x?: number): void;
│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/a.d.ts:1:18-1:21
│ │ │ │ 1: declare function foo(x?: number): void;
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
│ ╰ ╰
│ ╭ to:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/b.d.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/b.d.ts:1:1-1:40
│ │ │ │ 1: declare function foo(x?: string): void;
│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/b.d.ts:1:18-1:21
│ │ │ │ 1: declare function foo(x?: string): void;
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.ts:2:5-2:8
│ │ │ 2: foo();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,47 @@
╭ name: foo
├ kind: method
├ file: /tests/cases/fourslash/callHierarchyInterfaceMethod.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyInterfaceMethod.ts:2:5-2:17
│ │ 2: foo(): void;
│ │ ^^^^^^^^^^^^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyInterfaceMethod.ts:2:5-2:8
│ │ 2: foo(): void;
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: /tests/cases/fourslash/callHierarchyInterfaceMethod.ts
│ │ ├ kind: script
│ │ ├ file: /tests/cases/fourslash/callHierarchyInterfaceMethod.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyInterfaceMethod.ts:1:1-7:11
│ │ │ │ 1: interface I {
│ │ │ │ ^^^^^^^^^^^^^
│ │ │ │ 2: foo(): void;
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ │ 4:
│ │ │ │ ^
│ │ │ │ 5: const obj: I = { foo() {} };
│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ │ │ │ 6:
│ │ │ │ ^
│ │ │ │ 7: obj.foo();
│ │ │ │ ^^^^^^^^^^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyInterfaceMethod.ts:1:1-1:1
│ │ │ │ 1: interface I {
│ │ │ │ <
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyInterfaceMethod.ts:7:5-7:8
│ │ │ 7: obj.foo();
│ │ │ ^^^
│ ╰ ╰
╰ outgoing: none

View File

@ -0,0 +1,65 @@
╭ name: Bar
├ kind: function
├ file: /tests/cases/fourslash/main.tsx
├ span:
│ ╭ /tests/cases/fourslash/main.tsx:5:1-7:2
│ │ 5: function Bar() {
│ │ ^^^^^^^^^^^^^^^^
│ │ 6: baz();
│ │ ^^^^^^^^^^
│ │ 7: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/main.tsx:5:10-5:13
│ │ 5: function Bar() {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.tsx
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.tsx:1:1-3:2
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: return <Bar/>;
│ │ │ │ ^^^^^^^^^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.tsx:1:10-1:13
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.tsx:2:13-2:16
│ │ │ 2: return <Bar/>;
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/main.tsx
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/main.tsx:9:1-10:2
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 10: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/main.tsx:9:10-9:13
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/main.tsx:6:5-6:8
│ │ │ 6: baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,65 @@
╭ name: bar
├ kind: function
├ file: /tests/cases/fourslash/callHierarchyTaggedTemplate.ts
├ span:
│ ╭ /tests/cases/fourslash/callHierarchyTaggedTemplate.ts:5:1-7:2
│ │ 5: function bar(array: TemplateStringsArray, ...args: any[]) {
│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ │ 6: baz();
│ │ ^^^^^^^^^^
│ │ 7: }
│ │ ^
│ ╰
├ selectionSpan:
│ ╭ /tests/cases/fourslash/callHierarchyTaggedTemplate.ts:5:10-5:13
│ │ 5: function bar(array: TemplateStringsArray, ...args: any[]) {
│ │ ^^^
│ ╰
├ incoming:
│ ╭ from:
│ │ ╭ name: foo
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyTaggedTemplate.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyTaggedTemplate.ts:1:1-3:2
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 2: bar`a${1}b`;
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 3: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyTaggedTemplate.ts:1:10-1:13
│ │ │ │ 1: function foo() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ incoming: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyTaggedTemplate.ts:2:5-2:8
│ │ │ 2: bar`a${1}b`;
│ │ │ ^^^
│ ╰ ╰
├ outgoing:
│ ╭ to:
│ │ ╭ name: baz
│ │ ├ kind: function
│ │ ├ file: /tests/cases/fourslash/callHierarchyTaggedTemplate.ts
│ │ ├ span:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyTaggedTemplate.ts:9:1-10:2
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^^^^^^^^^^^^^^
│ │ │ │ 10: }
│ │ │ │ ^
│ │ │ ╰
│ │ ├ selectionSpan:
│ │ │ ╭ /tests/cases/fourslash/callHierarchyTaggedTemplate.ts:9:10-9:13
│ │ │ │ 9: function baz() {
│ │ │ │ ^^^
│ │ │ ╰
│ │ ╰ outgoing: none
│ ├ fromSpans:
│ │ ╭ /tests/cases/fourslash/callHierarchyTaggedTemplate.ts:6:5-6:8
│ │ │ 6: baz();
│ │ │ ^^^
╰ ╰ ╰

View File

@ -0,0 +1,17 @@
/// <reference path="fourslash.ts" />
//// function foo() {
//// new C().bar;
//// }
////
//// class C {
//// get /**/bar() {
//// return baz();
//// }
//// }
////
//// function baz() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// function foo() {
//// bar();
//// }
////
//// function /**/bar() {
//// new Baz();
//// }
////
//// class Baz {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// function foo() {
//// bar();
//// }
////
//// const /**/bar = () => {
//// baz();
//// }
////
//// function baz() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,17 @@
/// <reference path="fourslash.ts" />
//// function foo() {
//// new Bar();
//// }
////
//// const /**/Bar = class {
//// constructor() {
//// baz();
//// }
//// }
////
//// function baz() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// function foo() {
//// bar();
//// }
////
//// const /**/bar = function () {
//// baz();
//// }
////
//// function baz() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
// @experimentalDecorators: true
//// @bar
//// class Foo {
//// }
////
//// function /**/bar() {
//// baz();
//// }
////
//// function baz() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,21 @@
/// <reference path="fourslash.ts" />
// @filename: main.ts
//// import Bar from "./other";
////
//// function foo() {
//// new Bar();
//// }
// @filename: other.ts
//// export /**/default class {
//// constructor() {
//// baz();
//// }
//// }
////
//// function baz() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,19 @@
/// <reference path="fourslash.ts" />
// @filename: main.ts
//// import bar from "./other";
////
//// function foo() {
//// bar();
//// }
// @filename: other.ts
//// export /**/default function () {
//// baz();
//// }
////
//// function baz() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,20 @@
/// <reference path="fourslash.ts" />
// @filename: main.ts
//// import bar = require("./other");
////
//// function foo() {
//// bar();
//// }
// @filename: other.ts
//// export = /**/function () {
//// baz();
//// }
////
//// function baz() {
//// }
// NOTE: exported function is unnamed, so we expand the item to the entire file...
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,8 @@
/// <reference path="fourslash.ts" />
//// foo();
//// function /**/foo() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,20 @@
/// <reference path="fourslash.ts" />
//// function foo() {
//// bar();
//// }
////
//// function /**/bar() {
//// baz();
//// quxx();
//// baz();
//// }
////
//// function baz() {
//// }
////
//// function quxx() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
// @filename: a.d.ts
//// declare function foo(x?: number): void;
// @filename: b.d.ts
//// declare function foo(x?: string): void;
//// declare function foo(x?: boolean): void;
// @filename: main.ts
//// function bar() {
//// /**/foo();
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
// @filename: a.d.ts
//// declare function /**/foo(x?: number): void;
// @filename: b.d.ts
//// declare function foo(x?: string): void;
//// declare function foo(x?: boolean): void;
// @filename: main.ts
//// function bar() {
//// foo();
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
// @filename: a.d.ts
//// declare function foo(x?: number): void;
// @filename: b.d.ts
//// declare function /**/foo(x?: string): void;
//// declare function foo(x?: boolean): void;
// @filename: main.ts
//// function bar() {
//// foo();
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
// @filename: a.d.ts
//// declare function foo(x?: number): void;
// @filename: b.d.ts
//// declare function foo(x?: string): void;
//// declare function /**/foo(x?: boolean): void;
// @filename: main.ts
//// function bar() {
//// foo();
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
// @filename: a.d.ts
//// declare function foo(x?: number): void;
// @filename: b.d.ts
//// declare function foo(x?: string): void;
//// declare function foo(x?: boolean): void;
// @filename: main.ts
//// function /**/bar() {
//// foo();
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,12 @@
/// <reference path="fourslash.ts" />
//// interface I {
//// /**/foo(): void;
//// }
////
//// const obj: I = { foo() {} };
////
//// obj.foo();
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,17 @@
/// <reference path="fourslash.ts" />
// @jsx: preserve
// @filename: main.tsx
//// function foo() {
//// return <Bar/>;
//// }
////
//// function /**/Bar() {
//// baz();
//// }
////
//// function baz() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// function foo() {
//// bar`a${1}b`;
//// }
////
//// function /**/bar(array: TemplateStringsArray, ...args: any[]) {
//// baz();
//// }
////
//// function baz() {
//// }
goTo.marker();
verify.baselineCallHierarchy();

View File

@ -383,6 +383,7 @@ declare namespace FourSlashInterface {
readonly newFileContents: { readonly [fileName: string]: string };
readonly preferences?: UserPreferences;
}): void;
baselineCallHierarchy(): void;
moveToNewFile(options: {
readonly newFileContents: { readonly [fileName: string]: string };
readonly preferences?: UserPreferences;
@ -773,4 +774,4 @@ declare namespace completion {
export const statementKeywordsWithTypes: ReadonlyArray<Entry>;
export const statementKeywords: ReadonlyArray<Entry>;
export const statementInJsKeywords: ReadonlyArray<Entry>;
}
}