diff --git a/README.md b/README.md index 6af1d277..1226e85d 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,15 @@ there is no `shfmt`-specific configuration variable for this. If your editor is two-space indents then that's what it will use. If you're using tabs for indentation then `shfmt` will use that. +The `shfmt` integration also supports configuration via `.editorconfig`. If any `shfmt`-specific +configuration properties are found in `.editorconfig` then the config in `.editorconfig` will be +used and the language server config will be ignored. This follows `shfmt`'s approach of using either +`.editorconfig` or command line flags, but not both. Note that only `shfmt`-specific configuration +properties are read from `.editorconfig` - indentation preferences are still provided by the editor, +so to format using the indentation specified in `.editorconfig` make sure your editor is also +configured to read `.editorconfig`. It is possible to disable `.editorconfig` support and always use +the language server config by setting the "Ignore Editorconfig" configuration variable. + ## Logging The minimum logging level for the server can be adjusted using the `BASH_IDE_LOG_LEVEL` environment variable diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d832c80..e0906b6f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,6 +62,9 @@ importers: server: dependencies: + editorconfig: + specifier: 2.0.0 + version: 2.0.0 fast-glob: specifier: 3.3.2 version: 3.3.2 @@ -855,6 +858,10 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 + /@one-ini/wasm@0.1.1: + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + dev: false + /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true @@ -1335,7 +1342,6 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1348,7 +1354,6 @@ packages: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 - dev: true /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} @@ -1480,6 +1485,11 @@ packages: delayed-stream: 1.0.0 dev: true + /commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true @@ -1583,6 +1593,17 @@ packages: resolution: {integrity: sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==} dev: false + /editorconfig@2.0.0: + resolution: {integrity: sha512-s1NQ63WQ7RNXH6Efb2cwuyRlfpbtdZubvfNe4vCuoyGPewNPY7vah8JUSOFBiJ+jr99Qh8t0xKv0oITc1dclgw==} + engines: {node: '>=16'} + hasBin: true + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 11.1.0 + minimatch: 9.0.2 + semver: 7.5.4 + dev: false + /electron-to-chromium@1.4.449: resolution: {integrity: sha512-TxLRpRUj/107ATefeP8VIUWNOv90xJxZZbCW/eIbSZQiuiFANCx2b7u+GbVc9X4gU+xnbvypNMYVM/WArE1DNQ==} dev: true @@ -2692,7 +2713,6 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 - dev: true /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} @@ -2749,6 +2769,13 @@ packages: brace-expansion: 1.1.11 dev: true + /minimatch@9.0.2: + resolution: {integrity: sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: false + /minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -3029,7 +3056,6 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 - dev: true /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -3380,7 +3406,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} diff --git a/server/CHANGELOG.md b/server/CHANGELOG.md index 762c3434..d9a7414b 100644 --- a/server/CHANGELOG.md +++ b/server/CHANGELOG.md @@ -1,5 +1,9 @@ # Bash Language Server +## 5.4.0 + +- Add .editorconfig support for shfmt https://github.com/bash-lsp/bash-language-server/pull/1171 + ## 5.3.4 - Add additonal shfmt formatting config options https://github.com/bash-lsp/bash-language-server/pull/1168 diff --git a/server/package.json b/server/package.json index f7f6541b..60c69a26 100644 --- a/server/package.json +++ b/server/package.json @@ -3,7 +3,7 @@ "description": "A language server for Bash", "author": "Mads Hartmann", "license": "MIT", - "version": "5.3.4", + "version": "5.4.0", "main": "./out/server.js", "typings": "./out/server.d.ts", "bin": { @@ -17,6 +17,7 @@ "node": ">=16" }, "dependencies": { + "editorconfig": "2.0.0", "fast-glob": "3.3.2", "fuzzy-search": "3.2.1", "node-fetch": "2.7.0", diff --git a/server/src/__tests__/config.test.ts b/server/src/__tests__/config.test.ts index ba927820..edfeab43 100644 --- a/server/src/__tests__/config.test.ts +++ b/server/src/__tests__/config.test.ts @@ -17,7 +17,9 @@ describe('ConfigSchema', () => { "binaryNextLine": false, "caseIndent": false, "funcNextLine": false, + "ignoreEditorconfig": false, "keepPadding": false, + "languageDialect": "auto", "path": "shfmt", "simplifyCode": false, "spaceRedirects": false, @@ -38,7 +40,9 @@ describe('ConfigSchema', () => { binaryNextLine: true, caseIndent: true, funcNextLine: true, + ignoreEditorconfig: true, keepPadding: true, + languageDialect: 'posix', path: 'myshfmt', simplifyCode: true, spaceRedirects: true, @@ -63,7 +67,9 @@ describe('ConfigSchema', () => { "binaryNextLine": true, "caseIndent": true, "funcNextLine": true, + "ignoreEditorconfig": true, "keepPadding": true, + "languageDialect": "posix", "path": "myshfmt", "simplifyCode": true, "spaceRedirects": true, @@ -98,7 +104,9 @@ describe('getConfigFromEnvironmentVariables', () => { "binaryNextLine": false, "caseIndent": false, "funcNextLine": false, + "ignoreEditorconfig": false, "keepPadding": false, + "languageDialect": "auto", "path": "shfmt", "simplifyCode": false, "spaceRedirects": false, @@ -127,7 +135,9 @@ describe('getConfigFromEnvironmentVariables', () => { "binaryNextLine": false, "caseIndent": false, "funcNextLine": false, + "ignoreEditorconfig": false, "keepPadding": false, + "languageDialect": "auto", "path": "", "simplifyCode": false, "spaceRedirects": false, @@ -165,7 +175,9 @@ describe('getConfigFromEnvironmentVariables', () => { "binaryNextLine": false, "caseIndent": true, "funcNextLine": false, + "ignoreEditorconfig": false, "keepPadding": false, + "languageDialect": "auto", "path": "/path/to/shfmt", "simplifyCode": false, "spaceRedirects": false, diff --git a/server/src/config.ts b/server/src/config.ts index 26584cfa..b37eee15 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -46,6 +46,12 @@ export const ConfigSchema = z.object({ // Controls the executable used for Shfmt formatting. An empty string will disable formatting path: z.string().trim().default('shfmt'), + // Ignore shfmt config options in .editorconfig (always use language server config) + ignoreEditorconfig: z.boolean().default(false), + + // Language dialect to use when parsing (bash/posix/mksh/bats). + languageDialect: z.enum(['auto', 'bash', 'posix', 'mksh', 'bats']).default('auto'), + // Allow boolean operators (like && and ||) to start a line. binaryNextLine: z.boolean().default(false), @@ -84,6 +90,8 @@ export function getConfigFromEnvironmentVariables(): { shellcheckPath: process.env.SHELLCHECK_PATH, shfmt: { path: process.env.SHFMT_PATH, + ignoreEditorconfig: toBoolean(process.env.SHFMT_IGNORE_EDITORCONFIG), + languageDialect: process.env.SHFMT_LANGUAGE_DIALECT, binaryNextLine: toBoolean(process.env.SHFMT_BINARY_NEXT_LINE), caseIndent: toBoolean(process.env.SHFMT_CASE_INDENT), funcNextLine: toBoolean(process.env.SHFMT_FUNC_NEXT_LINE), diff --git a/server/src/shfmt/__tests__/index.test.ts b/server/src/shfmt/__tests__/index.test.ts index 481120a8..46379cd3 100644 --- a/server/src/shfmt/__tests__/index.test.ts +++ b/server/src/shfmt/__tests__/index.test.ts @@ -62,6 +62,17 @@ describe('formatter', () => { ) }) + it('should throw when parsing using the wrong language dialect', async () => { + expect(async () => { + await getFormattingResult({ + document: FIXTURE_DOCUMENT.SHFMT, + shfmtConfig: { languageDialect: 'posix' }, + }) + }).rejects.toThrow( + /Shfmt: exited with status 1: .*\/testing\/fixtures\/shfmt\.sh:25:14: (the "function" builtin exists in bash; tried parsing as posix|a command can only contain words and redirects; encountered \()/, + ) + }) + it('should format when shfmt is present', async () => { const [result] = await getFormattingResult({ document: FIXTURE_DOCUMENT.SHFMT }) expect(result).toMatchInlineSnapshot(` @@ -585,7 +596,7 @@ describe('formatter', () => { `) }) - it('should omit filename from the shfmt comment when it cannot be determined', async () => { + it('should omit filename from the shfmt command when it cannot be determined', async () => { // There's no easy way to see what filename has been passed to shfmt without inspecting the // contents of the logs. As a workaround, we set a non-file:// URI on a dodgy document to // trigger an exception and inspect the error message. @@ -602,4 +613,179 @@ describe('formatter', () => { /Shfmt: exited with status 1: :10:1: > must be followed by a word/, ) }) + + describe('getShfmtArguments()', () => { + const lspShfmtConfig = { + binaryNextLine: true, + funcNextLine: true, + simplifyCode: true, + } + const lspShfmtArgs = ['-bn', '-fn', '-s'] + const formatOptions = { tabSize: 2, insertSpaces: true } + + const formatter = new Formatter({ + executablePath: 'shfmt', + }) + + describe('when the document URI is not a filepath', () => { + let shfmtArgs: string[] + const filepath = `${FIXTURE_FOLDER}/shfmt.sh` + + beforeAll(async () => { + // @ts-expect-error Testing a private method + shfmtArgs = await formatter.getShfmtArguments( + `test://${filepath}`, + formatOptions, + lspShfmtConfig, + ) + }) + + it('should use language server config', async () => { + expect(shfmtArgs).toEqual(expect.arrayContaining(lspShfmtArgs)) + expect(shfmtArgs.length).toEqual(4) // indentation + }) + + it('should use indentation config from the editor', () => { + expect(shfmtArgs).toContain('-i=2') + }) + + it('should not include the filename argument', async () => { + expect(shfmtArgs).not.toContain(`--filename=${filepath}`) + }) + }) + + describe('when no .editorconfig exists', () => { + let shfmtArgs: string[] + const filepath = `${FIXTURE_FOLDER}/shfmt.sh` + + beforeAll(async () => { + // @ts-expect-error Testing a private method + shfmtArgs = await formatter.getShfmtArguments( + `file://${filepath}`, + formatOptions, + lspShfmtConfig, + ) + }) + + it('should use language server config', () => { + expect(shfmtArgs).toEqual(expect.arrayContaining(lspShfmtArgs)) + expect(shfmtArgs.length).toEqual(5) // indentation + filename + }) + + it('should use indentation config from the editor', () => { + expect(shfmtArgs).toContain('-i=2') + }) + + it('should include the filename argument', () => { + expect(shfmtArgs).toContain(`--filename=${filepath}`) + }) + }) + + describe('when an .editorconfig exists without shfmt options', () => { + let shfmtArgs: string[] + const filepath = `${FIXTURE_FOLDER}/shfmt-editorconfig/no-shfmt-properties/foo.sh` + + beforeAll(async () => { + // @ts-expect-error Testing a private method + shfmtArgs = await formatter.getShfmtArguments( + `file://${filepath}`, + formatOptions, + lspShfmtConfig, + ) + }) + + it('should use language server config', () => { + expect(shfmtArgs).toEqual(expect.arrayContaining(lspShfmtArgs)) + expect(shfmtArgs.length).toEqual(5) // indentation + filename + }) + + it('should use indentation config from the editor', () => { + expect(shfmtArgs).toContain('-i=2') + }) + + it('should include the filename argument', () => { + expect(shfmtArgs).toContain(`--filename=${filepath}`) + }) + }) + + describe('when an .editorconfig exists and contains only false shfmt options', () => { + let shfmtArgs: string[] + const filepath = `${FIXTURE_FOLDER}/shfmt-editorconfig/shfmt-properties-false/foo.sh` + + beforeAll(async () => { + // @ts-expect-error Testing a private method + shfmtArgs = await formatter.getShfmtArguments( + `file://${filepath}`, + formatOptions, + lspShfmtConfig, + ) + }) + + it('should use .editorconfig config (even though no options are enabled)', () => { + expect(shfmtArgs.length).toEqual(2) // indentation + filename + }) + + it('should use indentation config from the editor', () => { + expect(shfmtArgs).toContain('-i=2') + }) + + it('should include the filename argument', () => { + expect(shfmtArgs).toContain(`--filename=${filepath}`) + }) + }) + + describe('when an .editorconfig exists and contains one or more shfmt options', () => { + let shfmtArgs: string[] + const filepath = `${FIXTURE_FOLDER}/shfmt-editorconfig/shfmt-properties/foo.sh` + + beforeAll(async () => { + // @ts-expect-error Testing a private method + shfmtArgs = await formatter.getShfmtArguments( + `file://${filepath}`, + formatOptions, + lspShfmtConfig, + ) + }) + + it('should use .editorconfig config', () => { + expect(shfmtArgs).toEqual(expect.arrayContaining(['-ci', '-sr', "-ln='mksh'"])) + expect(shfmtArgs.length).toEqual(5) // indentation + filename + }) + + it('should use indentation config from the editor', () => { + expect(shfmtArgs).toContain('-i=2') + }) + + it('should include the filename argument', () => { + expect(shfmtArgs).toContain(`--filename=${filepath}`) + }) + }) + + describe('when an .editorconfig exists but ignoreEditorconfig is set', () => { + let shfmtArgs: string[] + const filepath = `${FIXTURE_FOLDER}/shfmt-editorconfig/shfmt-properties/foo.sh` + + beforeAll(async () => { + // @ts-expect-error Testing a private method + shfmtArgs = await formatter.getShfmtArguments( + `file://${filepath}`, + formatOptions, + { ...lspShfmtConfig, ignoreEditorconfig: true }, + ) + }) + + it('should use language server config', () => { + expect(shfmtArgs).toEqual(expect.arrayContaining(lspShfmtArgs)) + expect(shfmtArgs.length).toEqual(5) // indentation + filename + }) + + it('should use indentation config from the editor', () => { + expect(shfmtArgs).toContain('-i=2') + }) + + it('should include the filename argument', () => { + expect(shfmtArgs).toContain(`--filename=${filepath}`) + }) + }) + }) }) diff --git a/server/src/shfmt/index.ts b/server/src/shfmt/index.ts index da3ea0a3..a230d03c 100644 --- a/server/src/shfmt/index.ts +++ b/server/src/shfmt/index.ts @@ -1,6 +1,7 @@ import { spawn } from 'child_process' +import * as editorconfig from 'editorconfig' import * as LSP from 'vscode-languageserver/node' -import { TextDocument, TextEdit } from 'vscode-languageserver-textdocument' +import { DocumentUri, TextDocument, TextEdit } from 'vscode-languageserver-textdocument' import { logger } from '../util/logger' @@ -58,25 +59,89 @@ export class Formatter { ] } + private async getShfmtArguments( + documentUri: DocumentUri, + formatOptions?: LSP.FormattingOptions | null, + lspShfmtConfig?: Record | null, + ): Promise { + const args: string[] = [] + + // this is the config that we'll use to build args - default to language server config + let activeShfmtConfig = { ...lspShfmtConfig } + + // do we have a document stored on the local filesystem? + const filepathMatch = documentUri.match(/^file:\/\/(.*)$/) + if (filepathMatch) { + const filepath = filepathMatch[1] + args.push(`--filename=${filepathMatch[1]}`) + + if (!lspShfmtConfig?.ignoreEditorconfig) { + const editorconfigProperties = await editorconfig.parse(filepath) + logger.debug( + `Shfmt: found .editorconfig properties: ${JSON.stringify( + editorconfigProperties, + )}`, + ) + + const editorconfigShfmtConfig: Record = {} + editorconfigShfmtConfig.binaryNextLine = editorconfigProperties.binary_next_line + editorconfigShfmtConfig.caseIndent = editorconfigProperties.switch_case_indent + editorconfigShfmtConfig.funcNextLine = editorconfigProperties.function_next_line + editorconfigShfmtConfig.keepPadding = editorconfigProperties.keep_padding + // --simplify is not supported via .editorconfig + editorconfigShfmtConfig.spaceRedirects = editorconfigProperties.space_redirects + editorconfigShfmtConfig.languageDialect = editorconfigProperties.shell_variant + + // if we have any shfmt-specific options in .editorconfig, use the config in .editorconfig and + // ignore the language server config (this is similar to shfmt's approach of using either + // .editorconfig or command line flags, but not both) + if ( + editorconfigShfmtConfig.binaryNextLine !== undefined || + editorconfigShfmtConfig.caseIndent !== undefined || + editorconfigShfmtConfig.funcNextLine !== undefined || + editorconfigShfmtConfig.keepPadding !== undefined || + editorconfigShfmtConfig.spaceRedirects !== undefined || + editorconfigShfmtConfig.languageDialect !== undefined + ) { + logger.debug( + 'Shfmt: detected shfmt properties in .editorconfig - ignoring language server shfmt config', + ) + activeShfmtConfig = { ...editorconfigShfmtConfig } + } else { + logger.debug( + 'Shfmt: no shfmt properties found in .editorconfig - using language server shfmt config', + ) + } + } else { + logger.debug( + 'Shfmt: configured to ignore .editorconfig - using language server shfmt config', + ) + } + } + + // indentation always comes via the editor - if someone is using .editorconfig then the + // expectation is that they will have configured their editor's indentation in this way too + const indentation: number = formatOptions?.insertSpaces ? formatOptions.tabSize : 0 + args.push(`-i=${indentation}`) // --indent + + if (activeShfmtConfig?.binaryNextLine) args.push('-bn') // --binary-next-line + if (activeShfmtConfig?.caseIndent) args.push('-ci') // --case-indent + if (activeShfmtConfig?.funcNextLine) args.push('-fn') // --func-next-line + if (activeShfmtConfig?.keepPadding) args.push('-kp') // --keep-padding + if (activeShfmtConfig?.simplifyCode) args.push('-s') // --simplify + if (activeShfmtConfig?.spaceRedirects) args.push('-sr') // --space-redirects + if (activeShfmtConfig?.languageDialect) + args.push(`-ln=${activeShfmtConfig.languageDialect}`) // --language-dialect + + return args + } + private async runShfmt( document: TextDocument, formatOptions?: LSP.FormattingOptions | null, shfmtConfig?: Record | null, ): Promise { - const indentation: number = formatOptions?.insertSpaces ? formatOptions.tabSize : 0 - const args: string[] = [`-i=${indentation}`] // --indent - if (shfmtConfig?.binaryNextLine) args.push('-bn') // --binary-next-line - if (shfmtConfig?.caseIndent) args.push('-ci') // --case-indent - if (shfmtConfig?.funcNextLine) args.push('-fn') // --func-next-line - if (shfmtConfig?.keepPadding) args.push('-kp') // --keep-padding - if (shfmtConfig?.simplifyCode) args.push('-s') // --simplify - if (shfmtConfig?.spaceRedirects) args.push('-sr') // --space-redirects - - // If we can determine a local filename, pass that to shfmt to aid language dialect detection - const filePathMatch = document.uri.match(/^file:\/\/(.*)$/) - if (filePathMatch) { - args.push(`--filename=${filePathMatch[1]}`) - } + const args = await this.getShfmtArguments(document.uri, formatOptions, shfmtConfig) logger.debug(`Shfmt: running "${this.executablePath} ${args.join(' ')}"`) diff --git a/testing/fixtures/shfmt-editorconfig/no-shfmt-properties/.editorconfig b/testing/fixtures/shfmt-editorconfig/no-shfmt-properties/.editorconfig new file mode 100644 index 00000000..fbe3aeca --- /dev/null +++ b/testing/fixtures/shfmt-editorconfig/no-shfmt-properties/.editorconfig @@ -0,0 +1,3 @@ +[*] +indent_style = space +indent_size = 3 diff --git a/testing/fixtures/shfmt-editorconfig/shfmt-properties-false/.editorconfig b/testing/fixtures/shfmt-editorconfig/shfmt-properties-false/.editorconfig new file mode 100644 index 00000000..33a35262 --- /dev/null +++ b/testing/fixtures/shfmt-editorconfig/shfmt-properties-false/.editorconfig @@ -0,0 +1,6 @@ +[*] +indent_style = space +indent_size = 3 + +switch_case_indent = false +space_redirects = false diff --git a/testing/fixtures/shfmt-editorconfig/shfmt-properties/.editorconfig b/testing/fixtures/shfmt-editorconfig/shfmt-properties/.editorconfig new file mode 100644 index 00000000..0495f4fd --- /dev/null +++ b/testing/fixtures/shfmt-editorconfig/shfmt-properties/.editorconfig @@ -0,0 +1,7 @@ +[*] +indent_style = space +indent_size = 3 + +switch_case_indent = true +space_redirects = true +shell_variant = 'mksh' diff --git a/vscode-client/package.json b/vscode-client/package.json index 457653cc..7fe6dee4 100644 --- a/vscode-client/package.json +++ b/vscode-client/package.json @@ -83,6 +83,23 @@ "default": "shfmt", "description": "Controls the executable used for Shfmt formatting. An empty string will disable formatting." }, + "bashIde.shfmt.ignoreEditorconfig": { + "type": "boolean", + "default": false, + "description": "Ignore shfmt config options in .editorconfig (always use language server config)" + }, + "bashIde.shfmt.languageDialect": { + "type": "string", + "default": "auto", + "enum": [ + "auto", + "bash", + "posix", + "mksh", + "bats" + ], + "description": "Language dialect to use when parsing (bash/posix/mksh/bats)." + }, "bashIde.shfmt.binaryNextLine": { "type": "boolean", "default": false,