Skip to content

Commit effea70

Browse files
committed
Added initial template inheritance support
1 parent 492cb4a commit effea70

File tree

4 files changed

+67
-12
lines changed

4 files changed

+67
-12
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ If it is not possible to specify local or remote styles within each HTML file, t
8484
]
8585
```
8686

87+
## Template Inheritance
88+
89+
Template inheritance is supported for **`{% extends "base" %}`** and **`{{< base }}`** tags. Inheritance hierarchy will be scanned for `<link rel="stylesheet">` and `<style></style>` tags.
90+
8791
## Supported Languages
8892

8993
Supported languages can be configured with the `css.enabledLanguages` setting. By default

icon.png

542 Bytes
Loading

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vscode-html-css",
33
"displayName": "HTML CSS Support",
44
"description": "CSS support for HTML documents",
5-
"version": "1.2.4",
5+
"version": "1.2.5",
66
"publisher": "ecmel",
77
"license": "MIT",
88
"homepage": "https://github.com/ecmel/vscode-html-css",
@@ -21,6 +21,10 @@
2121
],
2222
"keywords": [
2323
"html",
24+
"twig",
25+
"pebble",
26+
"nunjucks",
27+
"mustache",
2428
"css",
2529
"class",
2630
"multi-root ready"

src/extension.ts

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fetch from "node-fetch";
22
import { parse, walk } from "css-tree";
3-
import { isAbsolute, join, dirname } from "path";
3+
import { basename, dirname, extname, isAbsolute, join } from "path";
44
import {
55
CancellationToken,
66
CompletionContext,
@@ -16,19 +16,22 @@ import {
1616
Range,
1717
TextDocument,
1818
Uri,
19-
workspace
19+
workspace,
2020
} from "vscode";
2121

2222
export class ClassCompletionItemProvider implements CompletionItemProvider, Disposable {
2323

2424
readonly none = "__!NONE!__";
2525
readonly start = new Position(0, 0);
2626
readonly cache = new Map<string, Map<string, CompletionItem>>();
27+
readonly empty = new Set<string>();
28+
readonly extends = new Map<string, Set<string>>();
2729
readonly disposables: Disposable[] = [];
2830
readonly isRemote = /^https?:\/\//i;
2931
readonly canComplete = /(id|class|className)\s*=\s*(["'])(?:(?!\2).)*$/si;
3032
readonly findLinkRel = /rel\s*=\s*(["'])((?:(?!\1).)+)\1/si;
3133
readonly findLinkHref = /href\s*=\s*(["'])((?:(?!\1).)+)\1/si;
34+
readonly findExtends = /(?:{{<|{{>|{%)\s*(?:extends)?\s*"?([\.0-9_a-z-A-Z]+)"?\s*(?:%}|}})/i;
3235

3336
dispose() {
3437
let e;
@@ -38,6 +41,16 @@ export class ClassCompletionItemProvider implements CompletionItemProvider, Disp
3841
}
3942

4043
this.cache.clear();
44+
this.extends.clear();
45+
}
46+
47+
watchFile(uri: Uri, listener: (e: Uri) => any) {
48+
const watcher = workspace.createFileSystemWatcher(uri.fsPath, true);
49+
50+
this.disposables.push(
51+
watcher.onDidChange(listener),
52+
watcher.onDidDelete(listener),
53+
watcher);
4154
}
4255

4356
getStyleSheets(uri: Uri): string[] {
@@ -79,15 +92,9 @@ export class ClassCompletionItemProvider implements CompletionItemProvider, Disp
7992

8093
workspace.fs.readFile(file).then(content => {
8194
const items = new Map<string, CompletionItem>();
82-
this.parseTextToItems(content.toString(), items);
83-
84-
const watcher = workspace.createFileSystemWatcher(file.fsPath, true);
85-
const updater = (e: Uri) => this.cache.delete(key);
86-
this.disposables.push(
87-
watcher.onDidChange(updater),
88-
watcher.onDidDelete(updater),
89-
watcher);
9095

96+
this.parseTextToItems(content.toString(), items);
97+
this.watchFile(file, e => this.cache.delete(key));
9198
this.cache.set(key, items);
9299
resolve(key);
93100
}, () => resolve(this.none));
@@ -180,6 +187,45 @@ export class ClassCompletionItemProvider implements CompletionItemProvider, Disp
180187
});
181188
}
182189

190+
findExtendedStyles(uri: Uri, text: string): Thenable<Set<string>> {
191+
return new Promise(resolve => {
192+
const parent = this.findExtends.exec(text);
193+
194+
if (parent) {
195+
const path = uri.fsPath;
196+
const ext = extname(path);
197+
const key = join(dirname(path), basename(parent[1], ext) + ext);
198+
const extend = this.extends.get(key);
199+
200+
if (extend) {
201+
resolve(extend);
202+
} else {
203+
const file = Uri.file(key);
204+
205+
workspace.fs.readFile(file).then(content => {
206+
const text = content.toString();
207+
208+
Promise.all([
209+
this.findDocumentLinks(file, text),
210+
this.findDocumentStyles(file, text),
211+
this.findExtendedStyles(file, text)
212+
]).then(sets => {
213+
const keys = new Set<string>();
214+
215+
sets.forEach(set => set.forEach(k => keys.add(k)));
216+
this.watchFile(file, e => this.extends.delete(key));
217+
this.extends.set(key, keys);
218+
resolve(keys);
219+
});
220+
221+
}, () => resolve(this.empty));
222+
}
223+
} else {
224+
resolve(this.empty);
225+
}
226+
});
227+
}
228+
183229
buildItems(sets: Set<string>[], kind: CompletionItemKind): CompletionItem[] {
184230
const items = new Map<string, CompletionItem>();
185231
const keys = new Set<string>();
@@ -219,7 +265,8 @@ export class ClassCompletionItemProvider implements CompletionItemProvider, Disp
219265
Promise.all([
220266
this.findStyleSheets(uri),
221267
this.findDocumentLinks(uri, text),
222-
this.findDocumentStyles(uri, text)
268+
this.findDocumentStyles(uri, text),
269+
this.findExtendedStyles(uri, text)
223270
]).then(keys => resolve(this.buildItems(keys, type)));
224271
} else {
225272
reject();

0 commit comments

Comments
 (0)