Permalink
Browse files

Added flexible indentation

  • Loading branch information...
1 parent df7f078 commit 000ea9da09718479ceea9ba1d1ac0d9696f7c064 @tj tj committed Mar 4, 2011
Showing with 57 additions and 64 deletions.
  1. +57 −29 lib/lexer.js
  2. +0 −35 test/jade.test.js
View
86 lib/lexer.js
@@ -13,11 +13,13 @@
*/
var Lexer = module.exports = function Lexer(str) {
- this.input = str.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' ');
+ this.input = str.replace(/\r\n|\r/g, '\n');
this.deferredTokens = [];
this.lastIndents = 0;
this.lineno = 1;
this.stash = [];
+ this.indentStack = [];
+ this.indentRe = null;
};
/**
@@ -147,9 +149,12 @@ Lexer.prototype = {
get eos() {
if (this.input.length) return;
- return this.lastIndents-- > 0
- ? this.tok('outdent')
- : this.tok('eos');
+ if (this.indentStack.length) {
+ this.indentStack.shift();
+ return this.tok('outdent');
+ } else {
+ return this.tok('eos');
+ }
},
/**
@@ -331,34 +336,57 @@ Lexer.prototype = {
*/
get indent() {
- var captures;
- if (captures = /^\n( *)/.exec(this.input)) {
+ var captures, re;
+
+ // established regexp
+ if (this.indentRe) {
+ captures = this.indentRe.exec(this.input);
+ // determine regexp
+ } else {
+ // tabs
+ re = /^\n(\t*) */;
+ captures = re.exec(this.input);
+
+ // spaces
+ if (captures && !captures[1].length) {
+ re = /^\n( *)/;
+ captures = re.exec(this.input);
+ }
+
+ // established
+ if (captures && captures[1].length) this.indentRe = re;
+ }
+
+ if (captures) {
+ var tok
+ , indents = captures[1].length;
+
++this.lineno;
- this.consume(captures[0].length);
- var tok = this.tok('indent', captures[1])
- , indents = tok.val.length / 2;
- if (this.input[0] === '\n') {
- tok.type = 'newline';
- return tok;
- } else if (indents % 1 !== 0) {
- throw new Error('Invalid indentation, got '
- + tok.val.length + ' space'
- + (tok.val.length > 1 ? 's' : '')
- + ', must be a multiple of two.');
- } else if (indents === this.lastIndents) {
- tok.type = 'newline';
- } else if (indents > this.lastIndents + 1) {
- throw new Error('Invalid indentation, got '
- + indents + ' expected '
- + (this.lastIndents + 1) + '.');
- } else if (indents < this.lastIndents) {
- var n = this.lastIndents - indents;
- tok.type = 'outdent';
- while (--n) {
- this.defer(this.tok('outdent'));
+ this.consume(indents + 1);
+
+ if (' ' == this.input[0] || '\t' == this.input[0]) {
+ throw new Error('Invalid indentation, you can use tabs or spaces but not both');
+ }
+
+ // blank line
+ if ('\n' == this.input[0]) return this.advance;
+
+ // outdent
+ if (this.indentStack.length && indents < this.indentStack[0]) {
+ while (this.indentStack.length && this.indentStack[0] > indents) {
+ this.stash.push(this.tok('outdent'));
+ this.indentStack.shift();
}
+ tok = this.stash.pop();
+ // indent
+ } else if (indents && indents != this.indentStack[0]) {
+ this.indentStack.unshift(indents);
+ tok = this.tok('indent');
+ // newline
+ } else {
+ tok = this.tok('newline');
}
- this.lastIndents = indents;
+
return tok;
}
},
View
35 test/jade.test.js
@@ -337,41 +337,6 @@ module.exports = {
assert.equal('foo <script> bar\n', render('| foo !{code} bar', { locals: { code: '<script>' }}));
},
- 'test invalid indentation multiple': function(assert){
- var err;
- try {
- render('\n\nul\n li\n li');
- } catch (e) {
- err = e;
- }
- assert.equal(
- "Jade:5\n 3. 'ul'\n 4. ' li'\n 5. ' li'\n\nInvalid indentation, got 1 space, must be a multiple of two.",
- err.message);
-
- var err;
- try {
- render('ul\n li', { filename: 'path/to/foo.jade' });
- } catch (e) {
- err = e;
- }
- assert.equal('path/to/foo.jade', err.path);
- assert.equal(
- "path/to/foo.jade:2\n 1. 'ul'\n 2. ' li'\n\nInvalid indentation, got 3 spaces, must be a multiple of two.",
- err.message);
- },
-
- 'test invalid indents': function(assert){
- var err;
- try {
- render('ul\n\n\n li');
- } catch (e) {
- err = e;
- }
- assert.equal(
- "Jade:4\n 2. ''\n 3. ''\n 4. ' li'\n\nInvalid indentation, got 2 expected 1.",
- err.message);
- },
-
'test code exceptions': function(assert){
var err;
try {

0 comments on commit 000ea9d

Please sign in to comment.