Skip to content

Commit

Permalink
feat: add linter for nix files with nix-instantiate
Browse files Browse the repository at this point in the history
  • Loading branch information
jnoortheen committed Aug 16, 2020
1 parent 6ff2623 commit cbf6abe
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 2 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Adds [nix](https://nixos.org/) language support for VSCode Editor.
+ with the help of [nixpkgs-format](https://github.com/nix-community/nixpkgs-fmt)
* Error Report
+ Using `nix-instantiate` errors reported

![](./images/docs/linting.png)

* Snippets

## Todos
Expand Down
Binary file added images/docs/linting.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@
},
"categories": [
"Programming Languages",
"Formatters",
"Snippets"
],
"keywords": [
"nix",
"IDE"
],
"bugs": {
"url": "https://github.com/jnoortheen/vscode-nix-ide/issues"
},
Expand All @@ -23,7 +28,7 @@
},
"main": "dist/extension.js",
"activationEvents": [
"*"
"onLanguage:nix"
],
"contributes": {
"languages": [
Expand Down
3 changes: 2 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from "vscode";
import { ExtensionContext } from "vscode";
import { formattingProviders } from "./formatter";
import { startLinting } from "./linter";

/**
* Activate this extension.
Expand All @@ -12,7 +13,7 @@ import { formattingProviders } from "./formatter";
* @return A promise for the initialization
*/
export const activate = async (context: ExtensionContext): Promise<any> => {
// startLinting(context);
startLinting(context);

let subs = [
vscode.languages.registerDocumentFormattingEditProvider,
Expand Down
113 changes: 113 additions & 0 deletions src/linter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"use strict";

import * as vscode from "vscode";
import { Diagnostic, ExtensionContext, TextDocument } from "vscode";
import { runInWorkspace } from "./process-runner";

/**
* Whether a given document is saved to disk and in Nix language.
*
* @param document The document to check
* @return Whether the document is a Nix document saved to disk
*/
const isSavedDocument = (document: TextDocument): boolean =>
!document.isDirty &&
0 <
vscode.languages.match(
{
language: "nix",
scheme: "file",
},
document
);

interface LintErrorType {
msg: string;
row: number;
col: number;
}

/**
* Exec pattern against the given text and return an array of all matches.
*
* @param text The output from nix-instantiate stderr
* @return All matches of pattern in text.
*/
const getErrors = (text: string): ReadonlyArray<LintErrorType> => {
const results = [];
const pattern = /^error: (.+), at .+:(\d+):(\d+)$/gm;
// We need to loop through the regexp here, so a let is required
let match = pattern.exec(text);
while (match !== null) {
results.push({
msg: match[1],
row: parseInt(match[2]),
col: parseInt(match[3]),
});
match = pattern.exec(text);
}
return results;
};

/**
* Parse errors from output for a given document.
*
* @param document The document to whose contents errors refer
* @param output The error output from shell.
* @return An array of all diagnostics
*/
const shellOutputToDiagnostics = (
document: TextDocument,
output: string
): ReadonlyArray<Diagnostic> => {
const diagnostics: Array<Diagnostic> = [];
for (const err of getErrors(output)) {
const range = document.validateRange(
new vscode.Range(err.row - 1, err.col - 2, err.row - 1, err.col + 2)
);
const diagnostic = new Diagnostic(range, err.msg);
diagnostic.source = "nix";
diagnostics.push(diagnostic);
}
return diagnostics;
};

/**
* Start linting files.
*
* @param context The extension context
*/
export const startLinting = (context: ExtensionContext): void => {
const diagnostics = vscode.languages.createDiagnosticCollection("nix");
context.subscriptions.push(diagnostics);

const lint = async (document: TextDocument) => {
if (isSavedDocument(document)) {
const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri);
try {
const result = await runInWorkspace(workspaceFolder, [
"nix-instantiate",
"--parse",
document.fileName,
]);
var d = shellOutputToDiagnostics(document, result.stderr);
} catch (error) {
vscode.window.showErrorMessage(error.toString());
diagnostics.delete(document.uri);
return;
}
diagnostics.set(document.uri, d as Diagnostic[]);
}
};

vscode.workspace.onDidOpenTextDocument(lint, null, context.subscriptions);
vscode.workspace.onDidSaveTextDocument(lint, null, context.subscriptions);
vscode.workspace.textDocuments.forEach(lint);

// Remove diagnostics for closed files
vscode.workspace.onDidCloseTextDocument(
(d) => diagnostics.delete(d.uri),
null,
context.subscriptions
);
};

0 comments on commit cbf6abe

Please sign in to comment.