Skip to content

Commit

Permalink
feat: Add linter feature using "Stillat/blade-parser-typescript" pars…
Browse files Browse the repository at this point in the history
…er (#13)
  • Loading branch information
yaegassy committed Jul 29, 2022
1 parent 7c8bf31 commit ed67d0e
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 12 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Laravel Blade Templates extension for [coc.nvim](https://github.com/neoclide/coc
- Format
- by [blade-formatter](https://github.com/shufo/blade-formatter)
- Lint
- by [laravel-blade-linter](https://github.com/bdelespierre/laravel-blade-linter)
- using [Stillat/blade-parser-typescript](https://github.com/Stillat/blade-parser-typescript)
- Completion
- Blade Snippets Completion
- Blade Directive Completion
Expand Down Expand Up @@ -95,15 +95,11 @@ resources/views/books/**/*

> In coc-blade, there is a code action feature to add a blade comment to disable the formatting.
### linter (laravel-blade-linter)
### linter (using Stillat/blade-parser-typescript)

You will need to have [laravel-blade-linter](https://github.com/bdelespierre/laravel-blade-linter) installed in your "Laravel project".
This feature is enabled by default. If you do not need the linter feature, set `blade.bladeParserLint.enable` to `false`

If "laravel-blade-linter" is not detected, the lint (diagnostics) feature is automatically disabled.

```sh
composer require --dev bdelespierre/laravel-blade-linter
```
- [DEMO](https://github.com/yaegassy/coc-blade/pull/13)

### snippets completion (laravel-blade-snippets-vscode)

Expand Down Expand Up @@ -155,7 +151,11 @@ Parses `bootstrap/cache/livewire-components.php` files and target component clas
- `blade.bladeFormatter.optWrapLineLength`: The length of line wrap size (`--wrap-line-length`), valid type `integer` or `null`, default: `null`
- `blade.bladeFormatter.optWrapAttributes`: The way to wrap attributes (`--wrap-attributes`), valid options `["auto", "force", "force-aligned", "force-expand-multiline", "aligned-multiple", "preserve", "preserve-aligned"]`, valid type `string` or `null`, default: `null`
- `blade.bladeFormatter.optSortTailwindcssClasses`: markdownDescription": "Sort Tailwindcss classes automatically. This option respects `tailwind.config.js` and sort classes according to settings, valid type `boolean` or `null`, default: `null`
- `blade.bladeLinter.enable`: Enable/Disable the linting feature by `laravel-blade-linter`, default: `true`
- `blade.bladeParserLint.enable`: Enable/Disable the linting feature using `stillat-blade-parser`, default: `true`
- `blade.bladeParserLint.debug`: Output the results of the parsing of stillat-blade-parser to the channel log, default: `false`
- `blade.bladeParserLint.optCustomIfs`: A list of custom if directives, default: `[]`
- `blade.bladeParserLint.optDirectives`: A list of directives that can be parsed, default: `[]`
- `blade.bladeParserLint.optIgnoreDirectives`: A list of directive names that should be ignored, default: `[]`

## Commands

Expand All @@ -182,7 +182,7 @@ nmap <silent> gA <Plug>(coc-codeaction)

- [shufo/blade-formatter](https://github.com/shufo/blade-formatter)
- [shufo/vscode-blade-formatter](https://github.com/shufo/vscode-blade-formatter)
- [bdelespierre/laravel-blade-linter](https://github.com/bdelespierre/laravel-blade-linter)
- [Stillat/blade-parser-typescript](https://github.com/Stillat/blade-parser-typescript)
- [onecentlin/laravel-blade-snippets-vscode](https://github.com/onecentlin/laravel-blade-snippets-vscode)

## License
Expand Down
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,31 @@
"default": false,
"markdownDescription": "Sort Tailwindcss classes automatically. This option respects `tailwind.config.js` and sort classes according to settings"
},
"blade.bladeParserLint.enable": {
"type": "boolean",
"default": true,
"description": "Enable/Disable the linting feature using stillat-blade-parser"
},
"blade.bladeParserLint.debug": {
"type": "boolean",
"default": false,
"description": "Output the results of the parsing of stillat-blade-parser to the channel log"
},
"blade.bladeParserLint.optCustomIfs": {
"type": "array",
"default": [],
"description": "A list of custom if directives"
},
"blade.bladeParserLint.optDirectives": {
"type": "array",
"default": [],
"description": "A list of directives that can be parsed"
},
"blade.bladeParserLint.optIgnoreDirectives": {
"type": "array",
"default": [],
"description": "A list of directive names that should be ignored"
},
"blade.bladeLinter.enable": {
"type": "boolean",
"default": true,
Expand All @@ -195,6 +220,7 @@
"dependencies": {
"blade-formatter": "^1.26.16",
"ignore": "^5.2.0",
"stillat-blade-parser": "^0.1.0",
"synckit": "^0.6.0"
}
}
20 changes: 20 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,26 @@ export function getConfigBladeLinterEnable() {
return workspace.getConfiguration('blade').get<boolean>('bladeLinter.enable', true);
}

export function getConfigBladeParserLintEnable() {
return workspace.getConfiguration('blade').get<boolean>('bladeParserLint.enable', true);
}

export function getConfigBladeParserLintDebug() {
return workspace.getConfiguration('blade').get<boolean>('bladeParserLint.debug', false);
}

export function getConfigBladeParserLintOptCustomIfs() {
return workspace.getConfiguration('blade').get<string[]>('bladeParserLint.optCustomIfs', []);
}

export function getConfigBladeParserLintOptDirectives() {
return workspace.getConfiguration('blade').get<string[]>('bladeParserLint.optDirectives', []);
}

export function getConfigBladeParserLintOptIgnoreDirectives() {
return workspace.getConfiguration('blade').get<string[]>('bladeParserLint.optIgnoreDirectives', []);
}

export function getConfigBladeCompletionEnable() {
return workspace.getConfiguration('blade').get<boolean>('completion.enable', true);
}
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as bladeCompletionFeature from './completions/completion';
import * as bladeDefinisionFeature from './definitions/definition';
import * as bladeFormatterDocumantFormattingEditFeature from './documentFormats/documentFormat';
import * as bladeHoverFeature from './hovers/hover';
import * as bladeLinterFeature from './linters/bladeLinter';
import * as bladeParserLintFeature from './linters/bladeParserLint';

export async function activate(context: ExtensionContext): Promise<void> {
if (!getConfigBladeEnable()) return;
Expand All @@ -20,8 +20,8 @@ export async function activate(context: ExtensionContext): Promise<void> {
bladeFormatterRunCommandFeature.register(context, outputChannel);
await bladeCompletionFeature.register(context, outputChannel);
bladeFormatterDocumantFormattingEditFeature.register(context, outputChannel);
bladeLinterFeature.register(context, outputChannel);
bladeHoverFeature.register(context);
bladeDefinisionFeature.register(context);
bladeCodeActionFeature.register(context);
bladeParserLintFeature.register(context, outputChannel);
}
145 changes: 145 additions & 0 deletions src/linters/bladeParserLint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import {
Diagnostic,
DiagnosticCollection,
DiagnosticSeverity,
ExtensionContext,
languages,
OutputChannel,
Position,
Range,
TextDocument,
workspace,
} from 'coc.nvim';

import { BladeDocument } from 'stillat-blade-parser/out/document/bladeDocument';
import { ParserOptions } from 'stillat-blade-parser/out/parser/parserOptions';

import {
getConfigBladeParserLintEnable,
getConfigBladeParserLintDebug,
getConfigBladeParserLintOptCustomIfs,
getConfigBladeParserLintOptDirectives,
getConfigBladeParserLintOptIgnoreDirectives,
} from '../config';

export async function register(context: ExtensionContext, outputChannel: OutputChannel) {
if (getConfigBladeParserLintEnable()) {
const engine = new BladeParserLintEngine(outputChannel);

// onOpen
workspace.documents.map(async (doc) => {
await engine.lint(doc.textDocument);
});
workspace.onDidOpenTextDocument(
async (e) => {
await engine.lint(e);
},
null,
context.subscriptions
);

// onChange
workspace.onDidChangeTextDocument(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async (_e) => {
const doc = await workspace.document;
await engine.lint(doc.textDocument);
},
null,
context.subscriptions
);

// onSave
workspace.onDidSaveTextDocument(
async (e) => {
await engine.lint(e);
},
null,
context.subscriptions
);
}
}

class BladeParserLintEngine {
private collection: DiagnosticCollection;
private outputChannel: OutputChannel;

constructor(outputChannel: OutputChannel) {
this.collection = languages.createDiagnosticCollection('bladeParser');
this.outputChannel = outputChannel;
}

public async lint(textDocument: TextDocument): Promise<void> {
if (textDocument.languageId !== 'blade') return;

const diagnostics: Diagnostic[] = [];
const content = textDocument.getText();

const parserDocument = new BladeDocument();
const parserOptions: ParserOptions = {
customIfs: getConfigBladeParserLintOptCustomIfs(),
directives: getConfigBladeParserLintOptDirectives(),
ignoreDirectives: getConfigBladeParserLintOptIgnoreDirectives(),
};

parserDocument.getParser().withParserOptions(parserOptions);

try {
const res = parserDocument.loadString(content);

res.errors.all().forEach((e) => {
// channel logging
if (getConfigBladeParserLintDebug()) {
this.outputChannel.appendLine(`${'#'.repeat(10)} bladeParser\n`);
this.outputChannel.appendLine(`errorCode: ${e.errorCode}`);
this.outputChannel.appendLine(`level: ${e.level}`);
this.outputChannel.appendLine(`message: ${e.message}`);
this.outputChannel.appendLine(`startPosition: ${JSON.stringify(e.node?.startPosition)}`);
this.outputChannel.appendLine(`endPosition: ${JSON.stringify(e.node?.endPosition)}\n`);
}

const message = e.message;
const level = e.level;
const severity = this.convertBladeErrorLevelToDiagnosticsSeverity(level);
const errorCode = e.errorCode;

let startPosition: Position | undefined;
let endPosition: Position | undefined;
if (e.node && e.node.startPosition && e.node.endPosition) {
// MEMO: The startPostion.char of this parser is adjusted by -1.
startPosition = Position.create(e.node.startPosition.line - 1, e.node.startPosition.char - 1);
// MEMO: The endPostiion of this parser is not very suitable for the linter, so we use startPostiion.
endPosition = Position.create(e.node.startPosition.line - 1, e.node.startPosition.char);
}

if (startPosition && endPosition) {
const diagnostic: Diagnostic = {
source: 'bladeParser',
code: errorCode,
range: Range.create(startPosition, endPosition),
message,
severity,
relatedInformation: [],
};

diagnostics.push(diagnostic);
}
});
} catch (e) {
this.collection.set(textDocument.uri, null);
}

this.collection.set(textDocument.uri, diagnostics);
}

private convertBladeErrorLevelToDiagnosticsSeverity(level: number) {
switch (level) {
case 0:
return DiagnosticSeverity.Error;
case 1:
return DiagnosticSeverity.Warning;
default:
return DiagnosticSeverity.Error;
}
}
}
37 changes: 37 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,11 @@ camelcase-css@^2.0.1:
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==

camelize@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
integrity sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg==

chai@^4.3.6:
version "4.3.6"
resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c"
Expand All @@ -383,6 +388,11 @@ chalk@^4.0.0, chalk@^4.1.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"

"charenc@>= 0.0.1":
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==

check-error@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
Expand Down Expand Up @@ -471,6 +481,11 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"

"crypt@>= 0.0.1":
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==

cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
Expand Down Expand Up @@ -1652,6 +1667,14 @@ semver@^7.3.7:
dependencies:
lru-cache "^6.0.0"

sha1@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848"
integrity sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==
dependencies:
charenc ">= 0.0.1"
crypt ">= 0.0.1"

shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
Expand Down Expand Up @@ -1679,6 +1702,15 @@ source-map-js@^1.0.2:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==

stillat-blade-parser@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/stillat-blade-parser/-/stillat-blade-parser-0.1.0.tgz#e83adf7991467b6b1970dc103ee3e9da6812eaac"
integrity sha512-jr2GbkvNKUXqwFn93YgkAzs08eZhnD2fqjGnM0fG74GWDEVoupqz8yw2p6xQgseptqRt/kVr3W2Y+NVnWuCzqA==
dependencies:
camelize "^1.0.0"
sha1 "^1.1.1"
uuid "^8.3.2"

string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
Expand Down Expand Up @@ -1839,6 +1871,11 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==

uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==

v8-compile-cache@^2.0.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
Expand Down

0 comments on commit ed67d0e

Please sign in to comment.