-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial linter with simple quick fix action to open link on salt-lint wiki. Signed-off-by: Roald Nefs <[email protected]>
- Loading branch information
Showing
7 changed files
with
433 additions
and
140 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
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 |
---|---|---|
@@ -1,65 +1,11 @@ | ||
# salt-lint README | ||
|
||
This is the README for your extension "salt-lint". After writing up a brief description, we recommend including the following sections. | ||
|
||
## Features | ||
|
||
Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file. | ||
|
||
For example if there is an image subfolder under your extension project workspace: | ||
|
||
\!\[feature X\]\(images/feature-x.png\) | ||
|
||
> Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow. | ||
# vscode-salt-lint | ||
|
||
## Requirements | ||
|
||
If you have any requirements or dependencies, add a section describing those and how to install and configure them. | ||
|
||
## Extension Settings | ||
|
||
Include if your extension adds any VS Code settings through the `contributes.configuration` extension point. | ||
|
||
For example: | ||
|
||
This extension contributes the following settings: | ||
|
||
* `myExtension.enable`: enable/disable this extension | ||
* `myExtension.thing`: set to `blah` to do something | ||
|
||
## Known Issues | ||
|
||
Calling out known issues can help limit users opening duplicate issues against your extension. | ||
|
||
## Release Notes | ||
|
||
Users appreciate release notes as you update your extension. | ||
|
||
### 1.0.0 | ||
|
||
Initial release of ... | ||
|
||
### 1.0.1 | ||
|
||
Fixed issue #. | ||
|
||
### 1.1.0 | ||
|
||
Added features X, Y, and Z. | ||
|
||
----------------------------------------------------------------------------------------------------------- | ||
|
||
## Working with Markdown | ||
|
||
**Note:** You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts: | ||
|
||
* Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux) | ||
* Toggle preview (`Shift+CMD+V` on macOS or `Shift+Ctrl+V` on Windows and Linux) | ||
* Press `Ctrl+Space` (Windows, Linux) or `Cmd+Space` (macOS) to see a list of Markdown snippets | ||
|
||
### For more information | ||
1. Ensure `salt-lint` is installed (`v0.1.0` or newer). | ||
2. Run [`Install Extension`](https://code.visualstudio.com/docs/editor/extension-gallery#_install-an-extension) command from [Command Palette](https://code.visualstudio.com/Docs/editor/codebasics#_command-palette). | ||
3. Search and choose `salt-lint`. | ||
|
||
* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown) | ||
* [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/) | ||
## Acknowledgements | ||
|
||
**Enjoy!** | ||
This extension is based on [timonwong's ShellCheck Linter](https://github.com/timonwong/vscode-shellcheck). |
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 |
---|---|---|
@@ -1,27 +1,9 @@ | ||
// The module 'vscode' contains the VS Code extensibility API | ||
// Import the module and reference it with the alias vscode in your code below | ||
import * as vscode from 'vscode'; | ||
import SaltLintProvider from './linter'; | ||
|
||
// this method is called when your extension is activated | ||
// your extension is activated the very first time the command is executed | ||
export function activate(context: vscode.ExtensionContext) { | ||
|
||
// Use the console to output diagnostic information (console.log) and errors (console.error) | ||
// This line of code will only be executed once when your extension is activated | ||
console.log('Congratulations, your extension "salt-lint" is now active!'); | ||
|
||
// The command has been defined in the package.json file | ||
// Now provide the implementation of the command with registerCommand | ||
// The commandId parameter must match the command field in package.json | ||
let disposable = vscode.commands.registerCommand('extension.helloWorld', () => { | ||
// The code you place here will be executed every time your command is executed | ||
|
||
// Display a message box to the user | ||
vscode.window.showInformationMessage('Hello World!'); | ||
}); | ||
|
||
context.subscriptions.push(disposable); | ||
const linter = new SaltLintProvider(context); | ||
context.subscriptions.push(linter); | ||
} | ||
|
||
// this method is called when your extension is deactivated | ||
export function deactivate() {} | ||
export function deactivate(): void {} |
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,202 @@ | ||
import * as path from 'path'; | ||
import * as vscode from 'vscode'; | ||
import * as cp from 'child_process'; | ||
import { ThrottledDelayer } from './utils/async'; | ||
|
||
namespace CommandIds { | ||
export const runLint: string = 'salt-lint.runLint'; | ||
export const openRuleDoc: string = 'salt-lint.openRuleDoc'; | ||
} | ||
|
||
interface SaltLintItem { | ||
id: string; | ||
message: string; | ||
filename: string; | ||
linenumber: number; | ||
line: string; | ||
severity: string; | ||
} | ||
|
||
function makeDiagnostic(textDocument: vscode.TextDocument, item: SaltLintItem): vscode.Diagnostic { | ||
let startPos = new vscode.Position(item.linenumber - 1, item.line.search(/\S/)); | ||
let endPos = new vscode.Position(item.linenumber - 1, item.line.length); | ||
|
||
const range = new vscode.Range(startPos, endPos); | ||
// const severity = levelToDiagnosticSeverity(item.level); | ||
const severity = vscode.DiagnosticSeverity.Warning; | ||
const diagnostic = new vscode.Diagnostic(range, item.message, severity); | ||
diagnostic.source = 'salt-lint'; | ||
diagnostic.code = item.id; | ||
// diagnostic.tags = scCodeToDiagnosticTags(item.code); | ||
return diagnostic; | ||
} | ||
|
||
export default class SaltLintProvider implements vscode.CodeActionProvider { | ||
|
||
public static LANGUAGE_ID = 'saltstack'; | ||
private channel: vscode.OutputChannel; | ||
private executableNotFound: boolean; | ||
private delayers!: { [key: string]: ThrottledDelayer<void> }; | ||
private readonly diagnosticCollection: vscode.DiagnosticCollection; | ||
|
||
public static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix]; | ||
|
||
constructor(private readonly context: vscode.ExtensionContext) { | ||
this.channel = vscode.window.createOutputChannel('salt-lint'); | ||
this.executableNotFound = false; | ||
this.diagnosticCollection = vscode.languages.createDiagnosticCollection(); | ||
|
||
// code actions | ||
context.subscriptions.push( | ||
vscode.languages.registerCodeActionsProvider('saltstack', this, { | ||
providedCodeActionKinds: SaltLintProvider.providedCodeActionKinds, | ||
}), | ||
); | ||
|
||
// commands | ||
context.subscriptions.push( | ||
vscode.commands.registerCommand(CommandIds.openRuleDoc, async (url: string) => { | ||
return await vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(url)); | ||
}), | ||
vscode.commands.registerTextEditorCommand(CommandIds.runLint, async (editor) => { | ||
return await this.triggerLint(editor.document); | ||
}), | ||
); | ||
|
||
this.delayers = Object.create(null); | ||
|
||
// event handlers | ||
vscode.workspace.onDidOpenTextDocument(this.triggerLint, this, context.subscriptions); | ||
vscode.workspace.onDidCloseTextDocument((textDocument) => { | ||
this.diagnosticCollection.delete(textDocument.uri); | ||
delete this.delayers[textDocument.uri.toString()]; | ||
}, null, context.subscriptions); | ||
|
||
// salt-lint all open SaltStack documents | ||
vscode.workspace.textDocuments.forEach(this.triggerLint, this); | ||
} | ||
|
||
public dispose(): void { | ||
this.diagnosticCollection.clear(); | ||
this.diagnosticCollection.dispose(); | ||
this.channel.dispose(); | ||
} | ||
|
||
public provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): vscode.ProviderResult<(vscode.Command | vscode.CodeAction)[]> { | ||
const actions: vscode.CodeAction[] = []; | ||
for (const diagnostic of context.diagnostics) { | ||
if (diagnostic.source !== 'salt-lint') { | ||
continue; | ||
} | ||
|
||
if (typeof diagnostic.code === 'string') { | ||
const ruleId = diagnostic.code; | ||
const title = `Show salt-lint Wiki for ${ruleId}`; | ||
const action = new vscode.CodeAction(title, vscode.CodeActionKind.QuickFix); | ||
action.command = { | ||
title: title, | ||
command: CommandIds.openRuleDoc, | ||
arguments: [`https://github.com/warpnet/salt-lint/wiki/${ruleId}`], | ||
}; | ||
actions.push(action); | ||
} | ||
} | ||
|
||
return actions; | ||
} | ||
|
||
private isAllowedTextDocument(textDocument: vscode.TextDocument): boolean { | ||
if (textDocument.languageId === SaltLintProvider.LANGUAGE_ID) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private triggerLint(textDocument: vscode.TextDocument): void { | ||
this.channel.appendLine(`[DEBUG] tiggerLint`); | ||
if (this.executableNotFound || !this.isAllowedTextDocument(textDocument)) { | ||
return; | ||
} | ||
|
||
const key = textDocument.uri.toString(); | ||
let delayer = this.delayers[key]; | ||
if (!delayer) { | ||
delayer = new ThrottledDelayer<void>(250); | ||
this.delayers[key] = delayer; | ||
} | ||
|
||
delayer.trigger(() => this.runLint(textDocument)); | ||
} | ||
|
||
private runLint(textDocument: vscode.TextDocument): Promise<void> { | ||
return new Promise<void>((resolve, reject) => { | ||
const executable = 'salt-lint'; | ||
let args = ['--json']; | ||
|
||
let cwd: string | undefined; | ||
cwd = textDocument.isUntitled ? vscode.workspace.rootPath : path.dirname(textDocument.fileName); | ||
|
||
const options = { cwd: cwd }; | ||
const childProcess = cp.spawn(executable, args, options); | ||
childProcess.on('error', (error: Error) => { | ||
if (!this.executableNotFound) { | ||
this.showSaltLintError(error, executable); | ||
} | ||
|
||
this.executableNotFound = true; | ||
path.resolve(); | ||
return; | ||
}); | ||
|
||
if (childProcess.pid) { | ||
childProcess.stdout.setEncoding('utf-8'); | ||
|
||
let state = textDocument.getText(); | ||
childProcess.stdin.write(state); | ||
childProcess.stdin.end(); | ||
|
||
const output: string[] = []; | ||
childProcess.stdout | ||
.on('data', (data: Buffer) => { | ||
output.push(data.toString()); | ||
}) | ||
.on('end', () => { | ||
let diagnostics: vscode.Diagnostic[] = []; | ||
if (output.length) { | ||
diagnostics = this.parseSaltLintResult(textDocument, output.join('')); | ||
} | ||
|
||
this.diagnosticCollection.set(textDocument.uri, diagnostics); | ||
resolve(); | ||
}); | ||
} else { | ||
resolve(); | ||
} | ||
}); | ||
} | ||
|
||
private parseSaltLintResult(textDocument: vscode.TextDocument, s: string): vscode.Diagnostic[] { | ||
const diagnostics: vscode.Diagnostic[] = []; | ||
|
||
const items = <SaltLintItem[]>JSON.parse(s); | ||
for (const item of items) { | ||
if (item) { | ||
diagnostics.push(makeDiagnostic(textDocument, item)); | ||
} | ||
} | ||
|
||
return diagnostics; | ||
} | ||
|
||
private showSaltLintError(error: any, executable: string): void { | ||
let message: string; | ||
if (error.code === 'ENOENT') { | ||
message = `Cannot salt-lint the Salt State file. The salt-lint program was not found.`; | ||
} else { | ||
message = error.message ? error.message : `Failed to run salt-lint using path: ${executable}. Reason is unknown.`; | ||
} | ||
|
||
vscode.window.showInformationMessage(message); | ||
} | ||
} |
Oops, something went wrong.