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 } from './util' ; const trimRegex = /[\u00a0]*[\d#\-\*\u2022]+\.?/ ; const hexColorRegex = /^#[\da-fA-F]{0,6}$/ ; 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' , 's' , 'samp' , 'select' , 'small' , 'span' , 'strike' , 'strong' , 'sub' , 'sup' , 'textarea' , 'tt' , 'u' , 'var' ] ; interface ExpandAbbreviationInput { syntax : string ; abbreviation : string ; rangeToReplace : vscode . Range ; textToWrap ? : string [ ] ; filter ? : string ; } interface PreviewRangesWithContent { previewRange : vscode . Range ; originalRange : vscode . Range ; originalContent : string ; textToWrapInPreview : string [ ] ; } export function wrapWithAbbreviation ( args ) { return doWrapping ( false , args ) ; } export function wrapIndividualLinesWithAbbreviation ( args ) { return doWrapping ( true , args ) ; } function doWrapping ( individualLines , args ) { if ( ! validate ( false ) || ! vscode . window . activeTextEditor ) { return ; } const editor = vscode . window . activeTextEditor ; if ( individualLines ) { if ( editor . selections . length === 1 && editor . selection . isEmpty ) { vscode . window . showInformationMessage ( 'Select more than 1 line and try again.' ) ; return ; } if ( editor . selections . find ( x => x . isEmpty ) ) { vscode . window . showInformationMessage ( 'Select more than 1 line in each selection and try again.' ) ; return ; } } args = args || { } ; if ( ! args [ 'language' ] ) { args [ 'language' ] = editor . document . languageId ; } const syntax = getSyntaxFromArgs ( args ) || 'html' ; const rootNode = parseDocument ( editor . document , false ) ; let inPreview = false ; let currentValue = '' ; const helper = getEmmetHelper ( ) ; let rangesToReplace = editor . selections . sort ( ( a , b ) => { return a . start . compareTo ( b . start ) ; } ) . map ( selection => { let rangeToReplace = selection . isReversed ? new vscode . Range ( selection . active , selection . anchor ) : selection ; if ( ! rangeToReplace . isSingleLine && rangeToReplace . end . character === 0 ) { const previousLine = rangeToReplace . end . line - 1 ; const lastChar = editor . 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 ) ; } else { rangeToReplace = new vscode . Range ( rangeToReplace . start . line , 0 , rangeToReplace . start . line , editor . document . lineAt ( rangeToReplace . start . line ) . text . length ) ; } } const firstLineOfSelection = editor . 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 ; let textToReplace = editor . document . getText ( rangeToReplace ) ; if ( individualLines ) { textToWrapInPreview = textToReplace . split ( '\n' ) . map ( x => x . trim ( ) ) ; } else { const wholeFirstLine = editor . document . lineAt ( rangeToReplace . start ) . text ; const otherMatches = wholeFirstLine . match ( /^(\s*)/ ) ; const preceedingWhitespace = otherMatches ? otherMatches [ 1 ] : '' ; textToWrapInPreview = rangeToReplace . isSingleLine ? [ textToReplace ] : [ '\n\t' + textToReplace . split ( '\n' + preceedingWhitespace ) . join ( '\n\t' ) + '\n' ] ; } textToWrapInPreview = textToWrapInPreview . map ( e => e . replace ( /(\$\d)/g , '\\$1' ) ) ; return { previewRange : rangeToReplace , originalRange : rangeToReplace , originalContent : textToReplace , textToWrapInPreview } ; } ) ; function revertPreview ( ) { return editor . edit ( builder => { for ( const rangeToReplace of rangesToReplace ) { builder . replace ( rangeToReplace . previewRange , rangeToReplace . originalContent ) ; rangeToReplace . previewRange = rangeToReplace . originalRange ; } } , { undoStopBefore : false , undoStopAfter : false } ) ; } function applyPreview ( expandAbbrList ) { let lastOldPreviewRange = new vscode . Range ( 0 , 0 , 0 , 0 ) ; let lastNewPreviewRange = new vscode . Range ( 0 , 0 , 0 , 0 ) ; let totalLinesInserted = 0 ; return editor . edit ( builder => { for ( let i = 0 ; i < rangesToReplace . length ; i ++ ) { const expandedText = expandAbbr ( expandAbbrList [ i ] ) || '' ; if ( ! expandedText ) { break ; } const oldPreviewRange = rangesToReplace [ i ] . previewRange ; const preceedingText = editor . document . getText ( new vscode . Range ( oldPreviewRange . start . line , 0 , oldPreviewRange . start . line , oldPreviewRange . start . character ) ) ; const indentPrefix = ( preceedingText . match ( /^(\s*)/ ) || [ '' , '' ] ) [ 1 ] ; let newText = expandedText . replace ( /\n/g , '\n' + indentPrefix ) ; newText = newText . replace ( /\$\{[\d]*\}/g , '|' ) ; newText = newText . replace ( /\$\{[\d]*(:[^}]*)?\}/g , ( match ) => { return match . replace ( /^\$\{[\d]*:/ , '' ) . replace ( '}' , '' ) ; } ) ; builder . replace ( oldPreviewRange , newText ) ; const expandedTextLines = newText . split ( '\n' ) ; const oldPreviewLines = oldPreviewRange . end . line - oldPreviewRange . start . line + 1 ; const newLinesInserted = expandedTextLines . length - oldPreviewLines ; let newPreviewLineStart = oldPreviewRange . start . line + totalLinesInserted ; let newPreviewStart = oldPreviewRange . start . character ; const newPreviewLineEnd = oldPreviewRange . end . line + totalLinesInserted + newLinesInserted ; let newPreviewEnd = expandedTextLines [ expandedTextLines . length - 1 ] . length ; if ( i > 0 && newPreviewLineEnd === lastNewPreviewRange . end . line ) { newPreviewStart = lastNewPreviewRange . end . character + ( oldPreviewRange . start . character - lastOldPreviewRange . end . character ) ; newPreviewEnd += newPreviewStart ; } else if ( i > 0 && newPreviewLineStart === lastNewPreviewRange . end . line ) { newPreviewStart = lastNewPreviewRange . end . character + ( oldPreviewRange . start . character - lastOldPreviewRange . end . character ) ; } else if ( expandedTextLines . length === 1 ) { newPreviewEnd += oldPreviewRange . start . character ; } lastOldPreviewRange = rangesToReplace [ i ] . previewRange ; rangesToReplace [ i ] . previewRange = lastNewPreviewRange = new vscode . Range ( newPreviewLineStart , newPreviewStart , newPreviewLineEnd , newPreviewEnd ) ; totalLinesInserted += newLinesInserted ; } } , { undoStopBefore : false , undoStopAfter : false } ) ; } function makeChanges ( inputAbbreviation , definitive ) { if ( ! inputAbbreviation || ! inputAbbreviation . trim ( ) || ! helper . isAbbreviationValid ( syntax , inputAbbreviation ) ) { return inPreview ? revertPreview ( ) . then ( ( ) => { return false ; } ) : Promise . resolve ( inPreview ) ; } let extractedResults = helper . extractAbbreviationFromText ( inputAbbreviation ) ; if ( ! extractedResults ) { return Promise . resolve ( inPreview ) ; } else if ( extractedResults . abbreviation !== inputAbbreviation ) { } let { abbreviation , filter } = extractedResults ; if ( definitive ) { const revertPromise = inPreview ? revertPreview ( ) : Promise . resolve ( ) ; return revertPromise . then ( ( ) => { const expandAbbrList = rangesToReplace . map ( rangesAndContent => { let rangeToReplace = rangesAndContent . originalRange ; let textToWrap ; if ( individualLines ) { textToWrap = rangesAndContent . textToWrapInPreview ; } else { textToWrap = rangeToReplace . isSingleLine ? [ '$TM_SELECTED_TEXT' ] : [ '\n\t$TM_SELECTED_TEXT\n' ] ; } return { syntax : syntax || '' , abbreviation , rangeToReplace , textToWrap , filter } ; } ) ; return expandAbbreviationInRange ( editor , expandAbbrList , ! individualLines ) . then ( ( ) => { return true ; } ) ; } ) ; } const expandAbbrList = rangesToReplace . map ( rangesAndContent => { return { syntax : syntax || '' , abbreviation , rangeToReplace : rangesAndContent . originalRange , textToWrap : rangesAndContent . textToWrapInPreview , filter } ; } ) ; return applyPreview ( expandAbbrList ) ; } function inputChanged ( value ) { if ( value !== currentValue ) { currentValue = value ; makeChanges ( value , false ) . then ( ( out ) => { if ( typeof out === 'boolean' ) { inPreview = out ; } } ) ; } return '' ; } const abbreviationPromise = ( args && args [ 'abbreviation' ] ) ? Promise . resolve ( args [ 'abbreviation' ] ) : vscode . window . showInputBox ( { prompt : 'Enter Abbreviation' , validateInput : inputChanged } ) ; return abbreviationPromise . then ( inputAbbreviation => { return makeChanges ( inputAbbreviation , true ) ; } ) ; } export function expandEmmetAbbreviation ( args ) { if ( ! validate ( ) || ! vscode . window . activeTextEditor ) { return fallbackTab ( ) ; } if ( vscode . window . activeTextEditor . selections . length === 1 && vscode . window . activeTextEditor . selection . isEmpty ) { const anchor = vscode . window . activeTextEditor . selection . anchor ; if ( anchor . character === 0 ) { return fallbackTab ( ) ; } const prevPositionAnchor = anchor . translate ( 0 , - 1 ) ; const prevText = vscode . window . activeTextEditor . document . getText ( new vscode . Range ( prevPositionAnchor , anchor ) ) ; if ( prevText === ' ' || prevText === '\t' ) { return fallbackTab ( ) ; } } args = args || { } ; if ( ! args [ 'language' ] ) { args [ 'language' ] = vscode . window . activeTextEditor . document . languageId ; } else { const excludedLanguages = vscode . workspace . getConfiguration ( 'emmet' ) [ 'excludeLanguages' ] ? vscode . workspace . getConfiguration ( 'emmet' ) [ 'excludeLanguages' ] : [ ] ; if ( excludedLanguages . indexOf ( vscode . window . activeTextEditor . document . languageId ) > - 1 ) { return fallbackTab ( ) ; } } const syntax = getSyntaxFromArgs ( args ) ; if ( ! syntax ) { return fallbackTab ( ) ; } const editor = vscode . window . activeTextEditor ; let rootNode ; let usePartialParsing = vscode . workspace . getConfiguration ( 'emmet' ) [ 'optimizeStylesheetParsing' ] === true ; 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 ) ; } if ( vscode . workspace . getConfiguration ( 'emmet' ) [ 'triggerExpansionOnTab' ] === true && editor . selections . find ( x => ! x . isEmpty ) ) { return fallbackTab ( ) ; } let abbreviationList = [ ] ; let firstAbbreviation ; let allAbbreviationsSame = true ; const helper = getEmmetHelper ( ) ; let getAbbreviation = ( document , selection , position , syntax ) : [ vscode . Range | null , string , string ] => { let rangeToReplace = selection ; let abbr = document . getText ( rangeToReplace ) ; if ( ! rangeToReplace . isEmpty ) { let extractedResults = helper . extractAbbreviationFromText ( abbr ) ; if ( extractedResults ) { return [ rangeToReplace , extractedResults . abbreviation , extractedResults . filter ] ; } return [ null , '' , '' ] ; } const currentLine = editor . document . lineAt ( position . line ) . text ; const textTillPosition = currentLine . substr ( 0 , position . character ) ; if ( syntax === 'html' ) { let matches = textTillPosition . match ( /<(\w+)$/ ) ; if ( matches ) { abbr = matches [ 1 ] ; rangeToReplace = new vscode . Range ( position . translate ( 0 , - ( abbr . length + 1 ) ) , position ) ; return [ rangeToReplace , abbr , '' ] ; } } let extractedResults = helper . extractAbbreviation ( editor . document , position , false ) ; if ( ! extractedResults ) { return [ null , '' , '' ] ; } let { abbreviationRange , abbreviation , filter } = extractedResults ; return [ new vscode . Range ( abbreviationRange . start . line , abbreviationRange . start . character , abbreviationRange . end . line , abbreviationRange . end . character ) , abbreviation , filter ] ; } ; let selectionsInReverseOrder = editor . selections . slice ( 0 ) ; selectionsInReverseOrder . sort ( ( a , b ) => { const posA = a . isReversed ? a . anchor : a . active ; const posB = b . isReversed ? b . anchor : b . active ; return posA . compareTo ( posB ) * - 1 ; } ) ; selectionsInReverseOrder . forEach ( selection => { let position = selection . isReversed ? selection . anchor : selection . active ; let [ rangeToReplace , abbreviation , filter ] = getAbbreviation ( editor . document , selection , position , syntax ) ; if ( ! rangeToReplace ) { return ; } if ( ! helper . isAbbreviationValid ( syntax , abbreviation ) ) { return ; } let currentNode = getNode ( rootNode , position , true ) ; let validateLocation = true ; let syntaxToUse = syntax ; if ( editor . document . languageId === 'html' ) { if ( isStyleAttribute ( currentNode , position ) ) { syntaxToUse = 'css' ; validateLocation = false ; } else { const embeddedCssNode = getEmbeddedCssNodeIfAny ( editor . document , currentNode , position ) ; if ( embeddedCssNode ) { currentNode = getNode ( embeddedCssNode , position , true ) ; syntaxToUse = 'css' ; } } } if ( validateLocation && ! isValidLocationForEmmetAbbreviation ( editor . document , rootNode , currentNode , syntaxToUse , position , rangeToReplace ) ) { return ; } if ( ! firstAbbreviation ) { firstAbbreviation = abbreviation ; } else if ( allAbbreviationsSame && firstAbbreviation !== abbreviation ) { allAbbreviationsSame = false ; } abbreviationList . push ( { syntax : syntaxToUse , abbreviation , rangeToReplace , filter } ) ; } ) ; return expandAbbreviationInRange ( editor , abbreviationList , allAbbreviationsSame ) . then ( success => { return success ? Promise . resolve ( undefined ) : fallbackTab ( ) ; } ) ; } function fallbackTab ( ) { if ( vscode . workspace . getConfiguration ( 'emmet' ) [ 'triggerExpansionOnTab' ] === true ) { return vscode . commands . executeCommand ( 'tab' ) ; } return Promise . resolve ( true ) ; } param document param rootNode param currentNode param syntax param position param abbreviationRange export function isValidLocationForEmmetAbbreviation ( document , rootNode , currentNode , syntax , position , abbreviationRange ) { if ( isStyleSheet ( syntax ) ) { const stylesheet = < Stylesheet > rootNode ; if ( stylesheet && ( stylesheet . comments || [ ] ) . some ( x => position . isAfterOrEqual ( x . start ) && position . isBeforeOrEqual ( x . end ) ) ) { return false ; } if ( ! currentNode ) { return true ; } if ( syntax !== 'sass' && syntax !== 'stylus' && currentNode . type === 'property' ) { if ( currentNode . parent && currentNode . parent . type !== 'rule' && currentNode . parent . type !== 'at-rule' ) { return false ; } const abbreviation = document . getText ( new vscode . Range ( abbreviationRange . start . line , abbreviationRange . start . character , abbreviationRange . end . line , abbreviationRange . end . character ) ) ; const propertyNode = < Property > currentNode ; if ( propertyNode . terminatorToken && propertyNode . separator && position . isAfterOrEqual ( propertyNode . separatorToken . end ) && position . isBeforeOrEqual ( propertyNode . terminatorToken . start ) && abbreviation . indexOf ( ':' ) === - 1 ) { return hexColorRegex . test ( abbreviation ) || abbreviation === '!' ; } if ( ! propertyNode . terminatorToken && propertyNode . separator && position . isAfterOrEqual ( propertyNode . separatorToken . end ) && abbreviation . indexOf ( ':' ) === - 1 ) { return hexColorRegex . test ( abbreviation ) || abbreviation === '!' ; } if ( hexColorRegex . test ( abbreviation ) || abbreviation === '!' ) { return false ; } } if ( currentNode . type !== 'rule' && currentNode . type !== 'at-rule' ) { return true ; } const currentCssNode = < Rule > currentNode ; if ( position . isAfter ( currentCssNode . contentStartToken . end ) ) { return true ; } 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 ; } return false ; } const startAngle = '<' ; const endAngle = '>' ; const escape = '\\' ; const question = '?' ; const currentHtmlNode = < HtmlNode > currentNode ; let start = new vscode . Position ( 0 , 0 ) ; if ( currentHtmlNode ) { if ( currentHtmlNode . name === 'script' ) { const typeAttribute = ( currentHtmlNode . attributes || [ ] ) . filter ( x => x . name . toString ( ) === 'type' ) [ 0 ] ; const typeValue = typeAttribute ? typeAttribute . value . toString ( ) : '' ; if ( allowedMimeTypesInScriptTag . indexOf ( typeValue ) > - 1 ) { return true ; } const isScriptJavascriptType = ! typeValue || typeValue === 'application/javascript' || typeValue === 'text/javascript' ; if ( isScriptJavascriptType ) { return ! ! getSyntaxFromArgs ( { language : 'javascript' } ) ; } return false ; } const innerRange = getInnerRange ( currentHtmlNode ) ; if ( ! innerRange || ! innerRange . contains ( position ) ) { return false ; } start = innerRange . start ; let lastChildBeforePosition = currentHtmlNode . firstChild ; while ( lastChildBeforePosition ) { if ( lastChildBeforePosition . end . isAfter ( position ) ) { break ; } start = lastChildBeforePosition . end ; lastChildBeforePosition = lastChildBeforePosition . nextSibling ; } } let textToBackTrack = document . getText ( new vscode . Range ( start . line , start . character , abbreviationRange . start . line , abbreviationRange . start . character ) ) ; if ( textToBackTrack . length > 500 ) { textToBackTrack = textToBackTrack . substr ( textToBackTrack . length - 500 ) ; } if ( ! textToBackTrack . trim ( ) ) { return true ; } let valid = true ; let foundSpace = false ; let i = textToBackTrack . length - 1 ; if ( textToBackTrack [ i ] === startAngle ) { return false ; } while ( i >= 0 ) { const char = textToBackTrack [ i ] ; i -- ; if ( ! foundSpace && /\s/ . test ( char ) ) { foundSpace = true ; continue ; } if ( char === question && textToBackTrack [ i ] === startAngle ) { i -- ; continue ; } if ( /\s/ . test ( char ) && textToBackTrack [ i ] === startAngle ) { i -- ; continue ; } if ( char !== startAngle && char !== endAngle ) { continue ; } if ( i >= 0 && textToBackTrack [ i ] === escape ) { i -- ; continue ; } if ( char === endAngle ) { if ( i >= 0 && textToBackTrack [ i ] === '=' ) { continue ; } else { break ; } } if ( char === startAngle ) { valid = ! foundSpace ; break ; } } return valid ; } returns function expandAbbreviationInRange ( editor , expandAbbrList , insertSameSnippet ) { if ( ! expandAbbrList || expandAbbrList . length === 0 ) { return Promise . resolve ( false ) ; } let insertPromises = [ ] ; if ( ! insertSameSnippet ) { expandAbbrList . sort ( ( a , b ) => { return b . rangeToReplace . start . compareTo ( a . rangeToReplace . start ) ; } ) . forEach ( ( expandAbbrInput ) => { let expandedText = expandAbbr ( expandAbbrInput ) ; if ( expandedText ) { insertPromises . push ( editor . insertSnippet ( new vscode . SnippetString ( expandedText ) , expandAbbrInput . rangeToReplace , { undoStopBefore : false , undoStopAfter : false } ) ) ; } } ) ; if ( insertPromises . length === 0 ) { return Promise . resolve ( false ) ; } return Promise . all ( insertPromises ) . then ( ( ) => Promise . resolve ( true ) ) ; } const anyExpandAbbrInput = expandAbbrList [ 0 ] ; let expandedText = expandAbbr ( anyExpandAbbrInput ) ; let allRanges = expandAbbrList . map ( value => { return new vscode . Range ( value . rangeToReplace . start . line , value . rangeToReplace . start . character , value . rangeToReplace . end . line , value . rangeToReplace . end . character ) ; } ) ; if ( expandedText ) { return editor . insertSnippet ( new vscode . SnippetString ( expandedText ) , allRanges ) ; } return Promise . resolve ( false ) ; } /**
 * Expands abbreviation as detailed in given input.
 */ function expandAbbr ( input ) { 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 ) { input . textToWrap = input . textToWrap . map ( line => { return line . replace ( trimRegex , '' ) . trim ( ) ; } ) ; } expandOptions [ 'text' ] = input . textToWrap ; if ( ! input . rangeToReplace . isSingleLine ) { expandOptions . profile [ 'inlineBreak' ] = 1 ; } } let expandedText ; try { if ( input . textToWrap ) { let parsedAbbr = helper . parseAbbreviation ( input . abbreviation , expandOptions ) ; if ( input . rangeToReplace . isSingleLine && input . textToWrap . length === 1 ) { let wrappingNode = parsedAbbr ; while ( wrappingNode && wrappingNode . children && wrappingNode . children . length > 0 ) { wrappingNode = wrappingNode . children [ wrappingNode . children . length - 1 ] ; } if ( wrappingNode && inlineElements . indexOf ( wrappingNode . name ) === - 1 && ( expandOptions [ 'profile' ] . hasOwnProperty ( 'format' ) ? expandOptions [ 'profile' ] . format : true ) ) { wrappingNode . value = '\n\t' + wrappingNode . value + '\n' ; } } expandedText = helper . expandAbbreviation ( parsedAbbr , expandOptions ) ; expandedText = expandedText . replace ( '\\$TM_SELECTED_TEXT' , '$TM_SELECTED_TEXT' ) ; } else { expandedText = helper . expandAbbreviation ( input . abbreviation , expandOptions ) ; } } catch ( e ) { vscode . window . showErrorMessage ( 'Failed to expand abbreviation' ) ; } return expandedText ; } export function getSyntaxFromArgs ( args ) { const mappedModes = getMappingForIncludedLanguages ( ) ; const language = args [ 'language' ] ; const parentMode = args [ 'parentMode' ] ; const excludedLanguages = vscode . workspace . getConfiguration ( 'emmet' ) [ 'excludeLanguages' ] ? vscode . workspace . getConfiguration ( 'emmet' ) [ 'excludeLanguages' ] : [ ] ; if ( excludedLanguages . indexOf ( language ) > - 1 ) { return ; } let syntax = getEmmetMode ( ( mappedModes [ language ] ? mappedModes [ language ] : language ) , excludedLanguages ) ; if ( ! syntax ) { syntax = getEmmetMode ( ( mappedModes [ parentMode ] ? mappedModes [ parentMode ] : parentMode ) , excludedLanguages ) ; } return syntax ; }