Skip to content

Commit

Permalink
Rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbie Ostermann committed Sep 7, 2022
1 parent 99d81fc commit 6b9ef77
Show file tree
Hide file tree
Showing 15 changed files with 480 additions and 305 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how

## [Unreleased]

- This release rewrites some of the extension code, but should leave the functionality the same.
- There is now an output channel (SQLFluff) that contains additional information about the commands and can help with debugging.
- The `executablePath` and `config` settings can now make use of configuration variables.
- (https:/sqlfluff/vscode-sqlfluff/pull/56) Fix issues with spawned procs, stdin use, env, and output.

## [0.0.7] - 2022-08-30

- [#57](https:/sqlfluff/vscode-sqlfluff/pull/57) Fix automated tests and JSON parsing errors.
Expand Down
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Edit your VS Code `settings.json` file manually or through the user interface.
If you want to manually update the `settings.json` file, open the VS Code command palette and type in `settings.json`. Select `Preferences: Open Settings`. Then, you can add any of the following configuration options to `settings.json`.

```json
"sqlfluff.config": "",
"sqlfluff.config": "${workspaceFolder}/.sqlfluff",
"sqlfluff.dialect": "mysql",
"sqlfluff.excludeRules": ["L009"],
"sqlfluff.executablePath": "sqlfluff",
Expand All @@ -36,6 +36,7 @@ DBT setup requires these settings to lint and format the document.
```json
"sqlfluff.linter.run": "onSave",
"sqlfluff.experimental.format.executeInTerminal": true,
"editor.formatOnSave": false,
```

### Format file
Expand All @@ -44,6 +45,27 @@ By default you will be able use SQLFluff fix your file by formatting. Same as ca

![plugin configuration](./media/format_config.gif)

### VSCode Variables

The `executablePath` and `config` settings can use some VSCode variables.
This is achieved by using the format `${variableName}` in the settings.
Here are a few useful ones.

- workspaceFolder: the path of the folder opened in VS Code
- workspaceFolderBasename: the last portion of the path of the folder opened in VS Code
- fileDirname: the current opened file's dirname

And here are a few that are probably useless.

- file: the current opened file
- relativeFile: the current opened file relative to workspaceFolder
- fileBasename: the last portion of the path to the file
- fileBasenameNoExtension: the last portion of the path to the file with no extension
- fileExtname: the current opened file's extension
- lineNumber: the current selected line number in the active file
- selectedText: the current selected text in the active file
- execPath: the path to the running VS Code executable

## Credits / Links

- [dorzey](https:/sqlfluff/vscode-sqlfluff)
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vscode-sqlfluff",
"displayName": "sqlfluff",
"version": "0.0.7",
"version": "0.0.8",
"description": "A linter and auto-formatter for SQLfluff, a popular linting tool for SQL and dbt.",
"publisher": "dorzey",
"icon": "images/icon.png",
Expand Down Expand Up @@ -95,7 +95,7 @@
"sqlfluff.workingDirectory": {
"type": "string",
"default": "",
"markdownDescription": "Set the working directory for the `sqlfluff lint` and `sqlfluff fix` commands. This defaults to the root path of the workspace."
"markdownDescription": "Set the working directory for the `sqlfluff lint` and `sqlfluff fix` commands. This defaults to the root path of the workspace. This setting should only be changed if absolutely necessary."
},
"sqlfluff.format.enabled": {
"type": "boolean",
Expand Down
23 changes: 11 additions & 12 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import * as vscode from "vscode";

import { EXCLUDE_RULE, SQLFLuffDocumentFormattingEditProvider, SQLFluffLinterProvider, SQLFluffQuickFix } from "./features/sqlFluffLinter";
import { EXCLUDE_RULE, FormattingEditProvider, LinterProvider, QuickFixProvider } from "./features/linter";

export const activate = (context: vscode.ExtensionContext) => {
new SQLFluffLinterProvider().activate(context.subscriptions);
new LinterProvider().activate(context.subscriptions);

vscode.languages.registerDocumentFormattingEditProvider("sql", new SQLFLuffDocumentFormattingEditProvider().activate());
vscode.languages.registerDocumentFormattingEditProvider("sql-bigquery", new SQLFLuffDocumentFormattingEditProvider().activate());
vscode.languages.registerDocumentFormattingEditProvider("jinja-sql", new SQLFLuffDocumentFormattingEditProvider().activate());
vscode.languages.registerDocumentFormattingEditProvider("sql", new FormattingEditProvider().activate());
vscode.languages.registerDocumentFormattingEditProvider("sql-bigquery", new FormattingEditProvider().activate());
vscode.languages.registerDocumentFormattingEditProvider("jinja-sql", new FormattingEditProvider().activate());

context.subscriptions.push(
vscode.languages.registerCodeActionsProvider("sql", new SQLFluffQuickFix(), {
providedCodeActionKinds: SQLFluffQuickFix.providedCodeActionKind
vscode.languages.registerCodeActionsProvider("sql", new QuickFixProvider(), {
providedCodeActionKinds: QuickFixProvider.providedCodeActionKind
}),
vscode.languages.registerCodeActionsProvider("sql-bigquery", new SQLFluffQuickFix(), {
providedCodeActionKinds: SQLFluffQuickFix.providedCodeActionKind
vscode.languages.registerCodeActionsProvider("sql-bigquery", new QuickFixProvider(), {
providedCodeActionKinds: QuickFixProvider.providedCodeActionKind
}),
vscode.languages.registerCodeActionsProvider("jinja-sql", new SQLFluffQuickFix(), {
providedCodeActionKinds: SQLFluffQuickFix.providedCodeActionKind
vscode.languages.registerCodeActionsProvider("jinja-sql", new QuickFixProvider(), {
providedCodeActionKinds: QuickFixProvider.providedCodeActionKind
})
);

Expand All @@ -41,4 +41,3 @@ function toggleRule(rule: string) {

return configuration.update("excludeRules", excludeRulesArray, vscode.ConfigurationTarget.Global);
}

File renamed without changes.
85 changes: 69 additions & 16 deletions src/features/Helpers/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import * as path from "path";
import * as vscode from "vscode";

import Variables from "./types/variables";
import { normalize, Utilities } from "./utilities";

export class Configuration {
public static executablePath(): string {
return vscode.workspace
let executablePath: string = vscode.workspace
.getConfiguration("sqlfluff")
.get("executablePath");
.get("executablePath") || "sqlfluff";

executablePath = Utilities.interpolateString(executablePath, Configuration.variables());

return executablePath;
}

private static config(): string[] {
const config: string = vscode.workspace
let config: string = vscode.workspace
.getConfiguration("sqlfluff")
.get("config");
.get("config") || "";

config = Utilities.interpolateString(config, Configuration.variables());

return config ? ["--config", config] : [];
}
Expand Down Expand Up @@ -57,10 +67,11 @@ export class Configuration {
return rules ? ["--rules", rules] : [];
}

public static workingDirectory(): string {
return vscode.workspace
public static workingDirectory(rootPath: string): string {
const workingDirectory: string = vscode.workspace
.getConfiguration("sqlfluff")
.get("workingDirectory");
return workingDirectory ? workingDirectory : rootPath;
}

public static formatEnabled(): boolean {
Expand All @@ -81,20 +92,12 @@ export class Configuration {
.get("run");
}

public static lintBufferArguments(): string[] {
return ["lint", "--format", "json", "-"];
}

public static lintFileArguments(): string[] {
return ["lint", "--format", "json"];
}

public static formatBufferArguments(): string[] {
return ["fix", "--force", "-"];
return ["--format", "json"];
}

public static formatFileArguments(): string[] {
return ["fix", "--force"];
return ["--force"];
}

public static extraArguments(): string[] {
Expand All @@ -109,4 +112,54 @@ export class Configuration {

return extraArguments;
}

/**
* @returns The variables for a terminal
*/
static variables(): Variables {
const rootPath = normalize(vscode.workspace.workspaceFolders[0].uri.fsPath);

const editor = vscode.window.activeTextEditor;
const fileName = editor ? normalize(editor.document.fileName) : null;

const vars: Variables = {
// - the path of the folder opened in VS Code
workspaceFolder: rootPath,

// - the last portion of the path of the folder opened in VS Code
workspaceFolderBasename: (rootPath) ? path.basename(rootPath) : null,

// - the current opened file
file: fileName,

// - the current opened file relative to workspaceFolder
relativeFile: (vscode.window.activeTextEditor && rootPath) ? normalize(path.relative(
rootPath,
fileName
)) : null,

// - the last portion of the path to the file
fileBasename: fileName ? path.basename(fileName) : null,

// - the last portion of the path to the file with no file extension
fileBasenameNoExtension: fileName ? path.parse(path.basename(fileName)).name : null,

// - the current opened file's dirname
fileDirname: fileName ? path.dirname(fileName) : null,

// - the current opened file's extension
fileExtname: fileName ? path.parse(path.basename(fileName)).ext : null,

// - the current selected line number in the active file
lineNumber: (editor) ? editor.selection.active.line + 1 : null,

// - the current selected text in the active file
selectedText: (editor) ? editor.document.getText(editor.selection) : null,

// - the path to the running VS Code executable
execPath: process.execPath
};

return vars;
}
}
File renamed without changes.
153 changes: 153 additions & 0 deletions src/features/Helpers/sqlfluff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import * as childProcess from "child_process";
import path = require("path");
import { StringDecoder } from "string_decoder";
import * as vscode from "vscode";

import { Configuration } from "./configuration";
import { LineDecoder } from "./lineDecoder";
import { normalize, Utilities } from "./utilities";

export enum SQLFluffCommand {
LINT = "lint",
FIX = "fix",
}

export interface SQLFluffCommandOutput {
succeeded: boolean;
lines: string[];
}

export interface SQLFluffCommandOptions {
targetFileFullPath?: string;
fileContents?: string;
}

export class SQLFluff {
static process: childProcess.ChildProcess;

public static run(cwd: string, command: SQLFluffCommand, args: string[], options: SQLFluffCommandOptions): Promise<SQLFluffCommandOutput> {
if (!options.fileContents && !options.targetFileFullPath) {
throw new Error("You must supply either a target file path or the file contents to scan");
}

if (SQLFluff.process) {
SQLFluff.process.kill("SIGKILL");
SQLFluff.process = undefined;
}

const normalizedCwd = normalize(cwd);

return new Promise<SQLFluffCommandOutput>((resolve) => {
const stdoutLint = new LineDecoder();
const stdoutFix = [];
let stdoutLines: string[];
const stderrLines = [];

const onStdoutDataEvent = (data: Buffer) => {
if (command === SQLFluffCommand.LINT) {
stdoutLint.write(data);
} else {
stdoutFix.push(data);
}
};

const onStderrDataEvent = (data: Buffer) => {
stderrLines.push(data.toString("utf8"));
};

const onCloseEvent = (code: number, signal: any) => {
Utilities.outputChannel.appendLine(`Received close event, code ${code} signal ${signal}`);
Utilities.outputChannel.appendLine("Raw stdout output:");

Utilities.appendHyphenatedLine();
if (command === SQLFluffCommand.LINT) {
stdoutLint.end();
stdoutLines = stdoutLint.getLines();
Utilities.outputChannel.appendLine(stdoutLines.join("\n"));
} else {
const encoding: BufferEncoding = "utf8";
const stringDecoder = new StringDecoder(encoding);

const stdoutAllLines = stdoutFix.reduce((response, buffer) => {
response += stringDecoder.write(buffer);
return response;
}, "");
stdoutLines = [stdoutAllLines];
Utilities.outputChannel.appendLine(stdoutLines.join("\n"));
}
Utilities.appendHyphenatedLine();

if (stderrLines.length > 0) {
Utilities.outputChannel.appendLine("Raw stderr output:");
Utilities.appendHyphenatedLine();
Utilities.outputChannel.appendLine(stderrLines.join("\n"));
Utilities.appendHyphenatedLine();
}

if (stderrLines?.length > 0) {
vscode.window.showErrorMessage(stderrLines.join("\n"));
}

return resolve({
succeeded: code === 0 || code === 65, // 0 = all good, 65 = lint passed, but found errors
lines: stdoutLines,
});
};

const shouldUseStdin = !!options.fileContents?.length;

const finalArgs = [
command,
...args,
...Configuration.extraArguments(),
];

if (shouldUseStdin) {
Utilities.outputChannel.appendLine("Reading from stdin, not file, input may be dirty/partial");
finalArgs.push("-");
} else {
Utilities.outputChannel.appendLine("Reading from file, not stdin");
// we want to use relative path to the file so intermediate sqlfluff config files can be found
const normalizedTargetFileFullPath = normalize(options.targetFileFullPath);
const targetFileRelativePath = path.relative(normalizedCwd, normalizedTargetFileFullPath);
finalArgs.push(targetFileRelativePath);
}

Utilities.outputChannel.appendLine("\n--------------------Executing Command--------------------\n");
Utilities.outputChannel.appendLine(Configuration.executablePath() + " " + finalArgs.join(" "));
Utilities.appendHyphenatedLine();

SQLFluff.process = childProcess.spawn(Configuration.executablePath(), finalArgs, {
cwd: normalizedCwd,
});

if (SQLFluff.process.pid) {
SQLFluff.process.stdout.on("data", onStdoutDataEvent);
SQLFluff.process.stderr.on("data", onStderrDataEvent);
SQLFluff.process.on("close", onCloseEvent);
if (shouldUseStdin) {
SQLFluff.process.stdin.write(options.fileContents);
SQLFluff.process.stdin.end();
}
}

SQLFluff.process.on("message", (message) => {
Utilities.outputChannel.appendLine("Received message from child process");
Utilities.outputChannel.appendLine(message.toString());
});

SQLFluff.process.on("error", (error: Error) => {
Utilities.outputChannel.appendLine("Child process threw error");
Utilities.outputChannel.appendLine(error.toString());
let { message } = error;

if ((error as any).code === "ENOENT") {
message = "The sqlfluff executable was not found. Use the 'Executable Path' setting to configure the location of the executable, or add it to your PATH.";
}

vscode.window.showErrorMessage(message);
resolve({ succeeded: false, lines: [] });
});
});
}
}
Loading

0 comments on commit 6b9ef77

Please sign in to comment.