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

Use 'workspace/configuration' to fetch settings #476

Merged
merged 1 commit into from
May 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions src/languageserver/handlers/settingsHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { xhr, configure as configureHttpRequests } from 'request-light';
import { DidChangeConfigurationParams, DocumentFormattingRequest, Connection } from 'vscode-languageserver';
import { DocumentFormattingRequest, Connection, DidChangeConfigurationNotification } from 'vscode-languageserver';
import { isRelativePath, relativeToAbsolutePath } from '../../languageservice/utils/paths';
import { checkSchemaURI, JSON_SCHEMASTORE_URL, KUBERNETES_SCHEMA_URL } from '../../languageservice/utils/schemaUrls';
import { LanguageService, LanguageSettings, SchemaPriority } from '../../languageservice/yamlLanguageService';
Expand All @@ -21,16 +21,35 @@ export class SettingsHandler {
) {}

public registerHandlers(): void {
this.connection.onDidChangeConfiguration((change) => this.configurationChangeHandler(change));
if (this.yamlSettings.hasConfigurationCapability) {
// Register for all configuration changes.
this.connection.client.register(DidChangeConfigurationNotification.type, undefined);
}
this.connection.onDidChangeConfiguration(() => this.pullConfiguration());
}

/**
* Run when the editor configuration is changed
* The client syncs the 'yaml', 'http.proxy', 'http.proxyStrictSSL' settings sections
* Update relevant settings with fallback to defaults if needed
* The server pull the 'yaml', 'http.proxy', 'http.proxyStrictSSL', '[yaml]' settings sections
*/
private configurationChangeHandler(change: DidChangeConfigurationParams): void {
const settings = change.settings as Settings;
async pullConfiguration(): Promise<void> {
const config = await this.connection.workspace.getConfiguration([
{ section: 'yaml' },
{ section: 'http.proxy' },
{ section: 'http.proxyStrictSSL' },
{ section: '[yaml]' },
]);
const settings: Settings = {
yaml: config[0],
http: {
proxy: config[1],
proxyStrictSSL: config[2],
},
yamlEditor: config[3],
};
this.setConfiguration(settings);
}

setConfiguration(settings: Settings): void {
configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL);

this.yamlSettings.specificValidatorPaths = [];
Expand Down Expand Up @@ -81,10 +100,8 @@ export class SettingsHandler {

this.yamlSettings.schemaConfigurationSettings = [];

if (settings['[yaml]'] && settings['[yaml]']['editor.tabSize']) {
this.yamlSettings.indentation = ' '.repeat(settings['[yaml]']['editor.tabSize']);
} else if (settings.editor?.tabSize) {
this.yamlSettings.indentation = ' '.repeat(settings.editor.tabSize);
if (settings.yamlEditor && settings.yamlEditor['editor.tabSize']) {
this.yamlSettings.indentation = ' '.repeat(settings.yamlEditor['editor.tabSize']);
}

for (const uri in this.yamlSettings.yamlConfigurationSettings) {
Expand Down
13 changes: 10 additions & 3 deletions src/yamlServerInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class YAMLServerInit {
languageService: LanguageService;
languageHandler: LanguageHandlers;
validationHandler: ValidationHandler;
settingsHandler: SettingsHandler;

private telemetry: Telemetry;
constructor(
Expand All @@ -48,6 +49,9 @@ export class YAMLServerInit {
this.yamlSettings.workspaceFolders = workspaceFoldersChanged(this.yamlSettings.workspaceFolders, changedFolders);
});
}
// need to call this after connection initialized
this.settingsHandler.registerHandlers();
this.settingsHandler.pullConfiguration();
});
}

Expand Down Expand Up @@ -81,6 +85,9 @@ export class YAMLServerInit {
this.yamlSettings.hasWorkspaceFolderCapability =
this.yamlSettings.capabilities.workspace && !!this.yamlSettings.capabilities.workspace.workspaceFolders;

this.yamlSettings.hasConfigurationCapability = !!(
this.yamlSettings.capabilities.workspace && !!this.yamlSettings.capabilities.workspace.configuration
);
this.registerHandlers();

return {
Expand Down Expand Up @@ -117,17 +124,17 @@ export class YAMLServerInit {
private registerHandlers(): void {
// Register all features that the language server has
this.validationHandler = new ValidationHandler(this.connection, this.languageService, this.yamlSettings);
const settingsHandler = new SettingsHandler(
this.settingsHandler = new SettingsHandler(
this.connection,
this.languageService,
this.yamlSettings,
this.validationHandler,
this.telemetry
);
settingsHandler.registerHandlers();
// this.settingsHandler.registerHandlers();
this.languageHandler = new LanguageHandlers(this.connection, this.languageService, this.yamlSettings, this.validationHandler);
this.languageHandler.registerHandlers();
new NotificationHandlers(this.connection, this.languageService, this.yamlSettings, settingsHandler).registerHandlers();
new NotificationHandlers(this.connection, this.languageService, this.yamlSettings, this.settingsHandler).registerHandlers();
new RequestHandlers(this.connection, this.languageService).registerHandlers();
new WorkspaceHandlers(this.connection, commandExecutor).registerHandlers();
}
Expand Down
7 changes: 5 additions & 2 deletions src/yamlSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ export interface Settings {
proxy: string;
proxyStrictSSL: boolean;
};
editor: {
tabSize: number;
yamlEditor: {
'editor.tabSize': number;
'editor.insertSpaces': boolean;
'editor.formatOnType': boolean;
};
}

Expand Down Expand Up @@ -77,6 +79,7 @@ export class SettingsState {
clientDynamicRegisterSupport = false;
hierarchicalDocumentSymbolSupport = false;
hasWorkspaceFolderCapability = false;
hasConfigurationCapability = false;
useVSCodeContentRequest = false;
}

Expand Down
38 changes: 33 additions & 5 deletions test/settingsHandlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,30 @@ import { SettingsHandler } from '../src/languageserver/handlers/settingsHandlers
import * as sinon from 'sinon';
import * as chai from 'chai';
import * as sinonChai from 'sinon-chai';
import { Connection } from 'vscode-languageserver';
import { Connection, RemoteWorkspace } from 'vscode-languageserver';
import { SettingsState } from '../src/yamlSettings';
import { ValidationHandler } from '../src/languageserver/handlers/validationHandlers';
import { LanguageService, LanguageSettings, SchemaConfiguration, SchemaPriority } from '../src';
import * as request from 'request-light';
import { setupLanguageService } from './utils/testHelper';
import { Telemetry } from '../src/languageserver/telemetry';
import { TestWorkspace } from './utils/testsTypes';

const expect = chai.expect;
chai.use(sinonChai);

describe('Settings Handlers Tests', () => {
const sandbox = sinon.createSandbox();
let connectionStub: sinon.SinonMockStatic;
const connection: Connection = {} as Connection;
let workspaceStub: sinon.SinonStubbedInstance<RemoteWorkspace>;
let languageService: sinon.SinonMockStatic;
let settingsState: SettingsState;
let validationHandler: sinon.SinonMock;
let xhrStub: sinon.SinonStub;

beforeEach(() => {
connectionStub = sandbox.mock();
workspaceStub = sandbox.createStubInstance(TestWorkspace);
connection.workspace = (workspaceStub as unknown) as RemoteWorkspace;
languageService = sandbox.mock();
settingsState = new SettingsState();
validationHandler = sandbox.mock(ValidationHandler);
Expand All @@ -51,7 +54,7 @@ describe('Settings Handlers Tests', () => {
}]}`,
});
const settingsHandler = new SettingsHandler(
(connectionStub as unknown) as Connection,
connection,
(languageService as unknown) as LanguageService,
settingsState,
(validationHandler as unknown) as ValidationHandler,
Expand All @@ -78,7 +81,7 @@ describe('Settings Handlers Tests', () => {

const languageService = languageServerSetup.languageService;
const settingsHandler = new SettingsHandler(
(connectionStub as unknown) as Connection,
connection,
languageService,
settingsState,
(validationHandler as unknown) as ValidationHandler,
Expand Down Expand Up @@ -143,4 +146,29 @@ describe('Settings Handlers Tests', () => {
});
});
});

describe('Settings fetch', () => {
it('should fetch preferences', async () => {
const settingsHandler = new SettingsHandler(
connection,
(languageService as unknown) as LanguageService,
settingsState,
(validationHandler as unknown) as ValidationHandler,
{} as Telemetry
);
workspaceStub.getConfiguration.resolves([{}, {}, {}, {}]);
const setConfigurationStub = sandbox.stub(settingsHandler, 'setConfiguration');

await settingsHandler.pullConfiguration();

expect(workspaceStub.getConfiguration).calledOnceWith([
{ section: 'yaml' },
{ section: 'http.proxy' },
{ section: 'http.proxyStrictSSL' },
{ section: '[yaml]' },
]);

expect(setConfigurationStub).calledOnce;
});
});
});
57 changes: 57 additions & 0 deletions test/utils/testsTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Red Hat. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Event, NotificationHandler, RequestHandler } from 'vscode-jsonrpc';
import {
ApplyWorkspaceEditParams,
WorkspaceEdit,
ApplyWorkspaceEditResponse,
ConfigurationItem,
WorkspaceFolder,
WorkspaceFoldersChangeEvent,
CreateFilesParams,
RenameFilesParams,
DeleteFilesParams,
} from 'vscode-languageserver-protocol';
import { Connection, RemoteWorkspace } from 'vscode-languageserver/lib/common/server';

/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
export class TestWorkspace implements RemoteWorkspace {
connection: Connection;
applyEdit(paramOrEdit: ApplyWorkspaceEditParams | WorkspaceEdit): Promise<ApplyWorkspaceEditResponse> {
throw new Error('Method not implemented.');
}
getConfiguration(): Promise<any>;
getConfiguration(section: string): Promise<any>;
getConfiguration(item: ConfigurationItem): Promise<any>;
getConfiguration(items: ConfigurationItem[]): Promise<any[]>;
getConfiguration(items?: any): Promise<any | any[]> {
throw new Error('Method not implemented.');
}
getWorkspaceFolders(): Promise<WorkspaceFolder[]> {
throw new Error('Method not implemented.');
}
onDidChangeWorkspaceFolders: Event<WorkspaceFoldersChangeEvent>;
onDidCreateFiles(handler: NotificationHandler<CreateFilesParams>): void {
throw new Error('Method not implemented.');
}
onDidRenameFiles(handler: NotificationHandler<RenameFilesParams>): void {
throw new Error('Method not implemented.');
}
onDidDeleteFiles(handler: NotificationHandler<DeleteFilesParams>): void {
throw new Error('Method not implemented.');
}
onWillCreateFiles(handler: RequestHandler<CreateFilesParams, WorkspaceEdit, never>): void {
throw new Error('Method not implemented.');
}
onWillRenameFiles(handler: RequestHandler<RenameFilesParams, WorkspaceEdit, never>): void {
throw new Error('Method not implemented.');
}
onWillDeleteFiles(handler: RequestHandler<DeleteFilesParams, WorkspaceEdit, never>): void {
throw new Error('Method not implemented.');
}
}