Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support services settings #22236

Merged
13 commits merged into from
Mar 20, 2018
7 changes: 5 additions & 2 deletions src/compiler/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,23 @@ namespace ts {

// Literals

/* @internal */ export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote: boolean): StringLiteral; // tslint:disable-line unified-signatures
/** If a node is passed, creates a string literal whose source text is read from a source node during emit. */
export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier): StringLiteral;
export function createLiteral(value: number): NumericLiteral;
export function createLiteral(value: boolean): BooleanLiteral;
export function createLiteral(value: string | number | boolean): PrimaryExpression;
export function createLiteral(value: string | number | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier): PrimaryExpression {
export function createLiteral(value: string | number | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote?: boolean): PrimaryExpression {
if (typeof value === "number") {
return createNumericLiteral(value + "");
}
if (typeof value === "boolean") {
return value ? createTrue() : createFalse();
}
if (isString(value)) {
return createStringLiteral(value);
const res = createStringLiteral(value);
if (isSingleQuote) res.singleQuote = true;
return res;
}
return createLiteralFromNode(value);
}
Expand Down
53 changes: 27 additions & 26 deletions src/harness/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,6 @@ namespace FourSlash {

function memoWrap(ls: ts.LanguageService, target: TestState): ts.LanguageService {
const cacheableMembers: (keyof typeof ls)[] = [
"getCompletionsAtPosition",
"getCompletionEntryDetails",
"getCompletionEntrySymbol",
"getQuickInfoAtPosition",
Expand Down Expand Up @@ -1228,8 +1227,8 @@ Actual: ${stringify(fullActual)}`);
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options);
}

private getCompletionEntryDetails(entryName: string, source?: string): ts.CompletionEntryDetails {
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source);
private getCompletionEntryDetails(entryName: string, source?: string, options?: ts.Options): ts.CompletionEntryDetails {
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source, options);
}

private getReferencesAtCaret() {
Expand Down Expand Up @@ -1728,7 +1727,7 @@ Actual: ${stringify(fullActual)}`);
Harness.IO.log(stringify(sigHelp));
}

public printCompletionListMembers(options: ts.GetCompletionsAtPositionOptions | undefined) {
public printCompletionListMembers(options: ts.Options | undefined) {
const completions = this.getCompletionListAtCaret(options);
this.printMembersOrCompletions(completions);
}
Expand Down Expand Up @@ -1827,7 +1826,7 @@ Actual: ${stringify(fullActual)}`);
}
else if (prevChar === " " && /A-Za-z_/.test(ch)) {
/* Completions */
this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset, ts.defaultOptions);
}

if (i % checkCadence === 0) {
Expand Down Expand Up @@ -2402,14 +2401,14 @@ Actual: ${stringify(fullActual)}`);
public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) {
this.goToMarker(markerName);

const actualCompletion = this.getCompletionListAtCaret({ includeExternalModuleExports: true, includeInsertTextCompletions: false }).entries.find(e =>
const actualCompletion = this.getCompletionListAtCaret({ ...ts.defaultOptions, includeCompletionsForModuleExports: true }).entries.find(e =>
e.name === options.name && e.source === options.source);

if (!actualCompletion.hasAction) {
this.raiseError(`Completion for ${options.name} does not have an associated action.`);
}

const details = this.getCompletionEntryDetails(options.name, actualCompletion.source);
const details = this.getCompletionEntryDetails(options.name, actualCompletion.source, options.options);
if (details.codeActions.length !== 1) {
this.raiseError(`Expected one code action, got ${details.codeActions.length}`);
}
Expand Down Expand Up @@ -2454,7 +2453,7 @@ Actual: ${stringify(fullActual)}`);
const { fixId, newFileContent } = options;
const fixIds = ts.mapDefined(this.getCodeFixes(this.activeFile.fileName), a => a.fixId);
ts.Debug.assert(ts.contains(fixIds, fixId), "No available code fix has that group id.", () => `Expected '${fixId}'. Available action ids: ${fixIds}`);
const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings);
const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.defaultOptions);
assert.deepEqual(commands, options.commands);
assert(changes.every(c => c.fileName === this.activeFile.fileName), "TODO: support testing codefixes that touch multiple files");
this.applyChanges(changes);
Expand Down Expand Up @@ -2483,7 +2482,7 @@ Actual: ${stringify(fullActual)}`);

public verifyCodeFix(options: FourSlashInterface.VerifyCodeFixOptions) {
const fileName = this.activeFile.fileName;
const actions = this.getCodeFixes(fileName, options.errorCode);
const actions = this.getCodeFixes(fileName, options.errorCode, options.options);
let index = options.index;
if (index === undefined) {
if (!(actions && actions.length === 1)) {
Expand Down Expand Up @@ -2522,7 +2521,7 @@ Actual: ${stringify(fullActual)}`);
* Rerieves a codefix satisfying the parameters, or undefined if no such codefix is found.
* @param fileName Path to file where error should be retrieved from.
*/
private getCodeFixes(fileName: string, errorCode?: number): ts.CodeFixAction[] {
private getCodeFixes(fileName: string, errorCode?: number, options: ts.Options = ts.defaultOptions): ts.CodeFixAction[] {
const diagnosticsForCodeFix = this.getDiagnostics(fileName, /*includeSuggestions*/ true).map(diagnostic => ({
start: diagnostic.start,
length: diagnostic.length,
Expand All @@ -2534,7 +2533,7 @@ Actual: ${stringify(fullActual)}`);
return;
}

return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings);
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings, options);
});
}

