Skip to content
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
35 changes: 24 additions & 11 deletions src/languageserver/handlers/languageHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import type {
WorkspaceEdit,
} from 'vscode-languageserver-types';
import { isKubernetesAssociatedDocument } from '../../languageservice/parser/isKubernetes';
import type { LanguageService } from '../../languageservice/yamlLanguageService';
import type { CustomFormatterOptions, LanguageService } from '../../languageservice/yamlLanguageService';
import type { SettingsState } from '../../yamlSettings';
import type { ValidationHandler } from './validationHandlers';
import { ResultLimitReachedNotification } from '../../requestTypes';
Expand Down Expand Up @@ -128,14 +128,14 @@ export class LanguageHandlers {
return [];
}

const formatEnabled = await this.resolveFormatterState(document);
if (!formatEnabled) {
const formatterSettings = await this.resolveFormatterSettings(document);
if (formatterSettings.enable === false) {
return [];
}

const customFormatterSettings = {
tabWidth: formatParams.options.tabSize,
...this.yamlSettings.yamlFormatterSettings,
...formatterSettings,
};

return this.languageService.doFormat(document, customFormatterSettings);
Expand Down Expand Up @@ -317,18 +317,31 @@ export class LanguageHandlers {
};
}

private async resolveFormatterState(document: TextDocument): Promise<boolean> {
const fallback = this.yamlSettings.yamlFormatterSettings.enable !== false;
private async resolveFormatterSettings(document: TextDocument): Promise<CustomFormatterOptions> {
const fallback = this.yamlSettings.yamlFormatterSettings;
if (this.yamlSettings.hasConfigurationCapability && this.connection.workspace?.getConfiguration) {
try {
const scopedLanguageSettings = await this.connection.workspace.getConfiguration({
section: `[${document.languageId}]`,
scopeUri: document.uri,
});
const [scopedFormatterSettings, scopedLanguageSettings] = await this.connection.workspace.getConfiguration([
{
section: 'yaml.format',
scopeUri: document.uri,
},
{
section: `[${document.languageId}]`,
scopeUri: document.uri,
},
]);

const resolvedSettings = {
...fallback,
...(scopedFormatterSettings ?? {}),
};

if (typeof scopedLanguageSettings?.['yaml.format.enable'] === 'boolean') {
return scopedLanguageSettings['yaml.format.enable'];
resolvedSettings.enable = scopedLanguageSettings['yaml.format.enable'];
}

return resolvedSettings;
} catch {
// ignore and fall back to global setting
}
Expand Down
40 changes: 38 additions & 2 deletions test/formatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import assert from 'assert';
import * as sinon from 'sinon';
import { TextDocument } from 'vscode-languageserver-textdocument';
import type { FormattingOptions, TextEdit } from 'vscode-languageserver-types';
import type { CustomFormatterOptions } from '../src';
import type { LanguageHandlers } from '../src/languageserver/handlers/languageHandlers';
Expand All @@ -15,7 +16,9 @@ import { setupLanguageService, setupTextDocument } from './utils/testHelper';
type LanguageHandlerWithConnection = {
connection: {
workspace: {
getConfiguration: (item?: { section?: string }) => Promise<unknown>;
getConfiguration: (
item?: { section?: string; scopeUri?: string } | Array<{ section?: string; scopeUri?: string }>
) => Promise<unknown>;
};
};
};
Expand Down Expand Up @@ -67,7 +70,7 @@ describe('Formatter Tests', () => {
yamlSettings.documents = new TextDocumentTestManager();
(yamlSettings.documents as TextDocumentTestManager).set(testTextDocument);
const connection = (languageHandler as unknown as LanguageHandlerWithConnection).connection;
sandbox.stub(connection.workspace, 'getConfiguration').resolves({ 'yaml.format.enable': false });
sandbox.stub(connection.workspace, 'getConfiguration').resolves([{}, { 'yaml.format.enable': false }]);
yamlSettings.hasConfigurationCapability = true;
const edits = await languageHandler.formatterHandler({
options: { tabSize: 2, insertSpaces: true },
Expand All @@ -76,6 +79,39 @@ describe('Formatter Tests', () => {
assert.equal(edits.length, 0);
});

it('Formatting uses resource-scoped settings for each document', async () => {
const content = 'root:\n - child: "text"\n';
const singleQuoteDocument = TextDocument.create('file:///workspace/single/test.yaml', 'yaml', 0, content);
const doubleQuoteDocument = TextDocument.create('file:///workspace/double/test.yaml', 'yaml', 0, content);
yamlSettings.documents = new TextDocumentTestManager();
(yamlSettings.documents as TextDocumentTestManager).set(singleQuoteDocument);
(yamlSettings.documents as TextDocumentTestManager).set(doubleQuoteDocument);
yamlSettings.yamlFormatterSettings = { singleQuote: false, enable: true };
yamlSettings.hasConfigurationCapability = true;

const connection = (languageHandler as unknown as LanguageHandlerWithConnection).connection;
const getConfiguration = sandbox.stub(connection.workspace, 'getConfiguration').callsFake(async (items) => {
assert.ok(Array.isArray(items));
assert.equal(items[0].section, 'yaml.format');
assert.equal(items[1].section, '[yaml]');
assert.equal(items[0].scopeUri, items[1].scopeUri);
return [{ singleQuote: items[0].scopeUri?.includes('/single/') }, {}];
});

const singleQuoteEdits = await languageHandler.formatterHandler({
options: { tabSize: 2, insertSpaces: true },
textDocument: singleQuoteDocument,
});
const doubleQuoteEdits = await languageHandler.formatterHandler({
options: { tabSize: 2, insertSpaces: true },
textDocument: doubleQuoteDocument,
});

assert.equal(singleQuoteEdits[0].newText, "root:\n - child: 'text'\n");
assert.equal(doubleQuoteEdits.length, 0);
assert.equal(getConfiguration.callCount, 2);
});

it('Formatting works with custom tags', async () => {
const content = 'cwd: !Test test';
const edits = await parseSetup(content);
Expand Down