Skip to content

Commit

Permalink
WIP #162 Add signature help for VOLUME
Browse files Browse the repository at this point in the history
Signed-off-by: Remy Suen <[email protected]>
  • Loading branch information
rcjsuen committed Aug 31, 2017
1 parent 3755ddf commit 35db4ea
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 39 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ All notable changes to this project will be documented in this file.
- SHELL
- STOPSIGNAL
- USER
- VOLUME
- WORKDIR

### Fixed
Expand Down
8 changes: 8 additions & 0 deletions src/dockerPlainText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export class PlainTextDocumentation {
"signatureUser_Signature2_Param": "The UID to use.",
"signatureUser_Signature3": "Set the UID and GID to use for running any RUN, CMD, and ENTRYPOINT instructions that follow.",
"signatureUser_Signature3_Param1": "The GID to use.",
"signatureVolume_Signature0": "Create mount points for holding externally mounted volumes from the native host or other containers.",
"signatureVolume_Signature0_Param0": "The name of the mount point.",
"signatureWorkdir": "Set the working directory for any ADD, COPY, CMD, ENTRYPOINT, or RUN instructions that follow.",
"signatureWorkdir_Param": "The absolute or relative path to use as the working directory. Will be created if it does not exist.",

Expand Down Expand Up @@ -290,6 +292,12 @@ export class PlainTextDocumentation {
signatureUser_Signature3: this.dockerMessages["signatureUser_Signature3"],
signatureUser_Signature3_Param0: this.dockerMessages["signatureUser_Signature2_Param"],
signatureUser_Signature3_Param1: this.dockerMessages["signatureUser_Signature3_Param1"],
signatureVolume_Signature0: this.dockerMessages["signatureVolume_Signature0"],
signatureVolume_Signature0_Param0: this.dockerMessages["signatureVolume_Signature0_Param0"],
signatureVolume_Signature0_Param1: this.dockerMessages["signatureVolume_Signature0_Param0"],
signatureVolume_Signature1: this.dockerMessages["signatureVolume_Signature0"],
signatureVolume_Signature1_Param1: this.dockerMessages["signatureVolume_Signature0_Param0"],
signatureVolume_Signature1_Param2: this.dockerMessages["signatureVolume_Signature0_Param0"],
signatureWorkdir: this.dockerMessages["signatureWorkdir"],
signatureWorkdir_Param: this.dockerMessages["signatureWorkdir_Param"]
};
Expand Down
87 changes: 74 additions & 13 deletions src/dockerSignatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,28 +203,28 @@ export class DockerSignatures {
}
]
};
let activeParameter = this.getJSONSignatureActiveParameter(entrypoint, position);
if (activeParameter === -1) {
activeParameter = this.getSignatureActiveParameter(entrypoint, position, 2);
let entrypointActiveParameter = this.getJSONSignatureActiveParameter(entrypoint, position, false);
if (entrypointActiveParameter === -1) {
entrypointActiveParameter = this.getSignatureActiveParameter(entrypoint, position, 2);
return {
signatures: [ entrypointShellSignature ],
activeSignature: 0,
activeParameter: activeParameter
activeParameter: entrypointActiveParameter
}
} else if (activeParameter === 0) {
} else if (entrypointActiveParameter === 0) {
return {
signatures: [
entrypointJsonSignature,
entrypointShellSignature
],
activeSignature: 0,
activeParameter: activeParameter
activeParameter: entrypointActiveParameter
}
}
return {
signatures: [ entrypointJsonSignature ],
activeSignature: 0,
activeParameter: activeParameter
activeParameter: entrypointActiveParameter
}
case "ENV":
const envSignatures = [
Expand Down Expand Up @@ -510,7 +510,7 @@ export class DockerSignatures {
activeSignature: 0,
activeParameter: null
};
shellSignatureHelp.activeParameter = this.getJSONSignatureActiveParameter(shell, position);
shellSignatureHelp.activeParameter = this.getJSONSignatureActiveParameter(shell, position, false);
return shellSignatureHelp.activeParameter === -1 ? null : shellSignatureHelp;
case "STOPSIGNAL":
return {
Expand Down Expand Up @@ -626,6 +626,65 @@ export class DockerSignatures {
}
}
return userSignatureHelp;
case "VOLUME":
const volume = instruction as JSONInstruction;
const volumeJsonSignature = {
label: "VOLUME [ \"/vol\", ... ]",
documentation: this.documentation.getDocumentation("signatureVolume_Signature1"),
parameters: [
{
label: "["
},
{
label: "\"/vol\"",
documentation: this.documentation.getDocumentation("signatureVolume_Signature1_Param1")
},
{
label: "...",
documentation: this.documentation.getDocumentation("signatureVolume_Signature1_Param2")
},
{
label: "]"
}
]
};
const volumeShellSignature = {
label: "VOLUME /vol ...",
documentation: this.documentation.getDocumentation("signatureVolume_Signature0"),
parameters: [
{
label: "/vol",
documentation: this.documentation.getDocumentation("signatureVolume_Signature0_Param0")
},
{
label: "...",
documentation: this.documentation.getDocumentation("signatureVolume_Signature0_Param1")
}
]
};
let volumeActiveParameter = this.getJSONSignatureActiveParameter(volume, position, true);
if (volumeActiveParameter === -1) {
volumeActiveParameter = this.getSignatureActiveParameter(volume, position, 1);
return {
signatures: [ volumeShellSignature ],
activeSignature: 0,
activeParameter: volumeActiveParameter
}
} else if (volumeActiveParameter === 0) {
return {
signatures: [
volumeJsonSignature,
volumeShellSignature
],
activeSignature: 0,
activeParameter: volumeActiveParameter
}
}
return {
signatures: [ volumeJsonSignature ],
activeSignature: 0,
activeParameter: volumeActiveParameter
}
case "WORKDIR":
return {
signatures: [
Expand Down Expand Up @@ -808,20 +867,22 @@ export class DockerSignatures {
return inTag || inDigest ? 1 : 0;
}

private getJSONSignatureActiveParameter(instruction: JSONInstruction, position: Position): number {
private getJSONSignatureActiveParameter(instruction: JSONInstruction, position: Position, singleParameter: boolean): number {
const closingBracket = instruction.getClosingBracket();
if (closingBracket) {
const range = closingBracket.getRange();
if (range.end.line === position.line && range.end.character === position.character) {
return 4;
return singleParameter ? 3 : 4;
} else if (closingBracket.isBefore(position)) {
return -1;
}
}

const parameter = instruction.getSecondJSONElement();
if (parameter && parameter.isBefore(position)) {
return 3;
if (!singleParameter) {
const parameter = instruction.getSecondJSONElement();
if (parameter && parameter.isBefore(position)) {
return 3;
}
}

const executable = instruction.getFirstJSONElement();
Expand Down
3 changes: 3 additions & 0 deletions src/parser/dockerfileParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Shell } from './instructions/shell';
import { StopSignal } from './instructions/stopSignal';
import { Workdir } from './instructions/workdir';
import { User } from './instructions/user';
import { Volume } from './instructions/volume';
import { Dockerfile } from './dockerfile';
import { DIRECTIVE_ESCAPE } from '../docker';

Expand Down Expand Up @@ -57,6 +58,8 @@ export class DockerfileParser {
return new Workdir(document, lineRange, escapeChar, instruction, instructionRange);
case "USER":
return new User(document, lineRange, escapeChar, instruction, instructionRange);
case "VOLUME":
return new Volume(document, lineRange, escapeChar, instruction, instructionRange);
}
return new Instruction(document, lineRange, escapeChar, instruction, instructionRange);
}
Expand Down
14 changes: 14 additions & 0 deletions src/parser/instructions/volume.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Remy Suen. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import { TextDocument, Range } from 'vscode-languageserver';
import { Argument } from '../argument';
import { JSONInstruction } from './jsonInstruction';

export class Volume extends JSONInstruction {

constructor(document: TextDocument, range: Range, escapeChar: string, instruction: string, instructionRange: Range) {
super(document, range, escapeChar, instruction, instructionRange);
}
}
115 changes: 89 additions & 26 deletions test/dockerSignatures.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,58 @@ function assertUser_GroupsOnly(signatureHelp: SignatureHelp, activeParameter: nu
assert.equal(signatureHelp.signatures[1].parameters[1].documentation, docs.getDocumentation("signatureUser_Signature3_Param1"));
}

function assertVolume_JSON(signature: SignatureInformation) {
assert.equal(signature.label, "VOLUME [ \"/vol\", ... ]");
assert.notEqual(signature.documentation, null);
assert.equal(signature.documentation, docs.getDocumentation("signatureVolume_Signature1"));
assert.equal(signature.parameters.length, 4);
assert.equal(signature.parameters[0].label, "[");
assert.equal(signature.parameters[0].documentation, null);
assert.equal(signature.parameters[1].label, "\"/vol\"");
assert.notEqual(signature.parameters[1].documentation, null);
assert.equal(signature.parameters[1].documentation, docs.getDocumentation("signatureVolume_Signature1_Param1"));
assert.equal(signature.parameters[2].label, "...");
assert.notEqual(signature.parameters[2].documentation, null);
assert.equal(signature.parameters[2].documentation, docs.getDocumentation("signatureVolume_Signature1_Param2"));
assert.equal(signature.parameters[3].label, "]");
assert.equal(signature.parameters[3].documentation, null);
}

function assertVolume_Shell(signature: SignatureInformation) {
assert.equal(signature.label, "VOLUME /vol ...");
assert.notEqual(signature.documentation, null);
assert.equal(signature.documentation, docs.getDocumentation("signatureVolume_Signature0"));
assert.equal(signature.parameters.length, 2);
assert.equal(signature.parameters[0].label, "/vol");
assert.notEqual(signature.parameters[0].documentation, null);
assert.equal(signature.parameters[0].documentation, docs.getDocumentation("signatureVolume_Signature0_Param0"));
assert.equal(signature.parameters[1].label, "...");
assert.notEqual(signature.parameters[1].documentation, null);
assert.equal(signature.parameters[1].documentation, docs.getDocumentation("signatureVolume_Signature0_Param1"));
}

function assertVolume(signatureHelp: SignatureHelp, activeParameter: number) {
assert.equal(signatureHelp.activeSignature, 0);
assert.equal(signatureHelp.activeParameter, activeParameter);
assert.equal(signatureHelp.signatures.length, 2);
assertVolume_JSON(signatureHelp.signatures[0]);
assertVolume_Shell(signatureHelp.signatures[1]);
}

function assertVolume_JSONOnly(signatureHelp: SignatureHelp, activeParameter: number) {
assert.equal(signatureHelp.activeSignature, 0);
assert.equal(signatureHelp.activeParameter, activeParameter);
assert.equal(signatureHelp.signatures.length, 1);
assertVolume_JSON(signatureHelp.signatures[0]);
}

function assertVolume_ShellOnly(signatureHelp: SignatureHelp, activeParameter: number) {
assert.equal(signatureHelp.activeSignature, 0);
assert.equal(signatureHelp.activeParameter, activeParameter);
assert.equal(signatureHelp.signatures.length, 1);
assertVolume_Shell(signatureHelp.signatures[0]);
}

function assertWorkdir(signatureHelp: SignatureHelp) {
assert.equal(signatureHelp.activeSignature, 0);
assert.equal(signatureHelp.activeParameter, 0);
Expand Down Expand Up @@ -823,7 +875,7 @@ describe("Dockerfile Signature Tests", function() {

testCopy(false);

function testParameterizedInstruction(instruction, trigger: boolean, assertAll: Function, assertJSON: Function, assertShell: Function) {
function testParameterizedInstruction(instruction, trigger: boolean, singleParameter: boolean, assertAll: Function, assertJSON: Function, assertShell: Function) {
const onbuild = trigger ? "ONBUILD " : "";
const prefix = onbuild + instruction;
const triggerOffset = onbuild.length;
Expand Down Expand Up @@ -862,15 +914,17 @@ describe("Dockerfile Signature Tests", function() {
});

it("...", function() {
assertJSON(compute(prefix + " [\"cmd\", \"/C\",", 0, offset + 14), 3);
assertJSON(compute(prefix + " [\"cmd\", \"/C\", ", 0, offset + 15), 3);
assertJSON(compute(prefix + " [\"cmd\", \"/C\", \"/C\"]", 0, offset + 19), 3);
const activeParameter = singleParameter ? 2 : 3;
assertJSON(compute(prefix + " [\"cmd\", \"/C\",", 0, offset + 14), activeParameter);
assertJSON(compute(prefix + " [\"cmd\", \"/C\", ", 0, offset + 15), activeParameter);
assertJSON(compute(prefix + " [\"cmd\", \"/C\", \"/C\"]", 0, offset + 19), activeParameter);
});

it("]", function() {
assertJSON(compute(prefix + " []", 0, offset + 3), 4);
assertJSON(compute(prefix + " [ ]", 0, offset + 5), 4);
assertJSON(compute(prefix + " [ \"cmd\" ]", 0, offset + 11), 4);
const activeParameter = singleParameter ? 3 : 4;
assertJSON(compute(prefix + " []", 0, offset + 3), activeParameter);
assertJSON(compute(prefix + " [ ]", 0, offset + 5), activeParameter);
assertJSON(compute(prefix + " [ \"cmd\" ]", 0, offset + 11), activeParameter);
});

it("invalid", function() {
Expand Down Expand Up @@ -939,35 +993,37 @@ describe("Dockerfile Signature Tests", function() {
});

it("...", function() {
assertShell(compute(prefix + " node --inspect ", 0, offset + 16), 2);

assertShell(compute(prefix + " node --inspect server.js", 0, offset + 16), 2);
assertShell(compute(prefix + " node --inspect server.js", 0, offset + 20), 2);
assertShell(compute(prefix + " node --inspect server.js", 0, offset + 25), 2);

assertShell(compute(prefix + " node --inspect server.js ", 0, offset + 16), 2);
assertShell(compute(prefix + " node --inspect server.js ", 0, offset + 20), 2);
assertShell(compute(prefix + " node --inspect server.js ", 0, offset + 25), 2);
assertShell(compute(prefix + " node --inspect server.js ", 0, offset + 26), 2);

assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 16), 2);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 20), 2);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 25), 2);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 26), 2);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 30), 2);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 37), 2);
const activeParameter = singleParameter ? 1 : 2;
assertShell(compute(prefix + " node --inspect ", 0, offset + 16), activeParameter);

assertShell(compute(prefix + " node --inspect server.js", 0, offset + 16), activeParameter);
assertShell(compute(prefix + " node --inspect server.js", 0, offset + 20), activeParameter);
assertShell(compute(prefix + " node --inspect server.js", 0, offset + 25), activeParameter);

assertShell(compute(prefix + " node --inspect server.js ", 0, offset + 16), activeParameter);
assertShell(compute(prefix + " node --inspect server.js ", 0, offset + 20), activeParameter);
assertShell(compute(prefix + " node --inspect server.js ", 0, offset + 25), activeParameter);
assertShell(compute(prefix + " node --inspect server.js ", 0, offset + 26), activeParameter);

assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 16), activeParameter);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 20), activeParameter);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 25), activeParameter);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 26), activeParameter);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 30), activeParameter);
assertShell(compute(prefix + " node --inspect server.js --port=8000", 0, offset + 37), activeParameter);
});

