Last active
October 17, 2015 05:40
-
-
Save jquense/265b46c740da4d20a718 to your computer and use it in GitHub Desktop.
An attempt at a jsx CodeMirror mode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global CodeMirror */ | |
function indexOf(string, pattern, from) { | |
if (typeof pattern === "string") { | |
var found = string.indexOf(pattern, from); | |
return found; | |
} | |
var m = pattern.exec(from ? string.slice(from) : string); | |
return m ? (m.index + from) : -1; | |
} | |
function getAllIndexes(string, val, from) { | |
var indexes = [], i = from || 0; | |
for(; i < string.length; i++) | |
if (string[i] === val) indexes.push(i); | |
return indexes; | |
} | |
CodeMirror.defineMode("jsx", function(config, parserConfig) { | |
var jsMode = CodeMirror.getMode(config, "javascript"); | |
var xmlMode = CodeMirror.getMode(config, { name: "xml", htmlMode: true }); | |
var TAG = /<([\w_:\.-]*)/ | |
, OPEN = '{', CLOSE = '}' | |
, foundTag = false; | |
function xmlToken(stream, state){ | |
var oldContent = stream.string | |
, found = indexOf(oldContent, OPEN, stream.pos) | |
, pos; | |
if ( !state.xmlState) | |
state.xmlState = CodeMirror.startState(xmlMode, jsMode.indent ? jsMode.indent(state.jsState, '') : 0) | |
state.active = 'xml' | |
if ( found === stream.pos) { | |
stream.match(OPEN) | |
pos = stream.pos | |
state.active = 'js' | |
state.inJsExpression = true | |
// get past the attr value state | |
stream.string = oldContent.slice(found) | |
xmlMode.token(stream, state.xmlState) | |
stream.string = oldContent | |
stream.pos = pos | |
return 'jsx-bracket' | |
} | |
else if ( found !== -1) | |
stream.string = oldContent.slice(0, found) | |
var style = xmlMode.token(stream, state.xmlState) | |
if ( found !== -1) stream.string = oldContent | |
if ( !foundTag ) | |
foundTag = !!state.xmlState.context | |
return style | |
} | |
function jsToken(stream, state){ | |
var oldContent = stream.string | |
, found; | |
// this only sort of works, its an attempt to solve the problem of | |
// not knowing when a } is a ending a jsx expression (prop={}) or | |
// a js block/object | |
// example: | |
// <div style={{ width: 5 }} onClick={()=>{ throw Error() }}> | |
if (state.inJsExpression) { | |
var opens = getAllIndexes(oldContent, OPEN, stream.pos).length | |
, closes = getAllIndexes(oldContent, CLOSE, stream.pos) | |
found = opens > closes | |
? -1 : closes[closes.length - 1] | |
} | |
if (found === stream.pos) { | |
stream.match(CLOSE); | |
state.active = 'xml' | |
state.inJsExpression = false | |
return 'jsx-bracket'; | |
} | |
else if ( found !== -1) | |
stream.string = oldContent.slice(0, found); | |
var style = jsMode.token(stream, state.jsState); | |
if (found > -1) stream.string = oldContent; | |
return style | |
} | |
return { | |
startState: function() { | |
return { | |
xmlState: CodeMirror.copyState(xmlMode, state.xmlState), | |
jsState: CodeMirror.startState(jsMode), | |
active: 'js', | |
inJsExpression: false | |
} | |
}, | |
copyState: function(state) { | |
return { | |
jsState: CodeMirror.copyState(jsMode, state.jsState), | |
xmlState: CodeMirror.copyState(xmlMode, state.xmlState), | |
active: state.active, | |
inJsExpression: state.inJsExpression, | |
expressionCloseIndex: null // do not copy over | |
} | |
}, | |
token: function(stream, state) { | |
var style; | |
if (state.active === 'js' ) { | |
if ( stream.match(TAG, false) ) { | |
style = xmlToken(stream, state) | |
} | |
else | |
style = jsToken(stream, state) | |
} | |
else { | |
if ( foundTag && state.xmlState.context == null) { | |
state.active = 'js' | |
foundTag = false | |
style = jsToken(stream, state) | |
} | |
else | |
style = xmlToken(stream, state) | |
} | |
return style | |
}, | |
indent: function(state, textAfter) { | |
return state.active === 'js' | |
? jsMode.indent(state.jsState, textAfter) | |
: xmlMode.indent(state.xmlState, textAfter) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment