UNPKG

@unisnips/ultisnips

Version:

Utilities for converting ultisnips in unisnips project

590 lines 19.6 kB
"use strict"; /** * Convert snippet text file into logical units called Tokens. * Ports https://github.com/SirVer/ultisnips/blob/master/pythonx/UltiSnips/snippet/parsing/lexer.py */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable @typescript-eslint/no-use-before-define */ var position_1 = require("../util/position"); var control_flow_1 = require("@bestminr/control-flow"); var StopIteration = /** @class */ (function (_super) { __extends(StopIteration, _super); function StopIteration() { return _super !== null && _super.apply(this, arguments) || this; } return StopIteration; }(control_flow_1.ExtensibleError)); exports.StopIteration = StopIteration; /** * Helper class to make iterating over text easier. * A simplified char stream */ var TextIterator = /** @class */ (function () { function TextIterator(text, startPosition) { this.idx = 0; this.text = text; this.textLineContents = text.split('\n'); this.line = startPosition.line; this.col = startPosition.column; this.startPosition = startPosition; } TextIterator.prototype.peek = function (len) { if (len === void 0) { len = 1; } if (len > 1) return this.text.substr(this.idx, len); else return this.text[this.idx]; }; /** return the next n characters */ TextIterator.prototype.next = function (n) { if (n === void 0) { n = 1; } if (n === 1) return this.nextOne(); var chars = []; for (var i = 0; i < n; i++) { chars.push(this.nextOne()); } return chars.join(''); }; /** roll back the stream n characters, won't return chars */ TextIterator.prototype.rollback = function (n) { if (n === void 0) { n = 1; } if (n === 1) return this.rollbackOne(); for (var i = 0; i < n; i++) { this.rollbackOne(); } }; /** return the next character */ TextIterator.prototype.nextOne = function () { if (this.idx > this.text.length) { throw new StopIteration(); } var rv = this.text[this.idx]; if (this.text[this.idx] === '\n') { this.line += 1; this.col = 0; } else { this.col += 1; } this.idx += 1; return rv; }; TextIterator.prototype.rollbackOne = function () { if (this.idx < 0) { return; } if (this.col <= 0) { this.line -= 1; this.col = this.textLineContents[this.line].length - 1; } else { this.col -= 1; } this.idx -= 1; }; Object.defineProperty(TextIterator.prototype, "position", { get: function () { var startOffset = this.startPosition.offset || 0; return new position_1.TextPosition(this.line, this.col, startOffset + this.idx); }, enumerable: true, configurable: true }); return TextIterator; }()); exports.TextIterator = TextIterator; var CHARS = { digits: '0123456789'.split(''), }; function parseIndexNumber(iter) { var rv = ''; var peekedV = iter.peek(); while (peekedV && CHARS.digits.includes(peekedV)) { rv += iter.next(); peekedV = iter.peek(); } return parseInt(rv); } function parseTillClosingBrace(iter) { var braceDepth = 1; var retChars = []; while (true) { if (EscapeCharToken.startsWithChar(iter, '{}')) { retChars.push(iter.next(2)); } else { var char = iter.next(); if (char === '{') braceDepth += 1; else if (char === '}') braceDepth -= 1; if (braceDepth < 1) { break; } retChars.push(char); } } return retChars.join(''); } exports.parseTillClosingBrace = parseTillClosingBrace; /** * Returns all chars till a non-escaped char is found. * * Will also consume the closing char, but and return it as second return value */ function parseTillUnescapedChar(iter, str, validWhenEndOfText) { var e_1, _a; if (validWhenEndOfText === void 0) { validWhenEndOfText = false; } var retChars = []; var char; try { while (true) { var chars = str.split(''); var escaped = false; try { for (var chars_1 = (e_1 = void 0, __values(chars)), chars_1_1 = chars_1.next(); !chars_1_1.done; chars_1_1 = chars_1.next()) { var c = chars_1_1.value; char = c; if (EscapeCharToken.startsWithChar(iter, char)) { retChars.push(iter.next(2)); escaped = true; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (chars_1_1 && !chars_1_1.done && (_a = chars_1.return)) _a.call(chars_1); } finally { if (e_1) throw e_1.error; } } if (!escaped) { char = iter.next(); if (chars.includes(char)) { break; } else { } retChars.push(char); } } } catch (error) { if (error instanceof StopIteration) { if (!validWhenEndOfText) { throw error; } } else { throw error; } } return [retChars.join(''), char]; } exports.parseTillUnescapedChar = parseTillUnescapedChar; /** * Represents a Token as parsed from a snippet definition. */ var Token = /** @class */ (function () { function Token(iter, opts) { if (opts === void 0) { opts = {}; } this.initialText = ''; this.isTransformable = false; this.parent = opts.parent; this.start = iter.position; this.parse(iter, opts.indent); this.end = iter.position; } // transform?: TransformationToken Token.startsHere = function (iter) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } return false; }; Object.defineProperty(Token.prototype, "tokenType", { /** * will be serialized to TokenNode */ get: function () { return 'Token'; }, enumerable: true, configurable: true }); Token.prototype.getTokenNodeData = function () { return {}; }; Token.prototype.toTokenNode = function () { return { type: this.tokenType, position: { start: this.start.toUnistPosition(), end: this.end.toUnistPosition(), }, data: this.getTokenNodeData(), }; }; return Token; }()); exports.Token = Token; var TabStopToken = /** @class */ (function (_super) { __extends(TabStopToken, _super); function TabStopToken() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.isTransformable = true; return _this; } TabStopToken.startsHere = function (iter) { return this.PATTERN.test(iter.peek(10)); }; TabStopToken.prototype.getTokenNodeData = function () { return { number: this.number }; }; Object.defineProperty(TabStopToken.prototype, "tokenType", { get: function () { return 'TabStop'; }, enumerable: true, configurable: true }); TabStopToken.prototype.parse = function (iter) { iter.next(); // $ iter.next(); // { this.number = parseIndexNumber(iter); if (iter.peek() === ':') { this.hasColon = true; iter.next(); } this.initialText = parseTillClosingBrace(iter); }; TabStopToken.PATTERN = /^\$\{\d+[:}]?/; return TabStopToken; }(Token)); exports.TabStopToken = TabStopToken; var VisualToken = /** @class */ (function (_super) { __extends(VisualToken, _super); function VisualToken() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.isTransformable = true; _this.alternativeText = ''; _this.options = null; return _this; } VisualToken.startsHere = function (iter) { return this.PATTERN.test(iter.peek(10)); }; Object.defineProperty(VisualToken.prototype, "tokenType", { get: function () { return 'Visual'; }, enumerable: true, configurable: true }); VisualToken.prototype.toString = function () { return "VisualToken(" + this.start + "," + this.end + ")"; }; VisualToken.prototype.parse = function (iter) { iter.next(8); // ${VISUAL if (iter.peek() === ':') { iter.next(); } var _a = __read(parseTillUnescapedChar(iter, '/}'), 2), text = _a[0], closingChar = _a[1]; this.alternativeText = unescape(text); if (closingChar === '/') { try { this.search = parseTillUnescapedChar(iter, '/')[0]; this.replace = parseTillUnescapedChar(iter, '/')[0]; this.options = parseTillClosingBrace(iter); } catch (error) { throw new Error("Invalid ${VISUAL} transformation! Forgot to escape a '/'?"); } } else { this.search = null; this.replace = null; } }; // ${VISUAL} VisualToken.PATTERN = /^\$\{VISUAL[:}\/]/; return VisualToken; }(Token)); exports.VisualToken = VisualToken; // export class TransformationToken extends Token { // search: string // replace: string // options: any = null // static startsHere(iter: TextIterator) { // return iter.peek() === '/' // } // get tokenType() { // return 'Transformation' // } // getTokenNodeData() { // return pick(this, [ // 'number', // 'search', // 'replace', // 'options', // ] as Array<keyof this>); // } // protected parse(iter: TextIterator) { // iter.next() // / // this.search = parseTillUnescapedChar(iter, '/')[0] // this.replace = parseTillUnescapedChar(iter, '/')[0] // this.options = parseTillClosingBrace(iter) // } // } var MirrorToken = /** @class */ (function (_super) { __extends(MirrorToken, _super); function MirrorToken() { return _super !== null && _super.apply(this, arguments) || this; } MirrorToken.startsHere = function (iter) { return this.PATTERN.test(iter.peek(10)); }; Object.defineProperty(MirrorToken.prototype, "tokenType", { get: function () { return 'Mirror'; }, enumerable: true, configurable: true }); MirrorToken.prototype.toString = function () { return "MirrorToken(" + this.start.toString() + "," + this.end.toString() + "," + this.number + ")"; }; MirrorToken.prototype.getTokenNodeData = function () { return { number: this.number }; }; MirrorToken.prototype.parse = function (iter) { iter.next(); // $ this.number = parseIndexNumber(iter); }; MirrorToken.PATTERN = /^\$\d+/; return MirrorToken; }(Token)); exports.MirrorToken = MirrorToken; /** * Represents UniSnips' builtin variable */ var UniSnipsVariableToken = /** @class */ (function (_super) { __extends(UniSnipsVariableToken, _super); function UniSnipsVariableToken() { return _super !== null && _super.apply(this, arguments) || this; } // static VALID_NAME_PATTERN = /[0-9A-Z_]/ UniSnipsVariableToken.startsHere = function (iter) { // $UNI_* return iter.peek(5) === '$UNI_'; }; UniSnipsVariableToken.prototype.toString = function () { return "UniSnipsVariableToken(" + this.start.toString() + "," + this.end.toString() + "," + this.name + ")"; }; UniSnipsVariableToken.prototype.parse = function (iter) { iter.next(5); // $UNI_ var _a = __read(parseTillUnescapedChar(iter, '$,\n./ ', true), 1), restName = _a[0]; this.name = 'UNI_' + restName; }; return UniSnipsVariableToken; }(Token)); exports.UniSnipsVariableToken = UniSnipsVariableToken; var EscapeCharToken = /** @class */ (function (_super) { __extends(EscapeCharToken, _super); function EscapeCharToken() { return _super !== null && _super.apply(this, arguments) || this; } EscapeCharToken.startsHere = function (iter, charsOrIndent) { if (typeof charsOrIndent === 'string') { return this.startsWithChar(iter, charsOrIndent); } }; EscapeCharToken.startsWithChar = function (iter, validEscapeChars) { if (validEscapeChars === void 0) { validEscapeChars = '{}\\$`'; } // console.log('cha', validEscapeChars) var chars = iter.peek(2); return chars.length === 2 && chars[0] === '\\' && validEscapeChars.indexOf(chars[1]) > -1; }; Object.defineProperty(EscapeCharToken.prototype, "tokenType", { get: function () { return 'EscapeChar'; }, enumerable: true, configurable: true }); EscapeCharToken.prototype.parse = function (iter) { iter.next(); // \ this.initialText = iter.next(); }; return EscapeCharToken; }(Token)); exports.EscapeCharToken = EscapeCharToken; /** * There are various script types, but they are all wrapped by '`' * * - `ehco "unisnips"` , shell * - `!js console.log('hello')`, js * - `!p snip.rv = "hi"`, python */ var ScriptCodeToken = /** @class */ (function (_super) { __extends(ScriptCodeToken, _super); function ScriptCodeToken() { return _super !== null && _super.apply(this, arguments) || this; } ScriptCodeToken.startsHere = function (iter) { return iter.peek() === '`'; }; Object.defineProperty(ScriptCodeToken.prototype, "tokenType", { get: function () { return 'ScriptCode'; }, enumerable: true, configurable: true }); ScriptCodeToken.prototype.getTokenNodeData = function () { return { scriptType: this.scriptType, scriptCode: this.scriptCode, }; }; ScriptCodeToken.prototype.parse = function (iter) { iter.next(); // ` var nextChars = iter.peek(5); var scriptType; if (this.containsWord(nextChars, '!')) { scriptType = 'shell'; iter.next(2); } else if (this.containsWord(nextChars, '!p')) { scriptType = 'python'; iter.next(3); } else if (this.containsWord(nextChars, '!v')) { scriptType = 'vim'; iter.next(3); } else if (this.containsWord(nextChars, '!js')) { scriptType = 'js'; iter.next(4); } if (scriptType) { this.scriptType = scriptType; var _a = __read(parseTillUnescapedChar(iter, '`'), 1), content = _a[0]; this.scriptCode = content; } }; ScriptCodeToken.prototype.containsWord = function (chars, word, validSeperator) { if (validSeperator === void 0) { validSeperator = [' ', '\n']; } var charsToCheck = chars.substr(0, word.length + 1); var lastChar = charsToCheck[charsToCheck.length - 1]; return charsToCheck.startsWith(word) && validSeperator.includes(lastChar); }; return ScriptCodeToken; }(Token)); exports.ScriptCodeToken = ScriptCodeToken; var EndOfTextToken = /** @class */ (function (_super) { __extends(EndOfTextToken, _super); function EndOfTextToken() { return _super !== null && _super.apply(this, arguments) || this; } Object.defineProperty(EndOfTextToken.prototype, "tokenType", { get: function () { return 'EndOfText'; }, enumerable: true, configurable: true }); EndOfTextToken.prototype.parse = function () { // nothing }; return EndOfTextToken; }(Token)); exports.EndOfTextToken = EndOfTextToken; function tokenize(text, indent, position, allowedTokens) { var e_2, _a; var iter = new TextIterator(text, position); var tokens = []; try { while (true) { var doneSomething = false; try { for (var allowedTokens_1 = (e_2 = void 0, __values(allowedTokens)), allowedTokens_1_1 = allowedTokens_1.next(); !allowedTokens_1_1.done; allowedTokens_1_1 = allowedTokens_1.next()) { var tokenCtor = allowedTokens_1_1.value; if (tokenCtor.startsHere(iter, indent)) { var token = new tokenCtor(iter, { indent: indent }); tokens.push(token); doneSomething = true; // if (token.isTransformable && token.transform) { // tokens.push(token.transform) // } break; } else { } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (allowedTokens_1_1 && !allowedTokens_1_1.done && (_a = allowedTokens_1.return)) _a.call(allowedTokens_1); } finally { if (e_2) throw e_2.error; } } if (!doneSomething) { iter.next(); } } } catch (error) { if (error instanceof StopIteration) { return tokens; } throw error; } return tokens; } exports.tokenize = tokenize; //# sourceMappingURL=tokenizer.js.map