Skip to content

Commit

Permalink
WIP #162 Add signature help for FROM
Browse files Browse the repository at this point in the history
Signed-off-by: Remy Suen <[email protected]>
  • Loading branch information
rcjsuen committed Aug 25, 2017
1 parent 066ea36 commit 81ca03d
Show file tree
Hide file tree
Showing 6 changed files with 512 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ All notable changes to this project will be documented in this file.
- instructions ([#162](https://github.com/rcjsuen/dockerfile-language-server-nodejs/issues/162))
- ARG
- EXPOSE
- FROM
- SHELL
- STOPSIGNAL
- USER
Expand Down
26 changes: 26 additions & 0 deletions src/dockerPlainText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ export class PlainTextDocumentation {
"signatureCopyFlagFrom_Param": "The build stage or image name to use as the source. Also may be a numeric index.",
"signatureExpose": "Define network ports for this container to listen on at runtime.",
"signatureExpose_Param0": "The port that this container should listen on.",
"signatureFrom_Signature0": "Set the base image to use for any subsequent instructions that follow.",
"signatureFrom_Signature0_Param": "The name of the base image to use.",
"signatureFrom_Signature1_Param1": "The tag of the base image to use.",
"signatureFrom_Signature2_Param1": "The digest of the base image to use.",
"signatureFrom_Signature3": "Set the base image to use for any subsequent instructions that follow and also give this build stage a name.",
"signatureFrom_Signature3_Param2": "The name of this build stage.",
"signatureFrom_Param2": "The name of this build stage.",
"signatureHealthcheck": "Define how Docker should test the container to check that it is still working.",
"signatureShell": "Override default shell used for the shell form of commands.",
"signatureShell_Param1": "The shell executable to use.",
Expand Down Expand Up @@ -196,6 +203,25 @@ export class PlainTextDocumentation {
signatureExpose: this.dockerMessages["signatureExpose"],
signatureExpose_Param0: this.dockerMessages["signatureExpose_Param0"],
signatureExpose_Param1: this.dockerMessages["signatureExpose_Param0"],
signatureFrom_Signature0: this.dockerMessages["signatureFrom_Signature0"],
signatureFrom_Signature0_Param: this.dockerMessages["signatureFrom_Signature0_Param"],
signatureFrom_Signature1: this.dockerMessages["signatureFrom_Signature0"],
signatureFrom_Signature1_Param0: this.dockerMessages["signatureFrom_Signature0_Param"],
signatureFrom_Signature1_Param1: this.dockerMessages["signatureFrom_Signature1_Param1"],
signatureFrom_Signature2: this.dockerMessages["signatureFrom_Signature0"],
signatureFrom_Signature2_Param0: this.dockerMessages["signatureFrom_Signature0_Param"],
signatureFrom_Signature2_Param1: this.dockerMessages["signatureFrom_Signature2_Param1"],
signatureFrom_Signature3: this.dockerMessages["signatureFrom_Signature3"],
signatureFrom_Signature3_Param0: this.dockerMessages["signatureFrom_Signature0_Param"],
signatureFrom_Signature3_Param2: this.dockerMessages["signatureFrom_Signature3_Param2"],
signatureFrom_Signature4: this.dockerMessages["signatureFrom_Signature3"],
signatureFrom_Signature4_Param0: this.dockerMessages["signatureFrom_Signature0_Param"],
signatureFrom_Signature4_Param1: this.dockerMessages["signatureFrom_Signature1_Param1"],
signatureFrom_Signature4_Param3: this.dockerMessages["signatureFrom_Signature3_Param2"],
signatureFrom_Signature5: this.dockerMessages["signatureFrom_Signature3"],
signatureFrom_Signature5_Param0: this.dockerMessages["signatureFrom_Signature0_Param"],
signatureFrom_Signature5_Param1: this.dockerMessages["signatureFrom_Signature2_Param1"],
signatureFrom_Signature5_Param3: this.dockerMessages["signatureFrom_Signature3_Param2"],
signatureHealthcheck: this.dockerMessages["signatureHealthcheck"],
signatureHealthcheckFlagInterval_Param: this.dockerMessages["hoverHealthcheckFlagInterval"],
signatureHealthcheckFlagRetries_Param: this.dockerMessages["hoverHealthcheckFlagRetries"],
Expand Down
166 changes: 165 additions & 1 deletion src/dockerSignatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import {
TextDocument, Range, Position, SignatureHelp
TextDocument, Range, Position, SignatureHelp, SignatureInformation
} from 'vscode-languageserver';
import { Dockerfile } from './parser/dockerfile';
import { Argument } from './parser/argument';
import { Instruction } from './parser/instruction';
import { Arg } from './parser/instructions/arg';
import { Copy } from './parser/instructions/copy';
import { From } from './parser/instructions/from';
import { Healthcheck } from './parser/instructions/healthcheck';
import { Shell } from './parser/instructions/shell';
import { PlainTextDocumentation } from './dockerPlainText';
Expand Down Expand Up @@ -180,6 +182,8 @@ export class DockerSignatures {
exposeSignatureHelp.activeParameter = 1;
}
return exposeSignatureHelp;
case "FROM":
return this.getFromSignatureHelp(position, instruction as From);
case "HEALTHCHECK":
let flags = (instruction as Healthcheck).getFlags();
for (let flag of flags) {
Expand Down Expand Up @@ -460,4 +464,164 @@ export class DockerSignatures {
}
return null;
}

private getFromSignatureHelp(position: Position, from: From): SignatureHelp {
let baseImage = {
label: "FROM baseImage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature0"),
parameters: [
{
label: "baseImage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature0_Param")
}
]
};
let baseImageTag = {
label: "FROM baseImage:tag",
documentation: this.documentation.getDocumentation("signatureFrom_Signature1"),
parameters: [
{
label: "baseImage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature1_Param0")
},
{
label: "tag",
documentation: this.documentation.getDocumentation("signatureFrom_Signature1_Param1")
}
]
};
let baseImageDigest = {
label: "FROM baseImage@digest",
documentation: this.documentation.getDocumentation("signatureFrom_Signature2"),
parameters: [
{
label: "baseImage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature2_Param0")
},
{
label: "digest",
documentation: this.documentation.getDocumentation("signatureFrom_Signature2_Param1")
}
]
};
let baseImageStage = {
label: "FROM baseImage AS stage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature3"),
parameters: [
{
label: "baseImage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature3_Param0")
},
{
label: "AS",
},
{
label: "stage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature3_Param2")
}
]
};
let baseImageTagStage = {
label: "FROM baseImage:tag AS stage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature4"),
parameters: [
{
label: "baseImage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature4_Param0")
},
{
label: "tag",
documentation: this.documentation.getDocumentation("signatureFrom_Signature4_Param1")
},
{
label: "AS",
},
{
label: "stage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature4_Param3")
}
]
};
let baseImageDigestStage = {
label: "FROM baseImage@digest AS stage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature5"),
parameters: [
{
label: "baseImage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature5_Param0")
},
{
label: "digest",
documentation: this.documentation.getDocumentation("signatureFrom_Signature5_Param1")
},
{
label: "AS",
},
{
label: "stage",
documentation: this.documentation.getDocumentation("signatureFrom_Signature5_Param3")
}
]
};
let fromSignatures = [
baseImage, baseImageTag, baseImageDigest,
baseImageStage, baseImageTagStage, baseImageDigestStage
];

