Skip to content

Commit acfa44b

Browse files
zigndcvharris
authored andcommitted
Fix error that occurs when you try to get completion and the extension is loading; Add limitations to the concurrency in resource intensive operations; Add progress indicator, useful for large projects
1 parent d7834b3 commit acfa44b

File tree

4 files changed

+127
-132
lines changed

4 files changed

+127
-132
lines changed

package.json

Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,54 @@
11
{
2-
"name": "html-css-class-completion",
3-
"displayName": "IntelliSense for CSS class names",
4-
"description": "Provides CSS class name completion for the HTML class attribute based on the CSS files in your workspace. Also supports React's className attribute.",
5-
"version": "1.10.2",
6-
"publisher": "Zignd",
7-
"engines": {
8-
"vscode": "^1.4.0"
9-
},
10-
"categories": [
11-
"Languages",
12-
"Other"
13-
],
14-
"activationEvents": [
15-
"*"
16-
],
17-
"contributes": {
18-
"commands": [
19-
{
20-
"command": "html-css-class-completion.cache",
21-
"title": "Cache CSS class definitions"
22-
}
23-
]
24-
},
25-
"icon": "images/icon.png",
26-
"repository": {
27-
"url": "https://github.com/Zignd/HTML-CSS-Class-Completion"
28-
},
29-
"main": "./out/src/extension",
30-
"scripts": {
31-
"vscode:prepublish": "tsc -p ./",
32-
"compile": "tsc -watch -p ./",
33-
"postinstall": "node ./node_modules/vscode/bin/install"
34-
},
35-
"devDependencies": {
36-
"@types/async": "^2.0.40",
37-
"@types/css": "^0.0.30",
38-
"@types/htmlparser2": "^3.7.29",
39-
"@types/lodash": "^4.14.63",
40-
"@types/mocha": "^2.2.41",
41-
"@types/node": "^7.0.18",
42-
"@types/request": "^0.0.43",
43-
"@types/request-promise": "^4.1.33",
44-
"typescript": "^2.3.2",
45-
"vscode": "^1.1.0"
46-
},
47-
"dependencies": {
48-
"async": "^2.4.0",
49-
"css": "^2.2.1",
50-
"htmlparser2": "^3.9.2",
51-
"request": "^2.81.0",
52-
"request-promise": "^4.2.1"
53-
}
2+
"name": "html-css-class-completion",
3+
"displayName": "IntelliSense for CSS class names",
4+
"description": "Provides CSS class name completion for the HTML class attribute based on the CSS files in your workspace. Also supports React's className attribute.",
5+
"version": "1.10.2",
6+
"publisher": "Zignd",
7+
"engines": {
8+
"vscode": "^1.4.0"
9+
},
10+
"categories": [
11+
"Languages",
12+
"Other"
13+
],
14+
"activationEvents": [
15+
"*"
16+
],
17+
"contributes": {
18+
"commands": [
19+
{
20+
"command": "html-css-class-completion.cache",
21+
"title": "Cache CSS class definitions"
22+
}
23+
]
24+
},
25+
"icon": "images/icon.png",
26+
"repository": {
27+
"url": "https://github.com/Zignd/HTML-CSS-Class-Completion"
28+
},
29+
"main": "./out/src/extension",
30+
"scripts": {
31+
"vscode:prepublish": "tsc -p ./",
32+
"compile": "tsc -watch -p ./",
33+
"postinstall": "node ./node_modules/vscode/bin/install"
34+
},
35+
"devDependencies": {
36+
"@types/bluebird": "^3.5.16",
37+
"@types/css": "^0.0.30",
38+
"@types/htmlparser2": "^3.7.29",
39+
"@types/lodash": "^4.14.63",
40+
"@types/node": "^7.0.18",
41+
"@types/request": "^0.0.43",
42+
"@types/request-promise": "^4.1.33",
43+
"typescript": "^2.3.2",
44+
"vscode": "^1.1.0"
45+
},
46+
"dependencies": {
47+
"bluebird": "^3.5.1",
48+
"css": "^2.2.1",
49+
"htmlparser2": "^3.9.2",
50+
"lodash": "^4.17.4",
51+
"request": "^2.81.0",
52+
"request-promise": "^4.2.1"
53+
}
5454
}

