Fourslash support

This commit is contained in:
Paul van Brenk
2016-08-05 16:21:46 -07:00
parent 94ea825f95
commit 466d26fc76
7 changed files with 94 additions and 3 deletions

View File

@@ -115,6 +115,15 @@ namespace ts {
return -1;
}
export function firstOrUndefined<T>(array: T[], predicate: (x: T) => boolean): T {
for (let i = 0, len = array.length; i < len; i++) {
if (predicate(array[i])) {
return array[i];
}
}
return undefined;
}
export function indexOfAnyCharCode(text: string, charCodes: number[], start?: number): number {
for (let i = start || 0, len = text.length; i < len; i++) {
if (contains(charCodes, text.charCodeAt(i))) {

View File

@@ -3031,5 +3031,33 @@
"Unknown typing option '{0}'.": {
"category": "Error",
"code": 17010
},
"Add missing 'super()' call.": {
"category": "CodeFix",
"code": 90001
},
"Make 'super()' call the first statement in the constructor.": {
"category": "CodeFix",
"code": 90002
},
"Change 'extends' to 'implements'": {
"category": "CodeFix",
"code": 90003
},
"Remove unused identifiers": {
"category": "CodeFix",
"code": 90004
},
"Implement interface on reference": {
"category": "CodeFix",
"code": 90005
},
"Implement interface on class": {
"category": "CodeFix",
"code": 90006
},
"Implement inherited abstract class": {
"category": "CodeFix",
"code": 90007
}
}

View File

@@ -2547,6 +2547,7 @@ namespace ts {
Warning,
Error,
Message,
CodeFix,
}
export enum ModuleResolutionKind {

View File

@@ -384,7 +384,7 @@ namespace FourSlash {
if (exists !== negative) {
this.printErrorLog(negative, this.getAllDiagnostics());
throw new Error("Failure between markers: " + startMarkerName + ", " + endMarkerName);
throw new Error(`Failure between markers: '${startMarkerName}', '${endMarkerName}'`);
}
}
@@ -638,7 +638,6 @@ namespace FourSlash {
}
}
public verifyCompletionListAllowsNewIdentifier(negative: boolean) {
const completions = this.getCompletionListAtCaret();
@@ -1479,7 +1478,7 @@ namespace FourSlash {
if (isFormattingEdit) {
const newContent = this.getFileContent(fileName);
if (newContent.replace(/\s/g, "") !== oldContent.replace(/\s/g, "")) {
if (this.removeWhitespace(newContent) !== this.removeWhitespace(oldContent)) {
this.raiseError("Formatting operation destroyed non-whitespace content");
}
}
@@ -1545,6 +1544,10 @@ namespace FourSlash {
}
}
private removeWhitespace(text: string): string {
return text.replace(/\s/g, "");
}
public goToBOF() {
this.goToPosition(0);
}
@@ -1862,6 +1865,44 @@ namespace FourSlash {
}
}
public verifyCodeFixAtPosition(expectedText: string, errorCode?: number) {
const ranges = this.getRanges();
if (ranges.length == 0) {
this.raiseError("At least one range should be specified in the testfile.");
}
const fileName = this.activeFile.fileName;
const diagnostics = this.getDiagnostics(fileName);
if (diagnostics.length === 0) {
this.raiseError("Errors expected.");
}
if (diagnostics.length > 1 && !errorCode) {
this.raiseError("When there's more than one error, you must specify the errror to fix.");
}
const diagnostic = !errorCode ? diagnostics[0] : ts.firstOrUndefined(diagnostics, d => d.code == errorCode);
const actual = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [`TS${diagnostic.code}`]);
if (!actual || actual.length == 0) {
this.raiseError("No codefixes returned.");
}
if (actual.length > 1) {
this.raiseError("More than 1 codefix returned.");
}
this.applyEdits(actual[0].changes[0].fileName, actual[0].changes[0].textChanges, /*isFormattingEdit*/ false);
const actualText = this.rangeText(ranges[0]);
if (this.removeWhitespace(actualText) !== this.removeWhitespace(expectedText)) {
this.raiseError(`Actual text doesn't match expected text. Actual: '${actualText}' Expected: '${expectedText}'`);
}
}
public verifyDocCommentTemplate(expected?: ts.TextInsertion) {
const name = "verifyDocCommentTemplate";
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition);
@@ -3066,6 +3107,10 @@ namespace FourSlashInterface {
this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true);
}
public codeFixAtPosition(expectedText: string, errorCode?: number): void {
this.state.verifyCodeFixAtPosition(expectedText, errorCode);
}
public navigationBar(json: any) {
this.state.verifyNavigationBar(json);
}

View File

@@ -453,6 +453,9 @@ namespace Harness.LanguageService {
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean {
return unwrapJSONCallResult(this.shim.isValidBraceCompletionAtPosition(fileName, position, openingBrace));
}
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): ts.CodeAction[] {
return unwrapJSONCallResult(this.shim.getCodeFixesAtPosition(fileName, start, end, JSON.stringify(errorCodes)));
}
getEmitOutput(fileName: string): ts.EmitOutput {
return unwrapJSONCallResult(this.shim.getEmitOutput(fileName));
}

View File

@@ -592,6 +592,10 @@ namespace ts.server {
throw new Error("Not Implemented Yet.");
}
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: string[]): ts.CodeAction[] {
throw new Error("Not Implemented Yet.");
}
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[] {
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
const args: protocol.FileLocationRequestArgs = {

View File

@@ -192,6 +192,7 @@ declare namespace FourSlashInterface {
noMatchingBracePositionInCurrentFile(bracePosition: number): void;
DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean): void;
noDocCommentTemplate(): void;
codeFixAtPosition(expectedText: string, errorCode?: number): void;
navigationBar(json: any): void;
navigationItemsListCount(count: number, searchValue: string, matchKind?: string): void;