Expose document highlighting to server.

This commit is contained in:
Tien Nguyen
2015-08-03 20:45:39 -07:00
parent 33ae0d639a
commit d53cfdcb5b
6 changed files with 159 additions and 3 deletions

View File

@@ -2132,7 +2132,7 @@ module FourSlash {
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 + '.');
this.raiseError('verifyOccurancesAtPositionListContains failed - item isWriteAccess value does not match, actual: ' + occurance.isWriteAccess + ', expected: ' + isWriteAccess + '.');
}
return;
}
@@ -2152,6 +2152,51 @@ module FourSlash {
}
}
private getDocumentHighlightsAtCurrentPosition(fileNamesToSearch: string[]) {
let filesToSearch = fileNamesToSearch.map(name => 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 i = 0; i < documentHighlights.length; i++) if (documentHighlights[i].fileName === fileName) {
let { highlightSpans } = documentHighlights[i];
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, currentDocumentHighlights) => {
return currentCount + currentDocumentHighlights.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);

View File

@@ -528,7 +528,29 @@ namespace ts.server {
}
getDocumentHighlights(fileName: string, position: number): DocumentHighlights[] {
throw new Error("Not Implemented Yet.");
var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
var args: protocol.FileLocationRequestArgs = {
file: fileName,
line: lineOffset.line,
offset: lineOffset.offset,
};
var request = this.processRequest<protocol.DocumentHighlightsRequest>(CommandNames.DocumentHighlights, args);
var response = this.processResponse<protocol.DocumentHighlightsResponse>(request);
return response.body.map(entry => { // convert ts.server.protocol.DocumentHighlightsItem to ts.DocumentHighlights
return {
fileName: entry.file,
highlightSpans: entry.highlightSpans.map(span => { // convert ts.server.protocol.HighlightSpan to ts.HighlighSpan
var start = this.lineOffsetToPosition(entry.file, span.start);
var end = this.lineOffsetToPosition(entry.file, span.end);
return {
textSpan: ts.createTextSpanFromBounds(start, end),
kind: span.kind
};
})
};
});
}
getOutliningSpans(fileName: string): OutliningSpan[] {

View File

@@ -238,6 +238,28 @@ 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 {
}
export interface HighLightSpan extends TextSpan {
kind: string
}
export interface DocumentHighlightsItem {
file: string,
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

View File

@@ -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): 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 filesToSearch = [fileName]; // only search for highlights inside the current file
let documentHighlights = compilerService.languageService.getDocumentHighlights(fileName, position, filesToSearch);
if (!documentHighlights) {
return undefined;
}
return documentHighlights.map(documentHighlight => { // convert ts.DocumentHighlights to ts.server.protocol.DocumentHighlightsItem
var file = documentHighlight.fileName;
return {
file: file,
highlightSpans: documentHighlight.highlightSpans.map(highlightSpan => { // convert to ts.HighlightSpan to ts.server.protocol.HighlightSpan
let { textSpan, kind } = highlightSpan;
let start = compilerService.host.positionToLineOffset(file, textSpan.start);
let end = compilerService.host.positionToLineOffset(file, ts.textSpanEnd(textSpan));
return {
start: start,
end: end,
kind: 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 } = <protocol.FileLocationRequestArgs>request.arguments;
return {response: this.getDocumentHighlights(line, offset, fileName), responseRequired: true};
},
[CommandNames.ProjectInfo]: (request: protocol.Request) => {
var { file, needFileNameList } = <protocol.ProjectInfoRequestArgs>request.arguments;
return {response: this.getProjectInfo(file, needFileNameList), responseRequired: true};