-
Notifications
You must be signed in to change notification settings - Fork 132
/
Copy pathconfig.ts
132 lines (104 loc) · 5.58 KB
/
config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { z } from 'zod'
import { DEFAULT_LOG_LEVEL, LOG_LEVEL_ENV_VAR, LOG_LEVELS } from './util/logger'
export const ConfigSchema = z.object({
// Maximum number of files to analyze in the background. Set to 0 to disable background analysis.
backgroundAnalysisMaxFiles: z.number().int().min(0).default(500),
// Enable diagnostics for source errors. Ignored if includeAllWorkspaceSymbols is true.
enableSourceErrorDiagnostics: z.boolean().default(false),
// Glob pattern for finding and parsing shell script files in the workspace. Used by the background analysis features across files.
globPattern: z.string().trim().default('**/*@(.sh|.inc|.bash|.command)'),
// Configure explainshell server endpoint in order to get hover documentation on flags and options.
// And empty string will disable the feature.
explainshellEndpoint: z.string().trim().default(''),
// Log level for the server. To set the right log level from the start please also use the environment variable 'BASH_IDE_LOG_LEVEL'.
logLevel: z.enum(LOG_LEVELS).default(DEFAULT_LOG_LEVEL),
// Controls how symbols (e.g. variables and functions) are included and used for completion, documentation, and renaming.
// If false, then we only include symbols from sourced files (i.e. using non dynamic statements like 'source file.sh' or '. file.sh' or following ShellCheck directives).
// If true, then all symbols from the workspace are included.
includeAllWorkspaceSymbols: z.boolean().default(false),
// Additional ShellCheck arguments. Note that we already add the following arguments: --shell, --format, --external-sources."
shellcheckArguments: z
.preprocess((arg) => {
let argsList: string[] = []
if (typeof arg === 'string') {
argsList = arg.split(' ')
} else if (Array.isArray(arg)) {
argsList = arg as string[]
}
return argsList.map((s) => s.trim()).filter((s) => s.length > 0)
}, z.array(z.string()))
.default([]),
// Controls the executable used for ShellCheck linting information. An empty string will disable linting.
shellcheckPath: z.string().trim().default('shellcheck'),
shfmt: 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),
// Indent patterns in case statements.
caseIndent: z.boolean().default(false),
// Place function opening braces on a separate line.
funcNextLine: z.boolean().default(false),
// (Deprecated) Keep column alignment padding.
keepPadding: z.boolean().default(false),
// Simplify code before formatting.
simplifyCode: z.boolean().default(false),
// Follow redirection operators with a space.
spaceRedirects: z.boolean().default(false),
})
.default({}),
})
export type Config = z.infer<typeof ConfigSchema>
export function getConfigFromEnvironmentVariables(): {
config: Config
environmentVariablesUsed: string[]
} {
const rawConfig = {
backgroundAnalysisMaxFiles: toNumber(process.env.BACKGROUND_ANALYSIS_MAX_FILES),
enableSourceErrorDiagnostics: toBoolean(process.env.ENABLE_SOURCE_ERROR_DIAGNOSTICS),
explainshellEndpoint: process.env.EXPLAINSHELL_ENDPOINT,
globPattern: process.env.GLOB_PATTERN,
includeAllWorkspaceSymbols: toBoolean(process.env.INCLUDE_ALL_WORKSPACE_SYMBOLS),
logLevel: process.env[LOG_LEVEL_ENV_VAR],
shellcheckArguments: process.env.SHELLCHECK_ARGUMENTS,
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),
keepPadding: toBoolean(process.env.SHFMT_KEEP_PADDING),
simplifyCode: toBoolean(process.env.SHFMT_SIMPLIFY_CODE),
spaceRedirects: toBoolean(process.env.SHFMT_SPACE_REDIRECTS),
},
}
const getUsedEnvvars = (rawConfigComponent: {[key: string]: any}): string[] => {
return Object.entries(rawConfigComponent)
.map(([key, value]) => (typeof value !== 'undefined' ? key : null))
.filter((key): key is string => key !== null)
.filter((key) => key !== 'logLevel') // logLevel is a special case that we ignore
};
// Since shfmt is structurally _always_ defined, we need to be careful to not
// incorrectly alert the user that they are doing something that needs to be
// changed.
//
// If other objects are added to rawConfig, please treat them similarly below.
const {shfmt, ... rest } = rawConfig;
const environmentVariablesUsed = getUsedEnvvars(rest).concat(getUsedEnvvars(shfmt));
const config = ConfigSchema.parse(rawConfig)
return { config, environmentVariablesUsed }
}
export function getDefaultConfiguration(): Config {
return ConfigSchema.parse({})
}
const toBoolean = (s?: string): boolean | undefined =>
typeof s !== 'undefined' ? s === 'true' || s === '1' : undefined
const toNumber = (s?: string): number | undefined =>
typeof s !== 'undefined' ? parseInt(s, 10) : undefined