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: 4 additions & 3 deletions src/harness/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1221,7 +1221,7 @@ Actual: ${stringify(fullActual)}`);
}

private getCompletionListAtCaret(options?: FourSlashInterface.CompletionsAtOptions): ts.CompletionInfo {
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options);
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition, options, options && options.settings);
}

private getCompletionEntryDetails(entryName: string, source?: string): ts.CompletionEntryDetails {
Expand Down Expand Up @@ -1818,7 +1818,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.defaultCompletionOptions, ts.defaultServicesSettings);
}

if (i % checkCadence === 0) {
Expand Down Expand Up @@ -2393,7 +2393,7 @@ 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.defaultCompletionOptions, includeExternalModuleExports: true }).entries.find(e =>
e.name === options.name && e.source === options.source);

if (!actualCompletion.hasAction) {
Expand Down Expand Up @@ -4618,6 +4618,7 @@ namespace FourSlashInterface {
export type ExpectedCompletionEntry = string | { name: string, insertText?: string, replacementSpan?: FourSlash.Range };
export interface CompletionsAtOptions extends ts.GetCompletionsAtPositionOptions {
isNewIdentifierLocation?: boolean;
settings?: ts.ServicesSettings;
}

export interface VerifyCompletionListContainsOptions extends ts.GetCompletionsAtPositionOptions {
Expand Down
4 changes: 2 additions & 2 deletions src/harness/harnessLanguageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,8 +417,8 @@ 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 {
return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options));
getCompletionsAtPosition(fileName: string, position: number, options: ts.GetCompletionsAtPositionOptions | undefined, settings: ts.ServicesSettings): ts.CompletionInfo {
return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position, options, settings));
}
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));
Expand Down
2 changes: 1 addition & 1 deletion src/harness/unittests/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ namespace ts.server {

session.onMessage(JSON.stringify(configureRequest));

assert.equal(session.getProjectService().getFormatCodeOptions().indentStyle, IndentStyle.Block);
assert.equal(session.getProjectService().getFormatCodeOptions("" as NormalizedPath).indentStyle, IndentStyle.Block);

const setOptionsRequest: protocol.SetCompilerOptionsForInferredProjectsRequest = {
command: CommandNames.CompilerOptionsForInferredProjects,
Expand Down
16 changes: 8 additions & 8 deletions src/harness/unittests/tsserverProjectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1321,13 +1321,13 @@ namespace ts.projectSystem {
service.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]);

const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultCompletionOptions, defaultServicesSettings);
// should contain completions for string
assert.isTrue(completions1.entries.some(e => e.name === "charAt"), "should contain 'charAt'");
assert.isFalse(completions1.entries.some(e => e.name === "toExponential"), "should not contain 'toExponential'");

service.closeClientFile(f2.path);
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 2, defaultCompletionOptions, defaultServicesSettings);
// should contain completions for string
assert.isFalse(completions2.entries.some(e => e.name === "charAt"), "should not contain 'charAt'");
assert.isTrue(completions2.entries.some(e => e.name === "toExponential"), "should contain 'toExponential'");
Expand All @@ -1353,11 +1353,11 @@ namespace ts.projectSystem {
service.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(service.externalProjects[0], [f1.path, f2.path, libFile.path]);

const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
const completions1 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultCompletionOptions, defaultServicesSettings);
assert.isTrue(completions1.entries.some(e => e.name === "somelongname"), "should contain 'somelongname'");

service.closeClientFile(f2.path);
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
const completions2 = service.externalProjects[0].getLanguageService().getCompletionsAtPosition(f1.path, 0, defaultCompletionOptions, defaultServicesSettings);
assert.isFalse(completions2.entries.some(e => e.name === "somelongname"), "should not contain 'somelongname'");
const sf2 = service.externalProjects[0].getLanguageService().getProgram().getSourceFile(f2.path);
assert.equal(sf2.text, "");
Expand Down Expand Up @@ -1962,7 +1962,7 @@ namespace ts.projectSystem {

// Check identifiers defined in HTML content are available in .ts file
const project = configuredProjectAt(projectService, 0);
let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
let completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 1, defaultCompletionOptions, defaultServicesSettings);
assert(completions && completions.entries[0].name === "hello", `expected entry hello to be in completion list`);

// Close HTML file
Expand All @@ -1976,7 +1976,7 @@ namespace ts.projectSystem {
checkProjectActualFiles(configuredProjectAt(projectService, 0), [file1.path, file2.path, config.path]);

// Check identifiers defined in HTML content are not available in .ts file
completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5, { includeExternalModuleExports: false, includeInsertTextCompletions: false });
completions = project.getLanguageService().getCompletionsAtPosition(file1.path, 5, defaultCompletionOptions, defaultServicesSettings);
assert(completions && completions.entries[0].name !== "hello", `unexpected hello entry in completion list`);
});

Expand Down Expand Up @@ -2629,7 +2629,7 @@ namespace ts.projectSystem {
assert.equal(lastEvent.data.project, project, "project name");
assert.isFalse(lastEvent.data.languageServiceEnabled, "Language service state");

const options = projectService.getFormatCodeOptions();
const options = projectService.getFormatCodeOptions(f1.path as ts.server.NormalizedPath);
const edits = project.getLanguageService().getFormattingEditsForDocument(f1.path, options);
assert.deepEqual(edits, [{ span: createTextSpan(/*start*/ 7, /*length*/ 3), newText: " " }]);
});
Expand Down Expand Up @@ -3525,7 +3525,7 @@ namespace ts.projectSystem {
const projectService = createProjectService(host);
projectService.openClientFile(f1.path);

const defaultSettings = projectService.getFormatCodeOptions();
const defaultSettings = projectService.getFormatCodeOptions(f1.path as ts.server.NormalizedPath);

// set global settings
const newGlobalSettings1 = clone(defaultSettings);
Expand Down
24 changes: 14 additions & 10 deletions src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ namespace ts.server {

export interface HostConfiguration {
formatCodeOptions: FormatCodeSettings;
servicesOptions: ServicesSettings;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a pattern we follow when choosing between "options" and settings"?

hostInfo: string;
extraFileExtensions?: JsFileExtensionInfo[];
}
Expand Down Expand Up @@ -442,6 +443,7 @@ namespace ts.server {

this.hostConfiguration = {
formatCodeOptions: getDefaultFormatCodeSettings(this.host),
servicesOptions: defaultServicesSettings,
hostInfo: "Unknown host",
extraFileExtensions: []
};
Expand Down Expand Up @@ -703,15 +705,14 @@ namespace ts.server {
return project.dirty && project.updateGraph();
}

getFormatCodeOptions(file?: NormalizedPath) {
let formatCodeSettings: FormatCodeSettings;
if (file) {
const info = this.getScriptInfoForNormalizedPath(file);
if (info) {
formatCodeSettings = info.getFormatCodeSettings();
}
}
return formatCodeSettings || this.hostConfiguration.formatCodeOptions;
getFormatCodeOptions(file: NormalizedPath) {
const info = this.getScriptInfoForNormalizedPath(file);
return info && info.getFormatCodeSettings() || this.hostConfiguration.formatCodeOptions;
}

getServicesSettings(file: NormalizedPath) {
const info = this.getScriptInfoForNormalizedPath(file);
return info && info.getServicesSettings() || this.hostConfiguration.servicesOptions;
}

private onSourceFileChanged(fileName: NormalizedPath, eventKind: FileWatcherEventKind) {
Expand Down Expand Up @@ -1829,7 +1830,7 @@ namespace ts.server {
if (args.file) {
const info = this.getScriptInfoForNormalizedPath(toNormalizedPath(args.file));
if (info) {
info.setFormatOptions(convertFormatOptions(args.formatOptions));
info.setSettings(convertFormatOptions(args.formatOptions), args.servicesOptions);
this.logger.info(`Host configuration update for file ${args.file}`);
}
}
Expand All @@ -1842,6 +1843,9 @@ namespace ts.server {
mergeMapLikes(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions));
this.logger.info("Format host information updated");
}
if (args.servicesOptions) {
mergeMapLikes(this.hostConfiguration.servicesOptions, args.servicesOptions);
}
if (args.extraFileExtensions) {
this.hostConfiguration.extraFileExtensions = args.extraFileExtensions;
// We need to update the project structures again as it is possible that existing
Expand Down
6 changes: 6 additions & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,8 @@ namespace ts.server.protocol {
*/
formatOptions?: FormatCodeSettings;

servicesOptions?: ServicesSettings;
Copy link
Contributor

@mhegazy mhegazy Mar 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider renaming this to just options. since we intend to make this the bag of all options that are not formatting,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the type be Options too?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah.


/**
* The host's additional supported .js file extensions
*/
Expand Down Expand Up @@ -2588,6 +2590,10 @@ namespace ts.server.protocol {
insertSpaceBeforeTypeAnnotation?: boolean;
}

export interface ServicesSettings {
quote: '"' | "'";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say create an enum for this, or even a literal union type that is not the quote. e.g. "double" | "single". VSCode at the end of the day will put this setting in a json file and having ppl wiring "\"" is not that great.

}

export interface CompilerOptions {
allowJs?: boolean;
allowSyntheticDefaultImports?: boolean;
Expand Down
23 changes: 15 additions & 8 deletions src/server/scriptInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ namespace ts.server {
* All projects that include this file
*/
readonly containingProjects: Project[] = [];
private formatCodeSettings: FormatCodeSettings;
private formatSettings: FormatCodeSettings | undefined;
private servicesSettings: ServicesSettings | undefined;

/* @internal */
fileWatcher: FileWatcher;
Expand Down Expand Up @@ -293,9 +294,8 @@ namespace ts.server {
return this.realpath && this.realpath !== this.path ? this.realpath : undefined;
}

getFormatCodeSettings() {
return this.formatCodeSettings;
}
getFormatCodeSettings(): FormatCodeSettings { return this.formatSettings; }
getServicesSettings(): ServicesSettings { return this.servicesSettings; }

attachToProject(project: Project): boolean {
const isNew = !this.isAttached(project);
Expand Down Expand Up @@ -388,12 +388,19 @@ namespace ts.server {
}
}

setFormatOptions(formatSettings: FormatCodeSettings): void {
setSettings(formatSettings: FormatCodeSettings, servicesSettings: ServicesSettings): void {
if (formatSettings) {
if (!this.formatCodeSettings) {
this.formatCodeSettings = getDefaultFormatCodeSettings(this.host);
if (!this.formatSettings) {
this.formatSettings = getDefaultFormatCodeSettings(this.host);
}
mergeMapLikes(this.formatSettings, formatSettings);
}

if (servicesSettings) {
if (!this.servicesSettings) {
this.servicesSettings = clone(defaultServicesSettings);
}
mergeMapLikes(this.formatCodeSettings, formatSettings);
mergeMapLikes(this.servicesSettings, servicesSettings);
}
}

Expand Down
Loading