-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add linter for nix files with nix-instantiate
- Loading branch information
1 parent
6ff2623
commit cbf6abe
Showing
5 changed files
with
124 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
}; |