From 79a2512769762928f089e4fd0b84764e37a1ce0d Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Sun, 22 Apr 2018 21:49:29 -0700 Subject: [PATCH] Emmet: Allow css completions in style tag/attribute, html completions in script tags with html type --- extensions/emmet/src/abbreviationActions.ts | 34 +++- .../emmet/src/defaultCompletionProvider.ts | 91 +++++++-- .../emmet/src/test/abbreviationAction.test.ts | 184 +++++++++++++++--- .../src/test/partialParsingStylesheet.test.ts | 35 ++-- extensions/emmet/src/util.ts | 34 ++++ 5 files changed, 313 insertions(+), 65 deletions(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index ad5934c6c998c..e072d33b280d5 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -5,11 +5,11 @@ import * as vscode from 'vscode'; import { Node, HtmlNode, Rule, Property, Stylesheet } from 'EmmetNode'; -import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode, parsePartialStylesheet } from './util'; +import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode, parsePartialStylesheet, isStyleAttribute, getEmbeddedCssNodeIfAny } from './util'; const trimRegex = /[\u00a0]*[\d|#|\-|\*|\u2022]+\.?/; const hexColorRegex = /^#\d+$/; - +const allowedMimeTypesInScriptTag = ['text/html', 'text/plain', 'text/x-template', 'text/template']; const inlineElements = ['a', 'abbr', 'acronym', 'applet', 'b', 'basefont', 'bdo', 'big', 'br', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'object', 'q', @@ -294,8 +294,24 @@ export function expandEmmetAbbreviation(args: any): Thenable { @@ -326,12 +342,12 @@ function fallbackTab(): Thenable { * Works only on html and css/less/scss syntax * @param document current Text Document * @param rootNode parsed document + * @param currentNode current node in the parsed document * @param syntax syntax of the abbreviation * @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, syntax: string, position: vscode.Position, abbreviationRange: vscode.Range): boolean { - const currentNode = rootNode ? getNode(rootNode, position, true) : null; +export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocument, rootNode: Node | undefined, currentNode: Node | null, syntax: string, position: vscode.Position, abbreviationRange: vscode.Range): boolean { if (isStyleSheet(syntax)) { const stylesheet = rootNode; if (stylesheet && (stylesheet.comments || []).some(x => position.isAfterOrEqual(x.start) && position.isBeforeOrEqual(x.end))) { @@ -407,6 +423,12 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen let start = new vscode.Position(0, 0); if (currentHtmlNode) { + if (currentHtmlNode.name === 'script') { + return (currentHtmlNode.attributes + && currentHtmlNode.attributes.some(x => x.name.toString() === 'type' + && allowedMimeTypesInScriptTag.indexOf(x.value.toString()) > -1)); + } + const innerRange = getInnerRange(currentHtmlNode); // Fix for https://github.com/Microsoft/vscode/issues/28829 diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts index fc7ac6d796ad4..92888ea1c4be9 100644 --- a/extensions/emmet/src/defaultCompletionProvider.ts +++ b/extensions/emmet/src/defaultCompletionProvider.ts @@ -6,11 +6,39 @@ import * as vscode from 'vscode'; import { Node, Stylesheet } from 'EmmetNode'; import { isValidLocationForEmmetAbbreviation } from './abbreviationActions'; -import { getEmmetHelper, getMappingForIncludedLanguages, parsePartialStylesheet, getEmmetConfiguration, getEmmetMode, isStyleSheet, parseDocument, } from './util'; +import { getEmmetHelper, getMappingForIncludedLanguages, parsePartialStylesheet, getEmmetConfiguration, getEmmetMode, isStyleSheet, parseDocument, getEmbeddedCssNodeIfAny, isStyleAttribute, getNode } from './util'; export class DefaultCompletionItemProvider implements vscode.CompletionItemProvider { + private lastCompletionType: string | undefined; + public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Thenable | undefined { + const completionResult = this.provideCompletionItemsInternal(document, position, token, context); + if (!completionResult) { + this.lastCompletionType = undefined; + return; + } + + return completionResult.then(completionList => { + if (!completionList || !completionList.items.length) { + this.lastCompletionType = undefined; + return completionList; + } + const item = completionList.items[0]; + const expandedText = item.documentation ? item.documentation.toString() : ''; + + if (expandedText.startsWith('<')) { + this.lastCompletionType = 'html'; + } else if (expandedText.indexOf(':') > 0 && expandedText.endsWith(';')) { + this.lastCompletionType = 'css'; + } else { + this.lastCompletionType = undefined; + } + return completionList; + }); + } + + private provideCompletionItemsInternal(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): Thenable | undefined { const emmetConfig = vscode.workspace.getConfiguration('emmet'); const excludedLanguages = emmetConfig['excludeLanguages'] ? emmetConfig['excludeLanguages'] : []; if (excludedLanguages.indexOf(document.languageId) > -1) { @@ -28,29 +56,60 @@ 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; + + if (document.languageId === 'html') { + if (context.triggerKind === vscode.CompletionTriggerKind.TriggerForIncompleteCompletions) { + switch (this.lastCompletionType) { + case 'html': + validateLocation = false; + break; + case 'css': + validateLocation = false; + syntax = 'css'; + break; + default: + break; + } + + } + if (validateLocation) { + rootNode = parseDocument(document, false); + currentNode = getNode(rootNode, position, true); + if (isStyleAttribute(currentNode, position)) { + syntax = 'css'; + validateLocation = false; + } else { + const embeddedCssNode = getEmbeddedCssNodeIfAny(document, currentNode, position); + if (embeddedCssNode) { + currentNode = getNode(embeddedCssNode, position, true); + syntax = 'css'; + } + } + } + + } + const extractAbbreviationResults = helper.extractAbbreviation(document, position, !isStyleSheet(syntax)); if (!extractAbbreviationResults || !helper.isAbbreviationValid(syntax, extractAbbreviationResults.abbreviation)) { return; } - let validateLocation = false; - let rootNode: Node | undefined = undefined; - - if (context.triggerKind !== vscode.CompletionTriggerKind.TriggerForIncompleteCompletions) { - validateLocation = syntax === 'html' || syntax === 'jsx' || syntax === 'xml' || isStyleSheet(document.languageId); - // If document can be css parsed, get currentNode - if (isStyleSheet(document.languageId)) { - let usePartialParsing = vscode.workspace.getConfiguration('emmet')['optimizeStylesheetParsing'] === true; - rootNode = usePartialParsing && document.lineCount > 1000 ? parsePartialStylesheet(document, position) : parseDocument(document, false); - if (!rootNode) { - return; - } - } else if (document.languageId === 'html') { - rootNode = parseDocument(document, false); + 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) : parseDocument(document, false); + if (!rootNode) { + return; } + currentNode = getNode(rootNode, position, true); } - if (validateLocation && !isValidLocationForEmmetAbbreviation(document, rootNode, syntax, position, extractAbbreviationResults.abbreviationRange)) { + + + if (validateLocation && !isValidLocationForEmmetAbbreviation(document, rootNode, currentNode, syntax, position, extractAbbreviationResults.abbreviationRange)) { return; } diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index a8bd637da32c1..14286654d05e4 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -25,13 +25,19 @@ const htmlContents = ` (ul>li.item$)*2 (ul>li.item$)*2+span (div>dl>(dt+dd)*2) + + `; @@ -216,7 +222,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { test('Expand css when inside style tag (HTML)', () => { return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { - editor.selection = new Selection(13, 3, 13, 6); + editor.selection = new Selection(13, 16, 13, 19); let expandPromise = expandEmmetAbbreviation({ language: 'css' }); if (!expandPromise) { return Promise.resolve(); @@ -228,32 +234,154 @@ suite('Tests for Expand Abbreviations (HTML)', () => { }); }); - // test('Expand css when inside style tag in completion list (HTML)', () => { - // const abbreviation = 'm10'; - // const expandedText = 'margin: 10px;'; - - // return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { - // editor.selection = new Selection(13, 3, 13, 6); - // const cancelSrc = new CancellationTokenSource(); - // const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); - // if (!completionPromise) { - // assert.equal(1, 2, `Problem with expanding m10`); - // return Promise.resolve(); - // } - - // return completionPromise.then((completionList: CompletionList) => { - // if (!completionList.items || !completionList.items.length) { - // assert.equal(1, 2, `Problem with expanding m10`); - // return Promise.resolve(); - // } - // const emmetCompletionItem = completionList.items[0]; - // assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`); - // assert.equal((emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); - // assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`); - // return Promise.resolve(); - // }); - // }); - // }); + test('Expand css when inside style tag in completion list (HTML)', () => { + const abbreviation = 'm10'; + const expandedText = 'margin: 10px;'; + + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(13, 16, 13, 19); + const cancelSrc = new CancellationTokenSource(); + const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); + if (!completionPromise) { + assert.equal(1, 2, `Problem with expanding m10`); + return Promise.resolve(); + } + + return completionPromise.then((completionList: CompletionList) => { + if (!completionList.items || !completionList.items.length) { + assert.equal(1, 2, `Problem with expanding m10`); + return Promise.resolve(); + } + const emmetCompletionItem = completionList.items[0]; + assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`); + assert.equal((emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); + assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`); + return Promise.resolve(); + }); + }); + }); + + test('No expanding text inside style tag if position is not for property name (HTML)', () => { + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(13, 14, 13, 14); + return expandEmmetAbbreviation(null).then(() => { + assert.equal(editor.document.getText(), htmlContents); + return Promise.resolve(); + }); + }); + }); + + test('No expanding text in completion list inside style tag if position is not for property name (HTML)', () => { + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(13, 14, 13, 14); + const cancelSrc = new CancellationTokenSource(); + const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); + assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + return Promise.resolve(); + }); + }); + + test('Expand css when inside style attribute (HTML)', () => { + const styleAttributeContent = '
'; + return withRandomFileEditor(styleAttributeContent, 'html', (editor, doc) => { + editor.selection = new Selection(0, 15, 0, 15); + let expandPromise = expandEmmetAbbreviation(null); + if (!expandPromise) { + return Promise.resolve(); + } + return expandPromise.then(() => { + assert.equal(editor.document.getText(), styleAttributeContent.replace('m10', 'margin: 10px;')); + return Promise.resolve(); + }); + }); + }); + + test('Expand css when inside style attribute in completion list (HTML)', () => { + const abbreviation = 'm10'; + const expandedText = 'margin: 10px;'; + + return withRandomFileEditor('
', 'html', (editor, doc) => { + editor.selection = new Selection(0, 15, 0, 15); + const cancelSrc = new CancellationTokenSource(); + const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); + if (!completionPromise) { + assert.equal(1, 2, `Problem with expanding m10`); + return Promise.resolve(); + } + + return completionPromise.then((completionList: CompletionList) => { + if (!completionList.items || !completionList.items.length) { + assert.equal(1, 2, `Problem with expanding m10`); + return Promise.resolve(); + } + const emmetCompletionItem = completionList.items[0]; + assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`); + assert.equal((emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); + assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`); + return Promise.resolve(); + }); + }); + }); + + test('Expand html when inside script tag with html type (HTML)', () => { + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(21, 12, 21, 12); + let expandPromise = expandEmmetAbbreviation(null); + if (!expandPromise) { + return Promise.resolve(); + } + return expandPromise.then(() => { + assert.equal(editor.document.getText(), htmlContents.replace('span.hello', '')); + return Promise.resolve(); + }); + }); + }); + + test('Expand html when inside script tag with html type (HTML)', () => { + const abbreviation = 'span.hello'; + const expandedText = ''; + + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(21, 12, 21, 12); + const cancelSrc = new CancellationTokenSource(); + const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); + if (!completionPromise) { + assert.equal(1, 2, `Problem with expanding span.hello`); + return Promise.resolve(); + } + + return completionPromise.then((completionList: CompletionList) => { + if (!completionList.items || !completionList.items.length) { + assert.equal(1, 2, `Problem with expanding span.hello`); + return Promise.resolve(); + } + const emmetCompletionItem = completionList.items[0]; + assert.equal(emmetCompletionItem.label, abbreviation, `Label of completion item doesnt match.`); + assert.equal((emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); + return Promise.resolve(); + }); + }); + }); + + test('No expanding text inside script tag with javascript type (HTML)', () => { + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(24, 12, 24, 12); + return expandEmmetAbbreviation(null).then(() => { + assert.equal(editor.document.getText(), htmlContents); + return Promise.resolve(); + }); + }); + }); + + test('No expanding text in completion list inside script tag with javascript type (HTML)', () => { + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + editor.selection = new Selection(24, 12, 24, 12); + const cancelSrc = new CancellationTokenSource(); + const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token, { triggerKind: CompletionTriggerKind.Invoke }); + assert.equal(!completionPromise, true, `Got unexpected comapletion promise instead of undefined`); + return Promise.resolve(); + }); + }); // test('No expanding when html is excluded in the settings', () => { // return workspace.getConfiguration('emmet').update('excludeLanguages', ['html'], ConfigurationTarget.Global).then(() => { diff --git a/extensions/emmet/src/test/partialParsingStylesheet.test.ts b/extensions/emmet/src/test/partialParsingStylesheet.test.ts index 6c3b995960fa9..59fae12a0d42f 100644 --- a/extensions/emmet/src/test/partialParsingStylesheet.test.ts +++ b/extensions/emmet/src/test/partialParsingStylesheet.test.ts @@ -7,12 +7,17 @@ import 'mocha'; import * as assert from 'assert'; import { withRandomFileEditor } from './testUtils'; import * as vscode from 'vscode'; -import { parsePartialStylesheet } from '../util'; +import { parsePartialStylesheet, getNode } 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, 'css', range.end, range); + } -suite('Tests for partial parse of Stylesheets', () => { test('Ignore block comment inside rule', function (): any { const cssContents = ` p { @@ -37,10 +42,10 @@ p { ] rangesForEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'css', range.end, range), true); + assert.equal(isValid(doc, range, 'css'), true); }); rangesNotEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'css', range.end, range), false); + assert.equal(isValid(doc, range, 'css'), false); }); return Promise.resolve(); @@ -67,7 +72,7 @@ dn { new vscode.Range(7, 2, 7, 4) // bg after ending of badly constructed block ]; rangesNotEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'scss', range.end, range), false); + assert.equal(isValid(doc, range, 'scss'), false); }); return Promise.resolve(); }); @@ -102,10 +107,10 @@ comment */ new vscode.Range(10, 2, 10, 3) // p after ending of block ]; rangesForEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'css', range.end, range), true); + assert.equal(isValid(doc, range, 'css'), true); }); rangesNotEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'css', range.end, range), false); + assert.equal(isValid(doc, range, 'css'), false); }); return Promise.resolve(); }); @@ -137,10 +142,10 @@ comment */ new vscode.Range(6, 3, 6, 4) // In selector ]; rangesForEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'scss', range.end, range), true); + assert.equal(isValid(doc, range, 'scss'), true); }); rangesNotEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'scss', range.end, range), false); + assert.equal(isValid(doc, range, 'scss'), false); }); return Promise.resolve(); }); @@ -169,10 +174,10 @@ comment */ new vscode.Range(1, 66, 1, 68) // Outside any ruleset ]; rangesForEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'scss', range.end, range), true); + assert.equal(isValid(doc, range, 'scss'), true); }); rangesNotEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'scss', range.end, range), false); + assert.equal(isValid(doc, range, 'scss'), false); }); return Promise.resolve(); }); @@ -204,10 +209,10 @@ p.#{dn} { new vscode.Range(7, 1, 7, 3) // dn inside ruleset whose selector uses nested interpolation ]; rangesForEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'scss', range.end, range), true); + assert.equal(isValid(doc, range, 'scss'), true); }); rangesNotEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'scss', range.end, range), false); + assert.equal(isValid(doc, range, 'scss'), false); }); return Promise.resolve(); }); @@ -242,10 +247,10 @@ ment */{ new vscode.Range(6, 3, 6, 4) // In c inside block comment ]; rangesForEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'scss', range.end, range), true); + assert.equal(isValid(doc, range, 'scss'), true); }); rangesNotEmmet.forEach(range => { - assert.equal(isValidLocationForEmmetAbbreviation(doc, parsePartialStylesheet(doc, range.end), 'scss', range.end, range), false); + assert.equal(isValid(doc, range, 'scss'), false); }); return Promise.resolve(); }); diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 37820db427fe2..2e9bbc931f6e3 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -529,3 +529,37 @@ export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: return (node && node.type === 'property') ? node : null; } } + + +export function getEmbeddedCssNodeIfAny(document: vscode.TextDocument, currentNode: Node | null, position: vscode.Position): Node | undefined { + if (!currentNode) { + return; + } + const currentHtmlNode = currentNode; + if (currentHtmlNode && currentHtmlNode.close) { + const innerRange = getInnerRange(currentHtmlNode); + if (innerRange && innerRange.contains(position)) { + 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)); + return parseStylesheet(buffer); + } + } + } +} + +export function isStyleAttribute(currentNode: Node | null, position: vscode.Position): boolean { + if (!currentNode) { + return false; + } + const currentHtmlNode = 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); +} \ No newline at end of file