multiplex.js 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. CodeMirror.multiplexingMode = function(outer /*, others */) {
  2. // Others should be {open, close, mode [, delimStyle]} objects
  3. var others = Array.prototype.slice.call(arguments, 1);
  4. var n_others = others.length;
  5. function indexOf(string, pattern, from) {
  6. if (typeof pattern == "string") return string.indexOf(pattern, from);
  7. var m = pattern.exec(from ? string.slice(from) : string);
  8. return m ? m.index + from : -1;
  9. }
  10. return {
  11. startState: function() {
  12. return {
  13. outer: CodeMirror.startState(outer),
  14. innerActive: null,
  15. inner: null
  16. };
  17. },
  18. copyState: function(state) {
  19. return {
  20. outer: CodeMirror.copyState(outer, state.outer),
  21. innerActive: state.innerActive,
  22. inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner)
  23. };
  24. },
  25. token: function(stream, state) {
  26. if (!state.innerActive) {
  27. var cutOff = Infinity, oldContent = stream.string;
  28. for (var i = 0; i < n_others; ++i) {
  29. var other = others[i];
  30. var found = indexOf(oldContent, other.open, stream.pos);
  31. if (found == stream.pos) {
  32. stream.match(other.open);
  33. state.innerActive = other;
  34. state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0);
  35. return other.delimStyle;
  36. } else if (found != -1 && found < cutOff) {
  37. cutOff = found;
  38. }
  39. }
  40. if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff);
  41. var outerToken = outer.token(stream, state.outer);
  42. if (cutOff != Infinity) stream.string = oldContent;
  43. return outerToken;
  44. } else {
  45. var curInner = state.innerActive, oldContent = stream.string;
  46. var found = indexOf(oldContent, curInner.close, stream.pos);
  47. if (found == stream.pos) {
  48. stream.match(curInner.close);
  49. state.innerActive = state.inner = null;
  50. return curInner.delimStyle;
  51. }
  52. if (found > -1) stream.string = oldContent.slice(0, found);
  53. var innerToken = curInner.mode.token(stream, state.inner);
  54. if (found > -1) stream.string = oldContent;
  55. var cur = stream.current(), found = cur.indexOf(curInner.close);
  56. if (found > -1) stream.backUp(cur.length - found);
  57. return innerToken;
  58. }
  59. },
  60. indent: function(state, textAfter) {
  61. var mode = state.innerActive ? state.innerActive.mode : outer;
  62. if (!mode.indent) return CodeMirror.Pass;
  63. return mode.indent(state.innerActive ? state.inner : state.outer, textAfter);
  64. },
  65. blankLine: function(state) {
  66. var mode = state.innerActive ? state.innerActive.mode : outer;
  67. if (mode.blankLine) {
  68. mode.blankLine(state.innerActive ? state.inner : state.outer);
  69. }
  70. if (!state.innerActive) {
  71. for (var i = 0; i < n_others; ++i) {
  72. var other = others[i];
  73. if (other.open === "\n") {
  74. state.innerActive = other;
  75. state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0);
  76. }
  77. }
  78. } else if (state.innerActive.close === "\n") {
  79. state.innerActive = state.inner = null;
  80. }
  81. },
  82. electricChars: outer.electricChars,
  83. innerMode: function(state) {
  84. return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer};
  85. }
  86. };
  87. };