volar-service-html 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/out/index.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { ServiceContext, Service } from '@volar/language-service';
2
+ import * as html from 'vscode-html-languageservice';
3
+ import { TextDocument } from 'vscode-languageserver-textdocument';
4
+ export declare function getHtmlDocument(document: TextDocument): html.HTMLDocument;
5
+ export interface PluginInstance extends ReturnType<Service> {
6
+ getHtmlLs: () => html.LanguageService;
7
+ updateCustomData(extraData: html.IHTMLDataProvider[]): void;
8
+ }
9
+ declare const _default: (options?: {
10
+ validLang?: string;
11
+ disableCustomData?: boolean;
12
+ }) => (context: ServiceContext | undefined) => PluginInstance;
13
+ export default _default;
package/out/index.js ADDED
@@ -0,0 +1,309 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.getHtmlDocument = void 0;
27
+ const html = __importStar(require("vscode-html-languageservice"));
28
+ const vscode = __importStar(require("vscode-languageserver-protocol"));
29
+ const path = __importStar(require("path"));
30
+ const parserLs = html.getLanguageService();
31
+ const htmlDocuments = new WeakMap();
32
+ function getHtmlDocument(document) {
33
+ const cache = htmlDocuments.get(document);
34
+ if (cache) {
35
+ const [cacheVersion, cacheDoc] = cache;
36
+ if (cacheVersion === document.version) {
37
+ return cacheDoc;
38
+ }
39
+ }
40
+ const doc = parserLs.parseHTMLDocument(document);
41
+ htmlDocuments.set(document, [document.version, doc]);
42
+ return doc;
43
+ }
44
+ exports.getHtmlDocument = getHtmlDocument;
45
+ exports.default = (options = {}) => (context) => {
46
+ // https://github.com/microsoft/vscode/blob/09850876e652688fb142e2e19fd00fd38c0bc4ba/extensions/html-language-features/server/src/htmlServer.ts#L183
47
+ const triggerCharacters = ['.', ':', '<', '"', '=', '/'];
48
+ if (!context) {
49
+ return { triggerCharacters };
50
+ }
51
+ let shouldUpdateCustomData = true;
52
+ let customData = [];
53
+ let extraData = [];
54
+ const htmlLs = html.getLanguageService({ fileSystemProvider: context.env.fileSystemProvider });
55
+ context.env.onDidChangeConfiguration?.(() => {
56
+ shouldUpdateCustomData = true;
57
+ });
58
+ return {
59
+ triggerCharacters,
60
+ async resolveRuleContext(context) {
61
+ if (options.validLang === 'html') {
62
+ await worker(context.document, (htmlDocument) => {
63
+ context.html = {
64
+ document: htmlDocument,
65
+ languageService: htmlLs,
66
+ };
67
+ });
68
+ }
69
+ return context;
70
+ },
71
+ getHtmlLs: () => htmlLs,
72
+ updateCustomData: updateExtraCustomData,
73
+ async provideCompletionItems(document, position) {
74
+ return worker(document, async (htmlDocument) => {
75
+ const configs = await context.env.getConfiguration?.('html.completion');
76
+ if (context.env.documentContext) {
77
+ return htmlLs.doComplete2(document, position, htmlDocument, context.env.documentContext, configs);
78
+ }
79
+ else {
80
+ return htmlLs.doComplete(document, position, htmlDocument, configs);
81
+ }
82
+ });
83
+ },
84
+ provideRenameRange(document, position) {
85
+ return worker(document, (htmlDocument) => {
86
+ const offset = document.offsetAt(position);
87
+ return htmlLs
88
+ .findDocumentHighlights(document, position, htmlDocument)
89
+ ?.find(h => offset >= document.offsetAt(h.range.start) && offset <= document.offsetAt(h.range.end))
90
+ ?.range;
91
+ });
92
+ },
93
+ provideRenameEdits(document, position, newName) {
94
+ return worker(document, (htmlDocument) => {
95
+ return htmlLs.doRename(document, position, newName, htmlDocument);
96
+ });
97
+ },
98
+ async provideHover(document, position) {
99
+ return worker(document, async (htmlDocument) => {
100
+ const hoverSettings = await context.env.getConfiguration?.('html.hover');
101
+ return htmlLs.doHover(document, position, htmlDocument, hoverSettings);
102
+ });
103
+ },
104
+ provideDocumentHighlights(document, position) {
105
+ return worker(document, (htmlDocument) => {
106
+ return htmlLs.findDocumentHighlights(document, position, htmlDocument);
107
+ });
108
+ },
109
+ provideDocumentLinks(document) {
110
+ return worker(document, () => {
111
+ if (!context.env.documentContext)
112
+ return;
113
+ return htmlLs.findDocumentLinks(document, context.env.documentContext);
114
+ });
115
+ },
116
+ provideDocumentSymbols(document) {
117
+ return worker(document, (htmlDocument) => {
118
+ // TODO: wait for https://github.com/microsoft/vscode-html-languageservice/pull/152
119
+ const symbols = [];
120
+ htmlDocument.roots.forEach(node => {
121
+ provideFileSymbolsInternal(document, node, symbols);
122
+ });
123
+ return symbols;
124
+ });
125
+ },
126
+ provideFoldingRanges(document) {
127
+ return worker(document, () => {
128
+ return htmlLs.getFoldingRanges(document);
129
+ });
130
+ },
131
+ provideSelectionRanges(document, positions) {
132
+ return worker(document, () => {
133
+ return htmlLs.getSelectionRanges(document, positions);
134
+ });
135
+ },
136
+ async provideDocumentFormattingEdits(document, formatRange, options) {
137
+ return worker(document, async () => {
138
+ const options_2 = await context.env.getConfiguration?.('html.format');
139
+ if (options_2?.enable === false) {
140
+ return;
141
+ }
142
+ { // https://github.com/microsoft/vscode/blob/dce493cb6e36346ef2714e82c42ce14fc461b15c/extensions/html-language-features/server/src/modes/formatting.ts#L13-L23
143
+ const endPos = formatRange.end;
144
+ let endOffset = document.offsetAt(endPos);
145
+ const content = document.getText();
146
+ if (endPos.character === 0 && endPos.line > 0 && endOffset !== content.length) {
147
+ // if selection ends after a new line, exclude that new line
148
+ const prevLineStart = document.offsetAt(vscode.Position.create(endPos.line - 1, 0));
149
+ while (isEOL(content, endOffset - 1) && endOffset > prevLineStart) {
150
+ endOffset--;
151
+ }
152
+ formatRange = vscode.Range.create(formatRange.start, document.positionAt(endOffset));
153
+ }
154
+ }
155
+ return htmlLs.format(document, formatRange, {
156
+ ...options_2,
157
+ ...options,
158
+ });
159
+ });
160
+ },
161
+ provideFormattingIndentSensitiveLines(document) {
162
+ return worker(document, (htmlDocument) => {
163
+ const lines = [];
164
+ /**
165
+ * comments
166
+ */
167
+ const scanner = htmlLs.createScanner(document.getText());
168
+ let token = scanner.scan();
169
+ let startCommentTagLine;
170
+ while (token !== html.TokenType.EOS) {
171
+ if (token === html.TokenType.StartCommentTag) {
172
+ startCommentTagLine = document.positionAt(scanner.getTokenOffset()).line;
173
+ }
174
+ else if (token === html.TokenType.EndCommentTag) {
175
+ const line = document.positionAt(scanner.getTokenOffset()).line;
176
+ for (let i = startCommentTagLine + 1; i <= line; i++) {
177
+ lines.push(i);
178
+ }
179
+ startCommentTagLine = undefined;
180
+ }
181
+ else if (token === html.TokenType.AttributeValue) {
182
+ const startLine = document.positionAt(scanner.getTokenOffset()).line;
183
+ for (let i = 1; i < scanner.getTokenText().split('\n').length; i++) {
184
+ lines.push(startLine + i);
185
+ }
186
+ }
187
+ token = scanner.scan();
188
+ }
189
+ /**
190
+ * tags
191
+ */
192
+ // https://github.com/beautify-web/js-beautify/blob/686f8c1b265990908ece86ce39291733c75c997c/js/src/html/options.js#L81
193
+ const indentSensitiveTags = new Set(['pre', 'textarea']);
194
+ htmlDocument.roots.forEach(function visit(node) {
195
+ if (node.tag !== undefined
196
+ && node.startTagEnd !== undefined
197
+ && node.endTagStart !== undefined
198
+ && indentSensitiveTags.has(node.tag)) {
199
+ for (let i = document.positionAt(node.startTagEnd).line + 1; i <= document.positionAt(node.endTagStart).line; i++) {
200
+ lines.push(i);
201
+ }
202
+ }
203
+ else {
204
+ node.children.forEach(visit);
205
+ }
206
+ });
207
+ return lines;
208
+ });
209
+ },
210
+ provideLinkedEditingRanges(document, position) {
211
+ return worker(document, (htmlDocument) => {
212
+ const ranges = htmlLs.findLinkedEditingRanges(document, position, htmlDocument);
213
+ if (!ranges)
214
+ return;
215
+ return { ranges };
216
+ });
217
+ },
218
+ async provideAutoInsertionEdit(document, position, insertContext) {
219
+ return worker(document, async (htmlDocument) => {
220
+ const lastCharacter = insertContext.lastChange.text[insertContext.lastChange.text.length - 1];
221
+ if (insertContext.lastChange.rangeLength === 0 && lastCharacter === '=') {
222
+ const enabled = (await context.env.getConfiguration?.('html.autoCreateQuotes')) ?? true;
223
+ if (enabled) {
224
+ const text = htmlLs.doQuoteComplete(document, position, htmlDocument, await context.env.getConfiguration?.('html.completion'));
225
+ if (text) {
226
+ return text;
227
+ }
228
+ }
229
+ }
230
+ if (insertContext.lastChange.rangeLength === 0 && (lastCharacter === '>' || lastCharacter === '/')) {
231
+ const enabled = (await context.env.getConfiguration?.('html.autoClosingTags')) ?? true;
232
+ if (enabled) {
233
+ const text = htmlLs.doTagComplete(document, position, htmlDocument);
234
+ if (text) {
235
+ return text;
236
+ }
237
+ }
238
+ }
239
+ });
240
+ },
241
+ };
242
+ async function initCustomData() {
243
+ if (shouldUpdateCustomData && !options.disableCustomData) {
244
+ shouldUpdateCustomData = false;
245
+ customData = await getCustomData();
246
+ htmlLs.setDataProviders(true, [...customData, ...extraData]);
247
+ }
248
+ }
249
+ function updateExtraCustomData(data) {
250
+ extraData = data;
251
+ htmlLs.setDataProviders(true, [...customData, ...extraData]);
252
+ }
253
+ async function getCustomData() {
254
+ const customData = await context?.env.getConfiguration?.('html.customData') ?? [];
255
+ const newData = [];
256
+ for (const customDataPath of customData) {
257
+ try {
258
+ const jsonPath = path.resolve(customDataPath);
259
+ newData.push(html.newHTMLDataProvider(customDataPath, require(jsonPath)));
260
+ }
261
+ catch (error) {
262
+ console.error(error);
263
+ }
264
+ }
265
+ return newData;
266
+ }
267
+ async function worker(document, callback) {
268
+ if (document.languageId !== (options.validLang ?? 'html'))
269
+ return;
270
+ const htmlDocument = getHtmlDocument(document);
271
+ if (!htmlDocument)
272
+ return;
273
+ await initCustomData();
274
+ return callback(htmlDocument);
275
+ }
276
+ };
277
+ function isEOL(content, offset) {
278
+ return isNewlineCharacter(content.charCodeAt(offset));
279
+ }
280
+ const CR = '\r'.charCodeAt(0);
281
+ const NL = '\n'.charCodeAt(0);
282
+ function isNewlineCharacter(charCode) {
283
+ return charCode === CR || charCode === NL;
284
+ }
285
+ function provideFileSymbolsInternal(document, node, symbols) {
286
+ const name = nodeToName(node);
287
+ const range = vscode.Range.create(document.positionAt(node.start), document.positionAt(node.end));
288
+ const symbol = vscode.DocumentSymbol.create(name, undefined, vscode.SymbolKind.Field, range, range);
289
+ symbols.push(symbol);
290
+ node.children.forEach(child => {
291
+ symbol.children ??= [];
292
+ provideFileSymbolsInternal(document, child, symbol.children);
293
+ });
294
+ }
295
+ function nodeToName(node) {
296
+ let name = node.tag;
297
+ if (node.attributes) {
298
+ const id = node.attributes['id'];
299
+ const classes = node.attributes['class'];
300
+ if (id) {
301
+ name += `#${id.replace(/[\"\']/g, '')}`;
302
+ }
303
+ if (classes) {
304
+ name += classes.replace(/[\"\']/g, '').split(/\s+/).map(className => `.${className}`).join('');
305
+ }
306
+ }
307
+ return name || '?';
308
+ }
309
+ //# sourceMappingURL=index.js.map
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "volar-service-html",
3
+ "version": "0.0.0",
4
+ "main": "out/index.js",
5
+ "license": "MIT",
6
+ "files": [
7
+ "rules.d.ts",
8
+ "out/**/*.js",
9
+ "out/**/*.d.ts"
10
+ ],
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/volarjs/services.git",
14
+ "directory": "packages/html"
15
+ },
16
+ "dependencies": {
17
+ "vscode-html-languageservice": "^5.0.4",
18
+ "vscode-languageserver-protocol": "^3.17.3",
19
+ "vscode-languageserver-textdocument": "^1.0.8"
20
+ },
21
+ "peerDependencies": {
22
+ "@volar/language-service": "*"
23
+ },
24
+ "peerDependenciesMeta": {
25
+ "@volar/language-service": {
26
+ "optional": true
27
+ }
28
+ },
29
+ "gitHead": "1011561ac4bbf79c53c3b80c27692569bf861519"
30
+ }
package/rules.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import * as html from 'vscode-html-languageservice';
2
+
3
+ declare module '@volar/language-service' {
4
+ interface RuleContext {
5
+ html?: {
6
+ document: html.HTMLDocument;
7
+ languageService: html.LanguageService;
8
+ }
9
+ }
10
+ }