it("valid JSON", function() {
const activeParameter = singleParameter ? 1 : 2;
assertShell(compute(prefix + " [] ", 0, offset + 4), 1);
assertShell(compute(prefix + " [ \"cmd\" ] ", 0, offset + 12), 2);
assertShell(compute(prefix + " [ \"cmd\" ] ", 0, offset + 12), activeParameter);
});
});
});
}

function testEntrypoint(trigger: boolean) {
testParameterizedInstruction("ENTRYPOINT", trigger, assertEntrypoint, assertEntrypoint_JSONOnly, assertEntrypoint_ShellOnly);
testParameterizedInstruction("ENTRYPOINT", trigger, false, assertEntrypoint, assertEntrypoint_JSONOnly, assertEntrypoint_ShellOnly);
}

testEntrypoint(false);
Expand Down Expand Up @@ -1678,6 +1734,12 @@ describe("Dockerfile Signature Tests", function() {

testUser(false);

function testVolume(trigger: boolean) {
testParameterizedInstruction("VOLUME", trigger, true, assertVolume, assertVolume_JSONOnly, assertVolume_ShellOnly);
}

testVolume(false);

function testWorkdir(trigger: boolean) {
let onbuild = trigger ? "ONBUILD " : "";
let triggerOffset = trigger ? 8 : 0;
Expand Down Expand Up @@ -1716,6 +1778,7 @@ describe("Dockerfile Signature Tests", function() {
testShell(true);
testStopsignal(true);
testUser(true);
testVolume(true);
testWorkdir(true);
});
});

0 comments on commit 35db4ea

Please sign in to comment.