src/extension.ts

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as async from 'async';
1+
import * as Bluebird from 'bluebird';
22
import * as _ from 'lodash';
33
import * as vscode from 'vscode';
44
import CssClassDefinition from './common/css-class-definition';
@@ -8,7 +8,7 @@ import Notifier from './notifier';
88
import ParseEngineGateway from './parse-engine-gateway';
99

1010
let notifier: Notifier = new Notifier('html-css-class-completion.cache');
11-
let uniqueDefinitions: CssClassDefinition[];
11+
let uniqueDefinitions: CssClassDefinition[] = [];
1212

1313
const completionTriggerChars = ['"', '\'', ' '];
1414

@@ -31,38 +31,41 @@ function cache(): Promise<void> {
3131
console.log('Found all parseable documents.');
3232
let definitions: CssClassDefinition[] = [];
3333

34+
let filesParsed: number = 0;
3435
let failedLogs: string = '';
3536
let failedLogsCount: number = 0;
3637

3738
console.log('Parsing documents and looking for CSS class definitions...');
38-
return async.eachLimit(uris, 100, async (uri, callback) => {
39-
try {
40-
Array.prototype.push.apply(definitions, await ParseEngineGateway.callParser(uri));
41-
callback();
42-
} catch (error) {
43-
failedLogs += `${uri.path}\n`;
44-
failedLogsCount++;
45-
callback();
46-
}
47-
}, (error) => {
48-
if (error) {
49-
console.error('Failed to parse the documents: ', error);
50-
return reject(error);
51-
}
5239

53-
uniqueDefinitions = _.uniqBy(definitions, def => def.className);
40+
try {
41+
await Bluebird.map(uris, async (uri) => {
42+
try {
43+
Array.prototype.push.apply(definitions, await ParseEngineGateway.callParser(uri));
44+
} catch (error) {
45+
failedLogs += `${uri.path}\n`;
46+
failedLogsCount++;
47+
}
48+
filesParsed++;
49+
notifier.notify('eye', 'Looking for CSS classes in the workspace... (' + ((filesParsed / uris.length) * 100).toFixed(2) + '%)', false);
50+
}, { concurrency: 30 });
51+
} catch (err) {
52+
console.error('Failed to parse the documents: ', err);
53+
notifier.notify('alert', 'Failed to cache the CSS classes in the workspace (click for another attempt)');
54+
return reject(err);
55+
}
56+
57+
uniqueDefinitions = _.uniqBy(definitions, def => def.className);
5458

55-
console.log('Summary:');
56-
console.log(uris.length, 'parseable documents found');
57-
console.log(definitions.length, 'CSS class definitions found');
58-
console.log(uniqueDefinitions.length, 'unique CSS class definitions found');
59-
console.log(failedLogsCount, 'failed attempts to parse. List of the documents:');
60-
console.log(failedLogs);
59+
console.log('Summary:');
60+
console.log(uris.length, 'parseable documents found');
61+
console.log(definitions.length, 'CSS class definitions found');
62+
console.log(uniqueDefinitions.length, 'unique CSS class definitions found');
63+
console.log(failedLogsCount, 'failed attempts to parse. List of the documents:');
64+
console.log(failedLogs);
6165

62-
notifier.notify('zap', 'CSS classes cached (click to cache again)');
66+
notifier.notify('zap', 'CSS classes cached (click to cache again)');
6367

64-
return resolve();
65-
});
68+
return resolve();
6669
} catch (error) {
6770
console.error('Failed to cache the class definitions during the iterations over the documents that were found:', error);
6871
notifier.notify('alert', 'Failed to cache the CSS classes in the workspace (click for another attempt)');

src/notifier.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,20 @@ class Notifier {
1111
this.statusBarItem.show();
1212
}
1313

14-
public notify(icon: string, text: string): void {
14+
public notify(icon: string, text: string, autoHide: boolean = true): void {
1515
if (this._timeoutId) {
1616
clearTimeout(this._timeoutId);
1717
}
1818

1919
this.statusBarItem.text = `$(${icon}) ${text}`;
2020
this.statusBarItem.tooltip = null;
2121

22-
this._timeoutId = setTimeout(() => {
23-
this.statusBarItem.text = `$(${icon})`;
24-
this.statusBarItem.tooltip = text;
25-
}, 5000);
22+
if (autoHide) {
23+
this._timeoutId = setTimeout(() => {
24+
this.statusBarItem.text = `$(${icon})`;
25+
this.statusBarItem.tooltip = text;
26+
}, 5000);
27+
}
2628
}
2729
}
2830

src/parse-engines/types/html-parse-engine.ts

Lines changed: 39 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as async from 'async';
1+
import * as Bluebird from 'bluebird';
22
import * as request from 'request-promise';
33
import * as css from 'css';
44
import * as html from 'htmlparser2';
@@ -12,60 +12,50 @@ class HtmlParseEngine implements ParseEngine {
1212
public languageId: string = 'html';
1313
public extension: string = 'html';
1414

15-
public parse(textDocument: SimpleTextDocument): Promise<CssClassDefinition[]> {
16-
return new Promise((resolve, reject) => {
17-
const definitions: CssClassDefinition[] = [];
18-
const urls: string[] = [];
19-
let tag: string;
20-
let isRelStylesheet: boolean = false;
21-
let linkHref: string;
22-
23-
const parser = new html.Parser({
24-
onopentagname: (name: string) => {
25-
tag = name;
26-
},
27-
onattribute: (name: string, value: string) => {
28-
if (name === "rel" && value === "stylesheet") {
29-
isRelStylesheet = true;
30-
}
15+
public async parse(textDocument: SimpleTextDocument): Promise<CssClassDefinition[]> {
16+
const definitions: CssClassDefinition[] = [];
17+
const urls: string[] = [];
18+
let tag: string;
19+
let isRelStylesheet: boolean = false;
20+
let linkHref: string;
3121

32-
if (tag === "link" && name === "href" && value.indexOf('http') === 0) {
33-
linkHref = value;
34-
}
35-
},
36-
ontext: (text: string) => {
37-
if (tag === "style") {
38-
definitions.push(...CssClassExtractor.extract(css.parse(text)));
39-
}
40-
},
41-
onclosetag: () => {
42-
if (tag === "link" && isRelStylesheet && linkHref) {
43-
urls.push(linkHref);
44-
}
45-
46-
isRelStylesheet = false;
47-
linkHref = null;
22+
const parser = new html.Parser({
23+
onopentagname: (name: string) => {
24+
tag = name;
25+
},
26+
onattribute: (name: string, value: string) => {
27+
if (name === "rel" && value === "stylesheet") {
28+
isRelStylesheet = true;
4829
}
49-
});
50-
51-
parser.write(textDocument.getText());
52-
parser.end();
5330

54-
return async.each(urls, async (url, callback) => {
55-
try {
56-
let content = await request.get(url);
57-
definitions.push(...CssClassExtractor.extract(css.parse(content)));
58-
return callback();
59-
} catch (error) {
60-
return callback(error);
31+
if (tag === "link" && name === "href" && value.indexOf('http') === 0) {
32+
linkHref = value;
33+
}
34+
},
35+
ontext: (text: string) => {
36+
if (tag === "style") {
37+
definitions.push(...CssClassExtractor.extract(css.parse(text)));
6138
}
62-
}, (error) => {
63-
if (error) {
64-
return reject(error);
39+
},
40+
onclosetag: () => {
41+
if (tag === "link" && isRelStylesheet && linkHref) {
42+
urls.push(linkHref);
6543
}
66-
return resolve(definitions);
67-
});
44+
45+
isRelStylesheet = false;
46+
linkHref = null;
47+
}
6848
});
49+
50+
parser.write(textDocument.getText());
51+
parser.end();
52+
53+
await Bluebird.map(urls, async (url) => {
54+
let content = await request.get(url);
55+
definitions.push(...CssClassExtractor.extract(css.parse(content)));
56+
}, { concurrency: 10 });
57+
58+
return definitions;
6959
}
7060
}
7161

0 commit comments

Comments
 (0)