From 0ae17e5ac77d3a37532ca1b783c9de5ace83e021 Mon Sep 17 00:00:00 2001 From: Nathan Totten Date: Wed, 26 Aug 2020 13:21:34 -0400 Subject: [PATCH] Improvements to document selectors, custom selectors. --- README.md | 4 + package.json | 8 ++ package.nls.json | 1 + src/LanguageResolver.ts | 16 ++- src/PrettierEditService.ts | 108 ++++++++++-------- src/StatusBarService.ts | 2 +- src/test/suite/config.test.ts | 5 + src/types.d.ts | 4 + .../config/customextension/.prettierrc | 10 ++ test-fixtures/config/customextension/test.abc | 7 ++ .../config/customextension/test.result.abc | 16 +++ test-fixtures/test.code-workspace | 5 +- 12 files changed, 136 insertions(+), 50 deletions(-) create mode 100644 test-fixtures/config/customextension/.prettierrc create mode 100644 test-fixtures/config/customextension/test.abc create mode 100644 test-fixtures/config/customextension/test.result.abc diff --git a/README.md b/README.md index ad7e75e5b..a1483a80e 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,10 @@ A list of languages IDs to disable this extension on. **Note: Disabling a language enabled in a parent folder will prevent formatting instead of letting any other formatter to run** +#### prettier.documentSelectors + +A list of [glob patterns](https://code.visualstudio.com/api/references/vscode-api#GlobPattern) to register Prettier formatter. Typically these will be in the format of `**/*.abc` to tell this extension to register itself as the formatter for all files with the `abc` extension. This feature can be useful when you have [overrides](https://prettier.io/docs/en/configuration.html#configuration-overrides) set in your config file to map custom extensions to a parser. + #### prettier.useEditorConfig (default: `true`) Whether or not to take .editorconfig into account when parsing configuration. See the [prettier.resolveConfig docs](https://prettier.io/docs/en/api.html) for details. diff --git a/package.json b/package.json index 3db762ac6..31b48d768 100644 --- a/package.json +++ b/package.json @@ -134,6 +134,14 @@ "markdownDescription": "%ext.config.disableLanguages%", "scope": "window" }, + "prettier.documentSelectors": { + "type": "array", + "items": { + "type": "string" + }, + "markdownDescription": "%ext.config.documentSelectors%", + "scope": "window" + }, "prettier.requireConfig": { "type": "boolean", "default": false, diff --git a/package.nls.json b/package.nls.json index 863c242fb..b0fd718d9 100644 --- a/package.nls.json +++ b/package.nls.json @@ -4,6 +4,7 @@ "ext.config.bracketSpacing": "Controls the printing of spaces inside object literals", "ext.config.configPath": "Path to the prettier configuration file", "ext.config.disableLanguages": "A list of languages IDs to disable this extension on", + "ext.config.documentSelectors": "A list of [glob patterns](https://code.visualstudio.com/api/references/vscode-api#GlobPattern) to register Prettier formatter", "ext.config.endOfLine": "Specify the end of line used by prettier", "ext.config.htmlWhitespaceSensitivity": "Specify the global whitespace sensitivity for HTML files.\n Valid options:\n- `css` - Respect the default value of CSS display property.\n- `strict` - Whitespaces are considered sensitive.\n- `ignores` - Whitespaces are considered insensitive.", "ext.config.ignorePath": "Path to a .prettierignore file", diff --git a/src/LanguageResolver.ts b/src/LanguageResolver.ts index 6176f8eda..01d37c47e 100644 --- a/src/LanguageResolver.ts +++ b/src/LanguageResolver.ts @@ -30,7 +30,7 @@ export class LanguageResolver { } } - public allEnabledLanguages(fsPath?: string): string[] { + public getSupportedLanguages(fsPath?: string): string[] { const enabledLanguages: string[] = []; this.getSupportLanguages(fsPath).forEach((lang) => { if (lang && lang.vscodeLanguageIds) { @@ -42,7 +42,7 @@ export class LanguageResolver { }); } - public rangeSupportedLanguages(): string[] { + public getRangeSupportedLanguages(): string[] { return [ "javascript", "javascriptreact", @@ -53,6 +53,18 @@ export class LanguageResolver { ]; } + public getSupportedFileExtensions(fsPath?: string) { + const extensions: string[] = []; + this.getSupportLanguages(fsPath).forEach((lang) => { + if (lang && lang.extensions) { + extensions.push(...lang.extensions); + } + }); + return extensions.filter((value, index, self) => { + return self.indexOf(value) === index; + }); + } + private getSupportLanguages(fsPath?: string) { const prettierInstance = this.moduleResolver.getPrettierInstance(fsPath); return prettierInstance.getSupportInfo().languages; diff --git a/src/PrettierEditService.ts b/src/PrettierEditService.ts index 272096c13..dd63e7cf9 100644 --- a/src/PrettierEditService.ts +++ b/src/PrettierEditService.ts @@ -114,15 +114,15 @@ export default class PrettierEditService implements Disposable { * Build formatter selectors */ private selectors = (): ISelectors => { - const { disableLanguages } = getConfig(); + const { disableLanguages, documentSelectors } = getConfig(); let allLanguages: string[]; if (workspace.workspaceFolders === undefined) { - allLanguages = this.languageResolver.allEnabledLanguages(); + allLanguages = this.languageResolver.getSupportedLanguages(); } else { allLanguages = []; for (const folder of workspace.workspaceFolders) { - const allWorkspaceLanguages = this.languageResolver.allEnabledLanguages( + const allWorkspaceLanguages = this.languageResolver.getSupportedLanguages( folder.uri.fsPath ); allWorkspaceLanguages.forEach((lang) => { @@ -133,61 +133,73 @@ export default class PrettierEditService implements Disposable { } } + let allExtensions: string[]; + if (workspace.workspaceFolders === undefined) { + allExtensions = this.languageResolver.getSupportedFileExtensions(); + } else { + allExtensions = []; + for (const folder of workspace.workspaceFolders) { + const allWorkspaceLanguages = this.languageResolver.getSupportedFileExtensions( + folder.uri.fsPath + ); + allWorkspaceLanguages.forEach((lang) => { + if (!allExtensions.includes(lang)) { + allExtensions.push(lang); + } + }); + } + } + this.loggingService.logInfo( - "Enabling prettier for languages", - allLanguages.sort() + `Enabling prettier for languages: ${allLanguages.sort().join(", ")}` ); - const allRangeLanguages = this.languageResolver.rangeSupportedLanguages(); this.loggingService.logInfo( - "Enabling prettier for range supported languages", - allRangeLanguages.sort() + `Enabling prettier for file extensions: ${allExtensions + .sort() + .join(", ")}` ); - const specialLanguageSelector: DocumentFilter[] = [ - // This selector is for settings.json files - { - language: "jsonc", - scheme: "vscode-userdata", - }, - ]; + if (documentSelectors && documentSelectors.length > 0) { + this.loggingService.logInfo( + `Enabling prettier for user defined selectors: ${documentSelectors + .sort() + .join(", ")}` + ); + } + const allRangeLanguages = this.languageResolver.getRangeSupportedLanguages(); + this.loggingService.logInfo( + `Enabling prettier for range supported languages: ${allRangeLanguages + .sort() + .join(", ")}` + ); + + // Language selector for file extensions + const extensionLanguageSelector: DocumentFilter = { + pattern: `**/*.{${allExtensions.map((e) => e.substring(1)).join(",")}}`, + }; + + const customLanguageSelectors: DocumentFilter[] = []; + documentSelectors.forEach((pattern) => { + customLanguageSelectors.push({ + pattern, + }); + }); + + // Language selectors for language IDs const globalLanguageSelector: DocumentFilter[] = allLanguages .filter((l) => !disableLanguages.includes(l)) .map((l) => ({ language: l })); const globalRangeLanguageSelector: DocumentFilter[] = allRangeLanguages .filter((l) => !disableLanguages.includes(l)) .map((l) => ({ language: l })); - if (workspace.workspaceFolders === undefined) { - // no workspace opened - return { - languageSelector: globalLanguageSelector.concat( - specialLanguageSelector - ), - rangeLanguageSelector: globalRangeLanguageSelector, - }; - } - // at least 1 workspace - const untitledLanguageSelector: DocumentFilter[] = globalLanguageSelector.map( - (l) => ({ language: l.language, scheme: "untitled" }) - ); - const untitledRangeLanguageSelector: DocumentFilter[] = globalRangeLanguageSelector.map( - (l) => ({ language: l.language, scheme: "untitled" }) - ); - const fileLanguageSelector: DocumentFilter[] = globalLanguageSelector.map( - (l) => ({ language: l.language, scheme: "file" }) - ); - const fileRangeLanguageSelector: DocumentFilter[] = globalRangeLanguageSelector.map( - (l) => ({ language: l.language, scheme: "file" }) - ); return { - languageSelector: untitledLanguageSelector - .concat(fileLanguageSelector) - .concat(specialLanguageSelector), - rangeLanguageSelector: untitledRangeLanguageSelector.concat( - fileRangeLanguageSelector - ), + languageSelector: globalLanguageSelector + .concat(customLanguageSelectors) + .concat(extensionLanguageSelector), + rangeLanguageSelector: globalRangeLanguageSelector, }; }; @@ -278,16 +290,20 @@ export default class PrettierEditService implements Disposable { let parser: prettier.BuiltInParserName | string | undefined; if (fileInfo && fileInfo.inferredParser) { parser = fileInfo.inferredParser; - } else { + } else if (languageId !== "plaintext") { + // Don't attempt VS Code language for plaintext because we never have + // a formatter for plaintext and most likely the reason for this is + // somebody has registered a custom file extension without properly + // configuring the parser in their prettier config. this.loggingService.logWarning( - "Parser not inferred, using VS Code language." + `Parser not inferred, trying VS Code language.` ); parser = this.languageResolver.getParserFromLanguageId(uri, languageId); } if (!parser) { this.loggingService.logError( - `Failed to resolve a parser, skipping file.` + `Failed to resolve a parser, skipping file. If you registered a custom file extension, be sure to configure the parser.` ); this.statusBarService.updateStatusBar(FormattingResult.Error); return; diff --git a/src/StatusBarService.ts b/src/StatusBarService.ts index 7103c25a2..20ce37003 100644 --- a/src/StatusBarService.ts +++ b/src/StatusBarService.ts @@ -70,7 +70,7 @@ export class StatusBarService { ? undefined : editor.document.fileName; const score = languages.match( - this.languageResolver.allEnabledLanguages(filePath), + this.languageResolver.getSupportedLanguages(filePath), editor.document ); const disabledLanguages: PrettierVSCodeConfig["disableLanguages"] = getConfig( diff --git a/src/test/suite/config.test.ts b/src/test/suite/config.test.ts index e3b303f41..618715ccc 100644 --- a/src/test/suite/config.test.ts +++ b/src/test/suite/config.test.ts @@ -42,4 +42,9 @@ suite("Test configurations", function () { /* cspell: disable-next-line */ testConfig("vscodeconfig/test.js", "vscodeconfig/test.result.js") ); + test( + "it formats custom file extension ", + /* cspell: disable-next-line */ + testConfig("customextension/test.abc", "customextension/test.result.abc") + ); }); diff --git a/src/types.d.ts b/src/types.d.ts index 09042f5df..70bcab90b 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -46,6 +46,10 @@ interface IExtensionConfig { * If true, this extension will process files in node_modules */ withNodeModules: boolean; + /** + * Additional file patterns to register for formatting + */ + documentSelectors: string[]; } /** * Configuration for prettier-vscode diff --git a/test-fixtures/config/customextension/.prettierrc b/test-fixtures/config/customextension/.prettierrc new file mode 100644 index 000000000..698344572 --- /dev/null +++ b/test-fixtures/config/customextension/.prettierrc @@ -0,0 +1,10 @@ +{ + "overrides": [ + { + "files": "*.abc", + "options": { + "parser": "babel" + } + } + ] +} \ No newline at end of file diff --git a/test-fixtures/config/customextension/test.abc b/test-fixtures/config/customextension/test.abc new file mode 100644 index 000000000..1c4aba461 --- /dev/null +++ b/test-fixtures/config/customextension/test.abc @@ -0,0 +1,7 @@ +function foo() { + const foo = ["aaaaaaaaa", "bbbbbb", + "c","a", "b", + "c","a", "b", + "c","a", "b", + "c"] +} \ No newline at end of file diff --git a/test-fixtures/config/customextension/test.result.abc b/test-fixtures/config/customextension/test.result.abc new file mode 100644 index 000000000..591fbaec1 --- /dev/null +++ b/test-fixtures/config/customextension/test.result.abc @@ -0,0 +1,16 @@ +function foo() { + const foo = [ + "aaaaaaaaa", + "bbbbbb", + "c", + "a", + "b", + "c", + "a", + "b", + "c", + "a", + "b", + "c", + ]; +} diff --git a/test-fixtures/test.code-workspace b/test-fixtures/test.code-workspace index 34271b745..40b922831 100644 --- a/test-fixtures/test.code-workspace +++ b/test-fixtures/test.code-workspace @@ -88,6 +88,9 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "prettier.trailingComma": "all", - "prettier.prettierPath": "" + "prettier.prettierPath": "", + "prettier.documentSelectors": [ + "**/*.abc" + ], } }