Skip to content

Commit

Permalink
Fix #14 Expose API to define format for hover results
Browse files Browse the repository at this point in the history
Added a new API to make it possible for clients to choose between
having the hover results be returned in Markdown or in plain text.

Signed-off-by: Remy Suen <[email protected]>
  • Loading branch information
rcjsuen committed Mar 7, 2018
1 parent cd32562 commit 553117f
Show file tree
Hide file tree
Showing 6 changed files with 900 additions and 1,222 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,23 @@ All notable changes to this project will be documented in this file.

## [Unreleased]
### Added
- new Capabilities interface for defining what features the language service should support and enable
```TypeScript
interface Capabilities {
/**
* Capabilities related to hover requests.
*/
hover?: {
/**
* Describes the content type that should be returned for hovers.
*/
contentFormat?: MarkupKind[];
}
}
```
- new computeCommandEdits function to DockerfileLanguageService ([#4](https://github.com/rcjsuen/dockerfile-language-service/issues/4))
- update documentation to state that ARG was introduced in Docker 1.9 ([#7](https://github.com/rcjsuen/dockerfile-language-service/issues/7))
- allow hover information to be returned in Markdown or plain text ([#14](https://github.com/rcjsuen/dockerfile-language-service/issues/14))

### Changed
- change the signature of DockerfileLanguageService's computeHighlightRanges function by removing its first URI string parameter ([#15](https://github.com/rcjsuen/dockerfile-language-service/issues/15))
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"dependencies": {
"dockerfile-ast": "0.0.3",
"dockerfile-utils": "0.0.7",
"vscode-languageserver-types": "^3.5.0"
"vscode-languageserver-types": "^3.6.0"
},
"main": "./lib/main.js",
"types": "./lib/main.d.ts",
Expand Down
145 changes: 96 additions & 49 deletions src/dockerHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,76 @@
* ------------------------------------------------------------------------------------------ */
'use strict';

import { TextDocument, Hover, Position } from 'vscode-languageserver-types';
import { DockerfileParser, Arg, Env, Instruction, ModifiableInstruction, Onbuild, Directive } from 'dockerfile-ast';
import { TextDocument, Hover, Position, MarkupKind, MarkupContent } from 'vscode-languageserver-types';
import { DockerfileParser, Dockerfile, Arg, Env, Instruction, ModifiableInstruction, Onbuild, Directive } from 'dockerfile-ast';
import { Util } from './docker';
import { MarkdownDocumentation } from './dockerMarkdown';
import { DockerDefinition } from './dockerDefinition';
import { PlainTextDocumentation } from './dockerPlainText';

export class DockerHover {

private markdown: MarkdownDocumentation;
private plainText: PlainTextDocumentation;

constructor(markdown: MarkdownDocumentation) {
constructor(markdown: MarkdownDocumentation, plainText: PlainTextDocumentation) {
this.markdown = markdown;
this.plainText = plainText;
}

onHover(content: string, position: Position): Hover | null {
public onHover(content: string, position: Position, markupKind: MarkupKind[]): Hover | null {
let dockerfile = DockerfileParser.parse(content);
let directive = dockerfile.getDirective();
let image = dockerfile.getContainingImage(position);
let key = this.computeHoverKey(dockerfile, position);
if (key) {
// if it's not a raw value, apply markup if necessary
if (markupKind && markupKind.length > 0) {
switch (markupKind[0]) {
case MarkupKind.Markdown:
let markdownDocumentation = this.markdown.getMarkdown(key);
if (markdownDocumentation) {
return {
contents: {
kind: MarkupKind.Markdown,
value: markdownDocumentation.contents as string
}
};
}
return null;
case MarkupKind.PlainText:
let plainTextDocumentation = this.plainText.getDocumentation(key);
if (plainTextDocumentation) {
return {
contents: {
kind: MarkupKind.PlainText,
value: plainTextDocumentation
}
};
}
}
return null;
}
return this.markdown.getMarkdown(key);
}

if (position.line === 0 && directive !== null && directive.getDirective() === Directive.escape) {
let range = directive.getNameRange();
if (Util.isInsideRange(position, range)) {
return this.markdown.getMarkdown(Directive.escape);
for (let instruction of image.getInstructions()) {
if (instruction instanceof Arg) {
// hovering over an argument defined by ARG
let property = instruction.getProperty();
if (property && Util.isInsideRange(position, property.getNameRange()) && property.getValue() !== null) {
return { contents: property.getValue() };
}
}

if (instruction instanceof Env) {
// hovering over an argument defined by ENV
for (let property of instruction.getProperties()) {
if (Util.isInsideRange(position, property.getNameRange()) && property.getValue() !== null) {
return {
contents: property.getValue()
};
}
}
}
}

Expand All @@ -44,64 +91,64 @@ export class DockerHover {
}
}

let property = DockerDefinition.findDefinition(dockerfile, position);
if (property && property.getValue() !== null) {
return { contents: property.getValue() };
}

return null;
}

/**
* Analyzes the Dockerfile at the given position to determine if the user
* is hovering over a keyword, a flag, or a directive.
*
* @param dockerfile the Dockerfile to check
* @param position the place that the user is hovering over
* @return the string key value for the keyword, flag, or directive that's
* being hovered over, or null if the user isn't hovering over
* such a word
*/
private computeHoverKey(dockerfile: Dockerfile, position: Position): string | null {
let directive = dockerfile.getDirective();
let image = dockerfile.getContainingImage(position);
if (position.line === 0 && directive !== null && directive.getDirective() === Directive.escape) {
let range = directive.getNameRange();
if (Util.isInsideRange(position, range)) {
return Directive.escape;
}
}

for (let instruction of image.getInstructions()) {
let instructionRange = instruction.getInstructionRange();
if (Util.isInsideRange(position, instructionRange)) {
return this.markdown.getMarkdown(instruction.getKeyword());
return instruction.getKeyword();
}

if (instruction instanceof Onbuild) {
// hovering over a trigger instruction of an ONBUILD
let range = instruction.getTriggerRange();
if (Util.isInsideRange(position, range)) {
return this.markdown.getMarkdown(instruction.getTrigger());
}
}

if (instruction instanceof Arg) {
// hovering over an argument defined by ARG
let property = instruction.getProperty();
if (property && Util.isInsideRange(position, property.getNameRange()) && property.getValue() !== null) {
return {
contents: property.getValue()
};
}
}

if (instruction instanceof Env) {
// hovering over an argument defined by ENV
for (let property of instruction.getProperties()) {
if (Util.isInsideRange(position, property.getNameRange()) && property.getValue() !== null) {
return {
contents: property.getValue()
};
}
return instruction.getTrigger();
}
}

let hover = this.getFlagsHover(position, instruction);
if (hover !== undefined) {
if (hover !== null) {
return hover;
}
}

let property = DockerDefinition.findDefinition(dockerfile, position);
if (property && property.getValue() !== null) {
return { contents: property.getValue() };
}

return null;
}

private getFlagsHover(position: Position, instruction: Instruction): Hover {
private getFlagsHover(position: Position, instruction: Instruction): string | null {
switch (instruction.getKeyword()) {
case "ADD":
let addFlags = (instruction as ModifiableInstruction).getFlags();
for (let flag of addFlags) {
if (Util.isInsideRange(position, flag.getNameRange())) {
switch (flag.getName()) {
case "chown":
return this.markdown.getMarkdown("ADD_FlagChown");
return "ADD_FlagChown";
}
}
}
Expand All @@ -112,9 +159,9 @@ export class DockerHover {
if (Util.isInsideRange(position, flag.getNameRange())) {
switch (flag.getName()) {
case "chown":
return this.markdown.getMarkdown("COPY_FlagChown");
return "COPY_FlagChown";
case "from":
return this.markdown.getMarkdown("COPY_FlagFrom");
return "COPY_FlagFrom";
}
}
}
Expand All @@ -125,13 +172,13 @@ export class DockerHover {
if (Util.isInsideRange(position, flag.getNameRange())) {
switch (flag.getName()) {
case "interval":
return this.markdown.getMarkdown("HEALTHCHECK_FlagInterval");
return "HEALTHCHECK_FlagInterval";
case "retries":
return this.markdown.getMarkdown("HEALTHCHECK_FlagRetries");
return "HEALTHCHECK_FlagRetries";
case "start-period":
return this.markdown.getMarkdown("HEALTHCHECK_FlagStartPeriod");
return "HEALTHCHECK_FlagStartPeriod";
case "timeout":
return this.markdown.getMarkdown("HEALTHCHECK_FlagTimeout");
return "HEALTHCHECK_FlagTimeout";
}
return null;
}
Expand All @@ -144,6 +191,6 @@ export class DockerHover {
}
break;
}
return undefined;
return null;
}
}
15 changes: 11 additions & 4 deletions src/languageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
* Copyright (c) Remy Suen. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import { DockerfileLanguageService, ILogger } from "./main";
import { DockerfileLanguageService, ILogger, Capabilities } from "./main";
import {
TextDocument, Position, CompletionItem, Range, CodeActionContext, Command, TextDocumentIdentifier, WorkspaceEdit, Location, DocumentHighlight, SymbolInformation, SignatureHelp, DocumentLink, TextEdit, Hover, FormattingOptions, Diagnostic
TextDocument, Position, CompletionItem, Range, CodeActionContext, Command, TextDocumentIdentifier, WorkspaceEdit, Location, DocumentHighlight, SymbolInformation, SignatureHelp, DocumentLink, TextEdit, Hover, FormattingOptions, Diagnostic, MarkupKind
} from "vscode-languageserver-types";
import * as DockerfileUtils from 'dockerfile-utils';
import { DockerAssist } from "./dockerAssist";
Expand All @@ -24,12 +24,19 @@ import { DockerFormatter } from "./dockerFormatter";
export class LanguageService implements DockerfileLanguageService {

private markdownDocumentation = new MarkdownDocumentation();
private plainTextDocumentation = new PlainTextDocumentation();
private logger: ILogger;

private markupKind: MarkupKind[];

public setLogger(logger: ILogger): void {
this.logger = logger;
}

public setCapabilities(capabilities: Capabilities) {
this.markupKind = capabilities && capabilities.hover && capabilities.hover.contentFormat;
}

public computeCodeActions(textDocument: TextDocumentIdentifier, range: Range, context: CodeActionContext): Command[] {
let dockerCommands = new DockerCommands();
return dockerCommands.analyzeDiagnostics(context.diagnostics, textDocument.uri);
Expand Down Expand Up @@ -70,8 +77,8 @@ export class LanguageService implements DockerfileLanguageService {
}

public computeHover(content: string, position: Position): Hover | null {
let dockerHover = new DockerHover(this.markdownDocumentation);
return dockerHover.onHover(content, position);
let dockerHover = new DockerHover(this.markdownDocumentation, this.plainTextDocumentation);
return dockerHover.onHover(content, position, this.markupKind);
}

public computeSymbols(textDocument: TextDocumentIdentifier, content: string): SymbolInformation[] {
Expand Down
16 changes: 15 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import {
TextDocument, Position, CompletionItem, Range, CodeActionContext, Command, TextDocumentIdentifier, WorkspaceEdit, Location, DocumentHighlight, SymbolInformation, SignatureHelp, TextEdit, DocumentLink, Hover, FormattingOptions, Diagnostic
TextDocument, Position, CompletionItem, Range, CodeActionContext, Command, TextDocumentIdentifier, WorkspaceEdit, Location, DocumentHighlight, SymbolInformation, SignatureHelp, TextEdit, DocumentLink, Hover, FormattingOptions, Diagnostic, MarkupKind
} from 'vscode-languageserver-types';
import { ValidatorSettings } from 'dockerfile-utils';
import { LanguageService } from './languageService';
Expand Down Expand Up @@ -38,8 +38,22 @@ export namespace DockerfileLanguageServiceFactory {
}
}

export interface Capabilities {
/**
* Capabilities related to hover requests.
*/
hover?: {
/**
* Describes the content type that should be returned for hovers.
*/
contentFormat?: MarkupKind[];
}
}

export interface DockerfileLanguageService {

setCapabilities(capabilities: Capabilities);

computeCodeActions(textDocument: TextDocumentIdentifier, range: Range, context: CodeActionContext): Command[];

computeCommandEdits(content: string, command: string, args: any[]): TextEdit[];
Expand Down
Loading

0 comments on commit 553117f

Please sign in to comment.