Dockerfile language server

This is a language server for Dockerfiles powered by Node.js
written in TypeScript. To build or to install this language server,
you will need to have Node.js
installed on your computer.
Supported features:
- code actions
- code completion
- definition
- diagnostics
- document highlight
- document symbols
- formatting
- hovers
- rename
Development Instructions
To build and compile this language server, please run the following
commands after cloning the repository with Git.
npm install
npm run build
npm test
If you are planning to change the code, use npm run watch to get the
TypeScript files transpiled on-the-fly as they are modified.
Once the code has finished compiling, you can connect a language server
client to the server via Node IPC, stdio, or sockets.
Installation Instructions
To install this language server onto your computer, please install the
dockerfile-language-server-nodejs npm module.
The -g flag will install the npm module globally onto your computer.
npm install -g dockerfile-language-server-nodejs
After the installation has completed, you can start the language
server with the docker-langserver binary. You should specify
the desired method of communciating with the language server via one
of the three arguments shown below.
docker-langserver --node-ipc
docker-langserver --stdio
docker-langserver --socket=<port>
Settings
Clients may send a workspace/didChangeConfiguration notification to
notify the server of settings changes.
The settings object that will be included with the notification must conform
to the following specification.
interface Settings {
docker: {
languageserver: {
diagnostics?: {
deprecatedMaintainer?: string,
directiveCasing?: string,
instructionCasing?: string,
instructionCmdMultiple?: string,
instructionEntrypointMultiple?: string
instructionHealthcheckMultiple?: string
}
}
}
}
Node IPC
With the child_process API, you can fork() a new Node.js process
running the language server and communicate with it using send(message)
and on('message', ...).
import * as child_process from "child_process";
let lspProcess = child_process.fork("out/src/server.js", [ "--node-ipc" ]);
let messageId = 1;
function send(method: string, params: object) {
let message = {
jsonrpc: "2.0",
id: messageId++,
method: method,
params: params
};
lspProcess.send(message);
}
function initialize() {
send("initialize", {
rootPath: process.cwd(),
processId: process.pid,
capabilities: {
}
});
}
lspProcess.on('message', function (json) {
console.log(json);
});
initialize();
Standard Input/Output
When writing directly to the process's stdin, the additional Content-Length
header must be included. Similarly, when reading from the process's stdout, the
header will be included in the response message.
import * as child_process from "child_process";
let lspProcess = child_process.spawn("node", [ "out/src/server.js", "--stdio" ]);
let messageId = 1;
function send(method: string, params: object) {
let message = {
jsonrpc: "2.0",
id: messageId++,
method: method,
params: params
};
let json = JSON.stringify(message);
let headers = "Content-Length: " + json.length + "\r\n\r\n";
lspProcess.stdin.write(headers, "ASCII");
lspProcess.stdin.write(json, "UTF-8");
}
function initialize() {
send("initialize", {
rootPath: process.cwd(),
processId: process.pid,
capabilities: {
}
});
}
lspProcess.stdout.on("data", (message) => {
console.log(message.toString());
});
initialize();
vscode-jsonrpc
The StreamMessageReader and StreamMessageWriter classes from the
vscode-jsonrpc module will handle the Content-Length headers for you so you
only have to worry about the actual request and response.
import * as child_process from "child_process";
import { StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc";
let lspProcess = child_process.spawn("node", [ "out/src/server.js", "--stdio" ]);
let messageId = 1;
const reader = new StreamMessageReader(lspProcess.stdout);
const writer = new StreamMessageWriter(lspProcess.stdin);
function send(method: string, params: object) {
let message = {
jsonrpc: "2.0",
id: messageId++,
method: method,
params: params
};
writer.write(message);
}
function initialize() {
send("initialize", {
rootPath: process.cwd(),
processId: process.pid,
capabilities: {
}
});
}
reader.listen((data) => {
console.log(data);
})
initialize();