Expand All @@ -2560,15 +2559,15 @@ Actual: ${stringify(fullActual)}`);
}
}

public verifyImportFixAtPosition(expectedTextArray: string[], errorCode?: number) {
public verifyImportFixAtPosition(expectedTextArray: string[], errorCode: number | undefined, options: ts.Options | undefined) {
const { fileName } = this.activeFile;
const ranges = this.getRanges().filter(r => r.fileName === fileName);
if (ranges.length !== 1) {
this.raiseError("Exactly one range should be specified in the testfile.");
}
const range = ts.first(ranges);

const codeFixes = this.getCodeFixes(fileName, errorCode);
const codeFixes = this.getCodeFixes(fileName, errorCode, options);

if (codeFixes.length === 0) {
if (expectedTextArray.length !== 0) {
Expand Down Expand Up @@ -2938,7 +2937,7 @@ Actual: ${stringify(fullActual)}`);

public verifyApplicableRefactorAvailableAtMarker(negative: boolean, markerName: string) {
const marker = this.getMarkerByName(markerName);
const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, marker.position);
const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, marker.position, ts.defaultOptions);
const isAvailable = applicableRefactors && applicableRefactors.length > 0;
if (negative && isAvailable) {
this.raiseError(`verifyApplicableRefactorAvailableAtMarker failed - expected no refactor at marker ${markerName} but found some.`);
Expand All @@ -2958,7 +2957,7 @@ Actual: ${stringify(fullActual)}`);
public verifyRefactorAvailable(negative: boolean, name: string, actionName?: string) {
const selection = this.getSelection();

let refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, selection) || [];
let refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, selection, ts.defaultOptions) || [];
refactors = refactors.filter(r => r.name === name && (actionName === undefined || r.actions.some(a => a.name === actionName)));
const isAvailable = refactors.length > 0;

Expand All @@ -2980,7 +2979,7 @@ Actual: ${stringify(fullActual)}`);
public verifyRefactor({ name, actionName, refactors }: FourSlashInterface.VerifyRefactorOptions) {
const selection = this.getSelection();

const actualRefactors = (this.languageService.getApplicableRefactors(this.activeFile.fileName, selection) || ts.emptyArray)
const actualRefactors = (this.languageService.getApplicableRefactors(this.activeFile.fileName, selection, ts.defaultOptions) || ts.emptyArray)
.filter(r => r.name === name && r.actions.some(a => a.name === actionName));
this.assertObjectsEqual(actualRefactors, refactors);
}
Expand All @@ -2991,7 +2990,7 @@ Actual: ${stringify(fullActual)}`);
throw new Error("Exactly one refactor range is allowed per test.");
}

const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, { pos: ranges[0].pos, end: ranges[0].end });
const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, ts.first(ranges), ts.defaultOptions);
const isAvailable = applicableRefactors && applicableRefactors.length > 0;
if (negative && isAvailable) {
this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected no refactor but found some.`);
Expand All @@ -3003,7 +3002,7 @@ Actual: ${stringify(fullActual)}`);

public applyRefactor({ refactorName, actionName, actionDescription, newContent: newContentWithRenameMarker }: FourSlashInterface.ApplyRefactorOptions) {
const range = this.getSelection();
const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range);
const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range, ts.defaultOptions);
const refactorsWithName = refactors.filter(r => r.name === refactorName);
if (refactorsWithName.length === 0) {
this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.\nAvailable refactors: ${refactors.map(r => r.name)}`);
Expand All @@ -3017,7 +3016,7 @@ Actual: ${stringify(fullActual)}`);
this.raiseError(`Expected action description to be ${JSON.stringify(actionDescription)}, got: ${JSON.stringify(action.description)}`);
}

const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName);
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.defaultOptions);
for (const edit of editInfo.edits) {
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
}
Expand Down Expand Up @@ -3062,14 +3061,14 @@ Actual: ${stringify(fullActual)}`);
formattingOptions = formattingOptions || this.formatCodeSettings;
const markerPos = this.getMarkerByName(markerName).position;

