Skip to content

Commit aecd021

Browse files
author
Mike Kaufman
committed
Initial checkin
1 parent 39682da commit aecd021

28 files changed

+2787
-2
lines changed

LICENSE.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 Microsoft
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
7+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,18 @@
1-
# vscode-htmlhint
2-
VS Code integration of HTMLHint, an HTML linter.
1+
# vscode-eslint
2+
VSCode extension to support HTML-Hint, an HTML linter.
3+
4+
## Development setup
5+
- run npm install inside the `htmlhint` and `htmlhint-server` folders
6+
- open VS Code on `htmlhint` and `htmlhint-server`
7+
8+
## Developing the server
9+
- open VS Code on `htmlhint-server`
10+
- run `npm run compile` or `npm run watch` to build the server and copy it into the `htmlhint` folder
11+
- to debug press F5 which attaches a debugger to the server
12+
13+
## Developing the extension/client
14+
- open VS Code on `htmlhint`
15+
- run F5 to build and debug the extension
16+
17+
18+

htmlhint-server/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
out
2+
node_modules

htmlhint-server/.vscode/launch.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": "0.1.0",
3+
// List of configurations. Add new configurations or edit existing ones.
4+
"configurations": [
5+
{
6+
"name": "Attach",
7+
"type": "node",
8+
"request": "attach",
9+
// TCP/IP address. Default is "localhost".
10+
"address": "localhost",
11+
// Port to attach to.
12+
"port": 6004,
13+
"sourceMaps": true,
14+
"outDir": "../htmlhint/server"
15+
}
16+
]
17+
}

htmlhint-server/.vscode/settings.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Place your settings in this file to overwrite default and user settings.
2+
{
3+
"javascript.validate.enable": false,
4+
"files.trimTrailingWhitespace": true,
5+
"editor.insertSpaces": false,
6+
"editor.tabSize": 4
7+
}

htmlhint-server/.vscode/tasks.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"version": "0.1.0",
3+
"command": "npm",
4+
"isShellCommand": true,
5+
"showOutput": "silent",
6+
"args": ["run", "watch"],
7+
"isWatching": true,
8+
"problemMatcher": "$tsc-watch"
9+
}

htmlhint-server/LICENSE.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 Microsoft
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
7+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

htmlhint-server/package.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "htmlhint-server",
3+
"version": "0.2.0",
4+
"description": "HtmlHint Linter Server",
5+
"engines": {
6+
"node": "*"
7+
},
8+
"private": true,
9+
"dependencies": {
10+
"htmlhint": "^0.9.12",
11+
"vscode-languageserver": "0.10.x"
12+
},
13+
"devDependencies": {
14+
"typescript": "^1.6.2"
15+
},
16+
"scripts": {
17+
"compile": "installServerIntoExtension ../htmlhint ./package.json ./src/tsconfig.json && tsc -p ./src",
18+
"watch": "installServerIntoExtension ../htmlhint ./package.json ./src/tsconfig.json && tsc --watch -p ./src"
19+
}
20+
}

htmlhint-server/src/server.ts

