Skip to content

Commit 251a322

Browse files
ficristomarijnh
authored andcommitted
[Dockerfile mode] Highlight strings and ports
- highlight strings - highlight ports as numbers in expose instruction - highlight comments as such only when they appear at the start of a line - consider also ` as a line continuation
1 parent 0704416 commit 251a322

File tree

2 files changed

+191
-42
lines changed

2 files changed

+191
-42
lines changed

mode/dockerfile/dockerfile.js

Lines changed: 121 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,64 @@
1111
})(function(CodeMirror) {
1212
"use strict";
1313

14+
var from = "from";
15+
var fromRegex = new RegExp("^(\\s*)\\b(" + from + ")\\b", "i");
16+
17+
var shells = ["run", "cmd", "entrypoint", "shell"];
18+
var shellsAsArrayRegex = new RegExp("^(\\s*)(" + shells.join('|') + ")(\\s+\\[)", "i");
19+
20+
var expose = "expose";
21+
var exposeRegex = new RegExp("^(\\s*)(" + expose + ")(\\s+)", "i");
22+
23+
var others = [
24+
"arg", "from", "maintainer", "label", "env",
25+
"add", "copy", "volume", "user",
26+
"workdir", "onbuild", "stopsignal", "healthcheck", "shell"
27+
];
28+
1429
// Collect all Dockerfile directives
15-
var instructions = ["arg", "from", "maintainer", "run", "cmd", "label", "expose", "env",
16-
"add", "copy", "entrypoint", "volume", "user",
17-
"workdir", "onbuild", "stopsignal", "healthcheck", "shell"],
30+
var instructions = [from, expose].concat(shells).concat(others),
1831
instructionRegex = "(" + instructions.join('|') + ")",
19-
instructionOnlyLine = new RegExp(instructionRegex + "\\s*$", "i"),
20-
instructionWithArguments = new RegExp(instructionRegex + "(\\s+)", "i");
32+
instructionOnlyLine = new RegExp("^(\\s*)" + instructionRegex + "(\\s*)(#.*)?$", "i"),
33+
instructionWithArguments = new RegExp("^(\\s*)" + instructionRegex + "(\\s+)", "i");
2134

2235
CodeMirror.defineSimpleMode("dockerfile", {
2336
start: [
2437
// Block comment: This is a line starting with a comment
2538
{
26-
regex: /#.*$/,
39+
regex: /^\s*#.*$/,
40+
sol: true,
2741
token: "comment"
2842
},
2943
{
30-
regex: /^(\s*)\b(from)\b/i,
44+
regex: fromRegex,
3145
token: [null, "keyword"],
46+
sol: true,
3247
next: "from"
3348
},
3449
// Highlight an instruction without any arguments (for convenience)
3550
{
3651
regex: instructionOnlyLine,
37-
token: "keyword"
52+
token: [null, "keyword", null, "error"],
53+
sol: true
54+
},
55+
{
56+
regex: shellsAsArrayRegex,
57+
token: [null, "keyword", null],
58+
sol: true,
59+
next: "array"
60+
},
61+
{
62+
regex: exposeRegex,
63+
token: [null, "keyword", null],
64+
sol: true,
65+
next: "expose"
3866
},
3967
// Highlight an instruction followed by arguments
4068
{
4169
regex: instructionWithArguments,
42-
token: ["keyword", null],
70+
token: [null, "keyword", null],
71+
sol: true,
4372
next: "arguments"
4473
},
4574
{
@@ -60,59 +89,122 @@
6089
next: "start"
6190
},
6291
{
63-
// ex: FROM golang:1.9.2-alpine3.6 AS build
64-
regex: /(\s*\S+\s+)(as)(\s+)\S+/i,
65-
token: [null, "keyword", null],
92+
regex: /(\s*\S+\s+)(as)/i,
93+
token: [null, "keyword"],
6694
next: "start"
6795
},
96+
// Fail safe return to start
6897
{
69-
// ex: FROM node:carbon
70-
regex: /\s*[^#]+$/,
7198
token: null,
7299
next: "start"
100+
}
101+
],
102+
single: [
103+
{
104+
regex: /(?:[^\\']|\\.)/,
105+
token: "string"
73106
},
74107
{
75-
// ex: FROM node:carbon # comment
76-
regex: /(\s*[^#]+)\s*(#.*)$/,
77-
token: [null, "comment"],
78-
next: "start"
108+
regex: /'/,
109+
token: "string",
110+
pop: true
111+
}
112+
],
113+
double: [
114+
{
115+
regex: /(?:[^\\"]|\\.)/,
116+
token: "string"
79117
},
80118
{
119+
regex: /"/,
120+
token: "string",
121+
pop: true
122+
}
123+
],
124+
array: [
125+
{
126+
regex: /\]/,
81127
token: null,
82128
next: "start"
129+
},
130+
{
131+
regex: /"(?:[^\\"]|\\.)*"?/,
132+
token: "string"
83133
}
84134
],
85-
arguments: [
135+
expose: [
86136
{
87-
// Line comment without instruction arguments is an error
88-
regex: /#.*$/,
89-
token: "error",
137+
regex: /\d+$/,
138+
token: "number",
90139
next: "start"
91140
},
92141
{
93-
regex: /[^#]+\\$/,
142+
regex: /[^\d]+$/,
143+
token: null,
144+
next: "start"
145+
},
146+
{
147+
regex: /\d+/,
148+
token: "number"
149+
},
150+
{
151+
regex: /[^\d]+/,
94152
token: null
95153
},
154+
// Fail safe return to start
96155
{
97-
// Match everything except for the inline comment
98-
regex: /[^#]+/,
99156
token: null,
100157
next: "start"
158+
}
159+
],
160+
arguments: [
161+
{
162+
regex: /^\s*#.*$/,
163+
sol: true,
164+
token: "comment"
165+
},
166+
{
167+
regex: /"(?:[^\\"]|\\.)*"?$/,
168+
token: "string",
169+
next: "start"
101170
},
102171
{
103-
regex: /$/,
172+
regex: /"/,
173+
token: "string",
174+
push: "double"
175+
},
176+
{
177+
regex: /'(?:[^\\']|\\.)*'?$/,
178+
token: "string",
179+
next: "start"
180+
},
181+
{
182+
regex: /'/,
183+
token: "string",
184+
push: "single"
185+
},
186+
{
187+
regex: /[^#"']+[\\`]$/,
188+
token: null
189+
},
190+
{
191+
regex: /[^#"']+$/,
104192
token: null,
105193
next: "start"
106194
},
195+
{
196+
regex: /[^#"']+/,
197+
token: null
198+
},
107199
// Fail safe return to start
108200
{
109201
token: null,
110202
next: "start"
111203
}
112204
],
113-
meta: {
114-
lineComment: "#"
115-
}
205+
meta: {
206+
lineComment: "#"
207+
}
116208
});
117209

