smalltalk.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. CodeMirror.defineMode('smalltalk', function(config) {
  2. var specialChars = /[+\-\/\\*~<>=@%|&?!.,:;^]/;
  3. var keywords = /true|false|nil|self|super|thisContext/;
  4. var Context = function(tokenizer, parent) {
  5. this.next = tokenizer;
  6. this.parent = parent;
  7. };
  8. var Token = function(name, context, eos) {
  9. this.name = name;
  10. this.context = context;
  11. this.eos = eos;
  12. };
  13. var State = function() {
  14. this.context = new Context(next, null);
  15. this.expectVariable = true;
  16. this.indentation = 0;
  17. this.userIndentationDelta = 0;
  18. };
  19. State.prototype.userIndent = function(indentation) {
  20. this.userIndentationDelta = indentation > 0 ? (indentation / config.indentUnit - this.indentation) : 0;
  21. };
  22. var next = function(stream, context, state) {
  23. var token = new Token(null, context, false);
  24. var aChar = stream.next();
  25. if (aChar === '"') {
  26. token = nextComment(stream, new Context(nextComment, context));
  27. } else if (aChar === '\'') {
  28. token = nextString(stream, new Context(nextString, context));
  29. } else if (aChar === '#') {
  30. if (stream.peek() === '\'') {
  31. stream.next();
  32. token = nextSymbol(stream, new Context(nextSymbol, context));
  33. } else {
  34. stream.eatWhile(/[^ .\[\]()]/);
  35. token.name = 'string-2';
  36. }
  37. } else if (aChar === '$') {
  38. if (stream.next() === '<') {
  39. stream.eatWhile(/[^ >]/);
  40. stream.next();
  41. }
  42. token.name = 'string-2';
  43. } else if (aChar === '|' && state.expectVariable) {
  44. token.context = new Context(nextTemporaries, context);
  45. } else if (/[\[\]{}()]/.test(aChar)) {
  46. token.name = 'bracket';
  47. token.eos = /[\[{(]/.test(aChar);
  48. if (aChar === '[') {
  49. state.indentation++;
  50. } else if (aChar === ']') {
  51. state.indentation = Math.max(0, state.indentation - 1);
  52. }
  53. } else if (specialChars.test(aChar)) {
  54. stream.eatWhile(specialChars);
  55. token.name = 'operator';
  56. token.eos = aChar !== ';'; // ; cascaded message expression
  57. } else if (/\d/.test(aChar)) {
  58. stream.eatWhile(/[\w\d]/);
  59. token.name = 'number';
  60. } else if (/[\w_]/.test(aChar)) {
  61. stream.eatWhile(/[\w\d_]/);
  62. token.name = state.expectVariable ? (keywords.test(stream.current()) ? 'keyword' : 'variable') : null;
  63. } else {
  64. token.eos = state.expectVariable;
  65. }
  66. return token;
  67. };
  68. var nextComment = function(stream, context) {
  69. stream.eatWhile(/[^"]/);
  70. return new Token('comment', stream.eat('"') ? context.parent : context, true);
  71. };
  72. var nextString = function(stream, context) {
  73. stream.eatWhile(/[^']/);
  74. return new Token('string', stream.eat('\'') ? context.parent : context, false);
  75. };
  76. var nextSymbol = function(stream, context) {
  77. stream.eatWhile(/[^']/);
  78. return new Token('string-2', stream.eat('\'') ? context.parent : context, false);
  79. };
  80. var nextTemporaries = function(stream, context) {
  81. var token = new Token(null, context, false);
  82. var aChar = stream.next();
  83. if (aChar === '|') {
  84. token.context = context.parent;
  85. token.eos = true;
  86. } else {
  87. stream.eatWhile(/[^|]/);
  88. token.name = 'variable';
  89. }
  90. return token;
  91. };
  92. return {
  93. startState: function() {
  94. return new State;
  95. },
  96. token: function(stream, state) {
  97. state.userIndent(stream.indentation());
  98. if (stream.eatSpace()) {
  99. return null;
  100. }
  101. var token = state.context.next(stream, state.context, state);
  102. state.context = token.context;
  103. state.expectVariable = token.eos;
  104. return token.name;
  105. },
  106. blankLine: function(state) {
  107. state.userIndent(0);
  108. },
  109. indent: function(state, textAfter) {
  110. var i = state.context.next === next && textAfter && textAfter.charAt(0) === ']' ? -1 : state.userIndentationDelta;
  111. return (state.indentation + i) * config.indentUnit;
  112. },
  113. electricChars: ']'
  114. };
  115. });
  116. CodeMirror.defineMIME('text/x-stsrc', {name: 'smalltalk'});