Emmet improve Expand Abbreviation perf (#113558)
This commit is contained in:
parent
b5cd082cd4
commit
15ba9aee0e
4 changed files with 82 additions and 155 deletions
|
@ -4,8 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Node, HtmlNode, Rule, Property, Stylesheet } from 'EmmetNode';
|
||||
import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode, parsePartialStylesheet, isStyleAttribute, getEmbeddedCssNodeIfAny, allowedMimeTypesInScriptTag, toLSTextDocument } from './util';
|
||||
import { Node, HtmlNode, Rule, Property, Stylesheet } from 'EmmetFlatNode';
|
||||
import { getEmmetHelper, getFlatNode, getMappingForIncludedLanguages, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode, parsePartialStylesheet, isStyleAttribute, getEmbeddedCssNodeIfAny, allowedMimeTypesInScriptTag, toLSTextDocument } from './util';
|
||||
import { getRootNode as parseDocument } from './parseDocument';
|
||||
|
||||
const trimRegex = /[\u00a0]*[\d#\-\*\u2022]+\.?/;
|
||||
const hexColorRegex = /^#[\da-fA-F]{0,6}$/;
|
||||
|
@ -59,7 +60,7 @@ function doWrapping(individualLines: boolean, args: any) {
|
|||
args['language'] = editor.document.languageId;
|
||||
}
|
||||
const syntax = getSyntaxFromArgs(args) || 'html';
|
||||
const rootNode = parseDocument(editor.document, false);
|
||||
const rootNode = parseDocument(editor.document, true);
|
||||
|
||||
let inPreview = false;
|
||||
let currentValue = '';
|
||||
|
@ -68,31 +69,40 @@ function doWrapping(individualLines: boolean, args: any) {
|
|||
// Fetch general information for the succesive expansions. i.e. the ranges to replace and its contents
|
||||
const rangesToReplace: PreviewRangesWithContent[] = editor.selections.sort((a: vscode.Selection, b: vscode.Selection) => { return a.start.compareTo(b.start); }).map(selection => {
|
||||
let rangeToReplace: vscode.Range = selection.isReversed ? new vscode.Range(selection.active, selection.anchor) : selection;
|
||||
const document = editor.document;
|
||||
if (!rangeToReplace.isSingleLine && rangeToReplace.end.character === 0) {
|
||||
const previousLine = rangeToReplace.end.line - 1;
|
||||
const lastChar = editor.document.lineAt(previousLine).text.length;
|
||||
const lastChar = document.lineAt(previousLine).text.length;
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start, new vscode.Position(previousLine, lastChar));
|
||||
} else if (rangeToReplace.isEmpty) {
|
||||
const { active } = selection;
|
||||
const currentNode = getNode(rootNode, active, true);
|
||||
if (currentNode && (currentNode.start.line === active.line || currentNode.end.line === active.line)) {
|
||||
rangeToReplace = new vscode.Range(currentNode.start, currentNode.end);
|
||||
const activeOffset = document.offsetAt(active);
|
||||
const currentNode = getFlatNode(rootNode, activeOffset, true);
|
||||
if (currentNode) {
|
||||
const currentNodeStart = document.positionAt(currentNode.start);
|
||||
const currentNodeEnd = document.positionAt(currentNode.end);
|
||||
if (currentNodeStart.line === active.line || currentNodeEnd.line === active.line) {
|
||||
rangeToReplace = new vscode.Range(currentNodeStart, currentNodeEnd);
|
||||
}
|
||||
else {
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start.line, 0, rangeToReplace.start.line, document.lineAt(rangeToReplace.start.line).text.length);
|
||||
}
|
||||
} else {
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start.line, 0, rangeToReplace.start.line, editor.document.lineAt(rangeToReplace.start.line).text.length);
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start.line, 0, rangeToReplace.start.line, document.lineAt(rangeToReplace.start.line).text.length);
|
||||
}
|
||||
}
|
||||
|
||||
const firstLineOfSelection = editor.document.lineAt(rangeToReplace.start).text.substr(rangeToReplace.start.character);
|
||||
const firstLineOfSelection = document.lineAt(rangeToReplace.start).text.substr(rangeToReplace.start.character);
|
||||
const matches = firstLineOfSelection.match(/^(\s*)/);
|
||||
const extraWhitespaceSelected = matches ? matches[1].length : 0;
|
||||
rangeToReplace = new vscode.Range(rangeToReplace.start.line, rangeToReplace.start.character + extraWhitespaceSelected, rangeToReplace.end.line, rangeToReplace.end.character);
|
||||
|
||||
let textToWrapInPreview: string[];
|
||||
const textToReplace = editor.document.getText(rangeToReplace);
|
||||
const textToReplace = document.getText(rangeToReplace);
|
||||
if (individualLines) {
|
||||
textToWrapInPreview = textToReplace.split('\n').map(x => x.trim());
|
||||
} else {
|
||||
const wholeFirstLine = editor.document.lineAt(rangeToReplace.start).text;
|
||||
const wholeFirstLine = document.lineAt(rangeToReplace.start).text;
|
||||
const otherMatches = wholeFirstLine.match(/^(\s*)/);
|
||||
const precedingWhitespace = otherMatches ? otherMatches[1] : '';
|
||||
textToWrapInPreview = rangeToReplace.isSingleLine ? [textToReplace] : ['\n\t' + textToReplace.split('\n' + precedingWhitespace).join('\n\t') + '\n'];
|
||||
|
@ -327,7 +337,7 @@ export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined
|
|||
if (editor.selections.length === 1 && isStyleSheet(editor.document.languageId) && usePartialParsing && editor.document.lineCount > 1000) {
|
||||
rootNode = parsePartialStylesheet(editor.document, editor.selection.isReversed ? editor.selection.anchor : editor.selection.active);
|
||||
} else {
|
||||
rootNode = parseDocument(editor.document, false);
|
||||
rootNode = parseDocument(editor.document, true);
|
||||
}
|
||||
|
||||
return rootNode;
|
||||
|
@ -342,24 +352,25 @@ export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined
|
|||
if (!helper.isAbbreviationValid(syntax, abbreviation)) {
|
||||
return;
|
||||
}
|
||||
let currentNode = getNode(getRootNode(), position, true);
|
||||
const offset = editor.document.offsetAt(position);
|
||||
let currentNode = getFlatNode(getRootNode(), offset, true);
|
||||
let validateLocation = true;
|
||||
let syntaxToUse = syntax;
|
||||
|
||||
if (editor.document.languageId === 'html') {
|
||||
if (isStyleAttribute(currentNode, position)) {
|
||||
if (isStyleAttribute(currentNode, offset)) {
|
||||
syntaxToUse = 'css';
|
||||
validateLocation = false;
|
||||
} else {
|
||||
const embeddedCssNode = getEmbeddedCssNodeIfAny(editor.document, currentNode, position);
|
||||
if (embeddedCssNode) {
|
||||
currentNode = getNode(embeddedCssNode, position, true);
|
||||
currentNode = getFlatNode(embeddedCssNode, offset, true);
|
||||
syntaxToUse = 'css';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (validateLocation && !isValidLocationForEmmetAbbreviation(editor.document, getRootNode(), currentNode, syntaxToUse, position, rangeToReplace)) {
|
||||
if (validateLocation && !isValidLocationForEmmetAbbreviation(editor.document, getRootNode(), currentNode, syntaxToUse, offset, rangeToReplace)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -393,10 +404,10 @@ function fallbackTab(): Thenable<boolean | undefined> {
|
|||
* @param position position to validate
|
||||
* @param abbreviationRange The range of the abbreviation for which given position is being validated
|
||||
*/
|
||||
export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocument, rootNode: Node | undefined, currentNode: Node | null, syntax: string, position: vscode.Position, abbreviationRange: vscode.Range): boolean {
|
||||
export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocument, rootNode: Node | undefined, currentNode: Node | undefined, syntax: string, offset: number, abbreviationRange: vscode.Range): boolean {
|
||||
if (isStyleSheet(syntax)) {
|
||||
const stylesheet = <Stylesheet>rootNode;
|
||||
if (stylesheet && (stylesheet.comments || []).some(x => position.isAfterOrEqual(x.start) && position.isBeforeOrEqual(x.end))) {
|
||||
if (stylesheet && (stylesheet.comments || []).some(x => offset >= x.start && offset <= x.end)) {
|
||||
return false;
|
||||
}
|
||||
// Continue validation only if the file was parse-able and the currentNode has been found
|
||||
|
@ -419,14 +430,14 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
const propertyNode = <Property>currentNode;
|
||||
if (propertyNode.terminatorToken
|
||||
&& propertyNode.separator
|
||||
&& position.isAfterOrEqual(propertyNode.separatorToken.end)
|
||||
&& position.isBeforeOrEqual(propertyNode.terminatorToken.start)
|
||||
&& offset >= propertyNode.separatorToken.end
|
||||
&& offset <= propertyNode.terminatorToken.start
|
||||
&& abbreviation.indexOf(':') === -1) {
|
||||
return hexColorRegex.test(abbreviation) || abbreviation === '!';
|
||||
}
|
||||
if (!propertyNode.terminatorToken
|
||||
&& propertyNode.separator
|
||||
&& position.isAfterOrEqual(propertyNode.separatorToken.end)
|
||||
&& offset >= propertyNode.separatorToken.end
|
||||
&& abbreviation.indexOf(':') === -1) {
|
||||
return hexColorRegex.test(abbreviation) || abbreviation === '!';
|
||||
}
|
||||
|
@ -444,7 +455,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
const currentCssNode = <Rule>currentNode;
|
||||
|
||||
// Position is valid if it occurs after the `{` that marks beginning of rule contents
|
||||
if (position.isAfter(currentCssNode.contentStartToken.end)) {
|
||||
if (offset > currentCssNode.contentStartToken.end) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -453,12 +464,16 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
// But we should assume it is a valid location for css properties under the parent rule
|
||||
if (currentCssNode.parent
|
||||
&& (currentCssNode.parent.type === 'rule' || currentCssNode.parent.type === 'at-rule')
|
||||
&& currentCssNode.selectorToken
|
||||
&& position.line !== currentCssNode.selectorToken.end.line
|
||||
&& currentCssNode.selectorToken.start.character === abbreviationRange.start.character
|
||||
&& currentCssNode.selectorToken.start.line === abbreviationRange.start.line
|
||||
) {
|
||||
return true;
|
||||
&& currentCssNode.selectorToken) {
|
||||
const position = document.positionAt(offset);
|
||||
const tokenStartPos = document.positionAt(currentCssNode.selectorToken.start);
|
||||
const tokenEndPos = document.positionAt(currentCssNode.selectorToken.end);
|
||||
if (position.line !== tokenEndPos.line
|
||||
&& tokenStartPos.character === abbreviationRange.start.character
|
||||
&& tokenStartPos.line === abbreviationRange.start.line
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -469,7 +484,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
const escape = '\\';
|
||||
const question = '?';
|
||||
const currentHtmlNode = <HtmlNode>currentNode;
|
||||
let start = new vscode.Position(0, 0);
|
||||
let start = 0;
|
||||
|
||||
if (currentHtmlNode) {
|
||||
if (currentHtmlNode.name === 'script') {
|
||||
|
@ -487,27 +502,27 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
|
|||
return false;
|
||||
}
|
||||
|
||||
const innerRange = getInnerRange(currentHtmlNode);
|
||||
|
||||
// Fix for https://github.com/microsoft/vscode/issues/28829
|
||||
if (!innerRange || !innerRange.contains(position)) {
|
||||
if (!currentHtmlNode.open || !currentHtmlNode.close ||
|
||||
!(currentHtmlNode.open.end <= offset && offset <= currentHtmlNode.close.start)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fix for https://github.com/microsoft/vscode/issues/35128
|
||||
// Find the position up till where we will backtrack looking for unescaped < or >
|
||||
// to decide if current position is valid for emmet expansion
|
||||
start = innerRange.start;
|
||||
start = currentHtmlNode.open.end;
|
||||
let lastChildBeforePosition = currentHtmlNode.firstChild;
|
||||
while (lastChildBeforePosition) {
|
||||
if (lastChildBeforePosition.end.isAfter(position)) {
|
||||
if (lastChildBeforePosition.end > offset) {
|
||||
break;
|
||||
}
|
||||
start = lastChildBeforePosition.end;
|
||||
lastChildBeforePosition = lastChildBeforePosition.nextSibling;
|
||||
}
|
||||
}
|
||||
let textToBackTrack = document.getText(new vscode.Range(start.line, start.character, abbreviationRange.start.line, abbreviationRange.start.character));
|
||||
const startPos = document.positionAt(start);
|
||||
let textToBackTrack = document.getText(new vscode.Range(startPos.line, startPos.character, abbreviationRange.start.line, abbreviationRange.start.character));
|
||||
|
||||
// Worse case scenario is when cursor is inside a big chunk of text which needs to backtracked
|
||||
// Backtrack only 500 offsets to ensure we dont waste time doing this
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Node, Stylesheet } from 'EmmetNode';
|
||||
import { Node, Stylesheet } from 'EmmetFlatNode';
|
||||
import { isValidLocationForEmmetAbbreviation, getSyntaxFromArgs } from './abbreviationActions';
|
||||
import { getEmmetHelper, getMappingForIncludedLanguages, parsePartialStylesheet, getEmmetConfiguration, getEmmetMode, isStyleSheet, parseDocument, getNode, allowedMimeTypesInScriptTag, trimQuotes, toLSTextDocument } from './util';
|
||||
import { getEmmetHelper, getMappingForIncludedLanguages, parsePartialStylesheet, getEmmetConfiguration, getEmmetMode, isStyleSheet, getFlatNode, allowedMimeTypesInScriptTag, trimQuotes, toLSTextDocument } from './util';
|
||||
import { getLanguageService, TokenType, Range as LSRange } from 'vscode-html-languageservice';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
export class DefaultCompletionItemProvider implements vscode.CompletionItemProvider {
|
||||
|
||||
|
@ -62,8 +63,8 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
|
|||
|
||||
const helper = getEmmetHelper();
|
||||
let validateLocation = syntax === 'html' || syntax === 'jsx' || syntax === 'xml';
|
||||
let rootNode: Node | undefined = undefined;
|
||||
let currentNode: Node | null = null;
|
||||
let rootNode: Node | undefined;
|
||||
let currentNode: Node | undefined;
|
||||
|
||||
const lsDoc = toLSTextDocument(document);
|
||||
position = document.validatePosition(position);
|
||||
|
@ -145,19 +146,18 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
|
|||
return;
|
||||
}
|
||||
|
||||
const offset = document.offsetAt(position);
|
||||
if (isStyleSheet(document.languageId) && context.triggerKind !== vscode.CompletionTriggerKind.TriggerForIncompleteCompletions) {
|
||||
validateLocation = true;
|
||||
let usePartialParsing = vscode.workspace.getConfiguration('emmet')['optimizeStylesheetParsing'] === true;
|
||||
rootNode = usePartialParsing && document.lineCount > 1000 ? parsePartialStylesheet(document, position) : <Stylesheet>parseDocument(document, false);
|
||||
rootNode = usePartialParsing && document.lineCount > 1000 ? parsePartialStylesheet(document, position) : <Stylesheet>getRootNode(document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
currentNode = getNode(rootNode, position, true);
|
||||
currentNode = getFlatNode(rootNode, offset, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (validateLocation && !isValidLocationForEmmetAbbreviation(document, rootNode, currentNode, syntax, position, toRange(extractAbbreviationResults.abbreviationRange))) {
|
||||
if (validateLocation && !isValidLocationForEmmetAbbreviation(document, rootNode, currentNode, syntax, offset, toRange(extractAbbreviationResults.abbreviationRange))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,15 +7,16 @@ import 'mocha';
|
|||
import * as assert from 'assert';
|
||||
import { withRandomFileEditor } from './testUtils';
|
||||
import * as vscode from 'vscode';
|
||||
import { parsePartialStylesheet, getNode } from '../util';
|
||||
import { parsePartialStylesheet, getFlatNode } from '../util';
|
||||
import { isValidLocationForEmmetAbbreviation } from '../abbreviationActions';
|
||||
|
||||
suite('Tests for partial parse of Stylesheets', () => {
|
||||
|
||||
function isValid(doc: vscode.TextDocument, range: vscode.Range, syntax: string): boolean {
|
||||
const rootNode = parsePartialStylesheet(doc, range.end);
|
||||
const currentNode = getNode(rootNode, range.end, true);
|
||||
return isValidLocationForEmmetAbbreviation(doc, rootNode, currentNode, syntax, range.end, range);
|
||||
const endOffset = doc.offsetAt(range.end);
|
||||
const currentNode = getFlatNode(rootNode, endOffset, true);
|
||||
return isValidLocationForEmmetAbbreviation(doc, rootNode, currentNode, syntax, endOffset, range);
|
||||
}
|
||||
|
||||
test('Ignore block comment inside rule', function (): any {
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
import * as vscode from 'vscode';
|
||||
import parse from '@emmetio/html-matcher';
|
||||
import parseStylesheet from '@emmetio/css-parser';
|
||||
import { Node, HtmlNode, Stylesheet } from 'EmmetNode';
|
||||
import { Node as FlatNode, HtmlNode as HtmlFlatNode, Property as FlatProperty, Rule as FlatRule, CssToken as FlatCssToken } from 'EmmetFlatNode';
|
||||
import { Node as FlatNode, HtmlNode as HtmlFlatNode, Property as FlatProperty, Rule as FlatRule, CssToken as FlatCssToken, Stylesheet as FlatStylesheet } from 'EmmetFlatNode';
|
||||
import { DocumentStreamReader } from './bufferStream';
|
||||
import * as EmmetHelper from 'vscode-emmet-helper';
|
||||
import { TextDocument as LSTextDocument } from 'vscode-languageserver-textdocument';
|
||||
|
@ -138,21 +137,6 @@ export function getEmmetMode(language: string, excludedLanguages: string[]): str
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given document using emmet parsing modules
|
||||
*/
|
||||
export function parseDocument(document: vscode.TextDocument, showError: boolean = true): Node | undefined {
|
||||
let parseContent = isStyleSheet(document.languageId) ? parseStylesheet : parse;
|
||||
try {
|
||||
return parseContent(new DocumentStreamReader(document));
|
||||
} catch (e) {
|
||||
if (showError) {
|
||||
vscode.window.showErrorMessage('Emmet: Failed to parse the file');
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const closeBrace = 125;
|
||||
const openBrace = 123;
|
||||
const slash = 47;
|
||||
|
@ -164,7 +148,7 @@ const star = 42;
|
|||
* @param document vscode.TextDocument
|
||||
* @param position vscode.Position
|
||||
*/
|
||||
export function parsePartialStylesheet(document: vscode.TextDocument, position: vscode.Position): Stylesheet | undefined {
|
||||
export function parsePartialStylesheet(document: vscode.TextDocument, position: vscode.Position): FlatStylesheet | undefined {
|
||||
const isCSS = document.languageId === 'css';
|
||||
let startPosition = new vscode.Position(0, 0);
|
||||
let endPosition = new vscode.Position(document.lineCount - 1, document.lineAt(document.lineCount - 1).text.length);
|
||||
|
@ -304,7 +288,10 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
|
|||
}
|
||||
|
||||
try {
|
||||
return parseStylesheet(new DocumentStreamReader(document, startPosition, new vscode.Range(startPosition, endPosition)));
|
||||
const startOffset = document.offsetAt(startPosition);
|
||||
const endOffset = document.offsetAt(endPosition);
|
||||
const buffer = ' '.repeat(startOffset) + document.getText().substring(startOffset, endOffset);
|
||||
return parseStylesheet(buffer);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
@ -313,31 +300,6 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position:
|
|||
/**
|
||||
* Returns node corresponding to given position in the given root node
|
||||
*/
|
||||
export function getNode(root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean): Node | null {
|
||||
if (!root) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let currentNode = root.firstChild;
|
||||
let foundNode: Node | null = null;
|
||||
|
||||
while (currentNode) {
|
||||
const nodeStart: vscode.Position = currentNode.start;
|
||||
const nodeEnd: vscode.Position = currentNode.end;
|
||||
if ((nodeStart.isBefore(position) && nodeEnd.isAfter(position))
|
||||
|| (includeNodeBoundary && (nodeStart.isBeforeOrEqual(position) && nodeEnd.isAfterOrEqual(position)))) {
|
||||
|
||||
foundNode = currentNode;
|
||||
// Dig deeper
|
||||
currentNode = currentNode.firstChild;
|
||||
} else {
|
||||
currentNode = currentNode.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
return foundNode;
|
||||
}
|
||||
|
||||
export function getFlatNode(root: FlatNode | undefined, offset: number, includeNodeBoundary: boolean): FlatNode | undefined {
|
||||
if (!root) {
|
||||
return;
|
||||
|
@ -380,35 +342,9 @@ export function getFlatNode(root: FlatNode | undefined, offset: number, includeN
|
|||
|
||||
export const allowedMimeTypesInScriptTag = ['text/html', 'text/plain', 'text/x-template', 'text/template', 'text/ng-template'];
|
||||
|
||||
/**
|
||||
* Returns HTML node corresponding to given position in the given root node
|
||||
* If position is inside a script tag of type template, then it will be parsed to find the inner HTML node as well
|
||||
*/
|
||||
export function getHtmlNode(document: vscode.TextDocument, root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean): HtmlNode | undefined {
|
||||
let currentNode = <HtmlNode>getNode(root, position, includeNodeBoundary);
|
||||
if (!currentNode) { return; }
|
||||
|
||||
const isTemplateScript = currentNode.name === 'script' &&
|
||||
(currentNode.attributes &&
|
||||
currentNode.attributes.some(x => x.name.toString() === 'type'
|
||||
&& allowedMimeTypesInScriptTag.indexOf(x.value.toString()) > -1));
|
||||
|
||||
if (isTemplateScript && currentNode.close &&
|
||||
(position.isAfter(currentNode.open.end) && position.isBefore(currentNode.close.start))) {
|
||||
|
||||
let buffer = new DocumentStreamReader(document, currentNode.open.end, new vscode.Range(currentNode.open.end, currentNode.close.start));
|
||||
|
||||
try {
|
||||
let scriptInnerNodes = parse(buffer);
|
||||
currentNode = <HtmlNode>getNode(scriptInnerNodes, position, includeNodeBoundary) || currentNode;
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the HTML node within an HTML document at a given position
|
||||
* If position is inside a script tag of type template, then it will be parsed to find the inner HTML node as well
|
||||
*/
|
||||
export function getHtmlFlatNode(documentText: string, root: FlatNode | undefined, offset: number, includeNodeBoundary: boolean): HtmlFlatNode | undefined {
|
||||
const currentNode: HtmlFlatNode | undefined = <HtmlFlatNode | undefined>getFlatNode(root, offset, includeNodeBoundary);
|
||||
|
@ -449,31 +385,6 @@ export function offsetRangeToVsRange(document: vscode.TextDocument, start: numbe
|
|||
return new vscode.Range(startPos, endPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns inner range of an html node.
|
||||
*/
|
||||
export function getInnerRange(currentNode: HtmlNode): vscode.Range | undefined {
|
||||
if (!currentNode.close) {
|
||||
return undefined;
|
||||
}
|
||||
return new vscode.Range(currentNode.open.end, currentNode.close.start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the deepest non comment node under given node
|
||||
*/
|
||||
export function getDeepestNode(node: Node | undefined): Node | undefined {
|
||||
if (!node || !node.children || node.children.length === 0 || !node.children.find(x => x.type !== 'comment')) {
|
||||
return node;
|
||||
}
|
||||
for (let i = node.children.length - 1; i >= 0; i--) {
|
||||
if (node.children[i].type !== 'comment') {
|
||||
return getDeepestNode(node.children[i]);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the deepest non comment node under given node
|
||||
*/
|
||||
|
@ -696,18 +607,18 @@ export function getCssPropertyFromDocument(editor: vscode.TextEditor, position:
|
|||
}
|
||||
|
||||
|
||||
export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNode: Node | null, position: vscode.Position): Node | undefined {
|
||||
export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNode: FlatNode | undefined, position: vscode.Position): FlatNode | undefined {
|
||||
if (!currentNode) {
|
||||
return;
|
||||
}
|
||||
const currentHtmlNode = <HtmlNode>currentNode;
|
||||
if (currentHtmlNode && currentHtmlNode.close) {
|
||||
const innerRange = getInnerRange(currentHtmlNode);
|
||||
if (innerRange && innerRange.contains(position)) {
|
||||
const currentHtmlNode = <HtmlFlatNode>currentNode;
|
||||
if (currentHtmlNode && currentHtmlNode.open && currentHtmlNode.close) {
|
||||
const offset = document.offsetAt(position);
|
||||
if (currentHtmlNode.open.end <= offset && offset <= currentHtmlNode.close.start) {
|
||||
if (currentHtmlNode.name === 'style'
|
||||
&& currentHtmlNode.open.end.isBefore(position)
|
||||
&& currentHtmlNode.close.start.isAfter(position)) {
|
||||
let buffer = new DocumentStreamReader(document, currentHtmlNode.open.end, new vscode.Range(currentHtmlNode.open.end, currentHtmlNode.close.start));
|
||||
&& currentHtmlNode.open.end < offset
|
||||
&& currentHtmlNode.close.start > offset) {
|
||||
const buffer = ' '.repeat(currentHtmlNode.open.end) + document.getText().substring(currentHtmlNode.open.end, currentHtmlNode.close.start);
|
||||
return parseStylesheet(buffer);
|
||||
}
|
||||
}
|
||||
|
@ -715,17 +626,17 @@ export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNo
|
|||
return;
|
||||
}
|
||||
|
||||
export function isStyleAttribute(currentNode: Node | null, position: vscode.Position): boolean {
|
||||
export function isStyleAttribute(currentNode: FlatNode | undefined, offset: number): boolean {
|
||||
if (!currentNode) {
|
||||
return false;
|
||||
}
|
||||
const currentHtmlNode = <HtmlNode>currentNode;
|
||||
const currentHtmlNode = <HtmlFlatNode>currentNode;
|
||||
const index = (currentHtmlNode.attributes || []).findIndex(x => x.name.toString() === 'style');
|
||||
if (index === -1) {
|
||||
return false;
|
||||
}
|
||||
const styleAttribute = currentHtmlNode.attributes[index];
|
||||
return position.isAfterOrEqual(styleAttribute.value.start) && position.isBeforeOrEqual(styleAttribute.value.end);
|
||||
return offset >= styleAttribute.value.start && offset <= styleAttribute.value.end;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue