import parse , { AbbreviationNode , AbbreviationAttribute , Abbreviation } from '@emmetio/abbreviation' ; import { findDeepest , isNode , Container } from './utils' ; import { Config } from '../config' ; /**
 * Finds matching snippet from `registry` and resolves it into a parsed abbreviation.
 * Resolved node is then updated or replaced with matched abbreviation tree.
 *
 * A HTML registry basically contains aliases to another Emmet abbreviations,
 * e.g. a predefined set of name, attributes and so on, possibly a complex
 * abbreviation with multiple elements. So we have to get snippet, parse it
 * and recursively resolve it.
 */ export default function resolveSnippets ( abbr , config ) { const stack = [ ] ; const reversed = config . options [ 'output.reverseAttributes' ] ; const resolve = ( child ) : Abbreviation | null => { const snippet = child . name && config . snippets [ child . name ] ; if ( ! snippet || stack . includes ( snippet ) ) { return null ; } const snippetAbbr = parse ( snippet , config ) ; stack . push ( snippet ) ; walkResolve ( snippetAbbr , resolve , config ) ; stack . pop ( ) ; for ( const topNode of snippetAbbr . children ) { if ( child . attributes ) { const from = topNode . attributes || [ ] ; const to = child . attributes || [ ] ; topNode . attributes = reversed ? to . concat ( from ) : from . concat ( to ) ; } mergeNodes ( child , topNode ) ; } return snippetAbbr ; } ; walkResolve ( abbr , resolve , config ) ; return abbr ; } function walkResolve ( node , resolve , config ) { let children = [ ] ; for ( const child of node . children ) { const resolved = resolve ( child ) ; if ( resolved ) { children = children . concat ( resolved . children ) ; const deepest = findDeepest ( resolved ) ; if ( isNode ( deepest . node ) ) { deepest . node . children = deepest . node . children . concat ( walkResolve ( child , resolve , config ) ) ; } } else { children . push ( child ) ; child . children = walkResolve ( child , resolve , config ) ; } } return node . children = children ; } /**
 * Adds data from first node into second node
 */ function mergeNodes ( from , to ) { if ( from . selfClosing ) { to . selfClosing = true ; } if ( from . value != null ) { to . value = from . value ; } if ( from . repeat ) { to . repeat = from . repeat ; } }