// CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE ;(function (mod) { if (typeof exports == 'object' && typeof module == 'object') // CommonJS mod(require('../../lib/codemirror'), require('../xml/xml'), require('../javascript/javascript')) else if (typeof define == 'function' && define.amd) // AMD define(['../../lib/codemirror', '../xml/xml', '../javascript/javascript'], mod) // Plain browser env else mod(CodeMirror) })(function (CodeMirror) { 'use strict' // Depth means the amount of open braces in JS context, in XML // context 0 means not in tag, 1 means in tag, and 2 means in tag // and js block comment. function Context(state, mode, depth, prev) { this.state = state this.mode = mode this.depth = depth this.prev = prev } function copyContext(context) { return new Context(CodeMirror.copyState(context.mode, context.state), context.mode, context.depth, context.prev && copyContext(context.prev)) } CodeMirror.defineMode( 'jsx', function (config, modeConfig) { var xmlMode = CodeMirror.getMode(config, { name: 'xml', allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true }) var jsMode = CodeMirror.getMode(config, (modeConfig && modeConfig.base) || 'javascript') function flatXMLIndent(state) { var tagName = state.tagName state.tagName = null var result = xmlMode.indent(state, '', '') state.tagName = tagName return result } function token(stream, state) { if (state.context.mode == xmlMode) return xmlToken(stream, state, state.context) else return jsToken(stream, state, state.context) } function xmlToken(stream, state, cx) { if (cx.depth == 2) { // Inside a JS /* */ comment if (stream.match(/^.*?\*\//)) cx.depth = 1 else stream.skipToEnd() return 'comment' } if (stream.peek() == '{') { xmlMode.skipAttribute(cx.state) var indent = flatXMLIndent(cx.state), xmlContext = cx.state.context // If JS starts on same line as tag if (xmlContext && stream.match(/^[^>]*>\s*$/, false)) { while (xmlContext.prev && !xmlContext.startOfLine) xmlContext = xmlContext.prev // If tag starts the line, use XML indentation level if (xmlContext.startOfLine) indent -= config.indentUnit // Else use JS indentation level else if (cx.prev.state.lexical) indent = cx.prev.state.lexical.indented // Else if inside of tag } else if (cx.depth == 1) { indent += config.indentUnit } state.context = new Context(CodeMirror.startState(jsMode, indent), jsMode, 0, state.context) return null } if (cx.depth == 1) { // Inside of tag if (stream.peek() == '<') { // Tag inside of tag xmlMode.skipAttribute(cx.state) state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)), xmlMode, 0, state.context) return null } else if (stream.match('//')) { stream.skipToEnd() return 'comment' } else if (stream.match('/*')) { cx.depth = 2 return token(stream, state) } } var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop if (/\btag\b/.test(style)) { if (/>$/.test(cur)) { if (cx.state.context) cx.depth = 0 else state.context = state.context.prev } else if (/^ -1) { stream.backUp(cur.length - stop) } return style } function jsToken(stream, state, cx) { if (stream.peek() == '<' && jsMode.expressionAllowed(stream, cx.state)) { state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, '', '')), xmlMode, 0, state.context) jsMode.skipExpression(cx.state) return null } var style = jsMode.token(stream, cx.state) if (!style && cx.depth != null) { var cur = stream.current() if (cur == '{') { cx.depth++ } else if (cur == '}') { if (--cx.depth == 0) state.context = state.context.prev } } return style } return { startState: function () { return { context: new Context(CodeMirror.startState(jsMode), jsMode) } }, copyState: function (state) { return { context: copyContext(state.context) } }, token: token, indent: function (state, textAfter, fullLine) { return state.context.mode.indent(state.context.state, textAfter, fullLine) }, innerMode: function (state) { return state.context }, } }, 'xml', 'javascript' ) CodeMirror.defineMIME('text/jsx', 'jsx') CodeMirror.defineMIME('text/typescript-jsx', { name: 'jsx', base: { name: 'javascript', typescript: true } }) })