This repository has been archived by the owner on Aug 22, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
81 lines (68 loc) · 2.75 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
'use strict';
import parseFields from '@emmetio/field-parser';
import OutputNode from './lib/output-node';
/**
* Default output of field (tabstop)
* @param {Number} index Field index
* @param {String} placeholder Field placeholder, can be null
* @return {String}
*/
const defaultField = (index, placeholder) => (placeholder || '');
/**
* Renders given parsed abbreviation `tree` via `formatter` function.
* @param {Node} tree Parsed Emmet abbreviation
* @param {Function} [field] Optional function to format field/tabstop (@see `defaultField`)
* @param {Function} formatter Output formatter function. It takes an output node—
* a special wrapper for parsed node that holds formatting and output properties—
* and updates its output properties to shape-up node’s output.
* Function arguments:
* – `outNode`: OutputNode
* – `renderFields`: a helper function that parses fields/tabstops from given
* text and replaces them with `field` function output.
* It also takes care about field indicies and ensures that the same indicies
* from different nodes won’t collide
*/
export default function render(tree, field, formatter) {
if (typeof formatter === 'undefined') {
formatter = field;
field = null;
}
field = field || defaultField;
// Each node may contain fields like `${1:placeholder}`.
// Since most modern editors will link all fields with the same
// index, we have to ensure that different nodes has their own indicies.
// We’ll use this `fieldState` object to globally increment field indices
// during output
const fieldState = { index: 1 };
const fieldsRenderer = text => text == null
? field(fieldState.index++)
: getFieldsModel(text, fieldState).mark(field);
return run(tree.children, formatter, fieldsRenderer);
}
function run(nodes, formatter, fieldsRenderer) {
return nodes.map(node => {
const outNode = formatter(new OutputNode(node, fieldsRenderer));
return outNode ? outNode.toString(run(node.children, formatter, fieldsRenderer)) : '';
}).join('');
}
/**
* Returns fields (tab-stops) model with properly updated indices that won’t
* collide with fields in other nodes of foprmatted tree
* @param {String|Object} text Text to get fields model from or model itself
* @param {Object} fieldState Abbreviation tree-wide field state reference
* @return {Object} Field model
*/
function getFieldsModel(text, fieldState) {
const model = typeof text === 'object' ? text : parseFields(text);
let largestIndex = -1;
model.fields.forEach(field => {
field.index += fieldState.index;
if (field.index > largestIndex) {
largestIndex = field.index;
}
});
if (largestIndex !== -1) {
fieldState.index = largestIndex + 1;
}
return model;
}