Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions htmlhint-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "htmlhint-server",
"version": "0.5.0",
"version": "0.5.2",
"description": "HtmlHint Linter Server",
"engines": {
"node": "*"
Expand All @@ -10,7 +10,8 @@
"fs": "0.0.2",
"htmlhint": "^0.9.13",
"strip-json-comments": "^2.0.0",
"vscode-languageserver": "^3.4.0"
"vscode-languageserver": "^3.4.0",
"glob": "5.0.15"
},
"devDependencies": {
"@types/node": "^6.0.41",
Expand Down
77 changes: 70 additions & 7 deletions htmlhint-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@ import * as path from 'path';
import * as server from 'vscode-languageserver';
import htmlhint = require('htmlhint');
import fs = require('fs');
import glob = require('glob');

let stripJsonComments: any = require('strip-json-comments');

interface Settings {
htmlhint: {
enable: boolean;
options: any;
rulesDir: string;
}
[key: string]: any;
}

let settings: Settings = null;
let linter: any = null;
let rulesLoaded: boolean = false;
let rootFolder: string = null;

/**
* This variable is used to cache loaded htmlhintrc objects. It is a dictionary from path -> config object.
Expand Down Expand Up @@ -60,6 +65,67 @@ function getRange(error: htmlhint.Error, lines: string[]): any {
};
}

function loadRules(): any {
rulesLoaded = false;
if (linter == null || !settings.htmlhint || !settings.htmlhint.rulesDir) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: looks like indentation is messed up in loadRules()


let rulesDir = settings.htmlhint.rulesDir;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should be able to do something like const rulesDir = path.resolve(settings.htmlhint.rulesDir);, and then drop all the stuff from lines 75-84?

Or alternatively, const rulesDir = path.resolve(rootDir, settings.htmlhint.rulesDir);

let absoluteDir = '';
//check absolute path
let exists = fs.existsSync(rulesDir);
if (exists) {
absoluteDir = rulesDir;
} else {
//relative dir, find it based on workspace roots
absoluteDir = path.resolve(rootFolder, rulesDir);
exists = fs.existsSync(absoluteDir);
}

if (exists) {
//load all rules
loadCustomRules(absoluteDir);
}
rulesLoaded = true;
}

// loadCustomRules is a direct copy from ./htmlhint-server/node_modules/htmlhint/bin/htmlhint lines 139-160
// load custom rles
function loadCustomRules(rulesdir:string): any{
rulesdir = rulesdir.replace(/\\/g, '/');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment here would help those of us who don't have a built-in regex parser. :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similiarly for other regexes used in here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied both loadCustomRules and loadRule from the htmlhint module's source code as it is not exposed for us to use.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally instead of copy/pasting this, we'd get these methods exposed via htmlhint. This way, we don't break the VS Code exetension when htmlhint changes their implementation. Do you know what a PR would look like to refactor in htmlhint? /cc @thedaviddias.

if(fs.existsSync(rulesdir)){
if(fs.statSync(rulesdir).isDirectory()){
rulesdir += /\/$/.test(rulesdir)?'':'/';
rulesdir += '**/*.js';
var arrFiles = glob.sync(rulesdir, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment here explaining what files you're going to load would be helpful as well.

'dot': false,
'nodir': true,
'strict': false,
'silent': true
});
arrFiles.forEach(function(file){
loadRule(file);
});
}
else{
loadRule(rulesdir);
}
}
}