const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos);
const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, markerPos, ts.defaultOptions);
const applicableRefactorToApply = ts.find(applicableRefactors, refactor => refactor.name === refactorNameToApply);

if (!applicableRefactorToApply) {
this.raiseError(`The expected refactor: ${refactorNameToApply} is not available at the marker location.`);
}

const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName);
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply, actionName, ts.defaultOptions);

for (const edit of editInfo.edits) {
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
Expand Down Expand Up @@ -4234,8 +4233,8 @@ namespace FourSlashInterface {
this.state.applyCodeActionFromCompletion(markerName, options);
}

public importFixAtPosition(expectedTextArray: string[], errorCode?: number): void {
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode);
public importFixAtPosition(expectedTextArray: string[], errorCode?: number, options?: ts.Options): void {
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode, options);
}

public navigationBar(json: any, options?: { checkSpans?: boolean }) {
Expand Down Expand Up @@ -4441,7 +4440,7 @@ namespace FourSlashInterface {
this.state.printCurrentSignatureHelp();
}

public printCompletionListMembers(options: ts.GetCompletionsAtPositionOptions | undefined) {
public printCompletionListMembers(options: ts.Options | undefined) {
this.state.printCompletionListMembers(options);
}

Expand Down Expand Up @@ -4638,11 +4637,11 @@ namespace FourSlashInterface {
}

export type ExpectedCompletionEntry = string | { name: string, insertText?: string, replacementSpan?: FourSlash.Range };
export interface CompletionsAtOptions extends ts.GetCompletionsAtPositionOptions {
export interface CompletionsAtOptions extends Partial<ts.Options> {
isNewIdentifierLocation?: boolean;
}

export interface VerifyCompletionListContainsOptions extends ts.GetCompletionsAtPositionOptions {
export interface VerifyCompletionListContainsOptions extends ts.Options {
sourceDisplay: string;
isRecommended?: true;
insertText?: string;
Expand All @@ -4663,6 +4662,7 @@ namespace FourSlashInterface {
description: string;
errorCode?: number;
index?: number;
options?: ts.Options;
}

export interface VerifyCodeFixAvailableOptions {
Expand All @@ -4686,6 +4686,7 @@ namespace FourSlashInterface {
name: string;
source?: string;
description: string;
options?: ts.Options;
}

export interface Diagnostic {
Expand Down
6 changes: 3 additions & 3 deletions src/harness/harnessLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,11 +420,11 @@ namespace Harness.LanguageService {
getEncodedSemanticClassifications(fileName: string, span: ts.TextSpan): ts.Classifications {
return unwrapJSONCallResult(this.shim.getEncodedSemanticClassifications(fileName, span.start, span.length));
}
getCompletionsAtPosition(fileName: string, position: number, options: ts.GetCompletionsAtPositionOptions | undefined): ts.CompletionInfo {
getCompletionsAtPosition(fileName: string, position: number, options: ts.Options | undefined): ts.CompletionInfo {
return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options));
}
getCompletionEntryDetails(fileName: string, position: number, entryName: string, options: ts.FormatCodeOptions | undefined, source: string | undefined): ts.CompletionEntryDetails {
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(options), source));
getCompletionEntryDetails(fileName: string, position: number, entryName: string, formatOptions: ts.FormatCodeOptions | undefined, source: string | undefined, options: ts.Options | undefined): ts.CompletionEntryDetails {
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName, JSON.stringify(formatOptions), source, options));
}
getCompletionEntrySymbol(): ts.Symbol {
throw new Error("getCompletionEntrySymbol not implemented across the shim layer.");
Expand Down
2 changes: 2 additions & 0 deletions src/harness/unittests/extractTestHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ namespace ts {
endPosition: selectionRange.end,
host: notImplementedHost,
formatContext: formatting.getFormatContext(testFormatOptions),
options: defaultOptions,
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));
assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
Expand Down Expand Up @@ -190,6 +191,7 @@ namespace ts {
endPosition: selectionRange.end,
host: notImplementedHost,
formatContext: formatting.getFormatContext(testFormatOptions),
options: defaultOptions,
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));
assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
Expand Down
Loading