= 0) {
const char = textToBackTrack[i];
i--;
if (!foundSpace && /\s/.test(char)) {
foundSpace = true;
continue;
}
if (char === question && textToBackTrack[i] === startAngle) {
i--;
continue;
}
// Fix for https://github.com/Microsoft/issues/55411
// A space is not a valid character right after < in a tag name.
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; // False alarm of cases like =>
}
else {
break;
}
}
if (char === startAngle) {
valid = !foundSpace;
break;
}
}
return valid;
}
exports.isValidLocationForEmmetAbbreviation = isValidLocationForEmmetAbbreviation;
function getSyntaxFromArgs(args) {
const mappedModes = util_1.getMappingForIncludedLanguages();
const language = args['language'];
const parentMode = args['parentMode'];
const excludedLanguages = coc_nvim_1.workspace.getConfiguration('emmet')['excludeLanguages'] ? coc_nvim_1.workspace.getConfiguration('emmet')['excludeLanguages'] : [];
if (excludedLanguages.indexOf(language) > -1) {
return;
}
let syntax = util_1.getEmmetMode((mappedModes[language] ? mappedModes[language] : language), excludedLanguages);
if (!syntax) {
syntax = util_1.getEmmetMode((mappedModes[parentMode] ? mappedModes[parentMode] : parentMode), excludedLanguages);
}
return syntax;
}
/***/ }),
/* 32 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const coc_nvim_1 = __webpack_require__(1);
const vscode_languageserver_protocol_1 = __webpack_require__(3);
const html_matcher_1 = __importDefault(__webpack_require__(33));
const css_parser_1 = __importDefault(__webpack_require__(36));
const bufferStream_1 = __webpack_require__(37);
exports.comparePosition = bufferStream_1.comparePosition;
let _emmetHelper;
let _currentExtensionsPath;
function positionInRange(position, range) {
let { start, end } = range;
if (bufferStream_1.comparePosition(position, start) < 0)
return -1;
if (bufferStream_1.comparePosition(position, end) > 0)
return 1;
return 0;
}
exports.positionInRange = positionInRange;
function getEmmetHelper() {
// Lazy load emmet-helper instead of importing it
// directly to reduce the start-up time of the extension
if (!_emmetHelper) {
_emmetHelper = __webpack_require__(38);
}
updateEmmetExtensionsPath();
return _emmetHelper;
}
exports.getEmmetHelper = getEmmetHelper;
/**
* Update Emmet Helper to use user snippets from the extensionsPath setting
*/
function updateEmmetExtensionsPath() {
if (!_emmetHelper) {
return;
}
let extensionsPath = coc_nvim_1.workspace.getConfiguration('emmet')['extensionsPath'];
if (_currentExtensionsPath !== extensionsPath) {
_currentExtensionsPath = extensionsPath;
_emmetHelper.updateExtensionsPath(extensionsPath, coc_nvim_1.workspace.rootPath).then(null, (err) => coc_nvim_1.workspace.showMessage(err, 'error'));
}
}
exports.updateEmmetExtensionsPath = updateEmmetExtensionsPath;
/**
* Mapping between languages that support Emmet and completion trigger characters
*/
exports.LANGUAGE_MODES = {
'html': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'jade': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'slim': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'haml': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'xml': ['.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'xsl': ['!', '.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'css': [':', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'scss': [':', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'sass': [':', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'less': [':', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'stylus': [':', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'javascriptreact': ['!', '.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
'typescriptreact': ['!', '.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
};
function isStyleSheet(syntax) {
let stylesheetSyntaxes = ['css', 'scss', 'sass', 'less', 'stylus', 'wxss'];
return (stylesheetSyntaxes.indexOf(syntax) > -1);
}
exports.isStyleSheet = isStyleSheet;
function validate(allowStylesheet = true) {
let doc = coc_nvim_1.workspace.getDocument(coc_nvim_1.workspace.bufnr);
if (!doc)
return false;
if (!allowStylesheet && isStyleSheet(doc.filetype)) {
return false;
}
return true;
}
exports.validate = validate;
function getMappingForIncludedLanguages() {
// 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
// language specific extensions can provide emmet completion support
const MAPPED_MODES = {
'handlebars': 'html',
'php': 'html'
};
const finalMappedModes = Object.create(null);
let includeLanguagesConfig = coc_nvim_1.workspace.getConfiguration('emmet')['includeLanguages'];
let includeLanguages = Object.assign({}, MAPPED_MODES, includeLanguagesConfig ? includeLanguagesConfig : {});
Object.keys(includeLanguages).forEach(syntax => {
if (typeof includeLanguages[syntax] === 'string' && exports.LANGUAGE_MODES[includeLanguages[syntax]]) {
finalMappedModes[syntax] = includeLanguages[syntax];
}
});
return finalMappedModes;
}
exports.getMappingForIncludedLanguages = getMappingForIncludedLanguages;
/**
* Get the corresponding emmet mode for given 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
*/
function getEmmetMode(language, excludedLanguages) {
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';
}
const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus'];
if (emmetModes.indexOf(language) > -1) {
return language;
}
return;
}
exports.getEmmetMode = getEmmetMode;
/**
* Parses the given document using emmet parsing modules
*/
function parseDocument(document, showError = true) {
let parseContent = isStyleSheet(document.languageId) ? css_parser_1.default : html_matcher_1.default;
try {
return parseContent(new bufferStream_1.DocumentStreamReader(document));
}
catch (e) {
if (showError) {
console.error('Emmet: Failed to parse the file');
}
}
return undefined;
}
exports.parseDocument = parseDocument;
const closeBrace = 125;
const openBrace = 123;
const slash = 47;
const star = 42;
/**
* Traverse the given document backward & forward from given position
* to find a complete ruleset, then parse just that to return a Stylesheet
* @param document TextDocument
* @param position Position
*/
function parsePartialStylesheet(document, position) {
const isCSS = document.languageId === 'css';
let startPosition = vscode_languageserver_protocol_1.Position.create(0, 0);
let lastLine = document.getText(vscode_languageserver_protocol_1.Range.create(document.lineCount - 1, 0, document.lineCount, 0)).replace(/\n$/, '');
let endPosition = vscode_languageserver_protocol_1.Position.create(document.lineCount - 1, lastLine.length);
const limitCharacter = document.offsetAt(position) - 5000;
const limitPosition = limitCharacter > 0 ? document.positionAt(limitCharacter) : startPosition;
const stream = new bufferStream_1.DocumentStreamReader(document, position);
function findOpeningCommentBeforePosition(pos) {
let text = document.getText(vscode_languageserver_protocol_1.Range.create(0, 0, pos.line, pos.character));
let offset = text.lastIndexOf('/*');
if (offset === -1) {
return;
}
return document.positionAt(offset);
}
function findClosingCommentAfterPosition(pos) {
let text = document.getText(vscode_languageserver_protocol_1.Range.create(pos.line, pos.character, document.lineCount - 1, lastLine.length));
let offset = text.indexOf('*/');
if (offset === -1) {
return;
}
offset += 2 + document.offsetAt(pos);
return document.positionAt(offset);
}
function consumeLineCommentBackwards() {
if (!isCSS && currentLine !== stream.pos.line) {
currentLine = stream.pos.line;
let line = document.getText(vscode_languageserver_protocol_1.Range.create(currentLine, 0, currentLine + 1, 0));
let startLineComment = line.indexOf('//');
if (startLineComment > -1) {
stream.pos = vscode_languageserver_protocol_1.Position.create(currentLine, startLineComment);
}
}
}
function consumeBlockCommentBackwards() {
if (stream.peek() === slash) {
if (stream.backUp(1) === star) {
stream.pos = findOpeningCommentBeforePosition(stream.pos) || startPosition;
}
else {
stream.next();
}
}
}
function consumeCommentForwards() {
if (stream.eat(slash)) {
if (stream.eat(slash) && !isCSS) {
stream.pos = vscode_languageserver_protocol_1.Position.create(stream.pos.line + 1, 0);
}
else if (stream.eat(star)) {
stream.pos = findClosingCommentAfterPosition(stream.pos) || endPosition;
}
}
}
// Go forward until we find a closing brace.
while (!stream.eof() && !stream.eat(closeBrace)) {
if (stream.peek() === slash) {
consumeCommentForwards();
}
else {
stream.next();
}
}
if (!stream.eof()) {
endPosition = stream.pos;
}
stream.pos = position;
let openBracesToFind = 1;
let currentLine = position.line;
let exit = false;
// Go back until we found an opening brace. If we find a closing one, consume its pair and continue.
while (!exit && openBracesToFind > 0 && !stream.sof()) {
consumeLineCommentBackwards();
switch (stream.backUp(1)) {
case openBrace:
openBracesToFind--;
break;
case closeBrace:
if (isCSS) {
stream.next();
startPosition = stream.pos;
exit = true;
}
else {
openBracesToFind++;
}
break;
case slash:
consumeBlockCommentBackwards();
break;
default:
break;
}
if (position.line - stream.pos.line > 100 || bufferStream_1.comparePosition(stream.pos, limitPosition) <= 0) {
exit = true;
}
}
// We are at an opening brace. We need to include its selector.
currentLine = stream.pos.line;
openBracesToFind = 0;
let foundSelector = false;
while (!exit && !stream.sof() && !foundSelector && openBracesToFind >= 0) {
consumeLineCommentBackwards();
const ch = stream.backUp(1);
if (/\s/.test(String.fromCharCode(ch))) {
continue;
}
switch (ch) {
case slash:
consumeBlockCommentBackwards();
break;
case closeBrace:
openBracesToFind++;
break;
case openBrace:
openBracesToFind--;
break;
default:
if (!openBracesToFind) {
foundSelector = true;
}
break;
}
if (!stream.sof() && foundSelector) {
startPosition = stream.pos;
}
}
try {
return css_parser_1.default(new bufferStream_1.DocumentStreamReader(document, startPosition, vscode_languageserver_protocol_1.Range.create(startPosition, endPosition)));
}
catch (e) {
return;
}
}
exports.parsePartialStylesheet = parsePartialStylesheet;
/**
* Returns node corresponding to given position in the given root node
*/
function getNode(root, position, includeNodeBoundary) {
if (!root) {
return null;
}
let currentNode = root.firstChild;
let foundNode = null;
while (currentNode) {
const nodeStart = currentNode.start;
const nodeEnd = currentNode.end;
if ((bufferStream_1.comparePosition(nodeStart, position) < 0 && bufferStream_1.comparePosition(nodeEnd, position) > 0)
|| (includeNodeBoundary && (bufferStream_1.comparePosition(nodeStart, position) <= 0 && bufferStream_1.comparePosition(nodeEnd, position) >= 0))) {
foundNode = currentNode;
// Dig deeper
currentNode = currentNode.firstChild;
}
else {
currentNode = currentNode.nextSibling;
}
}
return foundNode;
}
exports.getNode = getNode;
exports.allowedMimeTypesInScriptTag = ['text/html', 'text/plain', 'text/x-template', 'text/template', 'text/ng-template'];
/**
* Returns inner range of an html node.
* @param currentNode
*/
function getInnerRange(currentNode) {
if (!currentNode.close) {
return undefined;
}
return vscode_languageserver_protocol_1.Range.create(currentNode.open.end, currentNode.close.start);
}
exports.getInnerRange = getInnerRange;
/**
* Returns the deepest non comment node under given node
* @param node
*/
function getDeepestNode(node) {
if (!node || !node.children || node.children.length === 0 || !node.children.find(x => x.type !== 'comment')) {
return node;
}
for (let i = node.children.length - 1; i >= 0; i--) {
if (node.children[i].type !== 'comment') {
return getDeepestNode(node.children[i]);
}
}
return undefined;
}
exports.getDeepestNode = getDeepestNode;
function findNextWord(propertyValue, pos) {
let foundSpace = pos === -1;
let foundStart = false;
let foundEnd = false;
let newSelectionStart;
let newSelectionEnd;
while (pos < propertyValue.length - 1) {
pos++;
if (!foundSpace) {
if (propertyValue[pos] === ' ') {
foundSpace = true;
}
continue;
}
if (foundSpace && !foundStart && propertyValue[pos] === ' ') {
continue;
}
if (!foundStart) {
newSelectionStart = pos;
foundStart = true;
continue;
}
if (propertyValue[pos] === ' ') {
newSelectionEnd = pos;
foundEnd = true;
break;
}
}
if (foundStart && !foundEnd) {
newSelectionEnd = propertyValue.length;
}
return [newSelectionStart, newSelectionEnd];
}
exports.findNextWord = findNextWord;
function findPrevWord(propertyValue, pos) {
let foundSpace = pos === propertyValue.length;
let foundStart = false;
let foundEnd = false;
let newSelectionStart;
let newSelectionEnd;
while (pos > -1) {
pos--;
if (!foundSpace) {
if (propertyValue[pos] === ' ') {
foundSpace = true;
}
continue;
}
if (foundSpace && !foundEnd && propertyValue[pos] === ' ') {
continue;
}
if (!foundEnd) {
newSelectionEnd = pos + 1;
foundEnd = true;
continue;
}
if (propertyValue[pos] === ' ') {
newSelectionStart = pos + 1;
foundStart = true;
break;
}
}
if (foundEnd && !foundStart) {
newSelectionStart = 0;
}
return [newSelectionStart, newSelectionEnd];
}
exports.findPrevWord = findPrevWord;
function getEmmetConfiguration(syntax) {
const emmetConfig = coc_nvim_1.workspace.getConfiguration('emmet');
const syntaxProfiles = Object.assign({}, emmetConfig['syntaxProfiles'] || {});
const preferences = Object.assign({}, emmetConfig['preferences'] || {});
// jsx, xml and xsl syntaxes need to have self closing tags unless otherwise configured by user
if (syntax === 'jsx' || syntax === 'xml' || syntax === 'xsl') {
syntaxProfiles[syntax] = syntaxProfiles[syntax] || {};
if (typeof syntaxProfiles[syntax] === 'object'
&& !syntaxProfiles[syntax].hasOwnProperty('self_closing_tag') // Old Emmet format
&& !syntaxProfiles[syntax].hasOwnProperty('selfClosingStyle') // Emmet 2.0 format
) {
syntaxProfiles[syntax] = Object.assign({}, syntaxProfiles[syntax], { selfClosingStyle: 'xml' });
}
}
return {
preferences,
showExpandedAbbreviation: emmetConfig['showExpandedAbbreviation'],
showAbbreviationSuggestions: emmetConfig['showAbbreviationSuggestions'],
syntaxProfiles,
variables: emmetConfig['variables'],
excludeLanguages: emmetConfig['excludeLanguages'],
showSuggestionsAsSnippets: emmetConfig['showSuggestionsAsSnippets']
};
}
exports.getEmmetConfiguration = getEmmetConfiguration;
/**
* Itereates by each child, as well as nested child's children, in their order
* and invokes `fn` for each. If `fn` function returns `false`, iteration stops
*/
function iterateCSSToken(token, fn) {
for (let i = 0, il = token.size; i < il; i++) {
if (fn(token.item(i)) === false || iterateCSSToken(token.item(i), fn) === false) {
return false;
}
}
return true;
}
exports.iterateCSSToken = iterateCSSToken;
/**
* Returns `name` CSS property from given `rule`
*/
function getCssPropertyFromRule(rule, name) {
return rule.children.find(node => node.type === 'property' && node.name === name);
}
exports.getCssPropertyFromRule = getCssPropertyFromRule;
function getEmbeddedCssNodeIfAny(document, currentNode, position) {
if (!currentNode) {
return;
}
const currentHtmlNode = currentNode;
if (currentHtmlNode && currentHtmlNode.close) {
const innerRange = getInnerRange(currentHtmlNode);
if (innerRange && positionInRange(position, innerRange)) {
if (currentHtmlNode.name === 'style'
&& bufferStream_1.comparePosition(currentHtmlNode.open.end, position) < 0
&& bufferStream_1.comparePosition(currentHtmlNode.close.start, position) > 0) {
let buffer = new bufferStream_1.DocumentStreamReader(document, currentHtmlNode.open.end, vscode_languageserver_protocol_1.Range.create(currentHtmlNode.open.end, currentHtmlNode.close.start));
return css_parser_1.default(buffer);
}
}
}
return;
}
exports.getEmbeddedCssNodeIfAny = getEmbeddedCssNodeIfAny;
function isStyleAttribute(currentNode, position) {
if (!currentNode) {
return false;
}
const currentHtmlNode = currentNode;
const index = (currentHtmlNode.attributes || []).findIndex(x => x.name.toString() === 'style');
if (index === -1) {
return false;
}
const styleAttribute = currentHtmlNode.attributes[index];
return bufferStream_1.comparePosition(position, styleAttribute.value.start) >= 0 && bufferStream_1.comparePosition(position, styleAttribute.value.end) <= 0;
}
exports.isStyleAttribute = isStyleAttribute;
/***/ }),
/* 33 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defaultOptions", function() { return defaultOptions; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "match", function() { return match; });
/* harmony import */ var _emmetio_stream_reader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(34);
/* harmony import */ var _emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(35);
class Node {
constructor(stream, type, open, close) {
this.stream = stream;
this.type = type;
this.open = open;
this.close = close;
this.children = [];
this.parent = null;
}
/**
* Returns node name
* @return {String}
*/
get name() {
if (this.type === 'tag' && this.open) {
return this.open && this.open.name && this.open.name.value;
}
return '#' + this.type;
}
/**
* Returns attributes of current node
* @return {Array}
*/
get attributes() {
return this.open && this.open.attributes;
}
/**
* Returns node’s start position in stream
* @return {*}
*/
get start() {
return this.open && this.open.start;
}
/**
* Returns node’s start position in stream
* @return {*}
*/
get end() {
return this.close ? this.close.end : this.open && this.open.end;
}
get firstChild() {
return this.children[0];
}
get nextSibling() {
const ix = this.getIndex();
return ix !== -1 ? this.parent.children[ix + 1] : null;
}
get previousSibling() {
const ix = this.getIndex();
return ix !== -1 ? this.parent.children[ix - 1] : null;
}
/**
* Returns current element’s index in parent list of child nodes
* @return {Number}
*/
getIndex() {
return this.parent ? this.parent.children.indexOf(this) : -1;
}
/**
* Adds given node as a child
* @param {Node} node
* @return {Node} Current node
*/
addChild(node) {
this.removeChild(node);
this.children.push(node);
node.parent = this;
return this;
}
/**
* Removes given node from current node’s child list
* @param {Node} node
* @return {Node} Current node
*/
removeChild(node) {
const ix = this.children.indexOf(node);
if (ix !== -1) {
this.children.splice(ix, 1);
node.parent = null;
}
return this;
}
}
/**
* A token factory method
* @param {StreamReader} stream
* @param {Point|Function} start Tokens’ start location or stream consumer
* @param {Point} [end] Tokens’ end location
* @return {Token}
*/
var token = function(stream, start, end) {
return typeof start === 'function'
? eatToken(stream, start)
: new Token(stream, start, end);
};
/**
* Consumes characters from given stream that matches `fn` call and returns it
* as token, if consumed
* @param {StreamReader} stream
* @param {Function} test
* @return {Token}
*/
function eatToken(stream, test) {
const start = stream.pos;
if (stream.eatWhile(test)) {
return new Token(stream, start, stream.pos);
}
stream.pos = start;
}
/**
* A structure describing text fragment in content stream
*/
class Token {
/**
* @param {ContentStreamReader} stream
* @param {Point} start Tokens’ start location in content stream
* @param {Point} end Tokens’ end location in content stream
*/
constructor(stream, start, end) {
this.stream = stream;
this.start = start != null ? start : stream.start;
this.end = end != null ? end : stream.pos;
this._value = null;
}
/**
* Returns token textual value
* NB implemented as getter to reduce unnecessary memory allocations for
* strings that not required
* @return {String}
*/
get value() {
if (this._value === null) {
const start = this.stream.start;
const end = this.stream.pos;
this.stream.start = this.start;
this.stream.pos = this.end;
this._value = this.stream.current();
this.stream.start = start;
this.stream.pos = end;
}
return this._value;
}
toString() {
return this.value;
}
valueOf() {
return `${this.value} [${this.start}; ${this.end}]`;
}
}
const LANGLE = 60;
const RANGLE = 62; // < and >
const LSQUARE = 91;
const RSQUARE = 93; // [ and ]
const LROUND = 40;
const RROUND = 41; // ( and )
const LCURLY = 123;
const RCURLY = 125; // { and }
const opt = { throws: true };
/**
* Consumes paired tokens (like `[` and `]`) with respect of nesting and embedded
* quoted values
* @param {StreamReader} stream
* @return {Token} A token with consumed paired character
*/
var eatPaired = function(stream) {
const start = stream.pos;
const consumed = Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["eatPair"])(stream, LANGLE, RANGLE, opt)
|| Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["eatPair"])(stream, LSQUARE, RSQUARE, opt)
|| Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["eatPair"])(stream, LROUND, RROUND, opt)
|| Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["eatPair"])(stream, LCURLY, RCURLY, opt);
if (consumed) {
return token(stream, start);
}
};
const SLASH$1 = 47; // /
const EQUALS = 61; // =
const RIGHT_ANGLE$1 = 62; // >
/**
* Consumes attributes from given stream
* @param {StreamReader} stream
* @return {Array} Array of consumed attributes
*/
var eatAttributes = function(stream) {
const result = [];
let name, value, attr;
while (!stream.eof()) {
stream.eatWhile(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isSpace"]);
attr = { start: stream.pos };
// A name could be a regular name or expression:
// React-style –
// Angular-style –
if (attr.name = eatAttributeName(stream)) {
// Consumed attribute name. Can be an attribute with name
// or boolean attribute. The value can be React-like expression
if (stream.eat(EQUALS)) {
attr.value = eatAttributeValue(stream);
} else {
attr.boolean = true;
}
attr.end = stream.pos;
result.push(attr);
} else if (isTerminator(stream.peek())) {
// look for tag terminator in order to skip any other possible characters
// (maybe junk)
break;
} else {
stream.next();
}
}
return result;
};
/**
* Consumes attribute name from current location
* @param {StreamReader} stream
* @return {Token}
*/
function eatAttributeName(stream) {
return eatPaired(stream) || token(stream, isAttributeName);
}
/**
* Consumes attribute value from given location
* @param {StreamReader} stream
* @return {Token}
*/
function eatAttributeValue(stream) {
const start = stream.pos;
if (Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["eatQuoted"])(stream)) {
// Should return token that points to unquoted value.
// Use stream readers’ public API to traverse instead of direct
// manipulation
const current = stream.pos;
let valueStart, valueEnd;
stream.pos = start;
stream.next();
valueStart = stream.start = stream.pos;
stream.pos = current;
stream.backUp(1);
valueEnd = stream.pos;
const result = token(stream, valueStart, valueEnd);
stream.pos = current;
return result;
}
return eatPaired(stream) || eatUnquoted(stream);
}
/**
* Check if given code belongs to attribute name.
* NB some custom HTML variations allow non-default values in name, like `*ngFor`
* @param {Number} code
* @return {Boolean}
*/
function isAttributeName(code) {
return code !== EQUALS && !isTerminator(code) && !Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isSpace"])(code);
}
/**
* Check if given code is tag terminator
* @param {Number} code
* @return {Boolean}
*/
function isTerminator(code) {
return code === RIGHT_ANGLE$1 || code === SLASH$1;
}
/**
* Eats unquoted value from stream
* @param {StreamReader} stream
* @return {Token}
*/
function eatUnquoted(stream) {
return token(stream, isUnquoted);
}
/**
* Check if given character code is valid unquoted value
* @param {Number} code
* @return {Boolean}
*/
function isUnquoted(code) {
return !isNaN(code) && !Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isQuote"])(code) && !Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isSpace"])(code) && !isTerminator(code);
}
const DASH = 45; // -
const DOT = 46; // .
const SLASH = 47; // /
const COLON = 58; // :
const LEFT_ANGLE = 60; // <
const RIGHT_ANGLE = 62; // >
const UNDERSCORE = 95; // _
/**
* Parses tag definition (open or close tag) from given stream state
* @param {StreamReader} stream Content stream reader
* @return {Object}
*/
var tag = function(stream) {
const start = stream.pos;
if (stream.eat(LEFT_ANGLE)) {
const model = { type: stream.eat(SLASH) ? 'close' : 'open' };
if (model.name = eatTagName(stream)) {
if (model.type !== 'close') {
model.attributes = eatAttributes(stream);
stream.eatWhile(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isSpace"]);
model.selfClosing = stream.eat(SLASH);
}
if (stream.eat(RIGHT_ANGLE)) {
// tag properly closed
return Object.assign(token(stream, start), model);
}
}
}
// invalid tag, revert to original position
stream.pos = start;
return null;
};
/**
* Eats HTML identifier (tag or attribute name) from given stream
* @param {StreamReader} stream
* @return {Token}
*/
function eatTagName(stream) {
return token(stream, isTagName);
}
/**
* Check if given character code can be used as HTML/XML tag name
* @param {Number} code
* @return {Boolean}
*/
function isTagName(code) {
return Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isAlphaNumeric"])(code)
|| code === COLON // colon is used for namespaces
|| code === DOT // in rare cases declarative tag names may have dots in names
|| code === DASH
|| code === UNDERSCORE;
}
/**
* Eats array of character codes from given stream
* @param {StreamReader} stream
* @param {Number[]} codes Array of character codes
* @return {Boolean}
*/
function eatArray(stream, codes) {
const start = stream.pos;
for (let i = 0; i < codes.length; i++) {
if (!stream.eat(codes[i])) {
stream.pos = start;
return false;
}
}
stream.start = start;
return true;
}
/**
* Consumes section from given string which starts with `open` character codes
* and ends with `close` character codes
* @param {StreamReader} stream
* @param {Number[]} open
* @param {Number[]} close
* @return {Boolean} Returns `true` if section was consumed
*/
function eatSection(stream, open, close, allowUnclosed) {
const start = stream.pos;
if (eatArray(stream, open)) {
// consumed `');
/**
* Consumes HTML comment from given stream
* @param {StreamReader} stream
* @return {Token}
*/
var comment = function(stream) {
const start = stream.pos;
if (eatSection(stream, open, close, true)) {
const result = token(stream, start);
result.type = 'comment';
return result;
}
return null;
};
const open$1 = toCharCodes('');
/**
* Consumes CDATA from given stream
* @param {StreamReader} stream
* @return {Token}
*/
var cdata = function(stream) {
const start = stream.pos;
if (eatSection(stream, open$1, close$1, true)) {
const result = token(stream, start);
result.type = 'cdata';
return result;
}
return null;
};
const defaultOptions = {
/**
* Expect XML content in searching content. It alters how should-be-empty
* elements are treated: for example, in XML mode parser will try to locate
* closing pair for `
` tag
* @type {Boolean}
*/
xml: false,
special: ['script', 'style'],
/**
* List of elements that should be treated as empty (e.g. without closing tag)
* in non-XML syntax
* @type {Array}
*/
empty: ['img', 'meta', 'link', 'br', 'base', 'hr', 'area', 'wbr','col', 'embed', 'input', 'param', 'source', 'track']
};
/**
* Parses given content into a DOM-like structure
* @param {String|StreamReader} content
* @param {Object} options
* @return {Node}
*/
function parse(content, options) {
options = Object.assign({}, defaultOptions, options);
const stream = typeof content === 'string'
? new _emmetio_stream_reader__WEBPACK_IMPORTED_MODULE_0__["default"](content)
: content;
const root = new Node(stream, 'root');
const empty = new Set(options.empty);
const special = options.special.reduce(
(map, name) => map.set(name, toCharCodes(`${name}>`)), new Map());
const isEmpty = (token, name) =>
token.selfClosing || (!options.xml && empty.has(name));
let m, node, name, stack = [root];
while (!stream.eof()) {
if (m = match(stream)) {
name = getName(m);
if (m.type === 'open') {
// opening tag
node = new Node(stream, 'tag', m);
last(stack).addChild(node);
if (special.has(name)) {
node.close = consumeSpecial(stream, special.get(name));
} else if (!isEmpty(m, name)) {
stack.push(node);
}
} else if (m.type === 'close') {
// closing tag, find it’s matching opening tag
for (let i = stack.length - 1; i > 0; i--) {
if (stack[i].name.toLowerCase() === name) {
stack[i].close = m;
stack = stack.slice(0, i);
break;
}
}
} else {
last(stack).addChild(new Node(stream, m.type, m));
}
} else {
stream.next();
}
}
return root;
}
/**
* Matches known token in current state of given stream
* @param {ContentStreamReader} stream
* @return {Token}
*/
function match(stream) {
// fast-path optimization: check for `<` code
if (stream.peek() === 60 /* < */) {
return comment(stream) || cdata(stream) || tag(stream);
}
}
/**
* @param {StreamReader} stream
* @param {Number[]} codes
* @return {Token}
*/
function consumeSpecial(stream, codes) {
const start = stream.pos;
let m;
while (!stream.eof()) {
if (eatArray(stream, codes)) {
stream.pos = stream.start;
return tag(stream);
}
stream.next();
}
stream.pos = start;
return null;
}
/**
* Returns name of given matched token
* @param {Token} tag
* @return {String}
*/
function getName(tag$$1) {
return tag$$1.name ? tag$$1.name.value.toLowerCase() : `#${tag$$1.type}`;
}
function last(arr) {
return arr[arr.length - 1];
}
/* harmony default export */ __webpack_exports__["default"] = (parse);
/***/ }),
/* 34 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/**
* A streaming, character code-based string reader
*/
class StreamReader {
constructor(string, start, end) {
if (end == null && typeof string === 'string') {
end = string.length;
}
this.string = string;
this.pos = this.start = start || 0;
this.end = end;
}
/**
* Returns true only if the stream is at the end of the file.
* @returns {Boolean}
*/
eof() {
return this.pos >= this.end;
}
/**
* Creates a new stream instance which is limited to given `start` and `end`
* range. E.g. its `eof()` method will look at `end` property, not actual
* stream end
* @param {Point} start
* @param {Point} end
* @return {StreamReader}
*/
limit(start, end) {
return new this.constructor(this.string, start, end);
}
/**
* Returns the next character code in the stream without advancing it.
* Will return NaN at the end of the file.
* @returns {Number}
*/
peek() {
return this.string.charCodeAt(this.pos);
}
/**
* Returns the next character in the stream and advances it.
* Also returns undefined
when no more characters are available.
* @returns {Number}
*/
next() {
if (this.pos < this.string.length) {
return this.string.charCodeAt(this.pos++);
}
}
/**
* `match` can be a character code or a function that takes a character code
* and returns a boolean. If the next character in the stream 'matches'
* the given argument, it is consumed and returned.
* Otherwise, `false` is returned.
* @param {Number|Function} match
* @returns {Boolean}
*/
eat(match) {
const ch = this.peek();
const ok = typeof match === 'function' ? match(ch) : ch === match;
if (ok) {
this.next();
}
return ok;
}
/**
* Repeatedly calls eat
with the given argument, until it
* fails. Returns true
if any characters were eaten.
* @param {Object} match
* @returns {Boolean}
*/
eatWhile(match) {
const start = this.pos;
while (!this.eof() && this.eat(match)) {}
return this.pos !== start;
}
/**
* Backs up the stream n characters. Backing it up further than the
* start of the current token will cause things to break, so be careful.
* @param {Number} n
*/
backUp(n) {
this.pos -= (n || 1);
}
/**
* Get the string between the start of the current token and the
* current stream position.
* @returns {String}
*/
current() {
return this.substring(this.start, this.pos);
}
/**
* Returns substring for given range
* @param {Number} start
* @param {Number} [end]
* @return {String}
*/
substring(start, end) {
return this.string.slice(start, end);
}
/**
* Creates error object with current stream state
* @param {String} message
* @return {Error}
*/
error(message) {
const err = new Error(`${message} at char ${this.pos + 1}`);
err.originalMessage = message;
err.pos = this.pos;
err.string = this.string;
return err;
}
}
/* harmony default export */ __webpack_exports__["default"] = (StreamReader);
/***/ }),
/* 35 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "eatQuoted", function() { return eatQuoted; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isQuote", function() { return isQuote; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isAlpha", function() { return isAlpha; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isNumber", function() { return isNumber; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isAlphaNumeric", function() { return isAlphaNumeric; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isSpace", function() { return isSpace; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isWhiteSpace", function() { return isWhiteSpace; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "eatPair", function() { return eatPair; });
/**
* Methods for consuming quoted values
*/
const SINGLE_QUOTE = 39; // '
const DOUBLE_QUOTE = 34; // "
const defaultOptions = {
escape: 92, // \ character
throws: false
};
/**
* Consumes 'single' or "double"-quoted string from given string, if possible
* @param {StreamReader} stream
* @param {Number} options.escape A character code of quote-escape symbol
* @param {Boolean} options.throws Throw error if quotes string can’t be properly consumed
* @return {Boolean} `true` if quoted string was consumed. The contents
* of quoted string will be availabe as `stream.current()`
*/
var eatQuoted = function(stream, options) {
options = options ? Object.assign({}, defaultOptions, options) : defaultOptions;
const start = stream.pos;
const quote = stream.peek();
if (stream.eat(isQuote)) {
while (!stream.eof()) {
switch (stream.next()) {
case quote:
stream.start = start;
return true;
case options.escape:
stream.next();
break;
}
}
// If we’re here then stream wasn’t properly consumed.
// Revert stream and decide what to do
stream.pos = start;
if (options.throws) {
throw stream.error('Unable to consume quoted string');
}
}
return false;
};
function isQuote(code) {
return code === SINGLE_QUOTE || code === DOUBLE_QUOTE;
}
/**
* Check if given code is a number
* @param {Number} code
* @return {Boolean}
*/
function isNumber(code) {
return code > 47 && code < 58;
}
/**
* Check if given character code is alpha code (letter through A to Z)
* @param {Number} code
* @param {Number} [from]
* @param {Number} [to]
* @return {Boolean}
*/
function isAlpha(code, from, to) {
from = from || 65; // A
to = to || 90; // Z
code &= ~32; // quick hack to convert any char code to uppercase char code
return code >= from && code <= to;
}
/**
* Check if given character code is alpha-numeric (letter through A to Z or number)
* @param {Number} code
* @return {Boolean}
*/
function isAlphaNumeric(code) {
return isNumber(code) || isAlpha(code);
}
function isWhiteSpace(code) {
return code === 32 /* space */
|| code === 9 /* tab */
|| code === 160; /* non-breaking space */
}
/**
* Check if given character code is a space
* @param {Number} code
* @return {Boolean}
*/
function isSpace(code) {
return isWhiteSpace(code)
|| code === 10 /* LF */
|| code === 13; /* CR */
}
const defaultOptions$1 = {
escape: 92, // \ character
throws: false
};
/**
* Eats paired characters substring, for example `(foo)` or `[bar]`
* @param {StreamReader} stream
* @param {Number} open Character code of pair openinig
* @param {Number} close Character code of pair closing
* @param {Object} [options]
* @return {Boolean} Returns `true` if chacarter pair was successfully
* consumed, it’s content will be available as `stream.current()`
*/
function eatPair(stream, open, close, options) {
options = options ? Object.assign({}, defaultOptions$1, options) : defaultOptions$1;
const start = stream.pos;
if (stream.eat(open)) {
let stack = 1, ch;
while (!stream.eof()) {
if (eatQuoted(stream, options)) {
continue;
}
ch = stream.next();
if (ch === open) {
stack++;
} else if (ch === close) {
stack--;
if (!stack) {
stream.start = start;
return true;
}
} else if (ch === options.escape) {
stream.next();
}
}
// If we’re here then paired character can’t be consumed
stream.pos = start;
if (options.throws) {
throw stream.error(`Unable to find matching pair for ${String.fromCharCode(open)}`);
}
}
return false;
}
/***/ }),
/* 36 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "lexer", function() { return lexer; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Token", function() { return Token; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "any", function() { return any; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "selector", function() { return selector; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "value", function() { return value; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "keyword", function() { return keyword; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "variable", function() { return variable; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "formatting", function() { return formatting; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "comment", function() { return comment; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "whitespace", function() { return whitespace; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ident", function() { return ident; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "string", function() { return string; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "url", function() { return url; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "interpolation", function() { return interpolation; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "backtick", function() { return backtick; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parseMediaExpression", function() { return parseMediaExpression; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parsePropertyName", function() { return parsePropertyName; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parsePropertyValue", function() { return parsePropertyValue; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parseSelector", function() { return parseSelector; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createProperty", function() { return createProperty; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createRule", function() { return createRule; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createAtRule", function() { return createAtRule; });
/* harmony import */ var _emmetio_stream_reader__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(34);
/* harmony import */ var _emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(35);
/**
* Abstract container that contains nested nodes or other containers
*/
class Node {
constructor(type) {
this.type = type;
this.children = [];
this.parent = null;
}
get firstChild() {
return this.children[0];
}
get nextSibling() {
const ix = this.index();
return ix !== -1 ? this.parent.children[ix + 1] : null;
}
get previousSibling() {
const ix = this.index();
return ix !== -1 ? this.parent.children[ix - 1] : null;
}
/**
* Returns current element’s index in parent list of child nodes
* @return {Number}
*/
index() {
return this.parent ? this.parent.children.indexOf(this) : -1;
}
/**
* Adds given node as a child
* @param {Node} node
* @return {Node} Current node
*/
add(node) {
if (node) {
node.remove();
this.children.push(node);
node.parent = this;
}
return this;
}
/**
* Removes current node from its parent
* @return {Node} Current node
*/
remove() {
if (this.parent) {
const ix = this.index();
if (ix !== -1) {
this.parent.children.splice(ix, 1);
this.parent = null;
}
}
return this;
}
}
class Stylesheet extends Node {
constructor() {
super('stylesheet');
this.comments = [];
}
/**
* Returns node’s start position in stream
* @return {*}
*/
get start() {
const node = this.firstChild;
return node && node.start;
}
/**
* Returns node’s end position in stream
* @return {*}
*/
get end() {
const node = this.children[this.children.length - 1];
return node && node.end;
}
/**
* Adds comment token into a list.
* This somewhat awkward feature is required to properly detect comment
* ranges. Specifically, in Atom: it’s API provides scopes limited to current
* line only
* @param {Token} token
*/
addComment(token) {
this.comments.push(token);
}
}
/**
* Removes tokens that matches given criteria from start and end of given list
* @param {Token[]} tokens
* @return {Token[]}
*/
function trimTokens(tokens) {
tokens = tokens.slice();
let len;
while (len !== tokens.length) {
len = tokens.length;
if (isFormattingToken(tokens[0])) {
tokens.shift();
}
if (isFormattingToken(last(tokens))) {
tokens.pop();
}
}
return tokens;
}
/**
* Trims formatting tokens (whitespace and comments) from the beginning and end
* of given token list
* @param {Token[]} tokens
* @return {Token[]}
*/
function trimFormatting(tokens) {
return trimTokens(tokens, isFormattingToken);
}
/**
* Check if given token is a formatting one (whitespace or comment)
* @param {Token} token
* @return {Boolean}
*/
function isFormattingToken(token) {
const type = token && token.type;
return type === 'whitespace' || type === 'comment';
}
/**
* Consumes string char-by-char from given stream
* @param {StreamReader} stream
* @param {String} string
* @return {Boolean} Returns `true` if string was completely consumed
*/
function eatString(stream, string) {
const start = stream.pos;
for (let i = 0, il = string.length; i < il; i++) {
if (!stream.eat(string.charCodeAt(i))) {
stream.pos = start;
return false;
}
}
return true;
}
function consume(stream, match) {
const start = stream.pos;
if (stream.eat(match)) {
stream.start = start;
return true;
}
return false;
}
function consumeWhile(stream, match) {
const start = stream.pos;
if (stream.eatWhile(match)) {
stream.start = start;
return true;
}
return false;
}
function last(arr) {
return arr[arr.length - 1];
}
function valueOf(token) {
return token && token.valueOf();
}
/**
* A structure describing text fragment in content stream. It may contain
* other sub-fragments (also tokens) that represent current fragments’ logical
* parts
*/
class Token {
/**
* @param {StreamReader} stream
* @param {String} type Token type
* @param {Object} [start] Tokens’ start position in `stream`
* @param {Object} [end] Tokens’ end position in `stream`
*/
constructor(stream, type, start, end) {
this.stream = stream;
this.start = start != null ? start : stream.start;
this.end = end != null ? end : stream.pos;
this.type = type;
this._props = null;
this._value = null;
this._items = null;
}
get size() {
return this._items ? this._items.length : 0;
}
get items() {
return this._items;
}
clone(start, end) {
return new this.constructor(this.stream, this.type,
start != null ? start : this.start,
end != null ? end : this.end);
}
add(item) {
if (Array.isArray(item)) {
for (let i = 0, il = item.length; i < il; i++) {
this.add(item[i]);
}
} else if (item) {
if (!this._items) {
this._items = [item];
} else {
this._items.push(item);
}
}
return this;
}
remove(item) {
if (this._items) {
const ix = this._items.indexOf(item);
if (ix !== -1 ) {
this._items.splice(ix, 1);
}
}
return this;
}
item(i) {
const size = this.size;
return this._items && this._items[(size + i) % size];
}
limit() {
return this.stream.limit(this.start, this.end);
}
slice(from, to) {
const token = this.clone();
const items = this._items && this._items.slice(from, to);
if (items && items.length) {
token.start = items[0].start;
token.end = items[items.length - 1].end;
token.add(items);
} else if (items) {
// Empty token
token.start = token.end;
}
return token;
}
property(name, value) {
if (typeof value !== 'undefined') {
// set property value
if (!this._props) {
this._props = {};
}
this._props[name] = value;
}
return this._props && this._props[name];
}
/**
* Returns token textual representation
* @return {String}
*/
toString() {
return `${this.valueOf()} [${this.start}, ${this.end}] (${this.type})`;
}
valueOf() {
if (this._value === null) {
this._value = this.stream.substring(this.start, this.end);
}
return this._value;
}
}
const COMMA = 44; // ,
const PROP_DELIMITER$1 = 58; // :
const PROP_TERMINATOR$1 = 59; // ;
const RULE_START$1 = 123; // {
const RULE_END$1 = 125; // }
const types = new Map()
.set(COMMA, 'comma')
.set(PROP_DELIMITER$1, 'propertyDelimiter')
.set(PROP_TERMINATOR$1, 'propertyTerminator')
.set(RULE_START$1, 'ruleStart')
.set(RULE_END$1, 'ruleEnd');
/**
* Consumes separator token from given string
*/
function separator(stream) {
if (isSeparator(stream.peek())) {
const start = stream.pos;
const type = types.get(stream.next());
const token = new Token(stream, 'separator', start);
token.property('type', type);
return token;
}
}
function isSeparator(code) {
return code === COMMA
|| code === PROP_DELIMITER$1 || code === PROP_TERMINATOR$1
|| code === RULE_START$1 || code === RULE_END$1;
}
const ARGUMENTS_START = 40; // (
const ARGUMENTS_END = 41; // )
var args = function(stream, tokenConsumer) {
if (stream.peek() === ARGUMENTS_START) {
const start = stream.pos;
stream.next();
const tokens = [];
let t;
// in LESS, it’s possible to separate arguments list either by `;` or `,`.
// In first case, we should keep comma-separated item as a single argument
let usePropTerminator = false;
while (!stream.eof()) {
if (isUnexpectedTerminator(stream.peek()) || stream.eat(ARGUMENTS_END)) {
break;
}
t = tokenConsumer(stream);
if (!t) {
break;
}
if (isSemicolonSeparator(t)) {
usePropTerminator = true;
}
tokens.push(t);
}
stream.start = start;
return createArgumentList(stream, tokens, usePropTerminator);
}
};
function isUnexpectedTerminator(code) {
return code === RULE_START$1 || code === RULE_END$1;
}
function createArgumentList(stream, tokens, usePropTerminator) {
const argsToken = new Token(stream, 'arguments');
const isSeparator = usePropTerminator ? isSemicolonSeparator : isCommaSeparator;
let arg = [];
for (let i = 0, il = tokens.length, token; i < il; i++) {
token = tokens[i];
if (isSeparator(token)) {
argsToken.add(createArgument(stream, arg) || createEmptyArgument(stream, token.start));
arg.length = 0;
} else {
arg.push(token);
}
}
if (arg.length) {
argsToken.add(createArgument(stream, arg));
}
return argsToken;
}
function createArgument(stream, tokens) {
tokens = trimFormatting(tokens);
if (tokens.length) {
const arg = new Token(stream, 'argument', tokens[0].start, last(tokens).end);
for (let i = 0; i < tokens.length; i++) {
arg.add(tokens[i]);
}
return arg;
}
}
function createEmptyArgument(stream, pos) {
const token = new Token(stream, 'argument', pos, pos);
token.property('empty', true);
return token;
}
function isCommaSeparator(token) {
return token.property('type') === 'comma';
}
function isSemicolonSeparator(token) {
return token.property('type') === 'propertyTerminator';
}
const HYPHEN = 45;
const UNDERSCORE = 95;
function ident(stream) {
return eatIdent(stream) && new Token(stream, 'ident');
}
function eatIdent(stream) {
const start = stream.pos;
stream.eat(HYPHEN);
if (stream.eat(isIdentStart)) {
stream.eatWhile(isIdent);
stream.start = start;
return true;
}
stream.pos = start;
return false;
}
function isIdentStart(code) {
return code === UNDERSCORE || code === HYPHEN || Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isAlpha"])(code) || code >= 128;
}
function isIdent(code) {
return Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(code) || isIdentStart(code);
}
function prefixed(stream, tokenType, prefix, body, allowEmptyBody) {
const start = stream.pos;
if (stream.eat(prefix)) {
const bodyToken = body(stream, start);
if (bodyToken || allowEmptyBody) {
stream.start = start;
return new Token(stream, tokenType, start).add(bodyToken);
}
}
stream.pos = start;
}
const AT = 64; // @
/**
* Consumes at-keyword from given stream
*/
function atKeyword(stream) {
return prefixed(stream, 'at-keyword', AT, ident);
}
const HASH = 35; // #
const AT$1 = 64; // @
/**
* Consumes interpolation token, e.g. `#{expression}`
* @param {StreamReader} stream
* @param {Function} tokenConsumer
* @return {Token}
*/
function interpolation(stream, tokenConsumer) {
const start = stream.pos;
tokenConsumer = tokenConsumer || defaultTokenConsumer;
if ((stream.eat(HASH) || stream.eat(AT$1)) && stream.eat(RULE_START$1)) {
const container = new Token(stream, 'interpolation', start);
let stack = 1, token;
while (!stream.eof()) {
if (stream.eat(RULE_START$1)) {
stack++;
} else if (stream.eat(RULE_END$1)) {
stack--;
if (!stack) {
container.end = stream.pos;
return container;
}
} else if (token = tokenConsumer(stream)) {
container.add(token);
} else {
break;
}
}
}
stream.pos = start;
}
function eatInterpolation(stream) {
const start = stream.pos;
if ((stream.eat(HASH) || stream.eat(AT$1)) && Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["eatPair"])(stream, RULE_START$1, RULE_END$1)) {
stream.start = start;
return true;
}
stream.pos = start;
return false;
}
function defaultTokenConsumer(stream) {
const start = stream.pos;
while (!stream.eof()) {
if (stream.peek() === RULE_END$1) {
break;
}
eatString$1(stream) || stream.next();
}
if (start !== stream.pos) {
return new Token(stream, 'expression', start);
}
}
/**
* Consumes quoted string from current string and returns token with consumed
* data or `null`, if string wasn’t consumed
* @param {StreamReader} stream
* @return {StringToken}
*/
function string(stream) {
return eatString$1(stream, true);
}
function eatString$1(stream, asToken) {
let ch = stream.peek(), pos, tokens, token;
if (Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isQuote"])(ch)) {
stream.start = stream.pos;
stream.next();
const quote = ch;
const valueStart = stream.pos;
while (!stream.eof()) {
pos = stream.pos;
if (stream.eat(quote) || stream.eat(isNewline)) {
// found end of string or newline without preceding '\',
// which is not allowed (don’t throw error, for now)
break;
} else if (stream.eat(92 /* \ */)) {
// backslash allows newline in string
stream.eat(isNewline);
} else if (asToken && (token = interpolation(stream))) {
if (!tokens) {
tokens = [token];
} else {
tokens.push(token);
}
}
stream.next();
}
// Either reached EOF or explicitly stopped at string end
// NB use extra `asToken` param to return boolean instead of token to reduce
// memory allocations and improve performance
if (asToken) {
const token = new Token(stream, 'string');
const inner = new Token(stream, 'unquoted', valueStart, pos);
inner.add(tokens);
token.add(inner);
token.property('quote', quote);
return token;
}
return true;
}
return false;
}
function isNewline(code) {
return code === 10 /* LF */ || code === 13 /* CR */;
}
const ASTERISK = 42;
const SLASH = 47;
/**
* Consumes comment from given stream: either multi-line or single-line
* @param {StreamReader} stream
* @return {CommentToken}
*/
var comment = function(stream) {
return singleLineComment(stream) || multiLineComment(stream);
};
function singleLineComment(stream) {
if (eatSingleLineComment(stream)) {
const token = new Token(stream, 'comment');
token.property('type', 'single-line');
return token;
}
}
function multiLineComment(stream) {
if (eatMultiLineComment(stream)) {
const token = new Token(stream, 'comment');
token.property('type', 'multiline');
return token;
}
}
function eatComment(stream) {
return eatSingleLineComment(stream) || eatMultiLineComment(stream);
}
function eatSingleLineComment(stream) {
const start = stream.pos;
if (stream.eat(SLASH) && stream.eat(SLASH)) {
// single-line comment, consume till the end of line
stream.start = start;
while (!stream.eof()) {
if (isLineBreak(stream.next())) {
break;
}
}
return true;
}
stream.pos = start;
return false;
}
function eatMultiLineComment(stream) {
const start = stream.pos;
if (stream.eat(SLASH) && stream.eat(ASTERISK)) {
while (!stream.eof()) {
if (stream.next() === ASTERISK && stream.eat(SLASH)) {
break;
}
}
stream.start = start;
return true;
}
stream.pos = start;
return false;
}
function isLineBreak(code) {
return code === 10 /* LF */ || code === 13 /* CR */;
}
/**
* Consumes white-space tokens from given stream
*/
function whitespace(stream) {
return eatWhitespace(stream) && new Token(stream, 'whitespace');
}
function eatWhitespace(stream) {
return consumeWhile(stream, _emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isSpace"]);
}
const ATTR_START = 91; // [
const ATTR_END = 93; // ]
/**
* Consumes attribute from given string, e.g. value between [ and ]
* @param {StreamReader} stream
* @return {AttributeToken}
*/
function eatAttribuite(stream) {
const start = stream.pos;
if (stream.eat(ATTR_START)) {
skip(stream);
const name = ident(stream);
skip(stream);
const op = operator(stream);
skip(stream);
const value = string(stream) || ident(stream);
skip(stream);
stream.eat(ATTR_END);
return new Token(stream, 'attribute', start).add(name).add(op).add(value);
}
}
function skip(stream) {
while (!stream.eof()) {
if (!eatWhitespace(stream) && !eatComment(stream)) {
return true;
}
}
}
function operator(stream) {
return consumeWhile(stream, isOperator) && new Token(stream, 'operator');
}
function isOperator(code) {
return code === 126 /* ~ */
|| code === 124 /* | */
|| code === 94 /* ^ */
|| code === 36 /* $ */
|| code === 42 /* * */
|| code === 61; /* = */
}
const BACKTICK = 96; // `
/**
* Consumes backtick token, e.g. `...`
* @param {StreamReader} stream
* @param {Function} tokenConsumer
* @return {Token}
*/
function backtick(stream) {
if (eatBacktick(stream)) {
return new Token(stream, 'backtick');
}
}
function eatBacktick(stream) {
const start = stream.pos;
if (Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["eatPair"])(stream, BACKTICK, BACKTICK)) {
stream.start = start;
return true;
}
return false;
}
const CLASS = 46; // .
/**
* Consumes class fragment from given stream, e.g. `.foo`
* @param {StreamReader} stream
* @return {ClassToken}
*/
function className(stream) {
return prefixed(stream, 'class', CLASS, ident);
}
const ADJACENT_SIBLING = 43; // +
const GENERAL_SIBLING = 126; // ~
const CHILD = 62; // >
const NESTING = 38; // &
const types$1 = {
[ADJACENT_SIBLING]: 'adjacentSibling',
[GENERAL_SIBLING]: 'generalSibling',
[CHILD]: 'child',
[NESTING]: 'nesting'
};
/**
* Consumes combinator token from given string
*/
var combinator = function(stream) {
if (isCombinator(stream.peek())) {
const start = stream.pos;
const type = types$1[stream.next()];
const token = new Token(stream, 'combinator', start);
token.property('type', type);
return token;
}
};
function isCombinator(code) {
return code === ADJACENT_SIBLING || code === GENERAL_SIBLING
|| code === NESTING || code === CHILD;
}
const HASH$1 = 35;
function hash(stream) {
return prefixed(stream, 'hash', HASH$1, hashValue, true);
}
function hashValue(stream) {
if (eatHashValue(stream)) {
return new Token(stream, 'hash-value');
}
}
function eatHashValue(stream) {
return consumeWhile(stream, isHashValue);
}
function isHashValue(code) {
return Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(code) || Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isAlpha"])(code, 65 /* A */, 70 /* F */)
|| code === 95 /* _ */ || code === 45 /* - */
|| code > 128; /* non-ASCII */
}
const ID = 35; // #
/**
* Consumes id fragment from given stream, e.g. `#foo`
* @param {StreamReader} stream
* @return {Token}
*/
function id(stream) {
return prefixed(stream, 'id', ID, ident);
}
const IMPORTANT = 33; // !
/**
* Consumes !important token
* @param {StreamReader} stream
* @return {Token}
*/
function important(stream) {
return prefixed(stream, 'important', IMPORTANT, ident);
}
const DOT = 46; // .
/**
* Consumes number from given string, e.g. `10px`
* @param {StreamReader} stream
* @return {NumberToken}
*/
function number(stream) {
if (eatNumericPart(stream)) {
const start = stream.start;
const num = new Token(stream, 'value');
const unit = eatUnitPart(stream) ? new Token(stream, 'unit') : null;
return new Token(stream, 'number', start).add(num).add(unit);
}
}
function eatNumericPart(stream) {
const start = stream.pos;
stream.eat(isOperator$1);
if (stream.eatWhile(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isNumber"])) {
stream.start = start;
const decimalEnd = stream.pos;
if (!(stream.eat(DOT) && stream.eatWhile(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isNumber"]))) {
stream.pos = decimalEnd;
}
return true;
} else if (stream.eat(DOT) && stream.eatWhile(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isNumber"])) {
stream.start = start;
return true;
}
// TODO eat exponent part
stream.pos = start;
return false;
}
function eatUnitPart(stream) {
return eatIdent(stream) || eatPercent(stream);
}
function eatPercent(stream) {
return consume(stream, 37 /* % */);
}
function isOperator$1(code) {
return code === 45 /* - */ || code === 43 /* + */;
}
const NOT = 33; // !
const MULTIPLY = 42; // *
const PLUS = 43; // +
const MINUS = 45; // -
const DIVIDE = 47; // /
const LESS_THAN = 60; // <
const EQUALS = 61; // =
const GREATER_THAN = 62; // <
function operator$1(stream) {
return eatOperator(stream) && new Token(stream, 'operator');
}
function eatOperator(stream) {
if (consume(stream, isEquality)) {
stream.eatWhile(EQUALS);
return true;
} else if (consume(stream, isOperator$2)) {
return true;
}
return false;
}
function isEquality(code) {
return code === NOT || code === LESS_THAN || code === EQUALS || code === GREATER_THAN;
}
function isOperator$2(code) {
return code === MULTIPLY || code === PLUS || code === MINUS || code === DIVIDE
|| isEquality(code);
}
const PSEUDO = 58; // :
/**
* Consumes pseudo-selector from given stream
*/
var pseudo = function(stream) {
const start = stream.pos;
if (stream.eatWhile(PSEUDO)) {
const name = ident(stream);
if (name) {
return new Token(stream, 'pseudo', start).add(name);
}
}
stream.pos = start;
};
/**
* Consumes unquoted value from given stream
* @param {StreamReader} stream
* @return {UnquotedToken}
*/
var unquoted = function(stream) {
return eatUnquoted(stream) && new Token(stream, 'unquoted');
};
function eatUnquoted(stream) {
return consumeWhile(stream, isUnquoted);
}
function isUnquoted(code) {
return !isNaN(code) && !Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isQuote"])(code) && !Object(_emmetio_stream_reader_utils__WEBPACK_IMPORTED_MODULE_1__["isSpace"])(code)
&& code !== 40 /* ( */ && code !== 41 /* ) */ && code !== 92 /* \ */
&& !isNonPrintable(code);
}
function isNonPrintable(code) {
return (code >= 0 && code <= 8) || code === 11
|| (code >= 14 && code <= 31) || code === 127;
}
/**
* Consumes URL token from given stream
* @param {StreamReader} stream
* @return {Token}
*/
function url(stream) {
const start = stream.pos;
if (eatString(stream, 'url(')) {
eatWhitespace(stream);
const value = string(stream) || unquoted(stream);
eatWhitespace(stream);
stream.eat(41); // )
return new Token(stream, 'url', start).add(value);
}
stream.pos = start;
}
function eatUrl(stream) {
const start = stream.pos;
if (eatString(stream, 'url(')) {
eatWhitespace(stream);
eatString$1(stream) || eatUnquoted(stream);
eatWhitespace(stream);
stream.eat(41); // )
stream.start = start;
return true;
}
stream.pos = start;
return false;
}
const VARIABLE = 36; // $
/**
* Consumes SCSS variable from given stream
*/
function variable(stream) {
return prefixed(stream, 'variable', VARIABLE, variableName);
}
function variableName(stream) {
if (eatVariableName(stream)) {
return new Token(stream, 'name');
}
}
function eatVariableName(stream) {
return consumeWhile(stream, isVariableName);
}
function isVariableName(code) {
return code === VARIABLE || isIdent(code);
}
/**
* Group tokens by commonly used context
*/
function consumeToken(stream) {
const _token = any(stream) || args(stream, consumeToken);
if (_token && _token.type === 'ident') {
const _args = args(stream, consumeToken);
if (_args) {
// An identifier followed by arguments – function call
return new Token(stream, 'function', _token.start, _args.end).add(_token).add(_args);
}
}
return _token || unknown(stream);
}
function any(stream) {
return formatting(stream) || url(stream) || selector(stream) || value(stream)
|| separator(stream);
}
function selector(stream) {
return interpolation(stream) || backtick(stream) || ident(stream) || atKeyword(stream)
|| className(stream) || id(stream) || pseudo(stream) || eatAttribuite(stream)
|| combinator(stream);
}
function value(stream) {
return url(stream) || string(stream) || interpolation(stream) || backtick(stream)
|| number(stream) || hash(stream) || keyword(stream) || important(stream)
|| operator$1(stream);
}
function keyword(stream) {
return backtick(stream) || variable(stream) || atKeyword(stream) || ident(stream);
}
function formatting(stream) {
return comment(stream) || whitespace(stream);
}
function unknown(stream) {
stream.start = stream.pos;
const ch = stream.next();
if (ch != null) {
return new Token(stream, 'unknown');
}
}
/**
* Parses CSS rule selector
* @param {String|StreamReader} source
* @return {Token[]}
*/
function parseSelector(source) {
return parseList(source, 'selector');
}
/**
* Parses CSS property name. Mostly used for LESS where
* property-like entry might be used as a mixin call
* @param {String|StreamReader} source
* @return {Token}
*/
function parsePropertyName(source) {
const stream = typeof source === 'string' ? new _emmetio_stream_reader__WEBPACK_IMPORTED_MODULE_0__["default"](source) : source;
const items = [];
while (!stream.eof()) {
items.push(consumeToken(stream));
}
let token;
if (items.length === 1) {
token = items[0];
} else {
token = new Token(stream, 'property-name', stream.start, stream.end);
for (let i = 0, il = items.length; i < il; i++) {
token.add(items[i]);
}
}
return token;
}
/**
* Parses CSS property value
* @param {String|StreamReader} source
* @return {Token[]}
*/
function parsePropertyValue(source) {
return parseList(source);
}
/**
* Parses @media CSS rule expression
* @param {String|StreamReader} source
* @return {Token[]}
*/
function parseMediaExpression(source) {
return parseList(source);
}
/**
* Parses given source into a set of tokens, separated by comma. Each token contains
* parsed sub-items as independent tokens and so on. Mostly used to parse
* selectors and property values
* @param {String|StreamReader} source Source to parse
* @param {String} [tokenType] Type of first-level tokens.
* Default is `item`
* @return {Token[]}
*/
function parseList(source, tokenType) {
tokenType = tokenType || 'item';
const stream = typeof source === 'string' ? new _emmetio_stream_reader__WEBPACK_IMPORTED_MODULE_0__["default"](source) : source;
const items = [];
const fragments = [];
const flush = () => {
const clean = trimFormatting(fragments);
if (clean.length) {
const item = new Token(stream, tokenType, clean[0].start, last(clean).end);
for (let i = 0; i < clean.length; i++) {
item.add(clean[i]);
}
items.push(item);
}
fragments.length = 0;
};
let token;
while (!stream.eof()) {
if (stream.eat(44 /* , */)) {
flush();
} else if (token = consumeToken(stream)) {
if (token.type !== 'comment') {
fragments.push(token);
}
} else {
throw stream.error('Unexpected character');
}
}
flush();
return items;
}
/**
* Creates CSS rule from given tokens
* @param {StreamReader} stream
* @param {Token[]} tokens
* @param {Token} [content]
* @return {Rule}
*/
function createRule(stream, tokens, contentStart, contentEnd) {
if (!tokens.length) {
return null;
}
const name = tokens[0];
name.end = last(tokens).end;
return new Rule(stream, name, contentStart, contentEnd);
}
/**
* Represents CSS rule
* @type {Node}
*/
class Rule extends Node {
/**
* @param {StreamReader} stream
* @param {Token} name Rule’s name token
* @param {Token} contentStart Rule’s content start token
* @param {Token} [contentEnd] Rule’s content end token
*/
constructor(stream, name, contentStart, contentEnd) {
super('rule');
this.stream = stream;
this.selectorToken = name;
this.contentStartToken = contentStart;
this.contentEndToken = contentEnd || contentStart;
this._parsedSelector = null;
}
/**
* Returns rule selector
* @return {String}
*/
get selector() {
return valueOf(this.selectorToken);
}
get parsedSelector() {
if (!this._parsedSelector) {
this._parsedSelector = parseSelector(this.selectorToken.limit());
}
return this._parsedSelector;
}
/**
* Returns node’s start position in stream
* @return {*}
*/
get start() {
return this.selectorToken && this.selectorToken.start;
}
/**
* Returns node’s end position in stream
* @return {*}
*/
get end() {
const token = this.contentEndToken || this.contentStartToken || this.nameToken;
return token && token.end;
}
}
/**
* Creates CSS rule from given tokens
* @param {StreamReader} stream
* @param {Token[]} tokens
* @param {Token} [content]
* @return {Rule}
*/
function createAtRule(stream, tokens, contentStart, contentEnd) {
if (!tokens.length) {
return null;
}
let ix = 0, expression;
const name = tokens[ix++];
if (ix < tokens.length) {
expression = tokens[ix++];
expression.type = 'expression';
expression.end = last(tokens).end;
} else {
expression = new Token(stream, 'expression', name.end, name.end);
}
return new AtRule(stream, name, expression, contentStart, contentEnd);
}
class AtRule extends Node {
constructor(stream, name, expression, contentStart, contentEnd) {
super('at-rule');
this.stream = stream;
this.nameToken = name;
this.expressionToken = expression;
this.contentStartToken = contentStart;
this.contentEndToken = contentEnd || contentStart;
this._parsedExpression = null;
}
/**
* Returns at-rule name
* @return {String}
*/
get name() {
return valueOf(this.nameToken && this.nameToken.item(0));
}
get expression() {
return valueOf(this.expressionToken);
}
get parsedExpression() {
if (!this._parsedExpression) {
this._parsedExpression = parseMediaExpression(this.expressionToken.limit());
}
return this._parsedExpression;
}
/**
* Returns node’s start position in stream
* @return {*}
*/
get start() {
return this.nameToken && this.nameToken.start;
}
/**
* Returns node’s end position in stream
* @return {*}
*/
get end() {
const token = this.contentEndToken || this.contentStartToken || this.nameToken;
return token && token.end;
}
}
/**
* Factory method that creates property node from given tokens
* @param {StreamReader} stream
* @param {Token[]} tokens
* @param {Token} terminator
* @return {Property}
*/
function createProperty(stream, tokens, terminator) {
// NB in LESS, fragmented properties without value like `.foo.bar;` must be
// treated like mixin call
if (!tokens.length) {
return null;
}
let separator, value, ix = 0;
const name = tokens[ix++];
if (ix < tokens.length) {
value = tokens[ix++];
value.type = 'value';
value.end = last(tokens).end;
}
if (name && value) {
separator = new Token(stream, 'separator', name.end, value.start);
}
return new Property(
stream,
name,
value,
separator,
terminator
);
}
class Property extends Node {
constructor(stream, name, value, separator, terminator) {
super('property');
this.stream = stream;
this.nameToken = name;
this.valueToken = value;
this._parsedName = null;
this._parsedValue = null;
this.separatorToken = separator;
this.terminatorToken = terminator;
}
/**
* Property name
* @return {String}
*/
get name() {
return valueOf(this.nameToken);
}
/**
* Returns parsed sub-tokens of current property name
* @return {Token[]}
*/
get parsedName() {
if (!this._parsedName) {
this._parsedName = parsePropertyName(this.nameToken.limit());
}
return this._parsedName;
}
/**
* Property value
* @return {String}
*/
get value() {
return valueOf(this.valueToken);
}
/**
* Parsed value parts: a list of tokens, separated by comma. Each token may
* contains parsed sub-tokens and so on
* @return {Token[]}
*/
get parsedValue() {
if (!this._parsedValue) {
this._parsedValue = parsePropertyValue(this.valueToken.limit());
}
return this._parsedValue;
}
get separator() {
return valueOf(this.separatorToken);
}
get terminator() {
return valueOf(this.terminatorToken);
}
get start() {
const token = this.nameToken || this.separatorToken || this.valueToken
|| this.terminatorToken;
return token && token.start;
}
get end() {
const token = this.terminatorToken || this.valueToken
|| this.separatorToken || this.nameToken;
return token && token.end;
}
}
const LBRACE = 40; // (
const RBRACE = 41; // )
const PROP_DELIMITER = 58; // :
const PROP_TERMINATOR = 59; // ;
const RULE_START = 123; // {
const RULE_END = 125; // }
function parseStylesheet(source) {
const stream = typeof source === 'string' ? new _emmetio_stream_reader__WEBPACK_IMPORTED_MODULE_0__["default"](source) : source;
const root = new Stylesheet();
let ctx = root, child, accum, token;
let tokens = [];
const flush = () => {
if (accum) {
tokens.push(accum);
accum = null;
}
};
while (!stream.eof()) {
if (eatWhitespace(stream)) {
continue;
}
if (token = comment(stream)) {
root.addComment(token);
continue;
}
stream.start = stream.pos;
if (stream.eatWhile(PROP_DELIMITER)) {
// Property delimiter can be either a real property delimiter or a
// part of pseudo-selector.
if (!tokens.length) {
if (accum) {
// No consumed tokens yet but pending token: most likely it’s
// a CSS property
flush();
} else {
// No consumend or accumulated token, seems like a start of
// pseudo-selector, e.g. `::slotted`
accum = new Token(stream, 'preparse');
}
}
// Skip delimiter if there are already consumend tokens: most likely
// it’s a part of pseudo-selector
} else if (stream.eat(PROP_TERMINATOR)) {
flush();
ctx.add(createProperty(stream, tokens, new Token(stream, 'termintator')));
tokens.length = 0;
} else if (stream.eat(RULE_START)) {
flush();
if (tokens.length > 0) {
child = tokens[0].type === 'at-keyword'
? createAtRule(stream, tokens, new Token(stream, 'body-start'))
: createRule(stream, tokens, new Token(stream, 'body-start'));
ctx.add(child);
ctx = child;
tokens.length = 0;
}
} else if (stream.eat(RULE_END)) {
flush();
// Finalize context section
ctx.add(createProperty(stream, tokens));
if (ctx.type !== 'stylesheet') {
// In case of invalid stylesheet with redundant `}`,
// don’t modify root section.
ctx.contentEndToken = new Token(stream, 'body-end');
ctx = ctx.parent;
}
tokens.length = 0;
} else if (token = atKeyword(stream)) {
// Explictly consume @-tokens since it defines how rule or property
// should be pre-parsed
flush();
tokens.push(token);
} else if (eatUrl(stream) || eatInterpolation(stream) || eatBacktick(stream)
|| eatBraces(stream, root) || eatString$1(stream) || stream.next()) {
// NB explicitly consume `url()` token since it may contain
// an unquoted url like `http://example.com` which interferes
// with single-line comment
accum = accum || new Token(stream, 'preparse');
accum.end = stream.pos;
} else {
throw new Error(`Unexpected end-of-stream at ${stream.pos}`);
}
}
if (accum) {
tokens.push(accum);
}
// Finalize all the rest properties
ctx.add(createProperty(stream, tokens));
// Finalize unterminated rules
stream.start = stream.pos;
while (ctx && ctx !== root) {
ctx.contentEndToken = new Token(stream, 'body-end');
ctx = ctx.parent;
}
return root;
}
/**
* Parses given source into tokens
* @param {String|StreamReader} source
* @param {Function} [consumer] Token consumer function, for example, `selector`,
* `value` etc. from `lib/tokens` module. Default is generic `consumeToken`
* @return {Token[]}
*/
function lexer(source, consumer) {
consumer = consumer || consumeToken;
const stream = typeof source === 'string' ? new _emmetio_stream_reader__WEBPACK_IMPORTED_MODULE_0__["default"](source) : source;
const result = [];
let token;
while (!stream.eof() && (token = consumer(stream))) {
result.push(token);
}
return result;
}
/**
* Consumes content inside round braces. Mostly used to skip `;` token inside
* expressions since in LESS it is also used to separate function arguments
* @param {StringReader} stream
* @param {Stylesheet} root A stylesheet root. Used to accumulate comments
* @return {Boolean}
*/
function eatBraces(stream, root) {
if (stream.eat(LBRACE)) {
let stack = 1, token;
while (!stream.eof()) {
if (stream.eat(RBRACE)) {
stack--;
if (!stack) {
break;
}
} else if (stream.eat(LBRACE)) {
stack++;
} else if (eatUrl(stream) || eatString$1(stream)) {
continue;
} else if (token = comment(stream)) {
root.addComment(token);
continue;
} else {
stream.next();
}
}
return true;
}
return false;
}
/* harmony default export */ __webpack_exports__["default"] = (parseStylesheet);
/***/ }),
/* 37 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
/* Based on @sergeche's work in his emmet plugin */
const vscode_languageserver_protocol_1 = __webpack_require__(3);
function comparePosition(position, other) {
if (position.line > other.line)
return 1;
if (other.line == position.line && position.character > other.character)
return 1;
if (other.line == position.line && position.character == other.character)
return 0;
return -1;
}
exports.comparePosition = comparePosition;
/**
* A stream reader for VSCode's `TextDocument`
* Based on @emmetio/stream-reader and @emmetio/atom-plugin
*/
class DocumentStreamReader {
constructor(document, pos, limit) {
this.document = document;
this.start = this.pos = pos ? pos : vscode_languageserver_protocol_1.Position.create(0, 0);
this._sof = limit ? limit.start : vscode_languageserver_protocol_1.Position.create(0, 0);
this._eof = limit ? limit.end : vscode_languageserver_protocol_1.Position.create(document.lineCount - 1, this._lineLength(this.document.lineCount - 1));
this._eol = `\n`;
}
/**
* Returns true only if the stream is at the start of the file.
*/
sof() {
return comparePosition(this.pos, this._sof) <= 0;
}
/**
* Returns true only if the stream is at the end of the file.
*/
eof() {
return comparePosition(this.pos, this._eof) >= 0;
}
/**
* Creates a new stream instance which is limited to given range for given document
*/
limit(start, end) {
return new DocumentStreamReader(this.document, start, vscode_languageserver_protocol_1.Range.create(start, end));
}
/**
* Returns the next character code in the stream without advancing it.
* Will return NaN at the end of the file.
*/
peek() {
if (this.eof()) {
return NaN;
}
const line = this.getline(this.pos.line);
return this.pos.character < line.length ? line.charCodeAt(this.pos.character) : this._eol.charCodeAt(0);
}
getline(line) {
let content = this.document.getText();
let lines = content.split('\n');
return lines[line] || '';
}
/**
* Returns the next character in the stream and advances it.
* Also returns NaN when no more characters are available.
*/
next() {
if (this.eof()) {
return NaN;
}
const line = this.getline(this.pos.line);
let code;
if (this.pos.character < line.length) {
code = line.charCodeAt(this.pos.character);
this.pos = vscode_languageserver_protocol_1.Position.create(this.pos.line, this.pos.character + 1);
}
else {
code = this._eol.charCodeAt(this.pos.character - line.length);
this.pos = vscode_languageserver_protocol_1.Position.create(this.pos.line + 1, this.pos.character);
}
if (this.eof()) {
// restrict pos to eof, if in case it got moved beyond eof
this.pos = vscode_languageserver_protocol_1.Position.create(this._eof.line, this._eof.character);
}
return code;
}
/**
* Backs up the stream n characters. Backing it up further than the
* start of the current token will cause things to break, so be careful.
*/
backUp(n) {
let row = this.pos.line;
let column = this.pos.character;
column -= (n || 1);
while (row >= 0 && column < 0) {
row--;
column += this._lineLength(row);
}
this.pos = row < 0 || column < 0
? vscode_languageserver_protocol_1.Position.create(0, 0)
: vscode_languageserver_protocol_1.Position.create(row, column);
return this.peek();
}
/**
* Get the string between the start of the current token and the
* current stream position.
*/
current() {
return this.substring(this.start, this.pos);
}
/**
* Returns contents for given range
*/
substring(from, to) {
return this.document.getText(vscode_languageserver_protocol_1.Range.create(from, to));
}
/**
* Creates error object with current stream state
*/
error(message) {
const err = new Error(`${message} at row ${this.pos.line}, column ${this.pos.character}`);
return err;
}
/**
* Returns line length of given row, including line ending
*/
_lineLength(row) {
const line = this.getline(row);
return line.length;
}
/**
* `match` can be a character code or a function that takes a character code
* and returns a boolean. If the next character in the stream 'matches'
* the given argument, it is consumed and returned.
* Otherwise, `false` is returned.
*/
eat(match) {
const ch = this.peek();
const ok = typeof match === 'function' ? match(ch) : ch === match;
if (ok) {
this.next();
}
return ok;
}
/**
* Repeatedly calls eat
with the given argument, until it
* fails. Returns true
if any characters were eaten.
*/
eatWhile(match) {
const start = this.pos;
while (!this.eof() && this.eat(match)) { }
return comparePosition(this.pos, start) != 0;
}
}
exports.DocumentStreamReader = DocumentStreamReader;
/***/ }),
/* 38 */
/***/ (function(module, exports) {
module.exports = require("vscode-emmet-helper");
/***/ })
/******/ ])));