|
3 | 3 | * Licensed under the MIT License. See License.txt in the project root for license information.
|
4 | 4 | *--------------------------------------------------------------------------------------------*/
|
5 | 5 |
|
6 |
| -import { TextDocument, Position, Range } from 'vscode-languageclient'; |
7 | 6 | import { LanguageService, TokenType } from 'vscode-html-languageservice';
|
8 | 7 |
|
9 |
| -export interface LanguageRange extends Range { |
10 |
| - languageId: string | undefined; |
11 |
| - attributeValue?: boolean; |
12 |
| -} |
13 |
| - |
14 |
| -export interface HTMLDocumentRegions { |
15 |
| - getEmbeddedDocument(languageId: string, ignoreAttributeValues?: boolean): TextDocument; |
16 |
| - getLanguageRanges(range: Range): LanguageRange[]; |
17 |
| - getLanguageAtPosition(position: Position): string | undefined; |
18 |
| - getLanguagesInDocument(): string[]; |
19 |
| - getImportedScripts(): string[]; |
20 |
| -} |
21 |
| - |
22 |
| -export const CSS_STYLE_RULE = '__'; |
23 |
| - |
24 | 8 | interface EmbeddedRegion {
|
25 | 9 | languageId: string | undefined;
|
26 | 10 | start: number;
|
@@ -143,269 +127,6 @@ export function getCSSVirtualContent(
|
143 | 127 | return content;
|
144 | 128 | }
|
145 | 129 |
|
146 |
| - |
147 |
| -export function getDocumentRegions( |
148 |
| - languageService: LanguageService, |
149 |
| - document: TextDocument |
150 |
| -): HTMLDocumentRegions { |
151 |
| - const regions: EmbeddedRegion[] = []; |
152 |
| - const scanner = languageService.createScanner(document.getText()); |
153 |
| - let lastTagName = ''; |
154 |
| - let lastAttributeName: string | null = null; |
155 |
| - let languageIdFromType: string | undefined = undefined; |
156 |
| - const importedScripts: string[] = []; |
157 |
| - |
158 |
| - let token = scanner.scan(); |
159 |
| - while (token !== TokenType.EOS) { |
160 |
| - switch (token) { |
161 |
| - case TokenType.StartTag: |
162 |
| - lastTagName = scanner.getTokenText(); |
163 |
| - lastAttributeName = null; |
164 |
| - languageIdFromType = 'javascript'; |
165 |
| - break; |
166 |
| - case TokenType.Styles: |
167 |
| - regions.push({ |
168 |
| - languageId: 'css', |
169 |
| - start: scanner.getTokenOffset(), |
170 |
| - end: scanner.getTokenEnd() |
171 |
| - }); |
172 |
| - break; |
173 |
| - case TokenType.Script: |
174 |
| - regions.push({ |
175 |
| - languageId: languageIdFromType, |
176 |
| - start: scanner.getTokenOffset(), |
177 |
| - end: scanner.getTokenEnd() |
178 |
| - }); |
179 |
| - break; |
180 |
| - case TokenType.AttributeName: |
181 |
| - lastAttributeName = scanner.getTokenText(); |
182 |
| - break; |
183 |
| - case TokenType.AttributeValue: |
184 |
| - if (lastAttributeName === 'src' && lastTagName.toLowerCase() === 'script') { |
185 |
| - let value = scanner.getTokenText(); |
186 |
| - if (value[0] === "'" || value[0] === '"') { |
187 |
| - value = value.substr(1, value.length - 1); |
188 |
| - } |
189 |
| - importedScripts.push(value); |
190 |
| - } else if (lastAttributeName === 'type' && lastTagName.toLowerCase() === 'script') { |
191 |
| - if ( |
192 |
| - /["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test( |
193 |
| - scanner.getTokenText() |
194 |
| - ) |
195 |
| - ) { |
196 |
| - languageIdFromType = 'javascript'; |
197 |
| - } else if (/["']text\/typescript["']/.test(scanner.getTokenText())) { |
198 |
| - languageIdFromType = 'typescript'; |
199 |
| - } else { |
200 |
| - languageIdFromType = undefined; |
201 |
| - } |
202 |
| - } else { |
203 |
| - const attributeLanguageId = getAttributeLanguage(lastAttributeName!); |
204 |
| - if (attributeLanguageId) { |
205 |
| - let start = scanner.getTokenOffset(); |
206 |
| - let end = scanner.getTokenEnd(); |
207 |
| - const firstChar = document.getText()[start]; |
208 |
| - if (firstChar === "'" || firstChar === '"') { |
209 |
| - start++; |
210 |
| - end--; |
211 |
| - } |
212 |
| - regions.push({ |
213 |
| - languageId: attributeLanguageId, |
214 |
| - start, |
215 |
| - end, |
216 |
| - attributeValue: true |
217 |
| - }); |
218 |
| - } |
219 |
| - } |
220 |
| - lastAttributeName = null; |
221 |
| - break; |
222 |
| - } |
223 |
| - token = scanner.scan(); |
224 |
| - } |
225 |
| - return { |
226 |
| - getLanguageRanges: (range: Range) => getLanguageRanges(document, regions, range), |
227 |
| - getEmbeddedDocument: (languageId: string, ignoreAttributeValues: boolean) => |
228 |
| - getEmbeddedDocument(document, regions, languageId, ignoreAttributeValues), |
229 |
| - getLanguageAtPosition: (position: Position) => |
230 |
| - getLanguageAtPosition(document, regions, position), |
231 |
| - getLanguagesInDocument: () => getLanguagesInDocument(document, regions), |
232 |
| - getImportedScripts: () => importedScripts |
233 |
| - }; |
234 |
| -} |
235 |
| - |
236 |
| -function getLanguageRanges( |
237 |
| - document: TextDocument, |
238 |
| - regions: EmbeddedRegion[], |
239 |
| - range: Range |
240 |
| -): LanguageRange[] { |
241 |
| - const result: LanguageRange[] = []; |
242 |
| - let currentPos = range ? range.start : Position.create(0, 0); |
243 |
| - let currentOffset = range ? document.offsetAt(range.start) : 0; |
244 |
| - const endOffset = range ? document.offsetAt(range.end) : document.getText().length; |
245 |
| - for (const region of regions) { |
246 |
| - if (region.end > currentOffset && region.start < endOffset) { |
247 |
| - const start = Math.max(region.start, currentOffset); |
248 |
| - const startPos = document.positionAt(start); |
249 |
| - if (currentOffset < region.start) { |
250 |
| - result.push({ |
251 |
| - start: currentPos, |
252 |
| - end: startPos, |
253 |
| - languageId: 'html' |
254 |
| - }); |
255 |
| - } |
256 |
| - const end = Math.min(region.end, endOffset); |
257 |
| - const endPos = document.positionAt(end); |
258 |
| - if (end > region.start) { |
259 |
| - result.push({ |
260 |
| - start: startPos, |
261 |
| - end: endPos, |
262 |
| - languageId: region.languageId, |
263 |
| - attributeValue: region.attributeValue |
264 |
| - }); |
265 |
| - } |
266 |
| - currentOffset = end; |
267 |
| - currentPos = endPos; |
268 |
| - } |
269 |
| - } |
270 |
| - if (currentOffset < endOffset) { |
271 |
| - const endPos = range ? range.end : document.positionAt(endOffset); |
272 |
| - result.push({ |
273 |
| - start: currentPos, |
274 |
| - end: endPos, |
275 |
| - languageId: 'html' |
276 |
| - }); |
277 |
| - } |
278 |
| - return result; |
279 |
| -} |
280 |
| - |
281 |
| -function getLanguagesInDocument( |
282 |
| - _document: TextDocument, |
283 |
| - regions: EmbeddedRegion[] |
284 |
| -): string[] { |
285 |
| - const result = []; |
286 |
| - for (const region of regions) { |
287 |
| - if (region.languageId && result.indexOf(region.languageId) === -1) { |
288 |
| - result.push(region.languageId); |
289 |
| - if (result.length === 3) { |
290 |
| - return result; |
291 |
| - } |
292 |
| - } |
293 |
| - } |
294 |
| - result.push('html'); |
295 |
| - return result; |
296 |
| -} |
297 |
| - |
298 |
| -function getLanguageAtPosition( |
299 |
| - document: TextDocument, |
300 |
| - regions: EmbeddedRegion[], |
301 |
| - position: Position |
302 |
| -): string | undefined { |
303 |
| - const offset = document.offsetAt(position); |
304 |
| - for (const region of regions) { |
305 |
| - if (region.start <= offset) { |
306 |
| - if (offset <= region.end) { |
307 |
| - return region.languageId; |
308 |
| - } |
309 |
| - } else { |
310 |
| - break; |
311 |
| - } |
312 |
| - } |
313 |
| - return 'html'; |
314 |
| -} |
315 |
| - |
316 |
| -function getEmbeddedDocument( |
317 |
| - document: TextDocument, |
318 |
| - contents: EmbeddedRegion[], |
319 |
| - languageId: string, |
320 |
| - ignoreAttributeValues: boolean |
321 |
| -): TextDocument { |
322 |
| - let currentPos = 0; |
323 |
| - const oldContent = document.getText(); |
324 |
| - let result = ''; |
325 |
| - let lastSuffix = ''; |
326 |
| - for (const c of contents) { |
327 |
| - if (c.languageId === languageId && (!ignoreAttributeValues || !c.attributeValue)) { |
328 |
| - result = substituteWithWhitespace( |
329 |
| - result, |
330 |
| - currentPos, |
331 |
| - c.start, |
332 |
| - oldContent, |
333 |
| - lastSuffix, |
334 |
| - getPrefix(c) |
335 |
| - ); |
336 |
| - result += oldContent.substring(c.start, c.end); |
337 |
| - currentPos = c.end; |
338 |
| - lastSuffix = getSuffix(c); |
339 |
| - } |
340 |
| - } |
341 |
| - result = substituteWithWhitespace( |
342 |
| - result, |
343 |
| - currentPos, |
344 |
| - oldContent.length, |
345 |
| - oldContent, |
346 |
| - lastSuffix, |
347 |
| - '' |
348 |
| - ); |
349 |
| - return TextDocument.create(document.uri, languageId, document.version, result); |
350 |
| -} |
351 |
| - |
352 |
| -function getPrefix(c: EmbeddedRegion) { |
353 |
| - if (c.attributeValue) { |
354 |
| - switch (c.languageId) { |
355 |
| - case 'css': |
356 |
| - return CSS_STYLE_RULE + '{'; |
357 |
| - } |
358 |
| - } |
359 |
| - return ''; |
360 |
| -} |
361 |
| -function getSuffix(c: EmbeddedRegion) { |
362 |
| - if (c.attributeValue) { |
363 |
| - switch (c.languageId) { |
364 |
| - case 'css': |
365 |
| - return '}'; |
366 |
| - case 'javascript': |
367 |
| - return ';'; |
368 |
| - } |
369 |
| - } |
370 |
| - return ''; |
371 |
| -} |
372 |
| - |
373 |
| -function substituteWithWhitespace( |
374 |
| - result: string, |
375 |
| - start: number, |
376 |
| - end: number, |
377 |
| - oldContent: string, |
378 |
| - before: string, |
379 |
| - after: string |
380 |
| -) { |
381 |
| - let accumulatedWS = 0; |
382 |
| - result += before; |
383 |
| - for (let i = start + before.length; i < end; i++) { |
384 |
| - const ch = oldContent[i]; |
385 |
| - if (ch === '\n' || ch === '\r') { |
386 |
| - // only write new lines, skip the whitespace |
387 |
| - accumulatedWS = 0; |
388 |
| - result += ch; |
389 |
| - } else { |
390 |
| - accumulatedWS++; |
391 |
| - } |
392 |
| - } |
393 |
| - result = append(result, ' ', accumulatedWS - after.length); |
394 |
| - result += after; |
395 |
| - return result; |
396 |
| -} |
397 |
| - |
398 |
| -function append(result: string, str: string, n: number): string { |
399 |
| - while (n > 0) { |
400 |
| - if (n & 1) { |
401 |
| - result += str; |
402 |
| - } |
403 |
| - n >>= 1; |
404 |
| - str += str; |
405 |
| - } |
406 |
| - return result; |
407 |
| -} |
408 |
| - |
409 | 130 | function getAttributeLanguage(attributeName: string): string | null {
|
410 | 131 | const match = attributeName.match(/^(style)$|^(on\w+)$/i);
|
411 | 132 | if (!match) {
|
|
0 commit comments