From 81cca6cc606eeca3443998025bff3893fcfe3ad5 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 30 Oct 2017 20:56:19 -0700 Subject: [PATCH] Lazy load emmet helper to ease startup #35120 --- extensions/emmet/src/abbreviationActions.ts | 23 ++++--- .../emmet/src/defaultCompletionProvider.ts | 8 +-- extensions/emmet/src/extension.ts | 17 +----- extensions/emmet/src/selectItem.ts | 4 +- extensions/emmet/src/toggleComment.ts | 3 +- extensions/emmet/src/updateImageSize.ts | 3 +- extensions/emmet/src/util.ts | 61 ++++++++++++++++++- 7 files changed, 81 insertions(+), 38 deletions(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 2cf7e0501de49..0d474f1c3f1bf 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -5,8 +5,7 @@ import * as vscode from 'vscode'; import { Node, HtmlNode, Rule } from 'EmmetNode'; -import { getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration } from './util'; -import { getExpandOptions, extractAbbreviation, extractAbbreviationFromText, isStyleSheet, isAbbreviationValid, getEmmetMode, expandAbbreviation } from 'vscode-emmet-helper'; +import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode } from './util'; const trimRegex = /[\u00a0]*[\d|#|\-|\*|\u2022]+\.?/; @@ -26,9 +25,10 @@ export function wrapWithAbbreviation(args) { const editor = vscode.window.activeTextEditor; const abbreviationPromise = (args && args['abbreviation']) ? Promise.resolve(args['abbreviation']) : vscode.window.showInputBox({ prompt: 'Enter Abbreviation' }); const syntax = getSyntaxFromArgs({ language: editor.document.languageId }); + const helper = getEmmetHelper(); return abbreviationPromise.then(abbreviation => { - if (!abbreviation || !abbreviation.trim() || !isAbbreviationValid(syntax, abbreviation)) { return; } + if (!abbreviation || !abbreviation.trim() || !helper.isAbbreviationValid(syntax, abbreviation)) { return; } let expandAbbrList: ExpandAbbreviationInput[] = []; @@ -64,11 +64,12 @@ export function wrapIndividualLinesWithAbbreviation(args) { const abbreviationPromise = (args && args['abbreviation']) ? Promise.resolve(args['abbreviation']) : vscode.window.showInputBox({ prompt: 'Enter Abbreviation' }); const syntax = getSyntaxFromArgs({ language: editor.document.languageId }); const lines = editor.document.getText(editor.selection).split('\n').map(x => x.trim()); + const helper = getEmmetHelper(); return abbreviationPromise.then(inputAbbreviation => { - if (!inputAbbreviation || !inputAbbreviation.trim() || !isAbbreviationValid(syntax, inputAbbreviation)) { return; } + if (!inputAbbreviation || !inputAbbreviation.trim() || !helper.isAbbreviationValid(syntax, inputAbbreviation)) { return; } - let extractedResults = extractAbbreviationFromText(inputAbbreviation); + let extractedResults = helper.extractAbbreviationFromText(inputAbbreviation); if (!extractedResults) { return; } @@ -105,12 +106,13 @@ export function expandEmmetAbbreviation(args): Thenable { let abbreviationList: ExpandAbbreviationInput[] = []; let firstAbbreviation: string; let allAbbreviationsSame: boolean = true; + const helper = getEmmetHelper(); let getAbbreviation = (document: vscode.TextDocument, selection: vscode.Selection, position: vscode.Position, syntax: string): [vscode.Range, string, string] => { let rangeToReplace: vscode.Range = selection; let abbr = document.getText(rangeToReplace); if (!rangeToReplace.isEmpty) { - let extractedResults = extractAbbreviationFromText(abbr); + let extractedResults = helper.extractAbbreviationFromText(abbr); if (extractedResults) { return [rangeToReplace, extractedResults.abbreviation, extractedResults.filter]; } @@ -130,7 +132,7 @@ export function expandEmmetAbbreviation(args): Thenable { return [rangeToReplace, abbr, '']; } } - let extractedResults = extractAbbreviation(editor.document, position, false); + let extractedResults = helper.extractAbbreviation(editor.document, position, false); if (!extractedResults) { return [null, '', '']; } @@ -152,7 +154,7 @@ export function expandEmmetAbbreviation(args): Thenable { if (!rangeToReplace) { return; } - if (!isAbbreviationValid(syntax, abbreviation)) { + if (!helper.isAbbreviationValid(syntax, abbreviation)) { return; } @@ -277,7 +279,8 @@ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: Ex * Expands abbreviation as detailed in given input. */ function expandAbbr(input: ExpandAbbreviationInput): string { - const expandOptions = getExpandOptions(input.syntax, getEmmetConfiguration(input.syntax), input.filter); + const helper = getEmmetHelper(); + const expandOptions = helper.getExpandOptions(input.syntax, getEmmetConfiguration(input.syntax), input.filter); if (input.textToWrap) { if (input.filter && input.filter.indexOf('t') > -1) { @@ -297,7 +300,7 @@ function expandAbbr(input: ExpandAbbreviationInput): string { try { // Expand the abbreviation - let expandedText = expandAbbreviation(input.abbreviation, expandOptions); + let expandedText = helper.expandAbbreviation(input.abbreviation, expandOptions); if (input.textToWrap) { // All $anyword would have been escaped by the emmet helper. diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts index c332d63530b06..1c825fc21c32f 100644 --- a/extensions/emmet/src/defaultCompletionProvider.ts +++ b/extensions/emmet/src/defaultCompletionProvider.ts @@ -5,9 +5,8 @@ import * as vscode from 'vscode'; import { HtmlNode } from 'EmmetNode'; -import { doComplete, isStyleSheet, getEmmetMode, extractAbbreviation } from 'vscode-emmet-helper'; import { isValidLocationForEmmetAbbreviation } from './abbreviationActions'; -import { getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, getEmmetConfiguration } from './util'; +import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, getEmmetConfiguration, getEmmetMode, isStyleSheet } from './util'; const allowedMimeTypesInScriptTag = ['text/html', 'text/plain', 'text/x-template', 'text/template']; @@ -33,13 +32,14 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi return; } + const helper = getEmmetHelper(); let noiseCheckPromise: Thenable = Promise.resolve(); // Fix for https://github.com/Microsoft/vscode/issues/32647 // Check for document symbols in js/ts/jsx/tsx and avoid triggering emmet for abbreviations of the form symbolName.sometext // Presence of > or * or + in the abbreviation denotes valid abbreviation that should trigger emmet if (!isStyleSheet(syntax) && (document.languageId === 'javascript' || document.languageId === 'javascriptreact' || document.languageId === 'typescript' || document.languageId === 'typescriptreact')) { - let extractAbbreviationResults = extractAbbreviation(document, position); + let extractAbbreviationResults = helper.extractAbbreviation(document, position); if (extractAbbreviationResults) { let abbreviation: string = extractAbbreviationResults.abbreviation; if (abbreviation.startsWith('this.')) { @@ -57,7 +57,7 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi return; } - let result = doComplete(document, position, syntax, getEmmetConfiguration(syntax)); + let result = helper.doComplete(document, position, syntax, getEmmetConfiguration(syntax)); let newItems: vscode.CompletionItem[] = []; if (result && result.items) { result.items.forEach(item => { diff --git a/extensions/emmet/src/extension.ts b/extensions/emmet/src/extension.ts index 060654c5fa695..4acbf5dfa7b9b 100644 --- a/extensions/emmet/src/extension.ts +++ b/extensions/emmet/src/extension.ts @@ -17,13 +17,10 @@ import { fetchEditPoint } from './editPoint'; import { fetchSelectItem } from './selectItem'; import { evaluateMathExpression } from './evaluateMathExpression'; import { incrementDecrement } from './incrementDecrement'; -import { LANGUAGE_MODES, getMappingForIncludedLanguages } from './util'; -import { updateExtensionsPath } from 'vscode-emmet-helper'; +import { LANGUAGE_MODES, getMappingForIncludedLanguages, resolveUpdateExtensionsPath } from './util'; import { updateImageSize } from './updateImageSize'; import { reflectCssValue } from './reflectCssValue'; -import * as path from 'path'; - export function activate(context: vscode.ExtensionContext) { registerCompletionProviders(context); @@ -128,18 +125,6 @@ export function activate(context: vscode.ExtensionContext) { return reflectCssValue(); })); - let currentExtensionsPath = undefined; - let resolveUpdateExtensionsPath = () => { - let extensionsPath = vscode.workspace.getConfiguration('emmet')['extensionsPath']; - if (extensionsPath && !path.isAbsolute(extensionsPath)) { - extensionsPath = path.join(vscode.workspace.rootPath, extensionsPath); - } - if (currentExtensionsPath !== extensionsPath) { - currentExtensionsPath = extensionsPath; - updateExtensionsPath(currentExtensionsPath).then(null, err => vscode.window.showErrorMessage(err)); - } - }; - resolveUpdateExtensionsPath(); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { diff --git a/extensions/emmet/src/selectItem.ts b/extensions/emmet/src/selectItem.ts index 25b918231bdf6..bd82d2d1bed92 100644 --- a/extensions/emmet/src/selectItem.ts +++ b/extensions/emmet/src/selectItem.ts @@ -4,11 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { validate, parseDocument } from './util'; +import { validate, parseDocument, isStyleSheet } from './util'; import { nextItemHTML, prevItemHTML } from './selectItemHTML'; import { nextItemStylesheet, prevItemStylesheet } from './selectItemStylesheet'; -import { isStyleSheet } from 'vscode-emmet-helper'; - export function fetchSelectItem(direction: string): void { let editor = vscode.window.activeTextEditor; diff --git a/extensions/emmet/src/toggleComment.ts b/extensions/emmet/src/toggleComment.ts index 4de5b7e007bf0..1140473881db5 100644 --- a/extensions/emmet/src/toggleComment.ts +++ b/extensions/emmet/src/toggleComment.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { getNodesInBetween, getNode, parseDocument, sameNodes } from './util'; +import { getNodesInBetween, getNode, parseDocument, sameNodes, isStyleSheet } from './util'; import { Node, Stylesheet, Rule, HtmlNode } from 'EmmetNode'; -import { isStyleSheet } from 'vscode-emmet-helper'; import parseStylesheet from '@emmetio/css-parser'; import { DocumentStreamReader } from './bufferStream'; diff --git a/extensions/emmet/src/updateImageSize.ts b/extensions/emmet/src/updateImageSize.ts index 352cdd2e0469b..ea57e099e8a62 100644 --- a/extensions/emmet/src/updateImageSize.ts +++ b/extensions/emmet/src/updateImageSize.ts @@ -10,8 +10,7 @@ import { TextEditor, Range, Position, window, TextEdit } from 'vscode'; import * as path from 'path'; import { getImageSize } from './imageSizeHelper'; -import { isStyleSheet } from 'vscode-emmet-helper'; -import { parseDocument, getNode, iterateCSSToken, getCssPropertyFromRule } from './util'; +import { parseDocument, getNode, iterateCSSToken, getCssPropertyFromRule, isStyleSheet } from './util'; import { HtmlNode, CssToken, HtmlToken, Attribute, Property } from 'EmmetNode'; import { locateFile } from './locateFile'; import parseStylesheet from '@emmetio/css-parser'; diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 3bb9dad8639c1..3903c73190d4f 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -8,7 +8,32 @@ import parse from '@emmetio/html-matcher'; import parseStylesheet from '@emmetio/css-parser'; import { Node, HtmlNode, CssToken, Property } from 'EmmetNode'; import { DocumentStreamReader } from './bufferStream'; -import { isStyleSheet } from 'vscode-emmet-helper'; +import * as path from 'path'; + +let _emmetHelper; +let _currentExtensionsPath = undefined; + +export function getEmmetHelper() { + if (!_emmetHelper) { + _emmetHelper = require('vscode-emmet-helper'); + } + resolveUpdateExtensionsPath(); + return _emmetHelper; +} + +export function resolveUpdateExtensionsPath() { + if (!_emmetHelper) { + return; + } + let extensionsPath = vscode.workspace.getConfiguration('emmet')['extensionsPath']; + if (extensionsPath && !path.isAbsolute(extensionsPath)) { + extensionsPath = path.join(vscode.workspace.rootPath, extensionsPath); + } + if (_currentExtensionsPath !== extensionsPath) { + _currentExtensionsPath = extensionsPath; + _emmetHelper.updateExtensionsPath(_currentExtensionsPath).then(null, err => vscode.window.showErrorMessage(err)); + } +} export const LANGUAGE_MODES: Object = { 'html': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], @@ -26,6 +51,8 @@ export const LANGUAGE_MODES: Object = { 'typescriptreact': ['.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] }; +const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; + // Explicitly map languages that have built-in grammar in VS Code to their parent language // to get emmet completion support // For other languages, users will have to use `emmet.includeLanguages` or @@ -35,6 +62,11 @@ export const MAPPED_MODES: Object = { 'php': 'html' }; +export function isStyleSheet(syntax): boolean { + let stylesheetSyntaxes = ['css', 'scss', 'sass', 'less', 'stylus']; + return (stylesheetSyntaxes.indexOf(syntax) > -1); +} + export function validate(allowStylesheet: boolean = true): boolean { let editor = vscode.window.activeTextEditor; if (!editor) { @@ -59,6 +91,33 @@ export function getMappingForIncludedLanguages(): any { return finalMappedModes; } +/** +* Get the corresponding emmet mode for given vscode language mode +* Eg: jsx for typescriptreact/javascriptreact or pug for jade +* If the language is not supported by emmet or has been exlcuded via `exlcudeLanguages` setting, +* then nothing is returned +* +* @param language +* @param exlcudedLanguages Array of language ids that user has chosen to exlcude for emmet +*/ +export function getEmmetMode(language: string, excludedLanguages: string[]): string { + if (!language || excludedLanguages.indexOf(language) > -1) { + return; + } + if (/\b(typescriptreact|javascriptreact|jsx-tags)\b/.test(language)) { // treat tsx like jsx + return 'jsx'; + } + if (language === 'sass-indented') { // map sass-indented to sass + return 'sass'; + } + if (language === 'jade') { + return 'pug'; + } + if (emmetModes.indexOf(language) > -1) { + return language; + } +} + /** * Parses the given document using emmet parsing modules * @param document