generated from fuzdev/fuz_template
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgrammar_bash.ts
More file actions
175 lines (157 loc) · 4.81 KB
/
grammar_bash.ts
File metadata and controls
175 lines (157 loc) · 4.81 KB
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import type {AddSyntaxGrammar, SyntaxGrammarRaw} from './syntax_styler.js';
/**
* Bash/shell grammar for syntax highlighting.
*
* Standalone grammar (no base extension). Covers core bash syntax:
* comments, strings, variables, functions, keywords, builtins,
* operators, and redirections.
*
* Based on Prism (https://github.com/PrismJS/prism)
* by Lea Verou (https://lea.verou.me/)
*
* MIT license
*
* @see LICENSE
*/
export const add_grammar_bash: AddSyntaxGrammar = (syntax_styler) => {
// Shared inside grammar for command substitution — `rest` wired after construction
const command_sub_inside: SyntaxGrammarRaw = {
punctuation: /^\$\(|\)$/,
};
// Reusable balanced-paren pattern for $(...) — handles 2 levels of inner () nesting,
// which supports up to 3 levels of $() command substitution (4+ is vanishingly rare)
const command_sub_pattern = /\$\((?:[^()]*|\((?:[^()]*|\([^()]*\))*\))*\)/;
const grammar_bash = {
// Shebang at file start — matched before general comments
shebang: {
pattern: /^#!.*/,
alias: 'comment',
},
// Line comments — require whitespace or start-of-string before #
comment: {
pattern: /(^|\s)#.*/,
lookbehind: true,
greedy: true,
},
// Here-documents — must precede string to avoid delimiter consumption
heredoc: [
// Quoted delimiter (<<'DELIM' or <<"DELIM") — no expansion
{
pattern: /(^|[^<])<<-?\s*(?:['"])(\w+)(?:['"])[\t ]*\n[\s\S]*?\n[\t ]*\2(?=\s*$)/m,
lookbehind: true,
greedy: true,
alias: 'string',
inside: {
// No `m` flag — `^` matches start-of-string (opening) and `$` matches
// end-of-string (closing), so single-word content lines can't false-positive
heredoc_delimiter: [
{
pattern: /^<<-?\s*(?:['"])\w+(?:['"])/,
alias: 'punctuation',
},
{
pattern: /\w+$/,
alias: 'punctuation',
},
],
} as SyntaxGrammarRaw,
},
// Unquoted delimiter (<<DELIM) — with variable/command expansion
{
pattern: /(^|[^<])<<-?\s*(\w+)[\t ]*\n[\s\S]*?\n[\t ]*\2(?=\s*$)/m,
lookbehind: true,
greedy: true,
alias: 'string',
inside: {
heredoc_delimiter: [
{
pattern: /^<<-?\s*\w+/,
alias: 'punctuation',
},
{
pattern: /\w+$/,
alias: 'punctuation',
},
],
command_substitution: {
pattern: command_sub_pattern,
greedy: true,
inside: command_sub_inside,
},
variable: /\$\{[^}]+\}|\$(?:\w+|[!@#$*?\-0-9])/,
} as SyntaxGrammarRaw,
},
],
// Strings — three variants
string: [
// Double-quoted: supports escape sequences, variable interpolation, command substitution
{
pattern:
/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\((?:[^()]*|\((?:[^()]*|\([^()]*\))*\))*\)|\$(?!\()|[^"\\$])*"/,
lookbehind: true,
greedy: true,
inside: {
command_substitution: {
pattern: command_sub_pattern,
greedy: true,
inside: command_sub_inside,
},
variable: /\$\{[^}]+\}|\$(?:\w+|[!@#$*?\-0-9])/,
} as SyntaxGrammarRaw,
},
// Single-quoted: completely literal
{
pattern: /(^|[^\\](?:\\\\)*)'[^']*'/,
lookbehind: true,
greedy: true,
},
// ANSI-C quoting: $'...' with C-style escapes
{
pattern: /\$'(?:[^'\\]|\\[\s\S])*'/,
greedy: true,
},
],
// Command substitution $(...) — before variable since both start with $
command_substitution: {
pattern: command_sub_pattern,
greedy: true,
inside: command_sub_inside,
},
// Variables and parameter expansion
variable: /\$\{[^}]+\}|\$(?:\w+|[!@#$*?\-0-9])/,
// Function definitions — both styles
function: [
// function fname style
{
pattern: /(\bfunction\s+)\w+/,
lookbehind: true,
},
// fname() style
{
pattern: /\b\w+(?=\s*\(\s*\))/,
},
],
// Shell keywords
keyword:
/\b(?:if|then|else|elif|fi|for|while|until|do|done|case|esac|in|select|function|return|local|export|declare|typeset|readonly|unset|set|shift|trap|break|continue|coproc|time)\b/,
// Builtin commands
builtin:
/\b(?:echo|printf|cd|pwd|read|test|source|eval|exec|exit|getopts|hash|type|ulimit|umask|wait|kill|jobs|bg|fg|disown|alias|unalias|command|shopt)\b/,
// Boolean commands
boolean: /\b(?:true|false)\b/,
// File descriptors before redirections — must precede number
file_descriptor: {
pattern: /\B&\d\b|\b\d(?=>>?|<)/,
alias: 'important',
},
// Numbers: hex, octal, base-N, decimal
number: /\b(?:0x[\da-fA-F]+|0[0-7]+|\d+#[\da-zA-Z]+|\d+)\b/,
// Operators — longest first
operator: /\|\||&&|;;|&>>?|<<<?|>>?|=~|[!=]=|[<>]|[|&!]/,
// Punctuation
punctuation: /[{}[\]();,]/,
} satisfies SyntaxGrammarRaw;
// Wire circular reference so command substitutions get full bash highlighting
command_sub_inside.rest = grammar_bash;
syntax_styler.add_lang('bash', grammar_bash, ['sh', 'shell']);
};