mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-06 20:14:01 -06:00
Merge pull request #4139 from hoanhtien/documentHighlights
Expose document highlighting to server.
This commit is contained in:
commit
6980d56bfd
@ -2122,36 +2122,81 @@ module FourSlash {
|
||||
public verifyOccurrencesAtPositionListContains(fileName: string, start: number, end: number, isWriteAccess?: boolean) {
|
||||
this.taoInvalidReason = "verifyOccurrencesAtPositionListContains NYI";
|
||||
|
||||
let occurances = this.getOccurancesAtCurrentPosition();
|
||||
let occurrences = this.getOccurancesAtCurrentPosition();
|
||||
|
||||
if (!occurances || occurances.length === 0) {
|
||||
this.raiseError("verifyOccurancesAtPositionListContains failed - found 0 references, expected at least one.");
|
||||
if (!occurrences || occurrences.length === 0) {
|
||||
this.raiseError('verifyOccurancesAtPositionListContains failed - found 0 references, expected at least one.');
|
||||
}
|
||||
|
||||
for (let i = 0; i < occurances.length; i++) {
|
||||
let occurance = occurances[i];
|
||||
if (occurance && occurance.fileName === fileName && occurance.textSpan.start === start && ts.textSpanEnd(occurance.textSpan) === end) {
|
||||
if (typeof isWriteAccess !== "undefined" && occurance.isWriteAccess !== isWriteAccess) {
|
||||
this.raiseError(`verifyOccurancesAtPositionListContains failed - item isWriteAccess value doe not match, actual: ${occurance.isWriteAccess}, expected: ${isWriteAccess}.`);
|
||||
for (let occurrence of occurrences) {
|
||||
if (occurrence && occurrence.fileName === fileName && occurrence.textSpan.start === start && ts.textSpanEnd(occurrence.textSpan) === end) {
|
||||
if (typeof isWriteAccess !== "undefined" && occurrence.isWriteAccess !== isWriteAccess) {
|
||||
this.raiseError(`verifyOccurrencesAtPositionListContains failed - item isWriteAccess value does not match, actual: ${occurrence.isWriteAccess}, expected: ${isWriteAccess}.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let missingItem = { fileName: fileName, start: start, end: end, isWriteAccess: isWriteAccess };
|
||||
this.raiseError(`verifyOccurancesAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem)} in the returned list: (${JSON.stringify(occurances)})`);
|
||||
this.raiseError(`verifyOccurrencesAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem)} in the returned list: (${JSON.stringify(occurrences)})`);
|
||||
}
|
||||
|
||||
public verifyOccurrencesAtPositionListCount(expectedCount: number) {
|
||||
this.taoInvalidReason = "verifyOccurrencesAtPositionListCount NYI";
|
||||
|
||||
let occurances = this.getOccurancesAtCurrentPosition();
|
||||
let actualCount = occurances ? occurances.length : 0;
|
||||
let occurrences = this.getOccurancesAtCurrentPosition();
|
||||
let actualCount = occurrences ? occurrences.length : 0;
|
||||
if (expectedCount !== actualCount) {
|
||||
this.raiseError(`verifyOccurrencesAtPositionListCount failed - actual: ${actualCount}, expected:${expectedCount}`);
|
||||
}
|
||||
}
|
||||
|
||||
private getDocumentHighlightsAtCurrentPosition(fileNamesToSearch: string[]) {
|
||||
let filesToSearch = fileNamesToSearch.map(name => ts.combinePaths(this.basePath, name));
|
||||
return this.languageService.getDocumentHighlights(this.activeFile.fileName, this.currentCaretPosition, filesToSearch);
|
||||
}
|
||||
|
||||
public verifyDocumentHighlightsAtPositionListContains(fileName: string, start: number, end: number, fileNamesToSearch: string[], kind?: string) {
|
||||
this.taoInvalidReason = 'verifyDocumentHighlightsAtPositionListContains NYI';
|
||||
|
||||
let documentHighlights = this.getDocumentHighlightsAtCurrentPosition(fileNamesToSearch);
|
||||
|
||||
if (!documentHighlights || documentHighlights.length === 0) {
|
||||
this.raiseError('verifyDocumentHighlightsAtPositionListContains failed - found 0 highlights, expected at least one.');
|
||||
}
|
||||
|
||||
for (let documentHighlight of documentHighlights) {
|
||||
if (documentHighlight.fileName === fileName) {
|
||||
let { highlightSpans } = documentHighlight;
|
||||
|
||||
for (let highlight of highlightSpans) {
|
||||
if (highlight && highlight.textSpan.start === start && ts.textSpanEnd(highlight.textSpan) === end) {
|
||||
if (typeof kind !== "undefined" && highlight.kind !== kind) {
|
||||
this.raiseError('verifyDocumentHighlightsAtPositionListContains failed - item "kind" value does not match, actual: ' + highlight.kind + ', expected: ' + kind + '.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let missingItem = { fileName: fileName, start: start, end: end, kind: kind };
|
||||
this.raiseError('verifyOccurancesAtPositionListContains failed - could not find the item: ' + JSON.stringify(missingItem) + ' in the returned list: (' + JSON.stringify(documentHighlights) + ')');
|
||||
}
|
||||
|
||||
public verifyDocumentHighlightsAtPositionListCount(expectedCount: number, fileNamesToSearch: string[]) {
|
||||
this.taoInvalidReason = 'verifyDocumentHighlightsAtPositionListCount NYI';
|
||||
|
||||
let documentHighlights = this.getDocumentHighlightsAtCurrentPosition(fileNamesToSearch);
|
||||
let actualCount = documentHighlights
|
||||
? documentHighlights.reduce((currentCount, { highlightSpans }) => currentCount + highlightSpans.length, 0)
|
||||
: 0;
|
||||
|
||||
if (expectedCount !== actualCount) {
|
||||
this.raiseError('verifyDocumentHighlightsAtPositionListCount failed - actual: ' + actualCount + ', expected:' + expectedCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the text of the entire line the caret is currently at
|
||||
private getCurrentLineContent() {
|
||||
let text = this.getFileContent(this.activeFile.fileName);
|
||||
|
||||
@ -527,8 +527,33 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
getDocumentHighlights(fileName: string, position: number): DocumentHighlights[] {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] {
|
||||
let { line, offset } = this.positionToOneBasedLineOffset(fileName, position);
|
||||
let args: protocol.DocumentHighlightsRequestArgs = { file: fileName, line, offset, filesToSearch };
|
||||
|
||||
let request = this.processRequest<protocol.DocumentHighlightsRequest>(CommandNames.DocumentHighlights, args);
|
||||
let response = this.processResponse<protocol.DocumentHighlightsResponse>(request);
|
||||
|
||||
let self = this;
|
||||
return response.body.map(convertToDocumentHighlights);
|
||||
|
||||
function convertToDocumentHighlights(item: ts.server.protocol.DocumentHighlightsItem): ts.DocumentHighlights {
|
||||
let { file, highlightSpans } = item;
|
||||
|
||||
return {
|
||||
fileName: file,
|
||||
highlightSpans: highlightSpans.map(convertHighlightSpan)
|
||||
};
|
||||
|
||||
function convertHighlightSpan(span: ts.server.protocol.HighlightSpan): ts.HighlightSpan {
|
||||
let start = self.lineOffsetToPosition(file, span.start);
|
||||
let end = self.lineOffsetToPosition(file, span.end);
|
||||
return {
|
||||
textSpan: ts.createTextSpanFromBounds(start, end),
|
||||
kind: span.kind
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getOutliningSpans(fileName: string): OutliningSpan[] {
|
||||
|
||||
40
src/server/protocol.d.ts
vendored
40
src/server/protocol.d.ts
vendored
@ -156,6 +156,17 @@ declare namespace ts.server.protocol {
|
||||
arguments: FileLocationRequestArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments in document highlight request; include: filesToSearch, file,
|
||||
* line, offset.
|
||||
*/
|
||||
export interface DocumentHighlightsRequestArgs extends FileLocationRequestArgs {
|
||||
/**
|
||||
* List of files to search for document highlights.
|
||||
*/
|
||||
filesToSearch: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to definition request; value of command field is
|
||||
* "definition". Return response giving the file locations that
|
||||
@ -238,6 +249,35 @@ declare namespace ts.server.protocol {
|
||||
body?: OccurrencesResponseItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document highlights request; value of command field is
|
||||
* "documentHighlights". Return response giving spans that are relevant
|
||||
* in the file at a given line and column.
|
||||
*/
|
||||
export interface DocumentHighlightsRequest extends FileLocationRequest {
|
||||
arguments: DocumentHighlightsRequestArgs
|
||||
}
|
||||
|
||||
export interface HighlightSpan extends TextSpan {
|
||||
kind: string
|
||||
}
|
||||
|
||||
export interface DocumentHighlightsItem {
|
||||
/**
|
||||
* File containing highlight spans.
|
||||
*/
|
||||
file: string,
|
||||
|
||||
/**
|
||||
* Spans to highlight in file.
|
||||
*/
|
||||
highlightSpans: HighlightSpan[];
|
||||
}
|
||||
|
||||
export interface DocumentHighlightsResponse extends Response {
|
||||
body?: DocumentHighlightsItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find references request; value of command field is
|
||||
* "references". Return response giving the file locations that
|
||||
|
||||
@ -89,6 +89,7 @@ namespace ts.server {
|
||||
export const NavBar = "navbar";
|
||||
export const Navto = "navto";
|
||||
export const Occurrences = "occurrences";
|
||||
export const DocumentHighlights = "documentHighlights";
|
||||
export const Open = "open";
|
||||
export const Quickinfo = "quickinfo";
|
||||
export const References = "references";
|
||||
@ -313,7 +314,7 @@ namespace ts.server {
|
||||
}));
|
||||
}
|
||||
|
||||
private getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[]{
|
||||
private getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[] {
|
||||
fileName = ts.normalizePath(fileName);
|
||||
let project = this.projectService.getProjectForFile(fileName);
|
||||
|
||||
@ -343,6 +344,42 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
private getDocumentHighlights(line: number, offset: number, fileName: string, filesToSearch: string[]): protocol.DocumentHighlightsItem[] {
|
||||
fileName = ts.normalizePath(fileName);
|
||||
let project = this.projectService.getProjectForFile(fileName);
|
||||
|
||||
if (!project) {
|
||||
throw Errors.NoProject;
|
||||
}
|
||||
|
||||
let { compilerService } = project;
|
||||
let position = compilerService.host.lineOffsetToPosition(fileName, line, offset);
|
||||
|
||||
let documentHighlights = compilerService.languageService.getDocumentHighlights(fileName, position, filesToSearch);
|
||||
|
||||
if (!documentHighlights) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return documentHighlights.map(convertToDocumentHighlightsItem);
|
||||
|
||||
function convertToDocumentHighlightsItem(documentHighlights: ts.DocumentHighlights): ts.server.protocol.DocumentHighlightsItem {
|
||||
let { fileName, highlightSpans } = documentHighlights;
|
||||
|
||||
return {
|
||||
file: fileName,
|
||||
highlightSpans: highlightSpans.map(convertHighlightSpan)
|
||||
};
|
||||
|
||||
function convertHighlightSpan(highlightSpan: ts.HighlightSpan): ts.server.protocol.HighlightSpan {
|
||||
let { textSpan, kind } = highlightSpan;
|
||||
let start = compilerService.host.positionToLineOffset(fileName, textSpan.start);
|
||||
let end = compilerService.host.positionToLineOffset(fileName, ts.textSpanEnd(textSpan));
|
||||
return { start, end, kind };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getProjectInfo(fileName: string, needFileNameList: boolean): protocol.ProjectInfo {
|
||||
fileName = ts.normalizePath(fileName)
|
||||
let project = this.projectService.getProjectForFile(fileName)
|
||||
@ -937,6 +974,10 @@ namespace ts.server {
|
||||
var { line, offset, file: fileName } = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
return {response: this.getOccurrences(line, offset, fileName), responseRequired: true};
|
||||
},
|
||||
[CommandNames.DocumentHighlights]: (request: protocol.Request) => {
|
||||
var { line, offset, file: fileName, filesToSearch } = <protocol.DocumentHighlightsRequestArgs>request.arguments;
|
||||
return {response: this.getDocumentHighlights(line, offset, fileName, filesToSearch), responseRequired: true};
|
||||
},
|
||||
[CommandNames.ProjectInfo]: (request: protocol.Request) => {
|
||||
var { file, needFileNameList } = <protocol.ProjectInfoRequestArgs>request.arguments;
|
||||
return {response: this.getProjectInfo(file, needFileNameList), responseRequired: true};
|
||||
|
||||
@ -421,6 +421,14 @@ module FourSlashInterface {
|
||||
FourSlash.currentTestState.verifyOccurrencesAtPositionListCount(expectedCount);
|
||||
}
|
||||
|
||||
public documentHighlightsAtPositionContains(range: Range, fileNamesToSearch: string[], kind?: string) {
|
||||
FourSlash.currentTestState.verifyDocumentHighlightsAtPositionListContains(range.fileName, range.start, range.end, fileNamesToSearch, kind);
|
||||
}
|
||||
|
||||
public documentHighlightsAtPositionCount(expectedCount: number, fileNamesToSearch: string[]) {
|
||||
FourSlash.currentTestState.verifyDocumentHighlightsAtPositionListCount(expectedCount, fileNamesToSearch);
|
||||
}
|
||||
|
||||
public completionEntryDetailIs(entryName: string, text: string, documentation?: string, kind?: string) {
|
||||
FourSlash.currentTestState.verifyCompletionEntryDetails(entryName, text, documentation, kind);
|
||||
}
|
||||
|
||||
18
tests/cases/fourslash/server/documentHighlights01.ts
Normal file
18
tests/cases/fourslash/server/documentHighlights01.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/// <reference path="../fourslash.ts"/>
|
||||
|
||||
// @Filename: a.ts
|
||||
////function [|f|](x: typeof [|f|]) {
|
||||
//// [|f|]([|f|]);
|
||||
////}
|
||||
|
||||
let ranges = test.ranges();
|
||||
|
||||
for (let r of ranges) {
|
||||
goTo.position(r.start);
|
||||
verify.documentHighlightsAtPositionCount(ranges.length, ["a.ts"]);
|
||||
|
||||
for (let range of ranges) {
|
||||
verify.documentHighlightsAtPositionContains(range, ["a.ts"]);
|
||||
}
|
||||
}
|
||||
|
||||
35
tests/cases/fourslash/server/documentHighlights02.ts
Normal file
35
tests/cases/fourslash/server/documentHighlights02.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/// <reference path="../fourslash.ts"/>
|
||||
|
||||
// @Filename: a.ts
|
||||
////function [|foo|] () {
|
||||
//// return 1;
|
||||
////}
|
||||
////[|foo|]();
|
||||
|
||||
// @Filename: b.ts
|
||||
/////// <reference path="a.ts"/>
|
||||
////foo();
|
||||
|
||||
// open two files
|
||||
goTo.file("a.ts");
|
||||
goTo.file("b.ts");
|
||||
|
||||
let ranges = test.ranges();
|
||||
|
||||
for (let i = 0; i < ranges.length; ++i) {
|
||||
let r = ranges[i];
|
||||
|
||||
if (i < 2) {
|
||||
goTo.file("a.ts");
|
||||
}
|
||||
else {
|
||||
goTo.file("b.ts");
|
||||
}
|
||||
|
||||
goTo.position(r.start);
|
||||
verify.documentHighlightsAtPositionCount(3, ["a.ts", "b.ts"]);
|
||||
|
||||
for (let range of ranges) {
|
||||
verify.documentHighlightsAtPositionContains(range, ["a.ts", "b.ts"]);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user