diff --git a/extensions/emmet/.vscode/launch.json b/extensions/emmet/.vscode/launch.json index 8a89122dafb8dcbe4e91533e3e4b61fe25255b43..0b693f921eeb3342986dcc66abee739b6bd7c2fd 100644 --- a/extensions/emmet/.vscode/launch.json +++ b/extensions/emmet/.vscode/launch.json @@ -1,21 +1,31 @@ { - // Use IntelliSense to learn about possible Node.js debug attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "extensionHost", - "request": "launch", - "name": "Launch Extension", - "runtimeExecutable": "${execPath}", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}" - ], - "sourceMaps": true, - "outFiles": [ - "${workspaceFolder}/out/**/*.js" - ] - } - ] -} \ No newline at end of file + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "extensionHost", + "request": "launch", + "name": "Launch Extension", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "sourceMaps": true, + "outFiles": ["${workspaceFolder}/out/**/*.js"] + }, + { + "type": "extensionHost", + "request": "launch", + "name": "Launch Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test", + "--disable-extensions", + "--skip-getting-started", + ], + "sourceMaps": true, + "outFiles": ["${workspaceFolder}/out/**/*.js"] + } + ] +} diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index fd3478522318150b0ee8d55647c3a50e39119ef7..d88c3717ab3919bab2f9e0b2d9eaeaaffea67188 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -3,8 +3,8 @@ "displayName": "Emmet", "description": "%description%", "version": "1.0.0", - "publisher": "vscode", - "license": "MIT", + "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "^1.13.0" }, @@ -450,6 +450,7 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^0.1.1", "image-size": "^0.5.2", - "vscode-emmet-helper": "^1.2.15" + "vscode-emmet-helper": "^1.2.15", + "vscode-html-languageservice": "^3.0.3" } } diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 26510e803cb7d75985b09eb4b330fdeefcc50149..9508d3a6a0b154452ef88389274b116496237051 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -635,7 +635,7 @@ function expandAbbr(input: ExpandAbbreviationInput): string { return expandedText; } -function getSyntaxFromArgs(args: { [x: string]: string }): string | undefined { +export function getSyntaxFromArgs(args: { [x: string]: string }): string | undefined { const mappedModes = getMappingForIncludedLanguages(); const language: string = args['language']; const parentMode: string = args['parentMode']; diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts index cb9f433f6babe035c341fd3284f4dadfa5802d10..509ca805a980fc179fbd71450c40623c25808ca1 100644 --- a/extensions/emmet/src/defaultCompletionProvider.ts +++ b/extensions/emmet/src/defaultCompletionProvider.ts @@ -5,13 +5,16 @@ import * as vscode from 'vscode'; import { Node, Stylesheet } from 'EmmetNode'; -import { isValidLocationForEmmetAbbreviation } from './abbreviationActions'; -import { getEmmetHelper, getMappingForIncludedLanguages, parsePartialStylesheet, getEmmetConfiguration, getEmmetMode, isStyleSheet, parseDocument, getEmbeddedCssNodeIfAny, isStyleAttribute, getNode } from './util'; +import { isValidLocationForEmmetAbbreviation, getSyntaxFromArgs } from './abbreviationActions'; +import { getEmmetHelper, getMappingForIncludedLanguages, parsePartialStylesheet, getEmmetConfiguration, getEmmetMode, isStyleSheet, parseDocument, getNode, allowedMimeTypesInScriptTag, trimQuotes } from './util'; +import { getLanguageService, TextDocument, TokenType } from 'vscode-html-languageservice'; export class DefaultCompletionItemProvider implements vscode.CompletionItemProvider { private lastCompletionType: string | undefined; + private htmlLS = getLanguageService(); + public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, _: vscode.CancellationToken, context: vscode.CompletionContext): Thenable | undefined { const completionResult = this.provideCompletionItemsInternal(document, position, context); if (!completionResult) { @@ -76,20 +79,55 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi } if (validateLocation) { - rootNode = parseDocument(document, false); - currentNode = getNode(rootNode, position, true); - if (isStyleAttribute(currentNode, position)) { + const lsDoc = TextDocument.create(document.uri.toString(), 'html', 0, document.getText()); + const parsedLsDoc = this.htmlLS.parseHTMLDocument(lsDoc); + const positionOffset = document.offsetAt(position); + const node = parsedLsDoc.findNodeAt(positionOffset); + + if (node.tag === 'script') { + if (node.attributes && 'type' in node.attributes) { + const rawTypeAttrValue = node.attributes['type']; + if (rawTypeAttrValue) { + const typeAttrValue = trimQuotes(rawTypeAttrValue); + if (typeAttrValue === 'application/javascript' || typeAttrValue === 'text/javascript') { + if (!getSyntaxFromArgs({ language: 'javascript' })) { + return; + } else { + validateLocation = false; + } + } + + else if (allowedMimeTypesInScriptTag.indexOf(trimQuotes(rawTypeAttrValue)) > -1) { + validateLocation = false; + } + } + } else { + return; + } + } + else if (node.tag === 'style') { syntax = 'css'; validateLocation = false; } else { - const embeddedCssNode = getEmbeddedCssNodeIfAny(document, currentNode, position); - if (embeddedCssNode) { - currentNode = getNode(embeddedCssNode, position, true); - syntax = 'css'; + if (node.attributes && node.attributes['style']) { + const scanner = this.htmlLS.createScanner(document.getText(), node.start); + let tokenType = scanner.scan(); + let prevAttr = undefined; + while (tokenType !== TokenType.EOS && (scanner.getTokenEnd() <= positionOffset)) { + tokenType = scanner.scan(); + if (tokenType === TokenType.AttributeName) { + prevAttr = scanner.getTokenText(); + } + } + if (prevAttr === 'style') { + syntax = 'css'; + validateLocation = false; + } } } } + } const extractAbbreviationResults = helper.extractAbbreviation(document, position, !isStyleSheet(syntax)); diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index 0a99bae1862ceff8bb7eed9d6bf58ea296048bec..31f46891965f8a1c25331ff84e29390929fd148c 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -264,16 +264,6 @@ suite('Tests for Expand Abbreviations (HTML)', () => { }); }); - 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', async (editor, _doc) => { diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 8ce65ca04b3713a97428e92c70da858898b4539f..b6281272851c88881c6aeb414040e76690da4f10 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -608,3 +608,18 @@ export function isStyleAttribute(currentNode: Node | null, position: vscode.Posi } +export function trimQuotes(s: string) { + if (s.length <= 1) { + return s.replace(/['"]/, ''); + } + + if (s[0] === `'` || s[0] === `"`) { + s = s.slice(1); + } + + if (s[s.length - 1] === `'` || s[s.length - 1] === `"`) { + s = s.slice(0, -1); + } + + return s; +} \ No newline at end of file diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index a417831756a39cb894ec21733aad521a1f7fc7e4..7b22c5334269d7c6e597ab6a6c40d1945267d3b2 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -2478,11 +2478,35 @@ vscode-emmet-helper@^1.2.15: jsonc-parser "^1.0.0" vscode-languageserver-types "^3.6.0-next.1" +vscode-html-languageservice@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.3.tgz#0aeae18a59000e317447ea34965f72680a2140ef" + integrity sha512-U+upM3gHp3HaF3wXAnUduA6IDKcz6frWS/dTAju3cZVIyZwOLBBFElQVlLH0ycHyMzqUFrjvdv+kEyPAEWfQ/g== + dependencies: + vscode-languageserver-types "^3.15.0-next.2" + vscode-nls "^4.1.1" + vscode-uri "^2.0.3" + +vscode-languageserver-types@^3.15.0-next.2: + version "3.15.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" + integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== + vscode-languageserver-types@^3.6.0-next.1: version "3.6.0-next.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3" integrity sha512-n4G+hCgZwAhtcJSCkwJP153TLdcEBWwrIrb3Su/SpOkhmU7KjDgxaQBLA45hf+QbhB8uKQb+TVStPvbuYFHSMA== +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + +vscode-uri@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543" + integrity sha512-4D3DI3F4uRy09WNtDGD93H9q034OHImxiIcSq664Hq1Y1AScehlP3qqZyTkX/RWxeu0MRMHGkrxYqm2qlDF/aw== + vscode@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.0.1.tgz#3d161200615fe2af1d92ddc650751159411a513b"