+197
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*---------------------------------------------------------
2+
* Copyright (C) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------*/
4+
'use strict';
5+
6+
/// <reference path="typings/node/node.d.ts" />
7+
/// <reference path="typings/htmlhint/htmlhint.d.ts" />
8+
9+
//import * as fs from 'fs';
10+
import * as path from 'path';
11+
12+
import * as server from 'vscode-languageserver';
13+
14+
import htmlhint = require('htmlhint');
15+
16+
interface Settings {
17+
htmlhint: {
18+
enable: boolean;
19+
rulesDirectory: string;
20+
formatterDirectory: string
21+
}
22+
}
23+
24+
let settings: Settings = null;
25+
let rulesDirectory: string = null;
26+
let formatterDirectory: string = null;
27+
let linter: any = null;
28+
29+
/*
30+
let options: Lint.ILinterOptions = {
31+
formatter: "json",
32+
configuration: {},
33+
rulesDirectory: undefined,
34+
formattersDirectory: undefined
35+
};
36+
*/
37+
38+
let configCache = {
39+
filePath: <string>null,
40+
configuration: <any>null
41+
}
42+
43+
function getRange(error: htmlhint.Error, lines : string[] ) : any {
44+
45+
// approximate way to find the range of the element where the error is being reported.
46+
let line = lines[error.line - 1];
47+
var isWhitespace = false;
48+
var curr = error.col;
49+
while ( curr < line.length && !isWhitespace ) {
50+
var char = line[curr];
51+
isWhitespace = (char === ' ' || char === '\t' || char === '\n' || char === '\r' || char === '<' );
52+
++curr;
53+
}
54+
55+
if ( isWhitespace ) {
56+
--curr;
57+
}
58+
59+
return {
60+
start : {
61+
line: error.line - 1, // Html-hint line numbers are 1-based.
62+
character: error.col - 1
63+
},
64+
end: {
65+
line: error.line - 1,
66+
character: curr
67+
}
68+
};
69+
}
70+
71+
function makeDiagnostic(problem: htmlhint.Error, lines: string[]): server.Diagnostic {
72+
73+
return {
74+
severity: server.DiagnosticSeverity.Warning,
75+
message: problem.message,
76+
range: getRange(problem, lines),
77+
code: problem.rule.id
78+
};
79+
}
80+
81+
function getConfiguration(filePath: string): any {
82+
// TODO
83+
return {};
84+
/*
85+
if (configCache.configuration && configCache.filePath === filePath) {
86+
return configCache.configuration;
87+
}
88+
configCache = {
89+
filePath: filePath,
90+
configuration: linter.findConfiguration(null, filePath)
91+
}
92+
return configCache.configuration;
93+
*/
94+
}
95+
96+
function flushConfigCache() {
97+
configCache = {
98+
filePath: null,
99+
configuration: null
100+
}
101+
}
102+
103+
function getErrorMessage(err: any, document: server.ITextDocument): string {
104+
let result: string = null;
105+
if (typeof err.message === 'string' || err.message instanceof String) {
106+
result = <string>err.message;
107+
} else {
108+
result = `An unknown error occured while validating file: ${server.Files.uriToFilePath(document.uri) }`;
109+
}
110+
return result;
111+
}
112+
113+
function validateAllTextDocuments(connection: server.IConnection, documents: server.ITextDocument[]): void {
114+
let tracker = new server.ErrorMessageTracker();
115+
documents.forEach(document => {
116+
try {
117+
validateTextDocument(connection, document);
118+
} catch (err) {
119+
tracker.add(getErrorMessage(err, document));
120+
}
121+
});
122+
tracker.sendErrors(connection);
123+
}
124+
125+
function validateTextDocument(connection: server.IConnection, document: server.ITextDocument): void {
126+
try {
127+
doValidate(connection, document);
128+
} catch (err) {
129+
connection.window.showErrorMessage(getErrorMessage(err, document));
130+
}
131+
}
132+
133+
let connection: server.IConnection = server.createConnection(process.stdin, process.stdout);
134+
let documents: server.TextDocuments = new server.TextDocuments();
135+
documents.listen(connection);
136+
137+
connection.onInitialize((params): server.InitializeResult => {
138+
linter = htmlhint.HTMLHint;
139+
let result: server.InitializeResult = { capabilities: { textDocumentSync: documents.syncKind } };
140+
return result;
141+
});
142+
143+
144+
function doValidate(connection: server.IConnection, document: server.ITextDocument): void {
145+
try {
146+
let uri = document.uri;
147+
let fsPath = server.Files.uriToFilePath(uri);
148+
let contents = document.getText();
149+
let lines = contents.split('\n');
150+
151+
// TODO
152+
//options.configuration = getConfiguration(fsPath);
153+
154+
let errors : htmlhint.Error[] = linter.verify(contents);
155+
156+
let diagnostics: server.Diagnostic[] = [];
157+
if (errors.length > 0) {
158+
errors.forEach(each => {
159+
diagnostics.push(makeDiagnostic(each, lines));
160+
});
161+
}
162+
connection.sendDiagnostics({ uri, diagnostics });
163+
} catch (err) {
164+
let message: string = null;
165+
if (typeof err.message === 'string' || err.message instanceof String) {
166+
message = <string>err.message;
167+
throw new Error(message);
168+
}
169+
throw err;
170+
}
171+
}
172+
173+
// A text document has changed. Validate the document.
174+
documents.onDidChangeContent((event) => {
175+
// the contents of a text document has changed
176+
validateTextDocument(connection, event.document);
177+
});
178+
179+
// The VS Code htmlhint settings have changed. Revalidate all documents.
180+
connection.onDidChangeConfiguration((params) => {
181+
flushConfigCache();
182+
settings = params.settings;
183+
184+
if (settings.htmlhint) {
185+
rulesDirectory = settings.htmlhint.rulesDirectory;
186+
formatterDirectory = settings.htmlhint.formatterDirectory;
187+
}
188+
validateAllTextDocuments(connection, documents.all());
189+
});
190+
191+
// The watched htmlhint.json has changed. Revalidate all documents.
192+
connection.onDidChangeWatchedFiles((params) => {
193+
flushConfigCache();
194+
validateAllTextDocuments(connection, documents.all());
195+
});
196+
197+
connection.listen();

htmlhint-server/src/tsconfig.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES5",
4+
"module": "commonjs",
5+
"moduleResolution": "node",
6+
"sourceMap": true,
7+
"outDir": "../../htmlhint/server"
8+
},
9+
"exclude": [
10+
"node_modules"
11+
]
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
declare module 'htmlhint' {
3+
4+
export var HTMLHint: Verifier;
5+
6+
export interface Verifier {
7+
verify(text: string): Error[];
8+
}
9+
10+
export interface Error {
11+
type: string,
12+
message: string,
13+
raw: string,
14+
evidence: string,
15+
line: number,
16+
col: number,
17+
rule: Rule
18+
}
19+
20+
export interface Rule {
21+
id: string,
22+
description: string,
23+
link: string
24+
}
25+
26+
}

0 commit comments

Comments
 (0)