Comparing version 0.1.4 to 0.1.5
130
lib/lines.js
@@ -1,2 +0,3 @@ | ||
var assert = require("assert"); | ||
var assert = require("assert"), | ||
normalizeOptions = require("./options").normalize; | ||
@@ -49,12 +50,2 @@ // Goals: | ||
function getLineInfo(line) { | ||
var indent = leadingSpaceExp.exec(line)[0].length; | ||
return { | ||
line: line, | ||
indent: indent, | ||
sliceStart: indent, | ||
sliceEnd: line.length | ||
}; | ||
} | ||
function copyLineInfo(info) { | ||
@@ -69,6 +60,37 @@ return { | ||
var fromStringCache = {}, | ||
maxCacheKeyLen = 10; | ||
var fromStringCache = {}; | ||
var hasOwn = fromStringCache.hasOwnProperty; | ||
var maxCacheKeyLen = 10; | ||
function fromString(string) { | ||
function countSpaces(spaces, tabWidth) { | ||
var count = 0; | ||
var len = spaces.length; | ||
for (var i = 0; i < len; ++i) { | ||
var ch = spaces.charAt(i); | ||
if (ch === " ") { | ||
count += 1; | ||
} else if (ch === "\t") { | ||
assert.strictEqual(typeof tabWidth, "number"); | ||
assert.ok(tabWidth > 0); | ||
var next = Math.ceil(count / tabWidth) * tabWidth; | ||
if (next === count) { | ||
count += tabWidth; | ||
} else { | ||
count = next; | ||
} | ||
} else { | ||
assert.fail("unexpected whitespace character", ch); | ||
} | ||
} | ||
return count; | ||
} | ||
exports.countSpaces = countSpaces; | ||
function fromString(string, tabWidth) { | ||
if (string instanceof Lines) | ||
@@ -79,8 +101,21 @@ return string; | ||
if (fromStringCache.hasOwnProperty(string)) | ||
var tabless = string.indexOf("\t") < 0; | ||
var cacheable = tabless && (string.length <= maxCacheKeyLen); | ||
assert.ok(tabWidth || tabless, "encountered tabs, but no tab width specified"); | ||
if (cacheable && hasOwn.call(fromStringCache, string)) | ||
return fromStringCache[string]; | ||
var lines = new Lines(string.split("\n").map(getLineInfo)); | ||
var lines = new Lines(string.split("\n").map(function(line) { | ||
var spaces = leadingSpaceExp.exec(line)[0]; | ||
return { | ||
line: line, | ||
indent: countSpaces(spaces, tabWidth), | ||
sliceStart: spaces.length, | ||
sliceEnd: line.length | ||
}; | ||
})); | ||
if (string.length <= maxCacheKeyLen) | ||
if (cacheable) | ||
fromStringCache[string] = lines; | ||
@@ -91,13 +126,44 @@ | ||
exports.fromString = fromString; | ||
var emptyLines = fromString(""); | ||
Lp.toString = function() { | ||
var secret = getSecret(this); | ||
return secret.infos.map(function(info, i) { | ||
var toJoin = new Array(Math.max(info.indent, 0)); | ||
function isOnlyWhitespace(string) { | ||
return !/\S/.test(string); | ||
} | ||
toJoin.push(info.line.slice( | ||
Lp.toString = function(options) { | ||
options = normalizeOptions(options); | ||
var secret = getSecret(this), | ||
tabWidth = options.tabWidth; | ||
return secret.infos.map(function(info) { | ||
var indent = Math.max(info.indent, 0); | ||
var before = info.line.slice(0, info.sliceStart); | ||
if (isOnlyWhitespace(before) && | ||
(countSpaces(before, options.tabWidth) === indent)) | ||
// Reuse original spaces if the indentation is correct. | ||
return info.line.slice(0, info.sliceEnd); | ||
var tabs = 0; | ||
var spaces = indent; | ||
if (options.useTabs) { | ||
tabs = Math.floor(indent / tabWidth); | ||
spaces -= tabs * tabWidth; | ||
} | ||
var result = ""; | ||
if (tabs > 0) | ||
result += new Array(tabs + 1).join("\t"); | ||
if (spaces > 0) | ||
result += new Array(spaces + 1).join(" "); | ||
result += info.line.slice( | ||
info.sliceStart, | ||
info.sliceEnd)); | ||
info.sliceEnd); | ||
return toJoin.join(" "); | ||
return result; | ||
}).join("\n"); | ||
@@ -245,3 +311,3 @@ }; | ||
while (!/\S/.test(lines.charAt(pos))) | ||
while (isOnlyWhitespace(lines.charAt(pos))) | ||
if (!lines.nextPos(pos)) | ||
@@ -265,3 +331,3 @@ return null; | ||
while (lines.prevPos(pos)) | ||
if (/\S/.test(lines.charAt(pos))) | ||
if (!isOnlyWhitespace(lines.charAt(pos))) | ||
return pos; | ||
@@ -275,3 +341,3 @@ | ||
if (pos === null) | ||
return fromString(""); | ||
return emptyLines; | ||
return this.slice(pos); | ||
@@ -283,3 +349,3 @@ }; | ||
if (pos === null) | ||
return fromString(""); | ||
return emptyLines; | ||
assert.ok(this.nextPos(pos)); | ||
@@ -292,3 +358,3 @@ return this.slice(this.firstPos(), pos); | ||
if (start === null) | ||
return fromString(""); | ||
return emptyLines; | ||
@@ -429,3 +495,3 @@ var end = this.lastNonSpacePos(); | ||
if (infos.length < 1) | ||
return fromString(""); | ||
return emptyLines; | ||
@@ -436,3 +502,3 @@ return new Lines(infos); | ||
exports.concat = function(elements) { | ||
return fromString("").join(elements); | ||
return emptyLines.join(elements); | ||
}; | ||
@@ -445,3 +511,3 @@ | ||
assert.strictEqual(list.length, args.length + 1); | ||
return fromString("").join(list); | ||
return emptyLines.join(list); | ||
}; |
@@ -5,13 +5,10 @@ var assert = require("assert"), | ||
Visitor = require("./visitor").Visitor, | ||
tabExp = /\t/g, | ||
tabAsSpaces = " "; | ||
normalizeOptions = require("./options").normalize; | ||
function Parser(source) { | ||
function Parser(source, options) { | ||
assert.ok(this instanceof Parser); | ||
// Death to tabs! | ||
source = source.replace(tabExp, tabAsSpaces); | ||
var self = this, | ||
lines = require("./lines").fromString(source), | ||
lines = require("./lines").fromString( | ||
source, normalizeOptions(options).tabWidth), | ||
pure = require("esprima").parse(source, { | ||
@@ -18,0 +15,0 @@ loc: true, |
@@ -7,8 +7,11 @@ var assert = require("assert"), | ||
concat = linesModule.concat, | ||
normalizeOptions = require("./options").normalize, | ||
Parser = require("./parser").Parser; | ||
function Printer(parser) { | ||
function Printer(parser, options) { | ||
assert.ok(this instanceof Printer); | ||
assert.ok(parser instanceof Parser); | ||
options = normalizeOptions(options); | ||
function printWithComments(node) { | ||
@@ -42,5 +45,9 @@ if (!node) | ||
return genericPrint(node, printWithComments); | ||
return genericPrint(node, options, printWithComments); | ||
} | ||
function printGenerically(node) { | ||
return genericPrint(node, options, printGenerically); | ||
} | ||
this.print = print; | ||
@@ -52,7 +59,3 @@ this.printGenerically = printGenerically; | ||
function printGenerically(node) { | ||
return genericPrint(node, printGenerically); | ||
} | ||
function genericPrint(n, print) { | ||
function genericPrint(n, options, print) { | ||
if (!n) | ||
@@ -62,3 +65,3 @@ return fromString(""); | ||
if (typeof n === "string") | ||
return fromString(n); | ||
return fromString(n, options.tabWidth); | ||
@@ -104,3 +107,3 @@ assert.ok(n instanceof Object); | ||
case Syntax.Identifier: | ||
return fromString(n.name); | ||
return fromString(n.name, options.tabWidth); | ||
@@ -129,3 +132,3 @@ case Syntax.FunctionDeclaration: | ||
"{\n", | ||
naked.indent(4), | ||
naked.indent(options.tabWidth), | ||
"\n}" | ||
@@ -158,3 +161,3 @@ ]); | ||
prop.type = Syntax.Property; | ||
prop = print(prop).indent(4); | ||
prop = print(prop).indent(options.tabWidth); | ||
@@ -239,4 +242,4 @@ var multiLine = prop.length > 1; | ||
if (typeof n.value === "string") | ||
return fromString(nodeStr(n)); | ||
return fromString(n.value); | ||
return fromString(nodeStr(n), options.tabWidth); | ||
return fromString(n.value, options.tabWidth); | ||
@@ -287,3 +290,3 @@ case Syntax.UnaryExpression: | ||
else | ||
parts.push(",\n", print(decl).indent(4)); | ||
parts.push(",\n", print(decl).indent(options.tabWidth)); | ||
}); | ||
@@ -498,3 +501,3 @@ | ||
"\n", | ||
maybeAddSemicolon(clause).indent(4) | ||
maybeAddSemicolon(clause).indent(options.tabWidth) | ||
]); | ||
@@ -501,0 +504,0 @@ } |
@@ -6,3 +6,3 @@ var fs = require("fs"), | ||
exports.run = function(transformer) { | ||
exports.run = function(transformer, options) { | ||
fs.readFile(path, "utf-8", function(err, data) { | ||
@@ -14,8 +14,8 @@ if (err) { | ||
var parser = new Parser(data), | ||
printer = new Printer(parser); | ||
var parser = new Parser(data, options), | ||
printer = new Printer(parser, options); | ||
transformer(parser.getAst(), function(node) { | ||
var lines = printer.print(node, true); | ||
process.stdout.write(lines.toString()); | ||
process.stdout.write(lines.toString(options)); | ||
}); | ||
@@ -22,0 +22,0 @@ }); |
@@ -15,3 +15,3 @@ { | ||
], | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"homepage": "http://github.com/benjamn/recast", | ||
@@ -18,0 +18,0 @@ "repository": { |
@@ -279,9 +279,10 @@ var assert = require("assert"), | ||
var string = " xxx \n ", | ||
tabWidth = 4, // arbitrary | ||
lines = fromString(string); | ||
function test(string) { | ||
var lines = fromString(string); | ||
check(lines.trimLeft(), fromString(string.replace(/^\s+/, ""))); | ||
check(lines.trimRight(), fromString(string.replace(/\s+$/, ""))); | ||
check(lines.trim(), fromString(string.replace(/^\s+|\s+$/g, ""))); | ||
var lines = fromString(string, tabWidth); | ||
check(lines.trimLeft(), fromString(string.replace(/^\s+/, ""), tabWidth)); | ||
check(lines.trimRight(), fromString(string.replace(/\s+$/, ""), tabWidth)); | ||
check(lines.trim(), fromString(string.replace(/^\s+|\s+$/g, ""), tabWidth)); | ||
} | ||
@@ -314,1 +315,125 @@ | ||
}; | ||
exports.testCountSpaces = function(t) { | ||
var count = linesModule.countSpaces; | ||
assert.strictEqual(count(""), 0); | ||
assert.strictEqual(count(" "), 1); | ||
assert.strictEqual(count(" "), 2); | ||
assert.strictEqual(count(" "), 3); | ||
function check(s, tabWidth, result) { | ||
assert.strictEqual(count(s, tabWidth), result); | ||
} | ||
check("", 2, 0); | ||
check("", 3, 0); | ||
check("", 4, 0); | ||
check(" ", 2, 1); | ||
check("\t", 2, 2); | ||
check("\t\t", 2, 4); | ||
check(" \t\t", 2, 4); | ||
check(" \t \t", 2, 4); | ||
check(" \t \t", 2, 6); | ||
check(" \t \t", 2, 8); | ||
check(" \t \t", 2, 6); | ||
check(" \t \t", 2, 6); | ||
check(" ", 3, 1); | ||
check("\t", 3, 3); | ||
check("\t\t", 3, 6); | ||
check(" \t\t", 3, 6); | ||
check(" \t \t", 3, 6); | ||
check(" \t \t", 3, 6); | ||
check(" \t \t", 3, 6); | ||
check(" \t \t", 3, 9); | ||
check(" \t \t", 3, 9); | ||
check("\t\t\t ", 2, 9); | ||
check("\t\t\t ", 3, 12); | ||
check("\t\t\t ", 4, 15); | ||
t.finish(); | ||
}; | ||
exports.testIndentWithTabs = function(t) { | ||
var tabWidth = 4; | ||
var tabOpts = { tabWidth: tabWidth, useTabs: true }; | ||
var noTabOpts = { tabWidth: tabWidth, useTabs: false }; | ||
var code = [ | ||
"function f() {", | ||
"\treturn this;", | ||
"}" | ||
].join("\n"); | ||
function checkUnchanged(lines, code) { | ||
check(lines.toString(tabOpts), code); | ||
check(lines.toString(noTabOpts), code); | ||
check(lines.indent(3).indent(-5).indent(2).toString(tabOpts), code); | ||
check(lines.indent(-3).indent(4).indent(-1).toString(noTabOpts), code); | ||
} | ||
var lines = fromString(code, tabWidth); | ||
checkUnchanged(lines, code); | ||
check(lines.indent(1).toString(tabOpts), [ | ||
" function f() {", | ||
"\t return this;", | ||
" }" | ||
].join("\n")); | ||
check(lines.indent(tabWidth).toString(tabOpts), [ | ||
"\tfunction f() {", | ||
"\t\treturn this;", | ||
"\t}" | ||
].join("\n")); | ||
check(lines.indent(1).toString(noTabOpts), [ | ||
" function f() {", | ||
" return this;", | ||
" }" | ||
].join("\n")); | ||
check(lines.indent(tabWidth).toString(noTabOpts), [ | ||
" function f() {", | ||
" return this;", | ||
" }" | ||
].join("\n")); | ||
var funkyCode = [ | ||
" function g() { \t ", | ||
" \t\t return this; ", | ||
"\t} " | ||
].join("\n"); | ||
var funky = fromString(funkyCode, tabWidth); | ||
checkUnchanged(funky, funkyCode); | ||
check(funky.indent(1).toString(tabOpts), [ | ||
" function g() { \t ", | ||
"\t\t return this; ", | ||
"\t } " | ||
].join("\n")); | ||
check(funky.indent(2).toString(tabOpts), [ | ||
" function g() { \t ", | ||
"\t\t\treturn this; ", | ||
"\t } " | ||
].join("\n")); | ||
check(funky.indent(1).toString(noTabOpts), [ | ||
" function g() { \t ", | ||
" return this; ", | ||
" } " | ||
].join("\n")); | ||
check(funky.indent(2).toString(noTabOpts), [ | ||
" function g() { \t ", | ||
" return this; ", | ||
" } " | ||
].join("\n")); | ||
t.finish(); | ||
}; |
@@ -19,1 +19,2 @@ function submodule(id) { | ||
submodule("printer"); | ||
submodule("syntax"); |
73522
23
2243
3