const args = from.getArguments();
if (args.length >= 3 && args[2].isBefore(position)) {
return null;
} else if (args.length === 0) {
return {
signatures: fromSignatures,
activeSignature: 0,
activeParameter: 0
};
}

const image = args[0].getValue();
const digest = image.indexOf('@') !== -1;
const tag = !digest && image.indexOf(':') !== -1;
const stagesOnly = args.length > 1 || args[0].isBefore(position);
return {
signatures: this.getFromSignatures(fromSignatures, tag, digest, stagesOnly),
activeSignature: 0,
activeParameter: this.getFromActiveParameter(position, from, image, tag, digest, args)
};
}

private getFromSignatures(fromSignatures: SignatureInformation[], tag: boolean, digest: boolean, stagesOnly: boolean): SignatureInformation[] {
if (digest) {
return stagesOnly ? [ fromSignatures[5] ] : [ fromSignatures[2], fromSignatures[5] ];
} else if (tag) {
return stagesOnly ? [ fromSignatures[4] ] : [ fromSignatures[1], fromSignatures[4] ];
}
return stagesOnly ? [ fromSignatures[3], fromSignatures[4], fromSignatures[5] ] : fromSignatures;
}

private getFromActiveParameter(position: Position, from: From, image: string, tag: boolean, digest: boolean, args: Argument[]): number {
const inTag = tag && Util.isInsideRange(position, from.getImageTagRange());
const inDigest = digest && Util.isInsideRange(position, from.getImageDigestRange());
const inImage = !inTag && !inDigest && Util.isInsideRange(position, args[0].getRange());
if (args.length === 1) {
if (args[0].isBefore(position)) {
return tag || digest ? 2 : 1;
}
return inTag || inDigest ? 1 : 0;
} else if (args.length === 2) {
if (args[1].isBefore(position)) {
return tag || digest ? 3 : 2;
} else if (Util.isInsideRange(position, args[1].getRange()) || args[0].isBefore(position)) {
return tag || digest ? 2 : 1;
}
return inTag || inDigest ? 1 : 0;
}

if (Util.isInsideRange(position, args[2].getRange()) || args[1].isBefore(position)) {
return tag || digest ? 3 : 2;
} else if (Util.isInsideRange(position, args[1].getRange()) || args[0].isBefore(position)) {
return tag || digest ? 2 : 1;
}
return inTag || inDigest ? 1 : 0;
}
}
7 changes: 7 additions & 0 deletions src/parser/argument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export class Argument {
return this.value;
}

public isAfter(position: Position): boolean {
if (this.range.end.line < position.line) {
return true;
}
return this.range.start.line < position.line ? true : this.range.start.character > position.character;
}

public isBefore(position: Position): boolean {
if (this.range.start.line < position.line) {
return true;
Expand Down
19 changes: 19 additions & 0 deletions src/parser/instructions/from.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,25 @@ export class From extends Instruction {
return null;
}

/**
* Returns the range in the document that the digest of the base
* image encompasses.
*
* @return the base image's digest's range in the document, or null
* if no digest has been specified
*/
public getImageDigestRange(): Range | null {
let range = this.getImageRange();
if (range) {
let content = this.getRangeContent(range);
let index = content.lastIndexOf('@');
if (index !== -1) {
return Range.create(range.start.line, range.start.character + index + 1, range.end.line, range.end.character);
}
}
return null;
}

public getBuildStage(): string | null {
let range = this.getBuildStageRange();
return range === null ? null : this.getRangeContent(range);
Expand Down
Loading

0 comments on commit 81ca03d

Please sign in to comment.