// loadCustomRules is a modified copy from ./htmlhint-server/node_modules/htmlhint/bin/htmlhint lines 162-170
// Only the variable name HTMLHint has been changed to linter
// load rule
function loadRule(filepath: string): any{
filepath = path.resolve(filepath);
try{
var module = require(filepath);
module(linter);
}
catch(e){}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blanket catch seems problematic here. Is this here for a reaason?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect the idea is to skip a rule if it is broken. Given that this code is from htmlhint's source there might not be a way to properly report errors from there.

Can you please suggest a nice way we can report errors to the user as we have access to vscode here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think vs code APIs let you report errors with something like connection.window.showErrorMessage(...message...); See API docs [here](https://code.visualstudio.com/docs/extensionAPI/vscode-api_

}

/**
* Given an htmlhint.Error type return a VS Code server Diagnostic object
*/
Expand Down Expand Up @@ -178,17 +244,16 @@ function trace(message: string, verbose?: string): void {
}

connection.onInitialize((params: server.InitializeParams, token: server.CancellationToken) => {
let rootFolder = params.rootPath;
rootFolder = params.rootPath;
let initOptions: {
nodePath: string;
} = params.initializationOptions;
let nodePath = initOptions ? (initOptions.nodePath ? initOptions.nodePath : undefined) : undefined;

const result= server.Files.resolveModule2(rootFolder, 'htmlhint', nodePath, trace).
const result = server.Files.resolveModule2(rootFolder, 'htmlhint', nodePath, trace).
then((value): server.InitializeResult | server.ResponseError<server.InitializeError> => {
linter = value.HTMLHint;
//connection.window.showInformationMessage(`onInitialize() - found local htmlhint (version ! ${value.HTMLHint.version})`);

//connection.window.showInformationMessage(`onInitialize() using external htmlhint(version ! ${linter.version})`);
let result: server.InitializeResult = { capabilities: { textDocumentSync: documents.syncKind } };
return result;
}, (error) => {
Expand All @@ -208,11 +273,9 @@ function doValidate(connection: server.IConnection, document: server.TextDocumen
let fsPath = server.Files.uriToFilePath(uri);
let contents = document.getText();
let lines = contents.split('\n');

let config = getConfiguration(fsPath);

let errors: htmlhint.Error[] = linter.verify(contents, config);

let diagnostics: server.Diagnostic[] = [];
if (errors.length > 0) {
errors.forEach(each => {
Expand All @@ -239,7 +302,7 @@ documents.onDidChangeContent((event) => {
// The VS Code htmlhint settings have changed. Revalidate all documents.
connection.onDidChangeConfiguration((params) => {
settings = params.settings;

loadRules();

validateAllTextDocuments(connection, documents.all());
});
Expand Down
1 change: 1 addition & 0 deletions htmlhint-server/src/typings-custom/htmlhint.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ declare module 'htmlhint' {

export interface Verifier {
verify(text: string): Error[];
loadCustomRules(rulesdir: string): any;
}

export interface Error {
Expand Down
3 changes: 2 additions & 1 deletion htmlhint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ If your file type already has an associated language service other than "html",

## Settings

The HTMLHint extension provides three [settings](https://code.visualstudio.com/docs/customization/userandworkspace):
The HTMLHint extension provides four [settings](https://code.visualstudio.com/docs/customization/userandworkspace):

* `htmlhint.enable` - disable the HTMLHint extension globally or per workspace.
* `htmlhint.documentSelector` - specify additional language services to be linted
* `htmlhint.options` - provide a rule set to override on disk `.htmlhintrc` or HTMLHint defaults.
* `htmlhint.rulesDir` - provide an absolute or relative path to a folder containing custom rules.

You can change settings globally (**File** > **Preferences** > **User Settings**) or per workspace (**File** > **Preferences** > **Workspace Settings**). The **Preferences** menu is under **Code** on macOS.

Expand Down
7 changes: 6 additions & 1 deletion htmlhint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "HTMLHint",
"description": "VS Code integration for HTMLHint - A Static Code Analysis Tool for HTML",
"icon": "HTMLHint.png",
"version": "0.5.0",
"version": "0.5.2",
"publisher": "mkaufman",
"galleryBanner": {
"color": "#0000FF",
Expand Down Expand Up @@ -40,6 +40,11 @@
"default": true,
"description": "Control whether htmlhint is enabled for HTML files or not."
},
"htmlhint.rulesDir": {
"type": "string",
"default": "",
"description": "Folder containing custom rules, absolute or relative to workspace root."
},
"htmlhint.documentSelector": {
"type": "array",
"default": [
Expand Down