118210
CodeMirror.defineMIME("text/x-dockerfile", "dockerfile");

mode/dockerfile/test.js

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
"[keyword COPY] package*.json ./",
1616
"[keyword RUN] npm install",
1717
"[keyword COPY] . .",
18-
"[keyword EXPOSE] 8080 3000",
18+
"[keyword EXPOSE] [number 8080] [number 3000]",
1919
"[keyword ENV] NODE_ENV development",
20-
"[keyword CMD] [[\"npm\", \"start\"]]");
20+
"[keyword CMD] [[ [string \"npm\"], [string \"start\"] ]]");
2121

2222
// Ideally the last space should not be highlighted.
2323
MT("instruction_without_args_1",
24-
"[keyword CMD ]");
24+
"[keyword CMD] ");
2525

2626
MT("instruction_without_args_2",
2727
"[comment # An instruction without args...]",
@@ -35,15 +35,15 @@
3535
" && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*");
3636

3737
MT("from_comment",
38-
" [keyword FROM] debian:stretch [comment # I tend to use stable as that is more stable]",
39-
" [keyword FROM] debian:stretch [keyword AS] stable [comment # I am even more stable]",
38+
" [keyword FROM] debian:stretch # I tend to use stable as that is more stable",
39+
" [keyword FROM] debian:stretch [keyword AS] stable # I am even more stable",
4040
" [keyword FROM] [error # this is an error]");
4141

4242
MT("from_as",
4343
"[keyword FROM] golang:1.9.2-alpine3.6 [keyword AS] build",
4444
"[keyword COPY] --from=build /bin/project /bin/project",
45-
"[keyword ENTRYPOINT] [[\"/bin/project\"]]",
46-
"[keyword CMD] [[\"--help\"]]");
45+
"[keyword ENTRYPOINT] [[ [string \"/bin/project\"] ]]",
46+
"[keyword CMD] [[ [string \"--help\"] ]]");
4747

4848
MT("arg",
4949
"[keyword ARG] VERSION=latest",
@@ -52,20 +52,77 @@
5252
"[keyword RUN] echo $VERSION > image_version");
5353

