diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..075cc45
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+indent_style = space
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.gitignore b/.gitignore
index 3c84d65..7fe900c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,4 @@ package-lock.json
*.vsix
.DS_Store
/vscode.d.ts
-/vscode.proposed.d.ts
\ No newline at end of file
+/vscode.proposed.d.ts
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 3ac9aeb..c0a2258 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,7 +1,5 @@
{
- // See http://go.microsoft.com/fwlink/?LinkId=827846
- // for the documentation about the extensions.json format
- "recommendations": [
- "dbaeumer.vscode-eslint"
- ]
+ // See http://go.microsoft.com/fwlink/?LinkId=827846
+ // for the documentation about the extensions.json format
+ "recommendations": ["dbaeumer.vscode-eslint"]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 30bf8c2..ffeaf91 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,11 +1,11 @@
// Place your settings in this file to overwrite default and user settings.
{
- "files.exclude": {
- "out": false // set this to true to hide the "out" folder with the compiled JS files
- },
- "search.exclude": {
- "out": true // set this to false to include "out" folder in search results
- },
- // Turn off tsc task auto detection since we have the necessary tasks as npm scripts
- "typescript.tsc.autoDetect": "off"
-}
\ No newline at end of file
+ "files.exclude": {
+ "out": false // set this to true to hide the "out" folder with the compiled JS files
+ },
+ "search.exclude": {
+ "out": true // set this to false to include "out" folder in search results
+ },
+ // Turn off tsc task auto detection since we have the necessary tasks as npm scripts
+ "typescript.tsc.autoDetect": "off"
+}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..2568d6c
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,129 @@
+
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement via email to
+`admins`@`sqlfluff.com`. All complaints will be reviewed and investigated promptly
+and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..b2da736
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,11 @@
+# vscode-sqlfluff - Contributing
+
+:star2: **First** - thanks for being interested in improving vscode-sqlfluff! :smiley:
+
+:star2: **Second** - please read and familiarise yourself with both the content of this guide and also our [code of conduct](CODE_OF_CONDUCT.md).
+
+:star2: **Third** - the best way to get started contributing, is to use the tool in anger and then to submit bugs and features through github.
+
+:star2: **Fourth** - making sure that our documentation is up to date and useful for new users is really important. If you're a new user, you're in precisely the best position to do this. Pull requests are always welcome with documentationimprovements.
+
+:star2: **Fifth** - if you're so inclined - pull requests on the codebase are always welcome. Just fork the repo and create your branch from `master`. Please document your contributions in the `CHANGELOG.md`.
\ No newline at end of file
diff --git a/README.md b/README.md
index 998d054..542e6e7 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,6 @@
# vscode-sqlfluff
-
-
-
-
-
+![.github/workflows/ci.yml](https://github.com/dorzey/vscode-sqlfluff/workflows/.github/workflows/ci.yml/badge.svg)
A linter and auto-formatter for [SQLFluff](https://github.com/alanmcruickshank/sqlfluff), a popular linting tool for SQL and dbt.
diff --git a/src/extension.ts b/src/extension.ts
index 657870e..fd781a6 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -3,36 +3,36 @@ import * as vscode from "vscode";
import { EXCLUDE_RULE, SQLFLuffDocumentFormattingEditProvider, SQLFluffLinterProvider, SQLFluffQuickFix } from "./features/sqlFluffLinter";
export const activate = (context: vscode.ExtensionContext) => {
- new SQLFluffLinterProvider().activate(context.subscriptions);
+ new SQLFluffLinterProvider().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 SQLFLuffDocumentFormattingEditProvider().activate());
+ vscode.languages.registerDocumentFormattingEditProvider("sql-bigquery", new SQLFLuffDocumentFormattingEditProvider().activate());
+ vscode.languages.registerDocumentFormattingEditProvider("jinja-sql", new SQLFLuffDocumentFormattingEditProvider().activate());
- context.subscriptions.push(
- vscode.languages.registerCodeActionsProvider("sql", new SQLFluffQuickFix(), {
- providedCodeActionKinds: SQLFluffQuickFix.providedCodeActionKind
- })
- );
+ context.subscriptions.push(
+ vscode.languages.registerCodeActionsProvider("sql", new SQLFluffQuickFix(), {
+ providedCodeActionKinds: SQLFluffQuickFix.providedCodeActionKind
+ })
+ );
- context.subscriptions.push(vscode.commands.registerCommand(EXCLUDE_RULE, toggleRule));
+ context.subscriptions.push(vscode.commands.registerCommand(EXCLUDE_RULE, toggleRule));
};
// eslint-disable-next-line @typescript-eslint/no-empty-function
export const deactivate: any = () => { };
function toggleRule(rule: string) {
- const configuration = vscode.workspace.getConfiguration("sqlfluff");
- const excludeRulesArray: string[] = configuration.get("excludeRules");
+ const configuration = vscode.workspace.getConfiguration("sqlfluff");
+ const excludeRulesArray: string[] = configuration.get("excludeRules");
- if (!excludeRulesArray.includes(rule)) {
- excludeRulesArray.push(rule);
- }
+ if (!excludeRulesArray.includes(rule)) {
+ excludeRulesArray.push(rule);
+ }
- excludeRulesArray.sort((x, y) => {
- return parseInt(x.substring(1)) - parseInt(y.substring(1));
- });
+ excludeRulesArray.sort((x, y) => {
+ return parseInt(x.substring(1)) - parseInt(y.substring(1));
+ });
- return configuration.update("excludeRules", excludeRulesArray, vscode.ConfigurationTarget.Global);
+ return configuration.update("excludeRules", excludeRulesArray, vscode.ConfigurationTarget.Global);
}
diff --git a/src/features/formatter/process.ts b/src/features/formatter/process.ts
index ed71206..bf54207 100644
--- a/src/features/formatter/process.ts
+++ b/src/features/formatter/process.ts
@@ -7,7 +7,7 @@ export default class Process {
/**
* Creates a child process to execute a command.
- *
+ *
* @param command - The command to run.
* @param args - A list of string arguments.
* @param options - The working directory and environment variables.
diff --git a/src/features/sqlFluffLinter.ts b/src/features/sqlFluffLinter.ts
index 2ee6047..dc43ead 100644
--- a/src/features/sqlFluffLinter.ts
+++ b/src/features/sqlFluffLinter.ts
@@ -6,102 +6,102 @@ import { DocumentFormattingEditProvider } from "./formatter/formattingProvider";
import { Linter, LintingProvider } from "./utils/lintingProvider";
export class SQLFluffLinterProvider implements Linter {
- public languageId = ["sql", "jinja-sql", "sql-bigquery"];
+ public languageId = ["sql", "jinja-sql", "sql-bigquery"];
- public activate(subscriptions: Disposable[]) {
- const provider = new LintingProvider(this);
- provider.activate(subscriptions);
- }
+ public activate(subscriptions: Disposable[]) {
+ const provider = new LintingProvider(this);
+ provider.activate(subscriptions);
+ }
- public process(lines: string[]): Diagnostic[] {
- const diagnostics: Diagnostic[] = [];
- lines.forEach((line) => {
- let filePaths: Array;
- try {
- filePaths = JSON.parse(line);
- } catch (e) {
- // JSON.parse may fail if sqlfluff compilation prints non-JSON formatted messages
- console.warn(e);
- }
+ public process(lines: string[]): Diagnostic[] {
+ const diagnostics: Diagnostic[] = [];
+ lines.forEach((line) => {
+ let filePaths: Array;
+ try {
+ filePaths = JSON.parse(line);
+ } catch (e) {
+ // JSON.parse may fail if sqlfluff compilation prints non-JSON formatted messages
+ console.warn(e);
+ }
- if (filePaths) {
- filePaths.forEach((filePath: FilePath) => {
- filePath.violations.forEach((violation: Violation) => {
- const diagnostic = new Diagnostic(
- new Range(
- violation.line_no - 1,
- violation.line_pos,
- violation.line_no - 1,
- violation.line_pos
- ),
- violation.description,
- DiagnosticSeverity.Error,
- );
- diagnostic.code = violation.code;
- diagnostic.source = "sqlfluff";
- diagnostics.push(diagnostic);
- });
- });
- }
- });
+ if (filePaths) {
+ filePaths.forEach((filePath: FilePath) => {
+ filePath.violations.forEach((violation: Violation) => {
+ const diagnostic = new Diagnostic(
+ new Range(
+ violation.line_no - 1,
+ violation.line_pos,
+ violation.line_no - 1,
+ violation.line_pos
+ ),
+ violation.description,
+ DiagnosticSeverity.Error,
+ );
+ diagnostic.code = violation.code;
+ diagnostic.source = "sqlfluff";
+ diagnostics.push(diagnostic);
+ });
+ });
+ }
+ });
- return diagnostics;
- }
+ return diagnostics;
+ }
}
interface FilePath {
- violations: Array;
+ violations: Array;
}
export class SQLFLuffDocumentFormattingEditProvider {
- activate(): vscode.DocumentFormattingEditProvider {
- return new DocumentFormattingEditProvider();
- }
+ activate(): vscode.DocumentFormattingEditProvider {
+ return new DocumentFormattingEditProvider();
+ }
}
interface Violation {
- line_no: number,
- line_pos: number,
- description: string,
- code: string,
+ line_no: number,
+ line_pos: number,
+ description: string,
+ code: string,
}
export class SQLFluffQuickFix implements vscode.CodeActionProvider {
- public static readonly providedCodeActionKind = [
- vscode.CodeActionKind.QuickFix,
- ];
+ public static readonly providedCodeActionKind = [
+ vscode.CodeActionKind.QuickFix,
+ ];
- provideCodeActions(
- document: vscode.TextDocument,
- range: vscode.Range | vscode.Selection,
- context: vscode.CodeActionContext,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- token: vscode.CancellationToken
- ): vscode.CodeAction[] {
- // for each diagnostic entry that has the matching `code`, create a code action command
- return context.diagnostics.map((diagnostic) =>
- this.createCodeAction(diagnostic)
- );
- }
+ provideCodeActions(
+ document: vscode.TextDocument,
+ range: vscode.Range | vscode.Selection,
+ context: vscode.CodeActionContext,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ token: vscode.CancellationToken
+ ): vscode.CodeAction[] {
+ // for each diagnostic entry that has the matching `code`, create a code action command
+ return context.diagnostics.map((diagnostic) =>
+ this.createCodeAction(diagnostic)
+ );
+ }
- private createCodeAction(
- diagnostic: vscode.Diagnostic
- ): vscode.CodeAction {
- const action = new vscode.CodeAction(
- `Exclude Rule ${diagnostic.code}`,
- vscode.CodeActionKind.QuickFix
- );
- action.command = {
- command: EXCLUDE_RULE,
- title: `Exclude Rule ${diagnostic.code}`,
- tooltip: `This will exclude the rule ${diagnostic.code} in settings.json`,
- arguments: [diagnostic.code]
- };
- action.diagnostics = [diagnostic];
- action.isPreferred = true;
+ private createCodeAction(
+ diagnostic: vscode.Diagnostic
+ ): vscode.CodeAction {
+ const action = new vscode.CodeAction(
+ `Exclude Rule ${diagnostic.code}`,
+ vscode.CodeActionKind.QuickFix
+ );
+ action.command = {
+ command: EXCLUDE_RULE,
+ title: `Exclude Rule ${diagnostic.code}`,
+ tooltip: `This will exclude the rule ${diagnostic.code} in settings.json`,
+ arguments: [diagnostic.code]
+ };
+ action.diagnostics = [diagnostic];
+ action.isPreferred = true;
- return action;
- }
+ return action;
+ }
}
export const EXCLUDE_RULE = "sqlfluff.quickfix.command";
diff --git a/src/features/utils/async.ts b/src/features/utils/async.ts
index 28d8c57..71cdedd 100644
--- a/src/features/utils/async.ts
+++ b/src/features/utils/async.ts
@@ -1,125 +1,125 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
export interface ITask {
- (): T;
+ (): T;
}
/**
* A helper to prevent accumulation of sequential async tasks.
*/
export class Throttler {
- private activePromise!: Promise;
- private queuedPromise!: Promise;
- private queuedPromiseFactory!: ITask>;
-
- public queue(promiseFactory: ITask>): Promise {
- if (this.activePromise) {
- this.queuedPromiseFactory = promiseFactory;
-
- if (!this.queuedPromise) {
- const onComplete = () => {
- // @ts-ignore
- this.queuedPromise = null;
-
- const result = this.queue(this.queuedPromiseFactory);
- // @ts-ignore
- this.queuedPromiseFactory = null;
-
- return result;
- };
-
- this.queuedPromise = new Promise((resolve, reject) => {
- this.activePromise.then(onComplete, onComplete).then(resolve);
- });
- }
-
- return new Promise((resolve, reject) => {
- this.queuedPromise.then(resolve, reject);
- });
- }
-
- this.activePromise = promiseFactory();
-
- return new Promise((resolve, reject) => {
- this.activePromise.then((result: T) => {
- // @ts-ignore
- this.activePromise = null;
- resolve(result);
- }, (err: any) => {
- // @ts-ignore
- this.activePromise = null;
- reject(err);
- });
- });
- }
+ private activePromise!: Promise;
+ private queuedPromise!: Promise;
+ private queuedPromiseFactory!: ITask>;
+
+ public queue(promiseFactory: ITask>): Promise {
+ if (this.activePromise) {
+ this.queuedPromiseFactory = promiseFactory;
+
+ if (!this.queuedPromise) {
+ const onComplete = () => {
+ // @ts-ignore
+ this.queuedPromise = null;
+
+ const result = this.queue(this.queuedPromiseFactory);
+ // @ts-ignore
+ this.queuedPromiseFactory = null;
+
+ return result;
+ };
+
+ this.queuedPromise = new Promise((resolve, reject) => {
+ this.activePromise.then(onComplete, onComplete).then(resolve);
+ });
+ }
+
+ return new Promise((resolve, reject) => {
+ this.queuedPromise.then(resolve, reject);
+ });
+ }
+
+ this.activePromise = promiseFactory();
+
+ return new Promise((resolve, reject) => {
+ this.activePromise.then((result: T) => {
+ // @ts-ignore
+ this.activePromise = null;
+ resolve(result);
+ }, (err: any) => {
+ // @ts-ignore
+ this.activePromise = null;
+ reject(err);
+ });
+ });
+ }
}
/**
* A helper to delay execution of a task that is being requested often.
*/
export class Delayer {
- public defaultDelay: number;
- private timeout!: number;
- private completionPromise!: Promise;
- private onResolve!: (value: T | Thenable) => void;
- private task!: ITask;
-
- constructor(defaultDelay: number) {
- this.defaultDelay = defaultDelay;
- }
-
- public trigger(task: ITask, delay: number = this.defaultDelay): Promise {
- this.task = task;
- this.cancelTimeout();
-
- if (!this.completionPromise) {
- this.completionPromise = new Promise((resolve, reject) => {
- this.onResolve = resolve;
- }).then(() => {
- // @ts-ignore
- this.completionPromise = null;
- // @ts-ignore
- this.onResolve = null;
-
- const result = this.task();
- // @ts-ignore
- this.task = null;
-
- return result;
- });
- }
-
- // @ts-ignore
- this.timeout = setTimeout(() => {
- // @ts-ignore
- this.timeout = null;
- // @ts-ignore
- this.onResolve(null);
- }, delay);
-
- return this.completionPromise;
- }
-
- public isTriggered(): boolean {
- return this.timeout !== null;
- }
-
- public cancel(): void {
- this.cancelTimeout();
-
- if (this.completionPromise) {
- // @ts-ignore
- this.completionPromise = null;
- }
- }
-
- private cancelTimeout(): void {
- if (this.timeout !== null) {
- // @ts-ignore
- clearTimeout(this.timeout);
- // @ts-ignore
- this.timeout = null;
- }
- }
+ public defaultDelay: number;
+ private timeout!: number;
+ private completionPromise!: Promise;
+ private onResolve!: (value: T | Thenable) => void;
+ private task!: ITask;
+
+ constructor(defaultDelay: number) {
+ this.defaultDelay = defaultDelay;
+ }
+
+ public trigger(task: ITask, delay: number = this.defaultDelay): Promise {
+ this.task = task;
+ this.cancelTimeout();
+
+ if (!this.completionPromise) {
+ this.completionPromise = new Promise((resolve, reject) => {
+ this.onResolve = resolve;
+ }).then(() => {
+ // @ts-ignore
+ this.completionPromise = null;
+ // @ts-ignore
+ this.onResolve = null;
+
+ const result = this.task();
+ // @ts-ignore
+ this.task = null;
+
+ return result;
+ });
+ }
+
+ // @ts-ignore
+ this.timeout = setTimeout(() => {
+ // @ts-ignore
+ this.timeout = null;
+ // @ts-ignore
+ this.onResolve(null);
+ }, delay);
+
+ return this.completionPromise;
+ }
+
+ public isTriggered(): boolean {
+ return this.timeout !== null;
+ }
+
+ public cancel(): void {
+ this.cancelTimeout();
+
+ if (this.completionPromise) {
+ // @ts-ignore
+ this.completionPromise = null;
+ }
+ }
+
+ private cancelTimeout(): void {
+ if (this.timeout !== null) {
+ // @ts-ignore
+ clearTimeout(this.timeout);
+ // @ts-ignore
+ this.timeout = null;
+ }
+ }
}
/**
@@ -127,16 +127,16 @@ export class Delayer {
* preventing accumulation of consecutive executions, while the task runs.
*/
export class ThrottledDelayer extends Delayer> {
- private throttler: Throttler;
+ private throttler: Throttler;
- constructor(defaultDelay: number) {
- super(defaultDelay);
+ constructor(defaultDelay: number) {
+ super(defaultDelay);
- this.throttler = new Throttler();
- }
+ this.throttler = new Throttler();
+ }
- public trigger(promiseFactory: ITask>, delay?: number): Promise> {
- // @ts-ignore
- return super.trigger(() => { return this.throttler.queue(promiseFactory); }, delay);
- }
+ public trigger(promiseFactory: ITask>, delay?: number): Promise> {
+ // @ts-ignore
+ return super.trigger(() => { return this.throttler.queue(promiseFactory); }, delay);
+ }
}
diff --git a/src/features/utils/lineDecoder.ts b/src/features/utils/lineDecoder.ts
index a9974c6..b28fd00 100644
--- a/src/features/utils/lineDecoder.ts
+++ b/src/features/utils/lineDecoder.ts
@@ -1,64 +1,64 @@
import { StringDecoder } from "string_decoder";
export class LineDecoder {
- private stringDecoder: StringDecoder;
- private remaining: string;
- private lines: string[];
+ private stringDecoder: StringDecoder;
+ private remaining: string;
+ private lines: string[];
- constructor(encoding: BufferEncoding = "utf8") {
- this.stringDecoder = new StringDecoder(encoding);
- this.remaining = "";
- this.lines = [];
- }
+ constructor(encoding: BufferEncoding = "utf8") {
+ this.stringDecoder = new StringDecoder(encoding);
+ this.remaining = "";
+ this.lines = [];
+ }
- public write(buffer: Buffer): string[] {
- const result: string[] = [];
- const value = this.remaining
- ? this.remaining + this.stringDecoder.write(buffer)
- : this.stringDecoder.write(buffer);
+ public write(buffer: Buffer): string[] {
+ const result: string[] = [];
+ const value = this.remaining
+ ? this.remaining + this.stringDecoder.write(buffer)
+ : this.stringDecoder.write(buffer);
- if (value.length < 1) {
- this.lines = this.lines.concat(value);
- return result;
- }
+ if (value.length < 1) {
+ this.lines = this.lines.concat(value);
+ return result;
+ }
- let start = 0;
- let characterCode: number = value.charCodeAt(start);
- while (start < value.length && (characterCode === 10 || characterCode === 13)) {
- start++;
- }
+ let start = 0;
+ let characterCode: number = value.charCodeAt(start);
+ while (start < value.length && (characterCode === 10 || characterCode === 13)) {
+ start++;
+ }
- let idx = start;
- while (idx < value.length) {
- characterCode = value.charCodeAt(idx);
- if (characterCode === 10 || characterCode === 13) {
- result.push(value.substring(start, idx));
- idx++;
- characterCode = value.charCodeAt(idx);
- while (idx < value.length && (characterCode === 10 || characterCode === 13)) {
- idx++;
- }
- start = idx;
- } else {
- idx++;
- }
- }
+ let idx = start;
+ while (idx < value.length) {
+ characterCode = value.charCodeAt(idx);
+ if (characterCode === 10 || characterCode === 13) {
+ result.push(value.substring(start, idx));
+ idx++;
+ characterCode = value.charCodeAt(idx);
+ while (idx < value.length && (characterCode === 10 || characterCode === 13)) {
+ idx++;
+ }
+ start = idx;
+ } else {
+ idx++;
+ }
+ }
- this.remaining = start < value.length ? value.substring(start) : "";
- this.lines = this.lines.concat(result);
+ this.remaining = start < value.length ? value.substring(start) : "";
+ this.lines = this.lines.concat(result);
- return result;
- }
+ return result;
+ }
- public end(): string {
- if (this.remaining && this.remaining.length > 0) {
- this.lines = this.lines.concat(this.remaining);
- }
+ public end(): string {
+ if (this.remaining && this.remaining.length > 0) {
+ this.lines = this.lines.concat(this.remaining);
+ }
- return this.remaining;
- }
+ return this.remaining;
+ }
- public getLines(): string[] {
- return this.lines;
- }
+ public getLines(): string[] {
+ return this.lines;
+ }
}
diff --git a/src/features/utils/lintingProvider.ts b/src/features/utils/lintingProvider.ts
index 10265cb..3f665df 100644
--- a/src/features/utils/lintingProvider.ts
+++ b/src/features/utils/lintingProvider.ts
@@ -7,173 +7,173 @@ import { ThrottledDelayer } from "./async";
import { LineDecoder } from "./lineDecoder";
enum RunTrigger {
- onSave = "onSave",
- onType = "onType",
- off = "off"
+ onSave = "onSave",
+ onType = "onType",
+ off = "off"
}
export interface Linter {
- languageId: Array,
- process: (output: string[]) => vscode.Diagnostic[];
+ languageId: Array,
+ process: (output: string[]) => vscode.Diagnostic[];
}
export class LintingProvider {
- public oldExecutablePath: string;
-
- private executableNotFound: boolean;
- private documentListener!: vscode.Disposable;
- private diagnosticCollection!: vscode.DiagnosticCollection;
- private delayers!: { [key: string]: ThrottledDelayer; };
- private linter: Linter;
- private childProcess: cp.ChildProcess;
-
- constructor(linter: Linter) {
- this.linter = linter;
- this.executableNotFound = false;
- }
-
- public activate(subscriptions: vscode.Disposable[]) {
- this.diagnosticCollection = vscode.languages.createDiagnosticCollection();
- subscriptions.push(this);
- vscode.workspace.onDidChangeConfiguration(this.loadConfiguration, this, subscriptions);
- this.loadConfiguration();
-
- vscode.workspace.onDidOpenTextDocument(this.triggerLint, this, subscriptions);
- vscode.workspace.onDidCloseTextDocument((textDocument) => {
- this.diagnosticCollection.delete(textDocument.uri);
- delete this.delayers[textDocument.uri.toString()];
- }, null, subscriptions);
-
- // Lint all open documents documents
- vscode.workspace.textDocuments.forEach(this.triggerLint, this);
- }
-
- public dispose(): void {
- this.diagnosticCollection.clear();
- this.diagnosticCollection.dispose();
- }
-
- private loadConfiguration(): void {
- this.delayers = Object.create(null);
-
- if (this.executableNotFound) {
- this.executableNotFound = this.oldExecutablePath === Configuration.executablePath();
- }
-
- if (this.documentListener) {
- this.documentListener.dispose();
- }
-
- if (Configuration.runTrigger() === RunTrigger.onSave) {
- this.documentListener = vscode.workspace.onDidChangeTextDocument((e) => {
- this.triggerLint(e.document);
- });
- } else {
- this.documentListener = vscode.workspace.onDidSaveTextDocument(this.triggerLint, this);
- }
- this.documentListener = vscode.workspace.onDidSaveTextDocument(this.triggerLint, this);
-
- // Configuration has changed. Reevaluate all documents.
- vscode.workspace.textDocuments.forEach(this.triggerLint, this);
- }
-
- private triggerLint(textDocument: vscode.TextDocument): void {
- if (
- !this.linter.languageId.includes(textDocument.languageId) ||
- this.executableNotFound ||
- Configuration.runTrigger() === RunTrigger.off
- ) {
- return;
- }
-
- const key = textDocument.uri.toString();
- let delayer = this.delayers[key];
-
- if (!delayer) {
- delayer = new ThrottledDelayer(500);
- this.delayers[key] = delayer;
- }
-
- delayer.trigger(() => { return this.doLint(textDocument); });
- }
-
- private doLint(textDocument: vscode.TextDocument): Promise {
- return new Promise((resolve) => {
- const decoder = new LineDecoder();
- const executable = Configuration.executablePath();
- const rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath;
-
- let args: string[];
- let diagnostics: vscode.Diagnostic[] = [];
-
- const options = rootPath ?
- {
- cwd: rootPath,
- env: {
- LANG: "en_US.utf-8"
- }
- } : undefined;
-
- if (Configuration.runTrigger() === RunTrigger.onSave) {
- args = [...Configuration.lintFileArguments()];
- args.push(textDocument.fileName);
- } else {
- args = Configuration.lintBufferArguments();
- }
- args = args.concat(Configuration.extraArguments());
-
- if (this.childProcess) {
- this.childProcess.kill();
- }
- this.childProcess = cp.spawn(executable, args, options);
-
- this.childProcess.on("error", (error: Error) => {
- let message = "";
- if (this.executableNotFound) {
- resolve();
- return;
- }
-
- if ((error).code === "ENOENT") {
- message = `Cannot lint ${textDocument.fileName}. The executable was not found. Use the 'Executable Path' setting to configure the location of the executable`;
- } else {
- message = error.message ? error.message : `Failed to run executable using path: ${executable}. Reason is unknown.`;
- }
-
- this.oldExecutablePath = Configuration.executablePath();
- this.executableNotFound = true;
- vscode.window.showInformationMessage(message);
- resolve();
- });
-
- const onDataEvent = (data: Buffer) => {
- decoder.write(data);
- };
-
- const onEndEvent = () => {
- decoder.end();
-
- const lines = decoder.getLines();
- if (lines && lines.length > 0) {
- diagnostics = this.linter.process(lines);
- }
-
- this.diagnosticCollection.set(textDocument.uri, diagnostics);
- resolve();
- };
-
- if (this.childProcess.pid) {
- if (Configuration.runTrigger() === RunTrigger.onType) {
- this.childProcess.stdin.write(textDocument.getText());
- this.childProcess.stdin.end();
- }
-
- this.childProcess.stdout.on("data", onDataEvent);
- this.childProcess.stdout.on("end", onEndEvent);
- resolve();
- } else {
- resolve();
- }
- });
- }
+ public oldExecutablePath: string;
+
+ private executableNotFound: boolean;
+ private documentListener!: vscode.Disposable;
+ private diagnosticCollection!: vscode.DiagnosticCollection;
+ private delayers!: { [key: string]: ThrottledDelayer; };
+ private linter: Linter;
+ private childProcess: cp.ChildProcess;
+
+ constructor(linter: Linter) {
+ this.linter = linter;
+ this.executableNotFound = false;
+ }
+
+ public activate(subscriptions: vscode.Disposable[]) {
+ this.diagnosticCollection = vscode.languages.createDiagnosticCollection();
+ subscriptions.push(this);
+ vscode.workspace.onDidChangeConfiguration(this.loadConfiguration, this, subscriptions);
+ this.loadConfiguration();
+
+ vscode.workspace.onDidOpenTextDocument(this.triggerLint, this, subscriptions);
+ vscode.workspace.onDidCloseTextDocument((textDocument) => {
+ this.diagnosticCollection.delete(textDocument.uri);
+ delete this.delayers[textDocument.uri.toString()];
+ }, null, subscriptions);
+
+ // Lint all open documents documents
+ vscode.workspace.textDocuments.forEach(this.triggerLint, this);
+ }
+
+ public dispose(): void {
+ this.diagnosticCollection.clear();
+ this.diagnosticCollection.dispose();
+ }
+
+ private loadConfiguration(): void {
+ this.delayers = Object.create(null);
+
+ if (this.executableNotFound) {
+ this.executableNotFound = this.oldExecutablePath === Configuration.executablePath();
+ }
+
+ if (this.documentListener) {
+ this.documentListener.dispose();
+ }
+
+ if (Configuration.runTrigger() === RunTrigger.onSave) {
+ this.documentListener = vscode.workspace.onDidChangeTextDocument((e) => {
+ this.triggerLint(e.document);
+ });
+ } else {
+ this.documentListener = vscode.workspace.onDidSaveTextDocument(this.triggerLint, this);
+ }
+ this.documentListener = vscode.workspace.onDidSaveTextDocument(this.triggerLint, this);
+
+ // Configuration has changed. Reevaluate all documents.
+ vscode.workspace.textDocuments.forEach(this.triggerLint, this);
+ }
+
+ private triggerLint(textDocument: vscode.TextDocument): void {
+ if (
+ !this.linter.languageId.includes(textDocument.languageId) ||
+ this.executableNotFound ||
+ Configuration.runTrigger() === RunTrigger.off
+ ) {
+ return;
+ }
+
+ const key = textDocument.uri.toString();
+ let delayer = this.delayers[key];
+
+ if (!delayer) {
+ delayer = new ThrottledDelayer(500);
+ this.delayers[key] = delayer;
+ }
+
+ delayer.trigger(() => { return this.doLint(textDocument); });
+ }
+
+ private doLint(textDocument: vscode.TextDocument): Promise {
+ return new Promise((resolve) => {
+ const decoder = new LineDecoder();
+ const executable = Configuration.executablePath();
+ const rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath;
+
+ let args: string[];
+ let diagnostics: vscode.Diagnostic[] = [];
+
+ const options = rootPath ?
+ {
+ cwd: rootPath,
+ env: {
+ LANG: "en_US.utf-8"
+ }
+ } : undefined;
+
+ if (Configuration.runTrigger() === RunTrigger.onSave) {
+ args = [...Configuration.lintFileArguments()];
+ args.push(textDocument.fileName);
+ } else {
+ args = Configuration.lintBufferArguments();
+ }
+ args = args.concat(Configuration.extraArguments());
+
+ if (this.childProcess) {
+ this.childProcess.kill();
+ }
+ this.childProcess = cp.spawn(executable, args, options);
+
+ this.childProcess.on("error", (error: Error) => {
+ let message = "";
+ if (this.executableNotFound) {
+ resolve();
+ return;
+ }
+
+ if ((error).code === "ENOENT") {
+ message = `Cannot lint ${textDocument.fileName}. The executable was not found. Use the 'Executable Path' setting to configure the location of the executable`;
+ } else {
+ message = error.message ? error.message : `Failed to run executable using path: ${executable}. Reason is unknown.`;
+ }
+
+ this.oldExecutablePath = Configuration.executablePath();
+ this.executableNotFound = true;
+ vscode.window.showInformationMessage(message);
+ resolve();
+ });
+
+ const onDataEvent = (data: Buffer) => {
+ decoder.write(data);
+ };
+
+ const onEndEvent = () => {
+ decoder.end();
+
+ const lines = decoder.getLines();
+ if (lines && lines.length > 0) {
+ diagnostics = this.linter.process(lines);
+ }
+
+ this.diagnosticCollection.set(textDocument.uri, diagnostics);
+ resolve();
+ };
+
+ if (this.childProcess.pid) {
+ if (Configuration.runTrigger() === RunTrigger.onType) {
+ this.childProcess.stdin.write(textDocument.getText());
+ this.childProcess.stdin.end();
+ }
+
+ this.childProcess.stdout.on("data", onDataEvent);
+ this.childProcess.stdout.on("end", onEndEvent);
+ resolve();
+ } else {
+ resolve();
+ }
+ });
+ }
}
diff --git a/test/runTest.ts b/test/runTest.ts
index 36621c2..62ec431 100644
--- a/test/runTest.ts
+++ b/test/runTest.ts
@@ -1,23 +1,22 @@
-import * as path from 'path';
-
-import { runTests } from 'vscode-test';
+import * as path from "path";
+import { runTests } from "vscode-test";
const main = async () => {
- try {
- // The folder containing the Extension Manifest package.json
- // Passed to `--extensionDevelopmentPath`
- const extensionDevelopmentPath = path.resolve(__dirname, '../../');
+ try {
+ // The folder containing the Extension Manifest package.json
+ // Passed to `--extensionDevelopmentPath`
+ const extensionDevelopmentPath = path.resolve(__dirname, "../../");
- // The path to test runner
- // Passed to --extensionTestsPath
- const extensionTestsPath = path.resolve(__dirname, './suite/index');
+ // The path to test runner
+ // Passed to --extensionTestsPath
+ const extensionTestsPath = path.resolve(__dirname, "./suite/index");
- // Download VS Code, unzip it and run the integration test
- await runTests({ extensionDevelopmentPath, extensionTestsPath });
- } catch (err) {
- console.error('Failed to run tests');
- process.exit(1);
- }
+ // Download VS Code, unzip it and run the integration test
+ await runTests({ extensionDevelopmentPath, extensionTestsPath });
+ } catch (err) {
+ console.error("Failed to run tests");
+ process.exit(1);
+ }
};
main();
diff --git a/test/suite/extension.test.ts b/test/suite/extension.test.ts
index 1856ed7..4873a6f 100644
--- a/test/suite/extension.test.ts
+++ b/test/suite/extension.test.ts
@@ -1,68 +1,68 @@
-import * as assert from 'assert';
+import * as assert from "assert";
+import * as vscode from "vscode";
-import * as vscode from 'vscode';
-import { activate, getDocUri, toRange, format } from './helper';
+import { activate, format, getDocUri, toRange } from "./helper";
const TIMEOUT = 4000;
-suite('Extension Test Suite', () => {
- vscode.window.showInformationMessage('Start all tests.');
+suite("Extension Test Suite", () => {
+ vscode.window.showInformationMessage("Start all tests.");
- test('Good SQL should have zero diagnostics', async () => {
+ test("Good SQL should have zero diagnostics", async () => {
- const docUri = getDocUri('/test_sql/good.sql');
- await activate(docUri);
+ const docUri = getDocUri("/test_sql/good.sql");
+ await activate(docUri);
- const actualDiagnostics = vscode.languages.getDiagnostics(docUri);
+ const actualDiagnostics = vscode.languages.getDiagnostics(docUri);
- assert.strictEqual(actualDiagnostics.length, 0);
+ assert.strictEqual(actualDiagnostics.length, 0);
- }).timeout(TIMEOUT);
+ }).timeout(TIMEOUT);
- test('Bad SQL should have the correct diagnostics', async () => {
+ test("Bad SQL should have the correct diagnostics", async () => {
- const docUri = getDocUri('/test_sql/bad.sql');
- await activate(docUri);
+ const docUri = getDocUri("/test_sql/bad.sql");
+ await activate(docUri);
- const actualDiagnostics = vscode.languages.getDiagnostics(docUri);
+ const actualDiagnostics = vscode.languages.getDiagnostics(docUri);
- assert.strictEqual(actualDiagnostics.length, 3);
- [
- { range: toRange(0, 1, 0, 1), message: "Query produces an unknown number of result columns.", code: "L044" },
- { range: toRange(0, 10, 0, 10), message: "Inconsistent capitalisation of keywords.", code: "L010" },
- { range: toRange(0, 22, 0, 22), message: "Files must end with a trailing newline.", code: "L009" },
- ].forEach((expectedDiagnostic, i) => {
- assertDiagnosticIsEqual(actualDiagnostics[i], expectedDiagnostic);
- });
+ assert.strictEqual(actualDiagnostics.length, 3);
+ [
+ { range: toRange(0, 1, 0, 1), message: "Query produces an unknown number of result columns.", code: "L044" },
+ { range: toRange(0, 10, 0, 10), message: "Inconsistent capitalisation of keywords.", code: "L010" },
+ { range: toRange(0, 22, 0, 22), message: "Files must end with a trailing newline.", code: "L009" },
+ ].forEach((expectedDiagnostic, i) => {
+ assertDiagnosticIsEqual(actualDiagnostics[i], expectedDiagnostic);
+ });
- }).timeout(TIMEOUT);
+ }).timeout(TIMEOUT);
- test("Bad SQL has zero diagnostics after document format", async () => {
+ test("Bad SQL has zero diagnostics after document format", async () => {
- const docUri = getDocUri('/test_sql/format.sql');
- const document = await activate(docUri);
- const preFormatDiagnostics = vscode.languages.getDiagnostics(docUri);
- assert.strictEqual(preFormatDiagnostics.length, 1, "Pre-format diagnostics not expected length");
+ const docUri = getDocUri("/test_sql/format.sql");
+ const document = await activate(docUri);
+ const preFormatDiagnostics = vscode.languages.getDiagnostics(docUri);
+ assert.strictEqual(preFormatDiagnostics.length, 1, "Pre-format diagnostics not expected length");
- await format(document);
- await activate(docUri);
+ await format(document);
+ await activate(docUri);
- const postFormatDiagnostics = vscode.languages.getDiagnostics(docUri);
- assert.strictEqual(postFormatDiagnostics.length, 0, "Post-format diagnostics not expected length");
+ const postFormatDiagnostics = vscode.languages.getDiagnostics(docUri);
+ assert.strictEqual(postFormatDiagnostics.length, 0, "Post-format diagnostics not expected length");
- }).timeout(9999999);
+ }).timeout(9999999);
- const assertDiagnosticIsEqual = (actual: vscode.Diagnostic, expected: { range: any; message: any; code: any; }) => {
- assert.deepStrictEqual(actual.range, expected.range);
- assert.strictEqual(actual.severity, 0);
- assert.strictEqual(actual.message, expected.message);
- assert.strictEqual(actual.code, expected.code);
- assert.strictEqual(actual.source, "sqlfluff");
- };
+ const assertDiagnosticIsEqual = (actual: vscode.Diagnostic, expected: { range: any; message: any; code: any; }) => {
+ assert.deepStrictEqual(actual.range, expected.range);
+ assert.strictEqual(actual.severity, 0);
+ assert.strictEqual(actual.message, expected.message);
+ assert.strictEqual(actual.code, expected.code);
+ assert.strictEqual(actual.source, "sqlfluff");
+ };
});
diff --git a/test/suite/helper.ts b/test/suite/helper.ts
index 8edccf9..2ff61cd 100644
--- a/test/suite/helper.ts
+++ b/test/suite/helper.ts
@@ -1,8 +1,8 @@
-import * as vscode from 'vscode';
+import * as vscode from "vscode";
export const activate = async (docUri: vscode.Uri): Promise => {
// The extensionId is `publisher.name` from package.json
- const ext = vscode.extensions.getExtension('vscode-sqlfluff');
+ const ext = vscode.extensions.getExtension("vscode-sqlfluff");
await ext?.activate();
try {
const document = await vscode.workspace.openTextDocument(docUri);
@@ -22,7 +22,7 @@ export const format = async (document: vscode.TextDocument | undefined) => {
await vscode.commands.executeCommand("editor.action.formatDocument");
await sleep(2000);
await document.save();
- await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+ await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
} catch (e) {
console.error(e);
}
@@ -43,4 +43,4 @@ export const toRange = (sLine: number, sChar: number, eLine: number, eChar: numb
const start = new vscode.Position(sLine, sChar);
const end = new vscode.Position(eLine, eChar);
return new vscode.Range(start, end);
-};
\ No newline at end of file
+};
diff --git a/test/suite/index.ts b/test/suite/index.ts
index 613e8ff..453a603 100644
--- a/test/suite/index.ts
+++ b/test/suite/index.ts
@@ -1,38 +1,38 @@
-import * as path from 'path';
-import * as Mocha from 'mocha';
-import * as glob from 'glob';
+import * as glob from "glob";
+import * as Mocha from "mocha";
+import * as path from "path";
export const run = (): Promise => {
- // Create the mocha test
- const mocha = new Mocha({
- ui: 'tdd',
- color: true
- });
+ // Create the mocha test
+ const mocha = new Mocha({
+ ui: "tdd",
+ color: true
+ });
- const testsRoot = path.resolve(__dirname, '..');
+ const testsRoot = path.resolve(__dirname, "..");
- return new Promise((c, e) => {
- glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
- if (err) {
- return e(err);
- }
+ return new Promise((c, e) => {
+ glob("**/**.test.js", { cwd: testsRoot }, (err, files) => {
+ if (err) {
+ return e(err);
+ }
- // Add files to the test suite
- files.forEach(f => {return mocha.addFile(path.resolve(testsRoot, f));});
+ // Add files to the test suite
+ files.forEach(f => { return mocha.addFile(path.resolve(testsRoot, f)); });
- try {
- // Run the mocha test
- mocha.run(failures => {
- if (failures > 0) {
- e(new Error(`${failures} tests failed.`));
- } else {
- c();
- }
- });
- } catch (err) {
- console.error(err);
- e(err);
- }
- });
- });
+ try {
+ // Run the mocha test
+ mocha.run(failures => {
+ if (failures > 0) {
+ e(new Error(`${failures} tests failed.`));
+ } else {
+ c();
+ }
+ });
+ } catch (err) {
+ console.error(err);
+ e(err);
+ }
+ });
+ });
};
diff --git a/test/suite/test_sql/bad.sql b/test/suite/test_sql/bad.sql
index 8df055f..f3052e8 100644
--- a/test/suite/test_sql/bad.sql
+++ b/test/suite/test_sql/bad.sql
@@ -1,2 +1,2 @@
-- 割引金額
-select a from b;
\ No newline at end of file
+select a from b;
diff --git a/test/suite/test_sql/format.sql b/test/suite/test_sql/format.sql
index f411e70..c5fd447 100644
--- a/test/suite/test_sql/format.sql
+++ b/test/suite/test_sql/format.sql
@@ -1 +1 @@
-SELECT a FROM a_table;
\ No newline at end of file
+SELECT a FROM a_table;