123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- // CodeMirror, copyright (c) by Marijn Haverbeke and others
- // Distributed under an MIT license: https://codemirror.net/LICENSE
- /**
- * Smarty 2 and 3 mode.
- */
- ;(function (mod) {
- if (typeof exports == 'object' && typeof module == 'object')
- // CommonJS
- mod(require('../../lib/codemirror'))
- else if (typeof define == 'function' && define.amd)
- // AMD
- define(['../../lib/codemirror'], mod)
- // Plain browser env
- else mod(CodeMirror)
- })(function (CodeMirror) {
- 'use strict'
- CodeMirror.defineMode('smarty', function (config, parserConf) {
- var rightDelimiter = parserConf.rightDelimiter || '}'
- var leftDelimiter = parserConf.leftDelimiter || '{'
- var version = parserConf.version || 2
- var baseMode = CodeMirror.getMode(config, parserConf.baseMode || 'null')
- var keyFunctions = ['debug', 'extends', 'function', 'include', 'literal']
- var regs = {
- operatorChars: /[+\-*&%=<>!?]/,
- validIdentifier: /[a-zA-Z0-9_]/,
- stringChar: /['"]/,
- }
- var last
- function cont(style, lastType) {
- last = lastType
- return style
- }
- function chain(stream, state, parser) {
- state.tokenize = parser
- return parser(stream, state)
- }
- // Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode
- function doesNotCount(stream, pos) {
- if (pos == null) pos = stream.pos
- return version === 3 && leftDelimiter == '{' && (pos == stream.string.length || /\s/.test(stream.string.charAt(pos)))
- }
- function tokenTop(stream, state) {
- var string = stream.string
- for (var scan = stream.pos; ; ) {
- var nextMatch = string.indexOf(leftDelimiter, scan)
- scan = nextMatch + leftDelimiter.length
- if (nextMatch == -1 || !doesNotCount(stream, nextMatch + leftDelimiter.length)) break
- }
- if (nextMatch == stream.pos) {
- stream.match(leftDelimiter)
- if (stream.eat('*')) {
- return chain(stream, state, tokenBlock('comment', '*' + rightDelimiter))
- } else {
- state.depth++
- state.tokenize = tokenSmarty
- last = 'startTag'
- return 'tag'
- }
- }
- if (nextMatch > -1) stream.string = string.slice(0, nextMatch)
- var token = baseMode.token(stream, state.base)
- if (nextMatch > -1) stream.string = string
- return token
- }
- // parsing Smarty content
- function tokenSmarty(stream, state) {
- if (stream.match(rightDelimiter, true)) {
- if (version === 3) {
- state.depth--
- if (state.depth <= 0) {
- state.tokenize = tokenTop
- }
- } else {
- state.tokenize = tokenTop
- }
- return cont('tag', null)
- }
- if (stream.match(leftDelimiter, true)) {
- state.depth++
- return cont('tag', 'startTag')
- }
- var ch = stream.next()
- if (ch == '$') {
- stream.eatWhile(regs.validIdentifier)
- return cont('variable-2', 'variable')
- } else if (ch == '|') {
- return cont('operator', 'pipe')
- } else if (ch == '.') {
- return cont('operator', 'property')
- } else if (regs.stringChar.test(ch)) {
- state.tokenize = tokenAttribute(ch)
- return cont('string', 'string')
- } else if (regs.operatorChars.test(ch)) {
- stream.eatWhile(regs.operatorChars)
- return cont('operator', 'operator')
- } else if (ch == '[' || ch == ']') {
- return cont('bracket', 'bracket')
- } else if (ch == '(' || ch == ')') {
- return cont('bracket', 'operator')
- } else if (/\d/.test(ch)) {
- stream.eatWhile(/\d/)
- return cont('number', 'number')
- } else {
- if (state.last == 'variable') {
- if (ch == '@') {
- stream.eatWhile(regs.validIdentifier)
- return cont('property', 'property')
- } else if (ch == '|') {
- stream.eatWhile(regs.validIdentifier)
- return cont('qualifier', 'modifier')
- }
- } else if (state.last == 'pipe') {
- stream.eatWhile(regs.validIdentifier)
- return cont('qualifier', 'modifier')
- } else if (state.last == 'whitespace') {
- stream.eatWhile(regs.validIdentifier)
- return cont('attribute', 'modifier')
- }
- if (state.last == 'property') {
- stream.eatWhile(regs.validIdentifier)
- return cont('property', null)
- } else if (/\s/.test(ch)) {
- last = 'whitespace'
- return null
- }
- var str = ''
- if (ch != '/') {
- str += ch
- }
- var c = null
- while ((c = stream.eat(regs.validIdentifier))) {
- str += c
- }
- for (var i = 0, j = keyFunctions.length; i < j; i++) {
- if (keyFunctions[i] == str) {
- return cont('keyword', 'keyword')
- }
- }
- if (/\s/.test(ch)) {
- return null
- }
- return cont('tag', 'tag')
- }
- }
- function tokenAttribute(quote) {
- return function (stream, state) {
- var prevChar = null
- var currChar = null
- while (!stream.eol()) {
- currChar = stream.peek()
- if (stream.next() == quote && prevChar !== '\\') {
- state.tokenize = tokenSmarty
- break
- }
- prevChar = currChar
- }
- return 'string'
- }
- }
- function tokenBlock(style, terminator) {
- return function (stream, state) {
- while (!stream.eol()) {
- if (stream.match(terminator)) {
- state.tokenize = tokenTop
- break
- }
- stream.next()
- }
- return style
- }
- }
- return {
- startState: function () {
- return {
- base: CodeMirror.startState(baseMode),
- tokenize: tokenTop,
- last: null,
- depth: 0,
- }
- },
- copyState: function (state) {
- return {
- base: CodeMirror.copyState(baseMode, state.base),
- tokenize: state.tokenize,
- last: state.last,
- depth: state.depth,
- }
- },
- innerMode: function (state) {
- if (state.tokenize == tokenTop) return { mode: baseMode, state: state.base }
- },
- token: function (stream, state) {
- var style = state.tokenize(stream, state)
- state.last = last
- return style
- },
- indent: function (state, text, line) {
- if (state.tokenize == tokenTop && baseMode.indent) return baseMode.indent(state.base, text, line)
- else return CodeMirror.Pass
- },
- blockCommentStart: leftDelimiter + '*',
- blockCommentEnd: '*' + rightDelimiter,
- }
- })
- CodeMirror.defineMIME('text/x-smarty', 'smarty')
- })
|