5454
MT("label",
55-
"[keyword LABEL] com.example.label-with-value=\"foo\"",
56-
"[keyword LABEL] description=\"This text illustrates \"",
57-
" that label-values can span multiple lines.\"");
55+
"[keyword LABEL] com.example.label-with-value=[string \"foo\"]");
56+
57+
MT("label_multiline",
58+
"[keyword LABEL] description=[string \"This text illustrates ]\\",
59+
"[string that label-values can span multiple lines.\"]");
5860

5961
MT("maintainer",
60-
"[keyword MAINTAINER] Foo Bar \"foo@bar.com\"",
62+
"[keyword MAINTAINER] Foo Bar [string \"foo@bar.com\"] ",
6163
"[keyword MAINTAINER] Bar Baz <bar@baz.com>");
6264

65+
MT("env",
66+
"[keyword ENV] BUNDLE_PATH=[string \"$GEM_HOME\"] \\",
67+
" BUNDLE_APP_CONFIG=[string \"$GEM_HOME\"]");
68+
6369
MT("verify_keyword",
6470
"[keyword RUN] add-apt-repository ppa:chris-lea/node.js");
6571

6672
MT("scripts",
6773
"[comment # Set an entrypoint, to automatically install node modules]",
68-
"[keyword ENTRYPOINT] [[\"/bin/bash\", \"-c\", \"if [[ ! -d node_modules ]]; then npm install; fi; exec \\\"${@:0}\\\";\"]]",
74+
"[keyword ENTRYPOINT] [[ [string \"/bin/bash\"], [string \"-c\"], [string \"if [[ ! -d node_modules ]]; then npm install; fi; exec \\\"${@:0}\\\";\"] ]]",
6975
"[keyword CMD] npm start",
70-
"[keyword RUN] npm run build && npm run test");
76+
"[keyword RUN] npm run build && \\",
77+
"[comment # a comment between the shell commands]",
78+
" npm run test");
79+
80+
MT("strings_single",
81+
"[keyword FROM] buildpack-deps:stretch",
82+
"[keyword RUN] { \\",
83+
" echo [string 'install: --no-document']; \\",
84+
" echo [string 'update: --no-document']; \\",
85+
" } >> /usr/local/etc/gemrc");
86+
87+
MT("strings_single_multiline",
88+
"[keyword RUN] set -ex \\",
89+
" \\",
90+
" && buildDeps=[string ' ]\\",
91+
"[string bison ]\\",
92+
"[string dpkg-dev ]\\",
93+
"[string libgdbm-dev ]\\",
94+
"[string ruby ]\\",
95+
"[string '] \\",
96+
" && apt-get update");
97+
98+
MT("strings_single_multiline_2",
99+
"[keyword RUN] echo [string 'say \\' ]\\",
100+
"[string it works'] ");
101+
102+
MT("strings_double",
103+
"[keyword RUN] apt-get install -y --no-install-recommends $buildDeps \\",
104+
" \\",
105+
" && wget -O ruby.tar.xz [string \"https://cache.ruby-lang.org/pub/ruby/${RUBY_MAJOR%-rc}/ruby-$RUBY_VERSION.tar.xz\"] \\",
106+
" && echo [string \"$RUBY_DOWNLOAD_SHA256 *ruby.tar.xz\"] | sha256sum -c - ");
107+
108+
MT("strings_double_multiline",
109+
"[keyword RUN] echo [string \"say \\\" ]\\",
110+
"[string it works\"] ");
111+
112+
MT("escape",
113+
"[comment # escape=`]",
114+
"[keyword FROM] microsoft/windowsservercore",
115+
"[keyword RUN] powershell.exe -Command `",
116+
" $ErrorActionPreference = [string 'Stop']; `",
117+
" wget https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile c:\python-3.5.1.exe ; `",
118+
" Start-Process c:\python-3.5.1.exe -ArgumentList [string '/quiet InstallAllUsers=1 PrependPath=1'] -Wait ; `",
119+
" Remove-Item c:\python-3.5.1.exe -Force)");
120+
121+
MT("escape_strings",
122+
"[comment # escape=`]",
123+
"[keyword FROM] python:3.6-windowsservercore [keyword AS] python",
124+
"[keyword RUN] $env:PATH = [string 'C:\\Python;C:\\Python\\Scripts;{0}'] -f $env:PATH ; `",
125+
// It should not consider \' as escaped.
126+
// " Set-ItemProperty -Path [string 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\'] -Name Path -Value $env:PATH ;");
127+
" Set-ItemProperty -Path [string 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\' -Name Path -Value $env:PATH ;]");
71128
})();

0 commit comments

Comments
 (0)