diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ca335..89e2638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ All notable changes to this project will be documented in this file. - suggest build stage names in a COPY instruction ([#44](https://github.com/rcjsuen/dockerfile-language-server-nodejs/issues/44)) - add '=' as a trigger character - include the --start-period flag in HEALTHCHECK CMD items ([#78](https://github.com/rcjsuen/dockerfile-language-server-nodejs/issues/78)) - - HEALTHCHECK CMD flags ([#69](https://github.com/rcjsuen/dockerfile-language-server-nodejs/issues/69)) + - HEALTHCHECK CMD flags ([#69](https://github.com/rcjsuen/dockerfile-language-server-nodejs/issues/69), [#101](https://github.com/rcjsuen/dockerfile-language-server-nodejs/issues/101)) - suggest $ variables ([#93](https://github.com/rcjsuen/dockerfile-language-server-nodejs/issues/93)) - ARG and ENV variables - default Docker ARG variables diff --git a/src/dockerAssist.ts b/src/dockerAssist.ts index 44464a8..f26ee60 100644 --- a/src/dockerAssist.ts +++ b/src/dockerAssist.ts @@ -14,6 +14,7 @@ import { DockerHover } from './dockerHover'; import { DockerfileParser } from './parser/dockerfileParser'; import { Copy } from './parser/instructions/copy'; import { Healthcheck } from './parser/instructions/healthcheck'; +import { Onbuild } from './parser/instructions/onbuild'; import { Instruction } from './parser/instruction'; export class DockerAssist { @@ -117,9 +118,44 @@ export class DockerAssist { case "COPY": return this.createBuildStageProposals(dockerfile, instruction as Copy, position, offset); case "HEALTHCHECK": - return this.createHealthcheckProposals(dockerfile, instruction as Healthcheck, position, offset, prefix); + let subcommand = (instruction as Healthcheck).getSubcommand(); + if (subcommand && subcommand.isBefore(position)) { + return []; + } + return this.createHealthcheckProposals(dockerfile, position, offset, prefix); case "ONBUILD": let onbuildArgs = instruction.getArguments(); + if (onbuildArgs.length === 0 || Util.isInsideRange(position, onbuildArgs[0].getRange())) { + // no trigger instructions or the cursor is in the trigger instruction + previousWord = "ONBUILD"; + break instructionsCheck; + } else { + let trigger = (instruction as Onbuild).getTriggerInstruction(); + if (trigger === "HEALTHCHECK") { + if (onbuildArgs.length === 1) { + // suggest HEALTHCHECK flags + return this.createHealthcheckProposals(dockerfile, position, offset, prefix); + } + for (let i = 1; i < onbuildArgs.length; i++) { + let arg = onbuildArgs[i].getValue().toUpperCase(); + if (arg === "CMD" || arg === "NONE") { + return []; + } else if (Util.isInsideRange(position, onbuildArgs[i].getRange())) { + return this.createHealthcheckProposals(dockerfile, position, offset, prefix); + } + } + } + } + switch (onbuildArgs.length) { + case 0: + previousWord = "ONBUILD"; + break instructionsCheck; + case 1: + previousWord = "ONBUILD"; + break instructionsCheck; + default: + break; + } if (onbuildArgs.length === 0 || Util.isInsideRange(position, onbuildArgs[0].getRange())) { previousWord = "ONBUILD"; break instructionsCheck; @@ -207,12 +243,7 @@ export class DockerAssist { return []; } - private createHealthcheckProposals(dockerfile: Dockerfile, healthcheck: Healthcheck, position: Position, offset: number, prefix: string) { - let subcommand = healthcheck.getSubcommand(); - if (subcommand && subcommand.isBefore(position)) { - return []; - } - + private createHealthcheckProposals(dockerfile: Dockerfile, position: Position, offset: number, prefix: string) { let items: CompletionItem[] = []; if ("--interval".indexOf(prefix) === 0) { items.push(this.createHEALTHCHECK_FlagInterval(prefix, offset)); diff --git a/src/parser/instructions/onbuild.ts b/src/parser/instructions/onbuild.ts index 173874a..3405b61 100644 --- a/src/parser/instructions/onbuild.ts +++ b/src/parser/instructions/onbuild.ts @@ -11,6 +11,11 @@ export class Onbuild extends Instruction { super(document, range, escapeChar, instruction, instructionRange); } + public getTriggerInstruction(): string | null { + let trigger = this.getTrigger(); + return trigger === null ? null : trigger.toUpperCase(); + } + public getTrigger(): string | null { return this.getRangeContent(this.getTriggerRange()); } diff --git a/test/dockerAssist.test.ts b/test/dockerAssist.test.ts index 80cb266..e10a41e 100644 --- a/test/dockerAssist.test.ts +++ b/test/dockerAssist.test.ts @@ -1382,99 +1382,104 @@ describe('Docker Content Assist Tests', function() { }); }); - describe("HEALTHCHECK", function() { - function testHealthcheck(snippetSupport: boolean) { - describe("arguments", function() { - it("full", function() { - var items = computePosition("FROM busybox\nHEALTHCHECK ", 1, 12, snippetSupport); - assertHealthcheckItems(items, 1, 12, 1, 12, snippetSupport); - }); + function testHealthcheck(trigger: boolean) { + describe("HEALTHCHECK", function() { + let onbuild = trigger ? "ONBUILD " : ""; + let triggerOffset = onbuild.length; + function testFlags(snippetSupport: boolean) { + describe("arguments", function() { + it("full", function() { + var items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK ", 1, triggerOffset + 12, snippetSupport); + assertHealthcheckItems(items, 1, triggerOffset + 12, 1, triggerOffset + 12, snippetSupport); + }); - it("prefix", function() { - var items = computePosition("FROM busybox\nHEALTHCHECK -", 1, 13, snippetSupport); - assertHealthcheckItems(items, 1, 12, 1, 13, snippetSupport); + it("prefix", function() { + var items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK -", 1, triggerOffset + 13, snippetSupport); + assertHealthcheckItems(items, 1, triggerOffset + 12, 1, triggerOffset + 13, snippetSupport); - items = computePosition("FROM busybox\nHEALTHCHECK --", 1, 14, snippetSupport); - assertHealthcheckItems(items, 1, 12, 1, 14, snippetSupport); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK --", 1, triggerOffset + 14, snippetSupport); + assertHealthcheckItems(items, 1, triggerOffset + 12, 1, triggerOffset + 14, snippetSupport); - items = computePosition("FROM busybox\nHEALTHCHECK --inter", 1, 19, snippetSupport); - assert.equal(items.length, 1); - assertHEALTHCHECK_FlagInterval(items[0], 1, 12, 1, 19, snippetSupport); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK --inter", 1, triggerOffset + 19, snippetSupport); + assert.equal(items.length, 1); + assertHEALTHCHECK_FlagInterval(items[0], 1, triggerOffset + 12, 1, triggerOffset + 19, snippetSupport); - items = computePosition("FROM busybox\nHEALTHCHECK --ret", 1, 17, snippetSupport); - assert.equal(items.length, 1); - assertHEALTHCHECK_FlagRetries(items[0], 1, 12, 1, 17, snippetSupport); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK --ret", 1, triggerOffset + 17, snippetSupport); + assert.equal(items.length, 1); + assertHEALTHCHECK_FlagRetries(items[0], 1, triggerOffset + 12, 1, triggerOffset + 17, snippetSupport); - items = computePosition("FROM busybox\nHEALTHCHECK --start", 1, 19, snippetSupport); - assert.equal(items.length, 1); - assertHEALTHCHECK_FlagStartPeriod(items[0], 1, 12, 1, 19, snippetSupport); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK --start", 1, triggerOffset + 19, snippetSupport); + assert.equal(items.length, 1); + assertHEALTHCHECK_FlagStartPeriod(items[0], 1, triggerOffset + 12, 1, triggerOffset + 19, snippetSupport); - items = computePosition("FROM busybox\nHEALTHCHECK --time", 1, 18, snippetSupport); - assert.equal(items.length, 1); - assertHEALTHCHECK_FlagTimeout(items[0], 1, 12, 1, 18, snippetSupport); - }); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK --time", 1, triggerOffset + 18, snippetSupport); + assert.equal(items.length, 1); + assertHEALTHCHECK_FlagTimeout(items[0], 1, triggerOffset + 12, 1, triggerOffset + 18, snippetSupport); + }); - it("after command", function() { - var items = computePosition("FROM busybox\nHEALTHCHECK CMD", 1, 15, snippetSupport); - assert.equal(items.length, 0); + it("after command", function() { + var items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK CMD", 1, triggerOffset + 15, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK cmd", 1, 15, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK cmd", 1, triggerOffset + 15, snippetSupport); + assert.equal(items.length, 0); - var items = computePosition("FROM busybox\nHEALTHCHECK CMD ", 1, 16, snippetSupport); - assert.equal(items.length, 0); + var items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK CMD ", 1, triggerOffset + 16, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK cmd ", 1, 16, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK cmd ", 1, triggerOffset + 16, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK CMD\\\n", 2, 0, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK CMD\\\n", 2, 0, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK cmd\\\n", 2, 0, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK cmd\\\n", 2, 0, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK CMD \\\n", 2, 0, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK CMD \\\n", 2, 0, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK cmd \\\n", 2, 0, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK cmd \\\n", 2, 0, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK NONE", 1, 16, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK NONE", 1, triggerOffset + 16, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK none", 1, 16, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK none", 1, triggerOffset + 16, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK NONE ", 1, 17, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK NONE ", 1, triggerOffset + 17, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK none ", 1, 17, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK none ", 1, triggerOffset + 17, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK NONE \\\n", 2, 0, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK NONE \\\n", 2, 0, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK none \\\n", 2, 0, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK none \\\n", 2, 0, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK NONE\\\n", 2, 0, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK NONE\\\n", 2, 0, snippetSupport); + assert.equal(items.length, 0); - items = computePosition("FROM busybox\nHEALTHCHECK none\\\n", 2, 0, snippetSupport); - assert.equal(items.length, 0); + items = computePosition("FROM busybox\n" + onbuild + "HEALTHCHECK none\\\n", 2, 0, snippetSupport); + assert.equal(items.length, 0); + }); }); - }); - } + } - describe("snippets", function() { - testHealthcheck(true); - }); - + describe("snippets", function() { + testFlags(true); + }); + - describe("plain text", function() { - testHealthcheck(false); + describe("plain text", function() { + testFlags(true); + }); }); - }); + } + testHealthcheck(false); describe('ONBUILD nesting', function() { it('all', function() { @@ -1599,6 +1604,8 @@ describe('Docker Content Assist Tests', function() { assert.equal(proposals.length, 0); }); }); + + testHealthcheck(true); }); describe("variables", function() {