Merge branch 'master' into patch-1

This commit is contained in:
Liam Newman 2018-07-25 00:31:32 -07:00 committed by GitHub
commit 58ab66cf64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1274 additions and 1215 deletions

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ dist/
build/
js/lib/
.idea/
.vscode/

View File

@ -1,4 +1,6 @@
dist/**
js/bin/**
js/lib/**
js/test/resources/**
node_modules/**
python/**
@ -6,4 +8,6 @@ target/**
tools/**
test/resources/underscore-min.js
test/resources/underscore.js
web/third-party/**
web/lib/**
web/google-analytics.js

View File

@ -6,5 +6,6 @@
"nocomma": true,
"nonbsp": true,
"nonew": true,
"unused": true
"unused": true,
"esversion": 3
}

View File

@ -1,6 +1,6 @@
PROJECT_ROOT=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
BUILD_DIR=$(PROJECT_ROOT)/build
SCRIPT_DIR=$(PROJECT_ROOT)/tools
BUILD_DIR=$(PROJECT_ROOT)build
SCRIPT_DIR=$(PROJECT_ROOT)tools
SHELL=/bin/bash
PYTHON=$(SCRIPT_DIR)/python
NODE=$(SCRIPT_DIR)/node
@ -20,28 +20,29 @@ help:
ci: all git-status-clear
static: $(BUILD_DIR)/node
./node_modules/.bin/static
static: js/lib/*.js
@./node_modules/.bin/static -H '{"Cache-Control": "no-cache, must-revalidate"}'
js: js/lib/*.js
@echo Running unit tests...
./node_modules/.bin/mocha --recursive js/test && \
./js/test/node-src-index-tests.js
py: python/dist/*
generate-tests: $(BUILD_DIR)/tests
jstest: depends generate-tests js
@echo Testing javascript implementation...
@$(NODE) --version && \
./node_modules/.bin/mocha --recursive js/test && \
./js/test/shell-smoke-test.sh
pytest: depends generate-tests py
@echo Testing python implementation...
@cd python && \
$(PYTHON) --version && \
./jsbeautifier/tests/shell-smoke-test.sh
generate-tests: $(BUILD_DIR)/generate
beautify:
$(SCRIPT_DIR)/build.sh beautify
@ -49,7 +50,7 @@ beautify:
#######################################################
# javascript bundle generation
js/lib/*.js: $(BUILD_DIR)/node $(wildcard js/src/**/*) tools/template/* webpack.config.js
js/lib/*.js: $(BUILD_DIR)/node $(BUILD_DIR)/generate $(wildcard js/src/**/*) js/index.js tools/template/* webpack.config.js
$(SCRIPT_DIR)/build.sh js
@ -59,16 +60,17 @@ python/dist/*: $(BUILD_DIR)/python $(wildcard python/**/*.py) python/jsbeautifie
@cd python && \
$(PYTHON) setup.py sdist
# Test generation
$(BUILD_DIR)/tests: $(BUILD_DIR)/node test/generate-tests.js $(wildcard test/data/**/*)
$(BUILD_DIR)/generate: $(BUILD_DIR)/node test/generate-tests.js $(wildcard test/data/**/*)
$(NODE) test/generate-tests.js
@touch $(BUILD_DIR)/tests
@touch $(BUILD_DIR)/generate
# Handling dependencies
#######################################################
depends: $(BUILD_DIR)/node $(BUILD_DIR)/python
@$(NODE) --version
@$(PYTHON) --version
# update dependencies information
update: depends
@ -77,10 +79,12 @@ update: depends
# when we pull dependencies also pull docker image
# without this images can get stale and out of sync from CI system
$(BUILD_DIR)/node: package.json package-lock.json | $(BUILD_DIR)
@$(NODE) --version
$(NPM) install
@touch $(BUILD_DIR)/node
$(BUILD_DIR)/python: python/setup.py
@$(PYTHON) --version
$(PYTHON) -m pip install -e ./python
@touch $(BUILD_DIR)/python

View File

@ -71,11 +71,12 @@ if (typeof define === "function" && define.amd) {
});
} else {
(function(mod) {
var js_beautify = require('./lib/beautify');
var css_beautify = require('./lib/beautify-css');
var html_beautify = require('./lib/beautify-html');
var beautifier = require('./src/index');
beautifier.js_beautify = beautifier.js;
beautifier.css_beautify = beautifier.css;
beautifier.html_beautify = beautifier.html;
mod.exports = get_beautify(js_beautify, css_beautify, html_beautify);
mod.exports = get_beautify(beautifier, beautifier, beautifier);
})(module);
}

View File

@ -216,7 +216,7 @@ function set_file_editorconfig_opts(file, config) {
if (eConfigs.max_line_length === "off") {
config.wrap_line_length = 0;
} else {
config.wrap_line_length = parseInt(eConfigs.max_line_length);
config.wrap_line_length = parseInt(eConfigs.max_line_length, 10);
}
}

View File

@ -18,10 +18,18 @@
// code point above 128.
var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
var baseASCIIidentifierStartChars = "\x24\x40\x41-\x5a\x5f\x61-\x7a";
var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
var baseASCIIidentifierChars = "\x24\x30-\x39\x41-\x5a\x5f\x61-\x7a";
var nonASCIIidentifierChars = "\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f";
var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
//var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
//var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
var identifierStart = new RegExp("[" + baseASCIIidentifierStartChars + nonASCIIidentifierStartChars + "]");
var identifierChars = new RegExp("[" + baseASCIIidentifierChars + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
exports.identifier = new RegExp("[" + baseASCIIidentifierStartChars + nonASCIIidentifierStartChars + "][" + baseASCIIidentifierChars + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]*", 'g');
// Whether a single character denotes a newline.
@ -39,25 +47,25 @@ exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');
// Test whether a given character code starts an identifier.
exports.isIdentifierStart = function(code) {
// permit $ (36) and @ (64). @ is used in ES7 decorators.
if (code < 65) return code === 36 || code === 64;
// 65 through 91 are uppercase letters.
if (code < 91) return true;
// permit _ (95).
if (code < 97) return code === 95;
// 97 through 123 are lowercase letters.
if (code < 123) return true;
return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
// // permit $ (36) and @ (64). @ is used in ES7 decorators.
// if (code < 65) return code === 36 || code === 64;
// // 65 through 91 are uppercase letters.
// if (code < 91) return true;
// // permit _ (95).
// if (code < 97) return code === 95;
// // 97 through 123 are lowercase letters.
// if (code < 123) return true;
return identifierStart.test(String.fromCharCode(code));
};
// Test whether a given character is part of an identifier.
exports.isIdentifierChar = function(code) {
if (code < 48) return code === 36;
if (code < 58) return true;
if (code < 65) return false;
if (code < 91) return true;
if (code < 97) return code === 95;
if (code < 123) return true;
return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
// if (code < 48) return code === 36;
// if (code < 58) return true;
// if (code < 65) return false;
// if (code < 91) return true;
// if (code < 97) return code === 95;
// if (code < 123) return true;
return identifierChars.test(String.fromCharCode(code));
};

View File

@ -60,17 +60,6 @@ function InputScanner(input) {
return val;
};
this.peekCharCode = function(index) {
// basically here for acorn
var val = 0;
index = index || 0;
index += _position;
if (index >= 0 && index < _input_length) {
val = _input.charCodeAt(index);
}
return val;
};
this.test = function(pattern, index) {
index = index || 0;
index += _position;
@ -100,6 +89,63 @@ function InputScanner(input) {
}
return pattern_match;
};
this.readWhile = function(pattern) {
var val = '';
var match = this.match(pattern);
if (match) {
val = match[0];
}
return val;
};
this.readUntil = function(pattern) {
var val = '';
var match_index = _position;
pattern.lastIndex = _position;
var pattern_match = pattern.exec(_input);
if (pattern_match) {
match_index = pattern_match.index;
} else {
match_index = _input_length;
}
val = _input.substring(_position, match_index);
_position = match_index;
return val;
};
this.readUntilAfter = function(pattern) {
var val = '';
var match_index = _position;
pattern.lastIndex = _position;
var pattern_match = pattern.exec(_input);
if (pattern_match) {
match_index = pattern_match.index + pattern_match[0].length;
} else {
match_index = _input_length;
}
val = _input.substring(_position, match_index);
_position = match_index;
return val;
};
/* css beautifier legacy helpers */
this.peekUntilAfter = function(pattern) {
var start = _position;
var val = this.readUntilAfter(pattern);
_position = start;
return val;
};
this.lookBack = function(testVal) {
var start = _position - 1;
return start >= testVal.length && _input.substring(start - testVal.length, start)
.toLowerCase() === testVal;
};
}

View File

@ -27,20 +27,20 @@
*/
function OutputLine(parent) {
var _character_count = 0;
// use indent_count as a marker for lines that have preserved indentation
var _indent_count = -1;
this._character_count = 0;
// use indent_count as a marker for this._lines that have preserved indentation
this._indent_count = -1;
var _items = [];
this._items = [];
var _empty = true;
this.set_indent = function(level) {
_character_count = parent.baseIndentLength + level * parent.indent_length;
_indent_count = level;
this._character_count = parent.baseIndentLength + level * parent.indent_length;
this._indent_count = level;
};
this.get_character_count = function() {
return _character_count;
return this._character_count;
};
this.is_empty = function() {
@ -49,50 +49,50 @@ function OutputLine(parent) {
this.last = function() {
if (!this._empty) {
return _items[_items.length - 1];
return this._items[this._items.length - 1];
} else {
return null;
}
};
this.push = function(input) {
_items.push(input);
_character_count += input.length;
this._items.push(input);
this._character_count += input.length;
_empty = false;
};
this.pop = function() {
var item = null;
if (!_empty) {
item = _items.pop();
_character_count -= item.length;
_empty = _items.length === 0;
item = this._items.pop();
this._character_count -= item.length;
_empty = this._items.length === 0;
}
return item;
};
this.remove_indent = function() {
if (_indent_count > 0) {
_indent_count -= 1;
_character_count -= parent.indent_length;
if (this._indent_count > 0) {
this._indent_count -= 1;
this._character_count -= parent.indent_length;
}
};
this.trim = function() {
while (this.last() === ' ') {
_items.pop();
_character_count -= 1;
this._items.pop();
this._character_count -= 1;
}
_empty = _items.length === 0;
_empty = this._items.length === 0;
};
this.toString = function() {
var result = '';
if (!this._empty) {
if (_indent_count >= 0) {
result = parent.indent_cache[_indent_count];
if (this._indent_count >= 0) {
result = parent.indent_cache[this._indent_count];
}
result += _items.join('');
result += this._items.join('');
}
return result;
};
@ -105,7 +105,7 @@ function Output(indent_string, baseIndentString) {
this.indent_length = indent_string.length;
this.raw = false;
var lines = [];
this._lines = [];
this.baseIndentString = baseIndentString;
this.indent_string = indent_string;
this.previous_line = null;
@ -115,7 +115,7 @@ function Output(indent_string, baseIndentString) {
this.add_outputline = function() {
this.previous_line = this.current_line;
this.current_line = new OutputLine(this);
lines.push(this.current_line);
this._lines.push(this.current_line);
};
// initialize
@ -123,7 +123,7 @@ function Output(indent_string, baseIndentString) {
this.get_line_number = function() {
return lines.length;
return this._lines.length;
};
// Using object instead of string to allow for later expansion of info about each line
@ -143,7 +143,7 @@ function Output(indent_string, baseIndentString) {
};
this.get_code = function(end_with_newline, eol) {
var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
var sweet_code = this._lines.join('\n').replace(/[\r\n\t ]+$/, '');
if (end_with_newline) {
sweet_code += '\n';
@ -158,7 +158,7 @@ function Output(indent_string, baseIndentString) {
this.set_indent = function(level) {
// Never indent your first output indent at the start of the file
if (lines.length > 1) {
if (this._lines.length > 1) {
while (level >= this.indent_cache.length) {
this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
}
@ -192,9 +192,9 @@ function Output(indent_string, baseIndentString) {
};
this.remove_indent = function(index) {
var output_length = lines.length;
var output_length = this._lines.length;
while (index < output_length) {
lines[index].remove_indent();
this._lines[index].remove_indent();
index++;
}
};
@ -204,14 +204,14 @@ function Output(indent_string, baseIndentString) {
this.current_line.trim(indent_string, baseIndentString);
while (eat_newlines && lines.length > 1 &&
while (eat_newlines && this._lines.length > 1 &&
this.current_line.is_empty()) {
lines.pop();
this.current_line = lines[lines.length - 1];
this._lines.pop();
this.current_line = this._lines[this._lines.length - 1];
this.current_line.trim();
}
this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
this.previous_line = this._lines.length > 1 ? this._lines[this._lines.length - 2] : null;
};
this.just_added_newline = function() {
@ -220,11 +220,11 @@ function Output(indent_string, baseIndentString) {
this.just_added_blankline = function() {
if (this.just_added_newline()) {
if (lines.length === 1) {
if (this._lines.length === 1) {
return true; // start of the file and newline = blank
}
var line = lines[lines.length - 2];
var line = this._lines[this._lines.length - 2];
return line.is_empty();
}
return false;

View File

@ -26,10 +26,10 @@
SOFTWARE.
*/
var mergeOpts = require('core/options').mergeOpts;
var acorn = require('core/acorn');
var Output = require('core/output').Output;
var mergeOpts = require('../core/options').mergeOpts;
var acorn = require('../core/acorn');
var Output = require('../core/output').Output;
var InputScanner = require('../core/inputscanner').InputScanner;
var lineBreak = acorn.lineBreak;
var allLineBreaks = acorn.allLineBreaks;
@ -70,53 +70,28 @@ function Beautifier(source_text, options) {
source_text = source_text.replace(allLineBreaks, '\n');
// tokenizer
var whiteRe = /^\s+$/;
var whitespaceChar = /\s/;
var whitespacePattern = /(?:\s|\n)+/g;
var block_comment_pattern = /\/\*(?:[\s\S]*?)((?:\*\/)|$)/g;
var comment_pattern = /\/\/(?:[^\n\r\u2028\u2029]*)/g;
var pos = -1,
ch;
var ch;
var parenLevel = 0;
function next() {
ch = source_text.charAt(++pos);
return ch || '';
}
function peek(index) {
index = index || 0;
index += pos + 1;
var result = source_text.charAt(index) || '';
return result;
}
function peekIgnoreWhitespace(index) {
var prev_pos = pos;
eatWhitespace();
var result = peek(index);
pos = prev_pos - 1;
next();
return result;
}
var input;
function eatString(endChars) {
var start = pos;
while (next()) {
var result = '';
ch = input.next();
while (ch) {
result += ch;
if (ch === "\\") {
next();
} else if (endChars.indexOf(ch) !== -1) {
break;
} else if (ch === "\n") {
result += input.next();
} else if (endChars.indexOf(ch) !== -1 || ch === "\n") {
break;
}
ch = input.next();
}
return source_text.substring(start, pos + 1);
}
function peekString(endChar) {
var prev_pos = pos;
var str = eatString(endChar);
pos = prev_pos - 1;
next();
return str;
return result;
}
// Skips any white space in the source text from the current position.
@ -124,11 +99,11 @@ function Beautifier(source_text, options) {
// newline character found; if the user has preserve_newlines off, only
// the first newline will be output
function eatWhitespace(allowAtLeastOneNewLine) {
var result = whiteRe.test(peek());
var result = whitespaceChar.test(input.peek());
var isFirstNewLine = true;
while (whiteRe.test(peek())) {
next();
while (whitespaceChar.test(input.peek())) {
ch = input.next();
if (allowAtLeastOneNewLine && ch === '\n') {
if (preserve_newlines || isFirstNewLine) {
isFirstNewLine = false;
@ -139,47 +114,14 @@ function Beautifier(source_text, options) {
return result;
}
function skipWhitespace() {
var result = '';
if (ch && whiteRe.test(ch)) {
result = ch;
}
while (whiteRe.test(next())) {
result += ch;
}
return result;
}
function eatComment(singleLine) {
var start = pos;
if (!singleLine) {
next();
}
while (next()) {
if (!singleLine && ch === "*" && peek() === "/") {
next();
break;
} else if (singleLine && peek() === "\n") {
break;
}
}
return source_text.substring(start, pos) + ch;
}
function lookBack(str) {
return source_text.substring(pos - str.length, pos).toLowerCase() ===
str;
}
// Nested pseudo-class if we are insideRule
// and the next special character found opens
// a new block
function foundNestedPseudoClass() {
var openParen = 0;
for (var i = pos + 1; i < source_text.length; i++) {
var ch = source_text.charAt(i);
var i = 1;
var ch = input.peek(i);
while (ch) {
if (ch === "{") {
return true;
} else if (ch === '(') {
@ -193,6 +135,8 @@ function Beautifier(source_text, options) {
} else if (ch === ";" || ch === "}") {
return false;
}
i++;
ch = input.peek(i);
}
return false;
}
@ -243,10 +187,10 @@ function Beautifier(source_text, options) {
this.beautify = function() {
// reset
output = new Output(singleIndent, baseIndentString);
input = new InputScanner(source_text);
indentLevel = 0;
nestedLevel = 0;
pos = -1;
ch = null;
parenLevel = 0;
@ -254,25 +198,23 @@ function Beautifier(source_text, options) {
var insidePropertyValue = false;
var enteringConditionalGroup = false;
var insideAtExtend = false;
var top_ch = '';
var last_top_ch = '';
while (true) {
var whitespace = skipWhitespace();
var whitespace = input.readWhile(whitespacePattern);
var isAfterSpace = whitespace !== '';
last_top_ch = top_ch;
top_ch = ch;
ch = input.next();
if (!ch) {
break;
} else if (ch === '/' && peek() === '*') {
} else if (ch === '/' && input.peek() === '*') {
// /* css comment */
// Always start block comments on a new line.
// This handles scenarios where a block comment immediately
// follows a property definition on the same line or where
// minified code is being beautified.
output.add_new_line();
print_string(eatComment(false));
input.back();
print_string(input.readWhile(block_comment_pattern));
// Ensures any new lines following the comment are preserved
eatWhitespace(true);
@ -280,12 +222,13 @@ function Beautifier(source_text, options) {
// Block comments are followed by a new line so they don't
// share a line with other properties
output.add_new_line();
} else if (ch === '/' && peek() === '/') {
} else if (ch === '/' && input.peek() === '/') {
// // single line comment
// Preserves the space before a comment
// on the same line as a rule
output.space_before_token = true;
print_string(eatComment(true));
input.back();
print_string(input.readWhile(comment_pattern));
// Ensures any new lines following the comment are preserved
eatWhitespace(true);
@ -293,17 +236,16 @@ function Beautifier(source_text, options) {
preserveSingleSpace(isAfterSpace);
// deal with less propery mixins @{...}
if (peek() === '{') {
print_string(eatString('}'));
if (input.peek() === '{') {
print_string(ch + eatString('}'));
} else {
print_string(ch);
// strip trailing space, if present, for hash property checks
var variableOrRule = peekString(": ,;{}()[]/='\"");
var variableOrRule = input.peekUntilAfter(/[: ,;{}()[\]\/='"]/g);
if (variableOrRule.match(/[ :]$/)) {
// we have a variable or pseudo-class, add it and insert one space before continuing
next();
variableOrRule = eatString(": ").replace(/\s$/, '');
print_string(variableOrRule);
output.space_before_token = true;
@ -323,13 +265,11 @@ function Beautifier(source_text, options) {
}
}
}
} else if (ch === '#' && peek() === '{') {
} else if (ch === '#' && input.peek() === '{') {
preserveSingleSpace(isAfterSpace);
print_string(eatString('}'));
print_string(ch + eatString('}'));
} else if (ch === '{') {
if (peekIgnoreWhitespace() === '}') {
eatWhitespace();
next();
if (input.match(/[\t\n ]*}/g)) {
output.space_before_token = true;
print_string("{}");
@ -372,10 +312,9 @@ function Beautifier(source_text, options) {
output.add_new_line(true);
}
} else if (ch === ":") {
eatWhitespace();
if ((insideRule || enteringConditionalGroup) &&
!(lookBack("&") || foundNestedPseudoClass()) &&
!lookBack("(") && !insideAtExtend) {
!(input.lookBack("&") || foundNestedPseudoClass()) &&
!input.lookBack("(") && !insideAtExtend) {
// 'property: value' delimiter
// which could be in a conditional group query
print_string(':');
@ -388,12 +327,12 @@ function Beautifier(source_text, options) {
// sass nested pseudo-class don't use a space
// preserve space before pseudoclasses/pseudoelements, as it means "in any child"
if (lookBack(" ")) {
if (input.lookBack(" ")) {
output.space_before_token = true;
}
if (peek() === ":") {
if (input.peek() === ":") {
// pseudo-element
next();
ch = input.next();
print_string("::");
} else {
// pseudo-class
@ -402,7 +341,7 @@ function Beautifier(source_text, options) {
}
} else if (ch === '"' || ch === '\'') {
preserveSingleSpace(isAfterSpace);
print_string(eatString(ch));
print_string(ch + eatString(ch));
} else if (ch === ';') {
insidePropertyValue = false;
insideAtExtend = false;
@ -413,18 +352,19 @@ function Beautifier(source_text, options) {
// line. Block comments are also affected, but
// a new line is always output before one inside
// that section
if (peek() !== '/') {
if (input.peek() !== '/') {
output.add_new_line();
}
} else if (ch === '(') { // may be a url
if (lookBack("url")) {
if (input.lookBack("url")) {
print_string(ch);
eatWhitespace();
if (next()) {
ch = input.next();
if (ch) {
if (ch !== ')' && ch !== '"' && ch !== '\'') {
print_string(eatString(')'));
print_string(ch + eatString(')'));
} else {
pos--;
input.back();
parenLevel++;
}
}
@ -456,7 +396,7 @@ function Beautifier(source_text, options) {
print_string(ch);
eatWhitespace();
// squash extra whitespace
if (ch && whiteRe.test(ch)) {
if (ch && whitespaceChar.test(ch)) {
ch = '';
}
}
@ -468,7 +408,7 @@ function Beautifier(source_text, options) {
} else if (ch === '=') { // no whitespace before or after
eatWhitespace();
print_string('=');
if (whiteRe.test(ch)) {
if (whitespaceChar.test(ch)) {
ch = '';
}
} else if (ch === '!') { // !important

View File

@ -26,9 +26,9 @@
SOFTWARE.
*/
var mergeOpts = require('core/options').mergeOpts;
var acorn = require('core/acorn');
var mergeOpts = require('../core/options').mergeOpts;
var acorn = require('../core/acorn');
var InputScanner = require('../core/inputscanner').InputScanner;
var lineBreak = acorn.lineBreak;
var allLineBreaks = acorn.allLineBreaks;
@ -100,7 +100,7 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var',
'video', 'wbr', 'text',
// prexisting - not sure of full effect of removing, leaving in
'acronym', 'address', 'big', 'dt', 'ins', 'strike', 'tt',
'acronym', 'address', 'big', 'dt', 'ins', 'strike', 'tt'
];
unformatted = options.unformatted || [];
content_unformatted = options.content_unformatted || [
@ -142,13 +142,13 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
function Parser() {
this.pos = 0; //Parser position
this.token = '';
this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
parent: 'parent1',
parentcount: 1,
parent1: ''
parent: null,
tag: '',
indent_level: 0,
token: null
};
this.last_token = {
text: '',
@ -189,10 +189,10 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
}
}
return false;
},
}
};
// Return true if the given text is composed entirely of whitespace.
// Return true if the given texmake t is composed entirely of whitespace.
this.is_whitespace = function(text) {
for (var n = 0; n < text.length; n++) {
if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) {
@ -205,17 +205,14 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
this.traverse_whitespace = function() {
var input_char = '';
input_char = this.input.charAt(this.pos);
if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
if (this.Utils.in_array(this.input.peek(), this.Utils.whitespace)) {
this.newlines = 0;
while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
do {
input_char = this.input.next();
if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) {
this.newlines += 1;
}
this.pos++;
input_char = this.input.charAt(this.pos);
}
} while (this.Utils.in_array(this.input.peek(), this.Utils.whitespace));
return true;
}
return false;
@ -239,59 +236,27 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
this.get_content = function() { //function to capture regular content between tags
var input_char = '',
token = {
parent: this.tags.token,
text: '',
type: 'TK_CONTENT'
},
content = [],
handlebarsStarted = 0;
content = [];
while (this.input.charAt(this.pos) !== '<' || handlebarsStarted === 2) {
if (this.pos >= this.input.length) {
while (this.input.peek() !== '<' &&
!(indent_handlebars && this.input.test(/\{\{/g))) {
if (!this.input.hasNext()) {
if (!content.length) {
token.type = 'TK_EOF';
}
break;
}
if (handlebarsStarted < 2 && this.traverse_whitespace()) {
if (this.traverse_whitespace()) {
this.space_or_wrap(content);
continue;
}
input_char = this.input.charAt(this.pos);
if (indent_handlebars) {
if (input_char === '{') {
handlebarsStarted += 1;
} else if (handlebarsStarted < 2) {
handlebarsStarted = 0;
}
if (input_char === '}' && handlebarsStarted > 0) {
if (handlebarsStarted-- === 0) {
break;
}
}
// Handlebars parsing is complicated.
// {{#foo}} and {{/foo}} are formatted tags.
// {{something}} should get treated as content, except:
// {{else}} specifically behaves like {{#if}} and {{/if}}
var peek3 = this.input.substr(this.pos, 3);
if (peek3 === '{{#' || peek3 === '{{/') {
// These are tags and not content.
break;
} else if (peek3 === '{{!') {
token = this.get_tag();
token.type = 'TK_TAG_HANDLEBARS_COMMENT';
return token;
} else if (this.input.substr(this.pos, 2) === '{{') {
if (this.get_tag(true).text === '{{else}}') {
break;
}
}
}
this.pos++;
input_char = this.input.next();
this.line_char_count++;
content.push(input_char); //letter at-a-time (or string) inserted to an array
}
@ -300,104 +265,245 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
};
this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
if (this.pos === this.input.length) {
if (!this.input.hasNext()) {
return { text: '', type: 'TK_EOF' };
}
var content = '';
var reg_match = new RegExp('</' + name + '\\s*>', 'igm');
reg_match.lastIndex = this.pos;
var reg_array = reg_match.exec(this.input);
var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
if (this.pos < end_script) { //get everything in between the script tags
content = this.input.substring(this.pos, end_script);
this.pos = end_script;
}
content = this.input.readUntil(reg_match);
return { text: content, type: 'TK_' + name };
};
this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
if (this.tags[tag + 'count']) { //check for the existence of this tag type
this.tags[tag + 'count']++;
this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
} else { //otherwise initialize this tag type
this.tags[tag + 'count'] = 1;
this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
}
this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
this.record_tag = function(tag, token) { //function to record a tag and its parent in this.tags Object
var new_tag = {
parent: this.tags,
tag: tag,
indent_level: this.indent_level,
token: token
};
this.tags = new_tag;
};
this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
var temp_parent = this.tags.parent; //check to see if it's a closable tag.
while (temp_parent) { //till we reach '' (the initial value);
if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
break;
}
temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
}
if (temp_parent) { //if we caught something
this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
}
delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
if (this.tags[tag + 'count'] === 1) {
delete this.tags[tag + 'count'];
} else {
this.tags[tag + 'count']--;
var token = null;
var temp_parent = this.tags;
while (temp_parent) { //till we reach '' (the initial value);
if (temp_parent.tag === tag) { //if this is it use it
break;
}
temp_parent = temp_parent.parent;
}
if (temp_parent) {
token = temp_parent.token;
this.indent_level = temp_parent.indent_level;
this.tags = temp_parent.parent;
}
return token;
};
this.indent_to_tag = function(tag) {
// Match the indentation level to the last use of this tag, but don't remove it.
if (!this.tags[tag + 'count']) {
return;
}
var temp_parent = this.tags.parent;
while (temp_parent) {
if (tag + this.tags[tag + 'count'] === temp_parent) {
var temp_parent = this.tags;
while (temp_parent) { //till we reach '' (the initial value);
if (temp_parent.tag === tag) { //if this is it use it
break;
}
temp_parent = this.tags[temp_parent + 'parent'];
temp_parent = temp_parent.parent;
}
if (temp_parent) {
this.indent_level = this.tags[tag + this.tags[tag + 'count']];
this.indent_level = temp_parent.indent_level;
}
};
this.get_tag = function(peek) { //function to get a full tag and parse its type
this.get_tag = function() { //function to get a full tag and parse its type
var input_char = '',
token = {
parent: this.tags.token,
text: '',
type: '',
tag_type: '',
tag_name: '',
is_inline_tag: false,
is_unformatted: false,
is_content_unformatted: false,
is_opening_tag: false,
is_closing_tag: false
is_closing_tag: false,
multiline_content: false,
start_tag_token: null
},
content = [],
comment = '',
space = false,
first_attr = true,
has_wrapped_attrs = false,
tag_start, tag_end,
tag_readinging_finished = false,
tag_start_char,
orig_pos = this.pos,
orig_line_char_count = this.line_char_count,
is_tag_closed = false,
tail;
tag_check = '',
is_tag_closed = false;
peek = peek !== undefined ? peek : false;
var peek = this.input.peek();
var peek1 = this.input.peek(1);
var peek2 = this.input.peek(2);
if (peek === '<' && (peek1 === '!' || peek1 === '?' || peek1 === '%')) { //if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
tag_start_char = '<';
input_char = this.get_comment();
tag_check = input_char.match(/^<([^\s>]+)/)[1];
content = [input_char];
tag_readinging_finished = true;
do {
if (this.pos >= this.input.length) {
if (peek) {
this.pos = orig_pos;
this.line_char_count = orig_line_char_count;
} else if (indent_handlebars && peek === '{' && peek1 === '{' && peek2 === '!') { //if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
tag_start_char = '{';
input_char = this.get_comment();
tag_check = input_char.match(/^{{([^\s}]+)/)[1];
content = [input_char];
tag_readinging_finished = true;
} else if (peek === '<') {
content.push(this.input.next());
tag_start_char = '<';
tag_check = this.input.readUntil(/[\s>{]/g).toLowerCase();
content.push(tag_check);
space = true;
} else if (indent_handlebars && peek === '{' && peek1 === '{') {
content.push(this.input.next());
content.push(this.input.next());
if (peek2 === '#') {
content.push(this.input.next());
}
tag_start_char = '{';
tag_check = this.input.readUntil(/[\s}]/g).toLowerCase();
content.push(tag_check);
space = false;
}
token.is_closing_tag = tag_check.charAt(0) === '/';
token.tag_name = token.is_closing_tag ? tag_check.substr(1) : tag_check;
token.is_inline_tag = this.Utils.in_array(token.tag_name, inline_tags) || tag_start_char === '{';
token.is_unformatted = this.Utils.in_array(tag_check, unformatted);
token.is_content_unformatted = this.Utils.in_array(tag_check, content_unformatted);
//indent attributes an auto, forced, aligned or forced-align line-wrap
var alignment_size = wrap_attributes_indent_size;
if (is_wrap_attributes_force_aligned || is_wrap_attributes_aligned_multiple) {
alignment_size = content.join('').length + 1;
}
this.line_char_count += content.join('').length;
if (!tag_readinging_finished) {
while (this.input.hasNext()) {
input_char = this.input.next();
if (indent_handlebars && tag_start_char === '{' && content.length > 2 &&
input_char === '}' && content[content.length - 1] === '}') {
this.line_char_count++;
content.push(input_char);
break;
}
if (input_char === "'" || input_char === '"') {
input_char += this.get_unformatted(input_char);
space = true;
}
if (token.is_unformatted) {
content.push(input_char);
this.line_char_count++;
continue;
}
if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
space = true;
continue;
}
if (tag_start_char === '<') {
if (input_char === '=') { //no space before =
space = false;
}
if (is_wrap_attributes_force_expand_multiline && has_wrapped_attrs && !is_tag_closed && (input_char === '>' || input_char === '/')) {
if (this.input.test(/\/?\s*>/g, -1)) {
space = false;
is_tag_closed = true;
this.print_newline(false, content);
this.print_indentation(content);
}
}
}
if (space) {
if (tag_start_char === '{') {
this.line_char_count++;
content.push(' ');
space = false;
} else if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) {
//no space after = or before >
var wrapped = this.space_or_wrap(content);
var indentAttrs = wrapped && input_char !== '/' && !is_wrap_attributes_force;
space = false;
if (is_wrap_attributes_force && input_char !== '/') {
var force_first_attr_wrap = false;
if (is_wrap_attributes_force_expand_multiline && first_attr) {
var is_only_attribute = this.input.test(/\S*(="([^"]|\\")*")?\s*\/?\s*>/g, -1);
force_first_attr_wrap = !is_only_attribute;
}
if (!first_attr || force_first_attr_wrap) {
this.print_newline(false, content);
this.print_indentation(content);
indentAttrs = true;
}
}
if (indentAttrs) {
has_wrapped_attrs = true;
for (var count = 0; count < alignment_size; count++) {
// only ever further indent with spaces since we're trying to align characters
this.line_char_count++;
content.push(' ');
}
}
if (first_attr) {
for (var i = 0; i < content.length; i++) {
if (content[i] === ' ') {
first_attr = false;
break;
}
}
}
}
}
if (indent_handlebars && tag_start_char === '<') {
// When inside an angle-bracket tag, put spaces around
// handlebars not inside of strings.
if (input_char === '{' && this.input.peek() === '{') {
input_char += this.get_unformatted('}}');
if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') {
input_char = ' ' + input_char;
}
space = true;
}
}
//this.line_char_count += input_char.length;
this.line_char_count++;
content.push(input_char); //inserts character at-a-time (or string)
if (input_char === '>') {
break;
}
}
if (!input_char) {
if (content.length) {
token.text = content.join('');
} else {
@ -406,201 +512,49 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
return token;
}
input_char = this.input.charAt(this.pos);
this.pos++;
if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
space = true;
continue;
}
if (input_char === "'" || input_char === '"') {
input_char += this.get_unformatted(input_char);
space = true;
}
if (input_char === '=') { //no space before =
space = false;
}
tail = this.input.substr(this.pos - 1);
if (is_wrap_attributes_force_expand_multiline && has_wrapped_attrs && !is_tag_closed && (input_char === '>' || input_char === '/')) {
if (tail.match(/^\/?\s*>/)) {
space = false;
is_tag_closed = true;
this.print_newline(false, content);
this.print_indentation(content);
}
}
if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) {
//no space after = or before >
var wrapped = this.space_or_wrap(content);
var indentAttrs = wrapped && input_char !== '/' && !is_wrap_attributes_force;
space = false;
if (is_wrap_attributes_force && input_char !== '/') {
var force_first_attr_wrap = false;
if (is_wrap_attributes_force_expand_multiline && first_attr) {
var is_only_attribute = tail.match(/^\S*(="([^"]|\\")*")?\s*\/?\s*>/) !== null;
force_first_attr_wrap = !is_only_attribute;
}
if (!first_attr || force_first_attr_wrap) {
this.print_newline(false, content);
this.print_indentation(content);
indentAttrs = true;
}
}
if (indentAttrs) {
has_wrapped_attrs = true;
//indent attributes an auto, forced, aligned or forced-align line-wrap
var alignment_size = wrap_attributes_indent_size;
if (is_wrap_attributes_force_aligned || is_wrap_attributes_aligned_multiple) {
alignment_size = content.indexOf(' ') + 1;
}
for (var count = 0; count < alignment_size; count++) {
// only ever further indent with spaces since we're trying to align characters
content.push(' ');
}
}
if (first_attr) {
for (var i = 0; i < content.length; i++) {
if (content[i] === ' ') {
first_attr = false;
break;
}
}
}
}
if (indent_handlebars && tag_start_char === '<') {
// When inside an angle-bracket tag, put spaces around
// handlebars not inside of strings.
if ((input_char + this.input.charAt(this.pos)) === '{{') {
input_char += this.get_unformatted('}}');
if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') {
input_char = ' ' + input_char;
}
space = true;
}
}
if (input_char === '<' && !tag_start_char) {
tag_start = this.pos - 1;
tag_start_char = '<';
}
if (indent_handlebars && !tag_start_char) {
if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] === '{') {
if (input_char === '#' || input_char === '/' || input_char === '!') {
tag_start = this.pos - 3;
} else {
tag_start = this.pos - 2;
}
tag_start_char = '{';
}
}
this.line_char_count++;
content.push(input_char); //inserts character at-a-time (or string)
if (content[1] && (content[1] === '!' || content[1] === '?' || content[1] === '%')) { //if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
content = [this.get_comment(tag_start)];
break;
}
if (indent_handlebars && content[1] && content[1] === '{' && content[2] && content[2] === '!') { //if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
content = [this.get_comment(tag_start)];
break;
}
if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') {
break;
}
} while (input_char !== '>');
}
var tag_complete = content.join('');
var tag_index;
var tag_offset;
// must check for space first otherwise the tag could have the first attribute included, and
// then not un-indent correctly
if (tag_complete.search(/\s/) !== -1) { //if there's whitespace, thats where the tag name ends
tag_index = tag_complete.search(/\s/);
} else if (tag_complete.charAt(0) === '{') {
tag_index = tag_complete.indexOf('}');
} else { //otherwise go with the tag ending
tag_index = tag_complete.indexOf('>');
}
if (tag_complete.charAt(0) === '<' || !indent_handlebars) {
tag_offset = 1;
} else {
tag_offset = tag_complete.charAt(2) === '#' ? 3 : 2;
}
var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
token.is_closing_tag = tag_check.charAt(0) === '/';
token.tag_name = token.is_closing_tag ? tag_check.substr(1) : tag_check;
token.is_inline_tag = this.Utils.in_array(token.tag_name, inline_tags);
if (tag_complete.charAt(tag_complete.length - 2) === '/' ||
this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
token.tag_type = 'SINGLE';
token.type = 'TK_TAG_SINGLE';
token.is_closing_tag = true;
} else if (indent_handlebars && tag_complete.charAt(0) === '{' && tag_check === 'else') {
if (!peek) {
this.indent_to_tag('if');
token.tag_type = 'HANDLEBARS_ELSE';
this.indent_content = true;
this.traverse_whitespace();
}
} else if (this.Utils.in_array(tag_check, unformatted) ||
this.Utils.in_array(tag_check, content_unformatted)) {
this.indent_to_tag('if');
token.type = 'TK_TAG_HANDLEBARS_ELSE';
this.indent_content = true;
this.traverse_whitespace();
} else if (token.is_unformatted || token.is_content_unformatted) {
// do not reformat the "unformatted" or "content_unformatted" tags
if (this.Utils.in_array(tag_check, unformatted)) {
content = [this.input.slice(tag_start, this.pos)];
}
comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
content.push(comment);
tag_end = this.pos - 1;
token.tag_type = 'SINGLE';
token.type = 'TK_TAG_SINGLE';
token.is_closing_tag = true;
} else if (tag_check === 'script' &&
(tag_complete.search('type') === -1 ||
(tag_complete.search('type') > -1 &&
tag_complete.search(/\b(text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect)/) > -1))) {
if (!peek) {
this.record_tag(tag_check);
token.tag_type = 'SCRIPT';
}
this.record_tag(tag_check);
token.type = 'TK_TAG_SCRIPT';
} else if (tag_check === 'style' &&
(tag_complete.search('type') === -1 ||
(tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
if (!peek) {
this.record_tag(tag_check);
token.tag_type = 'STYLE';
}
this.record_tag(tag_check);
token.type = 'TK_TAG_STYLE';
} else if (tag_check.charAt(0) === '!') { //peek for <! comment
// for comments content is already correct.
if (!peek) {
token.tag_type = 'SINGLE';
this.traverse_whitespace();
}
} else if (!peek) {
token.type = 'TK_TAG_SINGLE';
this.traverse_whitespace();
} else {
if (token.is_closing_tag) { //this tag is a double tag so check for tag-ending
this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
token.tag_type = 'END';
token.start_tag_token = this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
token.type = 'TK_TAG_END';
} else { //otherwise it's a start-tag
this.record_tag(tag_check); //push it on the tag stack
this.record_tag(tag_check, token); //push it on the tag stack
if (tag_check.toLowerCase() !== 'html') {
this.indent_content = true;
}
token.tag_type = 'START';
token.type = 'TK_TAG_START';
token.is_opening_tag = true;
}
@ -617,28 +571,20 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
}
}
if (peek) {
this.pos = orig_pos;
this.line_char_count = orig_line_char_count;
}
token.text = content.join('');
token.type = 'TK_TAG_' + token.tag_type;
return token; //returns fully formatted tag
};
this.get_comment = function(start_pos) { //function to return comment content in its entirety
this.get_comment = function() { //function to return comment content in its entirety
// this is will have very poor perf, but will work for now.
var comment = '',
delimiter = '>',
matched = false;
this.pos = start_pos;
var input_char = this.input.charAt(this.pos);
this.pos++;
var input_char = this.input.next();
while (this.pos <= this.input.length) {
while (input_char) {
comment += input_char;
// only need to check for the delimiter if the last chars match
@ -679,8 +625,7 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
}
}
input_char = this.input.charAt(this.pos);
this.pos++;
input_char = this.input.next();
}
return comment;
@ -709,19 +654,18 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
return '';
}
var input_char = '';
var content = '';
var content = [];
var space = true;
var delimiterMatcher = tokenMatcher(delimiter);
do {
if (this.pos >= this.input.length) {
return content;
if (!this.input.hasNext()) {
return content.join('');
}
input_char = this.input.charAt(this.pos);
this.pos++;
input_char = this.input.next();
if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
if (!space) {
@ -729,7 +673,7 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
continue;
}
if (input_char === '\n' || input_char === '\r') {
content += '\n';
content.push('\n');
/* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array'
for (var i=0; i<this.indent_level; i++) {
content += this.indent_string;
@ -740,19 +684,19 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
continue;
}
}
content += input_char;
content.push(input_char);
delimiterMatcher.add(input_char);
this.line_char_count++;
space = true;
if (indent_handlebars && input_char === '{' && content.length && content.charAt(content.length - 2) === '{') {
if (indent_handlebars && input_char === '{' && content.length >= 2 && content[content.length - 2] === '{') {
// Handlebars expressions in strings should also be unformatted.
content += this.get_unformatted('}}');
content.push(this.get_unformatted('}}'));
// Don't consider when stopping for delimiters.
}
} while (delimiterMatcher.doesNotMatch());
return content;
return content.join('');
};
this.get_token = function() { //initial handler for token-retrieval
@ -762,6 +706,10 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
token = this.get_contents_to(type);
} else if (this.current_mode === 'CONTENT') {
token = this.get_content();
// If we find no content, just skip straight to looking for the next tag.
if (token.text === '') {
token = this.get_tag();
}
} else if (this.current_mode === 'TAG') {
token = this.get_tag();
}
@ -777,12 +725,14 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
return Array(level + 1).join(this.indent_string);
};
this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
this.printer = function(source_text, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
this.input = js_source || ''; //gets the input for the Parser
source_text = source_text || '';
// HACK: newline parsing inconsistent. This brute force normalizes the input.
this.input = this.input.replace(/\r\n|[\r\u2028\u2029]/g, '\n');
source_text = source_text.replace(/\r\n|[\r\u2028\u2029]/g, '\n');
this.input = new InputScanner(source_text); //gets the input for the Parser
this.output = [];
this.indent_character = indent_character;
@ -890,8 +840,12 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
switch (token.type) {
case 'TK_TAG_START':
if (!last_tag_token.is_inline_tag && !token.is_inline_tag) {
if (!token.is_inline_tag && multi_parser.last_token.type !== 'TK_CONTENT') {
if (token.parent) {
token.parent.multiline_content = true;
}
multi_parser.print_newline(false, multi_parser.output);
}
multi_parser.print_token(token.text);
if (multi_parser.indent_content) {
@ -914,16 +868,13 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_END':
if (!token.is_inline_tag) {
//Print new line only if the tag has no content and has child
if (!(!last_tag_token.is_closing_tag &&
token.tag_name === last_tag_token.tag_name &&
!multi_parser.Utils.in_array(token.tag_name, content_unformatted)
) &&
!last_tag_token.is_inline_tag
) {
multi_parser.print_newline(false, multi_parser.output);
}
if ((token.start_tag_token && token.start_tag_token.multiline_content) ||
!(token.is_inline_tag ||
(last_tag_token.is_inline_tag) ||
(multi_parser.last_token === last_tag_token && last_tag_token.is_opening_tag && token.is_closing_tag && last_tag_token.tag_name === token.tag_name) ||
(multi_parser.last_token.type === 'TK_CONTENT')
)) {
multi_parser.print_newline(false, multi_parser.output);
}
multi_parser.print_token(token.text);
last_tag_token = token;
@ -931,13 +882,9 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
break;
case 'TK_TAG_SINGLE':
// Don't add a newline before elements that should remain unformatted.
var tag_check = token.text.match(/^\s*<([a-z-]+)/i);
if (token.tag_name === '!--' && multi_parser.last_token.is_closing_tag && token.text.indexOf('\n') === -1) {
//Do nothing. Leave comments on same line.
} else if (!tag_check ||
!multi_parser.Utils.in_array(tag_check[1], inline_tags) &&
!multi_parser.Utils.in_array(tag_check[1], unformatted)
) {
} else if (!token.is_inline_tag && !token.is_unformatted) {
multi_parser.print_newline(false, multi_parser.output);
}
multi_parser.print_token(token.text);
@ -965,6 +912,7 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
multi_parser.indent();
multi_parser.indent_content = false;
}
last_tag_token = token;
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_HANDLEBARS_COMMENT':
@ -1032,7 +980,6 @@ function Beautifier(html_source, options, js_beautify, css_beautify) {
break;
}
multi_parser.last_token = token;
}
var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, '');

View File

@ -26,9 +26,9 @@
SOFTWARE.
*/
var js_beautify = require('javascript/index');
var css_beautify = require('css/index');
var html_beautify = require('html/index');
var js_beautify = require('./javascript/index');
var css_beautify = require('./css/index');
var html_beautify = require('./html/index');
function style_html(html_source, options, js, css) {
js = js || js_beautify;

View File

@ -26,9 +26,9 @@
SOFTWARE.
*/
var mergeOpts = require('core/options').mergeOpts;
var acorn = require('core/acorn');
var Output = require('core/output').Output;
var mergeOpts = require('../core/options').mergeOpts;
var acorn = require('../core/acorn');
var Output = require('../core/output').Output;
var Tokenizer = require('./tokenizer').Tokenizer;
function remove_redundant_indentation(output, frame) {
@ -668,7 +668,7 @@ function Beautifier(js_source_text, options) {
if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
print_newline();
} else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
} else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.' || last_type === 'TK_COMMA') {
// do nothing on (( and )( and ][ and ]( and .(
// TODO: Consider whether forcing this is required. Review failing tests when removed.
allow_wrap_or_preserved_newline(current_token.wanted_newline);
@ -1294,6 +1294,9 @@ function Beautifier(js_source_text, options) {
space_after = false;
} else if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) {
// unary operators (and binary +/- pretending to be unary) special cases
if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR') {
allow_wrap_or_preserved_newline();
}
space_before = false;
space_after = false;

View File

@ -26,9 +26,9 @@
SOFTWARE.
*/
var InputScanner = require('core/inputscanner').InputScanner;
var Token = require('core/token').Token;
var acorn = require('core/acorn');
var InputScanner = require('../core/inputscanner').InputScanner;
var Token = require('../core/token').Token;
var acorn = require('../core/acorn');
function trim(s) {
return s.replace(/^\s+|\s+$/g, '');
@ -45,7 +45,9 @@ function in_array(what, arr) {
function Tokenizer(input_string, opts) {
var whitespace = "\n\r\t ".split('');
var whitespacePattern = /[\n\r\u2028\u2029\t ]+/g;
var newlinePattern = /([\t ]*)(\r\n|[\n\r\u2028\u2029])?/g;
var digit = /[0-9]/;
var digit_bin = /[01]/;
var digit_oct = /[01234567]/;
@ -144,17 +146,10 @@ function Tokenizer(input_string, opts) {
function tokenize_next() {
var resulting_string;
var whitespace_on_this_line = [];
n_newlines = 0;
whitespace_before_token = '';
var c = input.next();
if (c === null) {
return ['', 'TK_EOF'];
}
var last_token;
if (tokens.length) {
last_token = tokens[tokens.length - 1];
@ -163,26 +158,39 @@ function Tokenizer(input_string, opts) {
last_token = new Token('TK_START_BLOCK', '{');
}
while (in_array(c, whitespace)) {
if (acorn.newline.test(c)) {
if (!(c === '\n' && input.peek(-2) === '\r')) {
n_newlines += 1;
whitespace_on_this_line = [];
}
resulting_string = input.readWhile(whitespacePattern);
if (resulting_string !== '') {
if (resulting_string === ' ') {
whitespace_before_token = resulting_string;
} else {
whitespace_on_this_line.push(c);
}
c = input.next();
if (c === null) {
return ['', 'TK_EOF'];
newlinePattern.lastIndex = 0;
var nextMatch = newlinePattern.exec(resulting_string);
while (nextMatch[2]) {
n_newlines += 1;
nextMatch = newlinePattern.exec(resulting_string);
}
whitespace_before_token = nextMatch[1];
}
}
if (whitespace_on_this_line.length) {
whitespace_before_token = whitespace_on_this_line.join('');
resulting_string = input.readWhile(acorn.identifier);
if (resulting_string !== '') {
if (!(last_token.type === 'TK_DOT' ||
(last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) &&
in_array(resulting_string, reserved_words)) {
if (resulting_string === 'in' || resulting_string === 'of') { // hack for 'in' and 'of' operators
return [resulting_string, 'TK_OPERATOR'];
}
return [resulting_string, 'TK_RESERVED'];
}
return [resulting_string, 'TK_WORD'];
}
var c = input.next();
if (c === null) {
return ['', 'TK_EOF'];
}
if (digit.test(c) || (c === '.' && input.testChar(digit))) {
@ -244,28 +252,6 @@ function Tokenizer(input_string, opts) {
return [c, 'TK_WORD'];
}
if (acorn.isIdentifierStart(input.peekCharCode(-1))) {
if (input.hasNext()) {
while (acorn.isIdentifierChar(input.peekCharCode())) {
c += input.next();
if (!input.hasNext()) {
break;
}
}
}
if (!(last_token.type === 'TK_DOT' ||
(last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) &&
in_array(c, reserved_words)) {
if (c === 'in' || c === 'of') { // hack for 'in' and 'of' operators
return [c, 'TK_OPERATOR'];
}
return [c, 'TK_RESERVED'];
}
return [c, 'TK_WORD'];
}
if (c === '(' || c === '[') {
return [c, 'TK_START_EXPR'];
}
@ -462,9 +448,7 @@ function Tokenizer(input_string, opts) {
if (sep === '/') {
// regexps may have modifiers /regexp/MOD , so fetch those, too
// Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
while (input.hasNext() && acorn.isIdentifierStart(input.peekCharCode())) {
resulting_string += input.next();
}
resulting_string += input.readWhile(acorn.identifier);
}
}
return [resulting_string, 'TK_STRING'];

View File

@ -15,7 +15,7 @@ requirejs.config({
});
function amd_beautifier_index_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js Require.js (index file)...');
console.log('Testing ' + name + ' with node.js Require.js (index)...');
var results = new SanityTest();
var beautify = requirejs('beautify/index');
@ -27,11 +27,11 @@ function amd_beautifier_index_tests(name, test_runner) {
beautify.css);
console.log(results.results_raw());
return results;
return results.get_exitcode();
}
function amd_beautifier_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js Require.js (separate file)...');
function amd_beautifier_bundle_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js Require.js (bundle)...');
var results = new SanityTest();
var js_beautify = requirejs('beautify-lib/beautify'),
css_beautify = requirejs('beautify-lib/beautify-css'),
@ -45,18 +45,19 @@ function amd_beautifier_tests(name, test_runner) {
css_beautify.css_beautify);
console.log(results.results_raw());
return results;
return results.get_exitcode();
}
if (require.main === module) {
process.exit(
amd_beautifier_tests('js-beautifier', run_javascript_tests).get_exitcode() +
amd_beautifier_index_tests('js-beautifier', run_javascript_tests).get_exitcode() +
amd_beautifier_tests('cs-beautifier', run_css_tests).get_exitcode() +
amd_beautifier_index_tests('css-beautifier', run_css_tests).get_exitcode() +
amd_beautifier_tests('html-beautifier', run_html_tests).get_exitcode() +
amd_beautifier_index_tests('html-beautifier', run_html_tests).get_exitcode()
);
var exit = 0;
exit = exit || amd_beautifier_bundle_tests('js-beautifier', run_javascript_tests);
exit = exit || amd_beautifier_bundle_tests('cs-beautifier', run_css_tests);
exit = exit || amd_beautifier_bundle_tests('html-beautifier', run_html_tests);
exit = exit || amd_beautifier_index_tests('js-beautifier', run_javascript_tests);
exit = exit || amd_beautifier_index_tests('css-beautifier', run_css_tests);
exit = exit || amd_beautifier_index_tests('html-beautifier', run_html_tests);
process.exit(exit);
}

View File

@ -3033,7 +3033,12 @@ function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_be
//============================================================
// Inline tags formatting
reset_options();
test_fragment('<div><span></span></div><span><div></div></span>');
test_fragment(
'<div><span></span></div><span><div></div></span>',
// -- output --
'<div><span></span></div><span>\n' +
' <div></div>\n' +
'</span>');
test_fragment(
'<div><div><span><span>Nested spans</span></span></div></div>',
// -- output --

View File

@ -3441,6 +3441,39 @@ function run_javascript_tests(test_obj, Urlencoded, js_beautify, html_beautify,
' new Date().getTime()\n' +
'].join("-");');
// Issue 1374 - Parameters starting with ! or [ merged into single line
bt(
'fn(\n' +
' 1,\n' +
' !1,\n' +
' 1,\n' +
' [1]\n' +
')');
// Issue 1288 - Negative numbers remove newlines in array
bt(
'var array = [\n' +
' -1,\n' +
' 0,\n' +
' "a",\n' +
' -2,\n' +
' 1,\n' +
' -3,\n' +
'];');
// Issue 1229 - Negated expressions in array
bt(
'a = [\n' +
' true && 1,\n' +
' true && 1,\n' +
' true && 1\n' +
']\n' +
'a = [\n' +
' !true && 1,\n' +
' !true && 1,\n' +
' !true && 1\n' +
']');
// Issue #996 - Input ends with backslash throws exception
test_fragment(
'sd = 1;\n' +

View File

@ -7,9 +7,7 @@ var fs = require('fs'),
SanityTest = require('./sanitytest'),
Benchmark = require('benchmark'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
js_beautify = require('../index').js_beautify,
css_beautify = require('../index').css_beautify,
html_beautify = require('../index').html_beautify;
beautifier = require('../src/index');
function node_beautifier_html_tests() {
console.log('Testing performance...');
@ -20,16 +18,16 @@ function node_beautifier_html_tests() {
};
//warm-up
html_beautify(index_html, options);
html_beautify(data_attr, options);
beautifier.html(index_html, options);
beautifier.html(data_attr, options);
var suite = new Benchmark.Suite();
suite.add("html-beautify (index.html)", function() {
html_beautify(index_html, options);
beautifier.html(index_html, options);
})
.add("html-beautify (base64 image)", function() {
html_beautify(data_attr, options);
beautifier.html(data_attr, options);
})
// add listeners
.on('cycle', function(event) {

View File

@ -6,9 +6,7 @@ var fs = require('fs'),
SanityTest = require('./sanitytest'),
Benchmark = require('benchmark'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
js_beautify = require('../index').js_beautify,
css_beautify = require('../index').css_beautify,
html_beautify = require('../index').html_beautify;
beautifier = require('../src/index');
function node_beautifier_tests() {
console.log('Testing performance...');
@ -19,16 +17,16 @@ function node_beautifier_tests() {
};
//warm-up
js_beautify(data, options);
js_beautify(data_min, options);
beautifier.js(data, options);
beautifier.js(data_min, options);
var suite = new Benchmark.Suite();
suite.add("js-beautify (underscore)", function() {
js_beautify(data, options);
beautifier.js(data, options);
})
.add("js-beautify (underscore-min)", function() {
js_beautify(data_min, options);
beautifier.js(data_min, options);
})
// add listeners
.on('cycle', function(event) {

View File

@ -10,17 +10,23 @@ function test_legacy_names() {
var beautify = require('../index');
var results = new SanityTest();
console.log('First ensure that legacy import names equal the new ones');
console.log('Ensure all expected functions are defined');
results.expect(typeof beautify.js, 'function');
results.expect(typeof beautify.css, 'function');
results.expect(typeof beautify.html, 'function');
console.log('Ensure that legacy import names equal the new ones');
results.expect(beautify.js, beautify.js_beautify);
results.expect(beautify.css, beautify.css_beautify);
results.expect(beautify.html, beautify.html_beautify);
console.log(results.results_raw());
return results;
return results.get_exitcode();
}
function node_beautifier_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js CommonJS...');
function node_beautifier_index_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js CommonJS (index)...');
var beautify = require('../index');
var results = new SanityTest();
@ -32,14 +38,34 @@ function node_beautifier_tests(name, test_runner) {
beautify.css);
console.log(results.results_raw());
return results;
return results.get_exitcode();
}
function node_beautifier_bundle_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js CommonJS (bundle)...');
var beautify = require('../lib/beautifier');
var results = new SanityTest();
test_runner(
results,
Urlencoded,
beautify.js,
beautify.html,
beautify.css);
console.log(results.results_raw());
return results.get_exitcode();
}
if (require.main === module) {
process.exit(
test_legacy_names() +
node_beautifier_tests('js-beautifier', run_javascript_tests).get_exitcode() +
node_beautifier_tests('css-beautifier', run_css_tests).get_exitcode() +
node_beautifier_tests('html-beautifier', run_html_tests).get_exitcode()
);
var exit = 0;
exit = exit || test_legacy_names();
exit = exit || node_beautifier_index_tests('js-beautifier', run_javascript_tests);
exit = exit || node_beautifier_index_tests('css-beautifier', run_css_tests);
exit = exit || node_beautifier_index_tests('html-beautifier', run_html_tests);
exit = exit || node_beautifier_bundle_tests('js-beautifier', run_javascript_tests);
exit = exit || node_beautifier_bundle_tests('css-beautifier', run_css_tests);
exit = exit || node_beautifier_bundle_tests('html-beautifier', run_html_tests);
process.exit(exit);
}

48
js/test/node-src-index-tests.js Executable file
View File

@ -0,0 +1,48 @@
#!/usr/bin/env node
/*jshint node:true */
var SanityTest = require('./sanitytest'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
run_javascript_tests = require('./generated/beautify-javascript-tests').run_javascript_tests,
run_css_tests = require('./generated/beautify-css-tests').run_css_tests,
run_html_tests = require('./generated/beautify-html-tests').run_html_tests;
function test_names() {
var beautify = require('../index');
var results = new SanityTest();
console.log('Ensure all expected functions are defined');
results.expect(typeof beautify.js, 'function');
results.expect(typeof beautify.css, 'function');
results.expect(typeof beautify.html, 'function');
console.log(results.results_raw());
return results.get_exitcode();
}
function node_beautifier_index_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js CommonJS (src/index)...');
var beautify = require('../src/index');
var results = new SanityTest();
test_runner(
results,
Urlencoded,
beautify.js,
beautify.html,
beautify.css);
console.log(results.results_raw());
return results.get_exitcode();
}
if (require.main === module) {
var exit = 0;
exit = exit || test_names();
exit = exit || node_beautifier_index_tests('js-beautifier', run_javascript_tests);
exit = exit || node_beautifier_index_tests('css-beautifier', run_css_tests);
exit = exit || node_beautifier_index_tests('html-beautifier', run_html_tests);
process.exit(exit);
}

View File

@ -1,58 +0,0 @@
<!doctype html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js"></script>
<script type="text/javascript">
require(["../lib/beautify-html"],function(html_beautify){
var input = document.getElementById("input").value;
var output = html_beautify.html_beautify(input);
document.getElementById("output").innerHTML = output;
});
</script>
<style type="text/css">
#output{
border: 1px solid black;
height: 300px;
width: 100%;
}
#input{
width: 100%;
height: 300px;
}
</style>
</head>
<body>
<h1>RequireJS test</h1>
<p>
This example loads the html-beautifier by using a relative path in the require call to the beautify-html.js file.
(also works works with absolute paths)
</p>
<pre>
require(["../lib/beautify-html"],function(html_beautify){
var input = document.getElementById("input").value;
var output = html_beautify.html_beautify(input);
document.getElementById("output").innerHTML = output;
});
</pre>
<h2>Input</h2>
<textarea name="" id="input">
<ul><li><a href="test"></a></li></ul>
</textarea>
<h2>Output</h2>
<textarea name="" id="output">
</textarea>
</body>
</html>

View File

@ -14,7 +14,7 @@
},
"files": [
"js/bin/",
"js/lib/",
"js/lib/*.js",
"js/lib/unpackers/",
"js/index.js",
"js/src/**/*"

View File

@ -5,6 +5,7 @@ import copy
from .options import BeautifierOptions
from jsbeautifier.core.options import mergeOpts
from jsbeautifier.core.output import Output
from jsbeautifier.core.inputscanner import InputScanner
from jsbeautifier.__version__ import __version__
#
@ -33,6 +34,11 @@ from jsbeautifier.__version__ import __version__
# SOFTWARE.
whitespaceChar = re.compile(r"\s")
whitespacePattern = re.compile(r"(?:\s|\n)+")
# WORD_RE = re.compile("[\w$\-_]")
def default_options():
return BeautifierOptions()
@ -64,15 +70,10 @@ CSS beautifier (http://jsbeautifier.org/)
else:
return 0
WHITE_RE = re.compile("^\s+$")
WORD_RE = re.compile("[\w$\-_]")
class Printer:
def __init__(self, beautifier, indent_char, indent_size, default_indent=""):
self.beautifier = beautifier
self.indentSize = indent_size
def __init__(self, indent_char, indent_size, default_indent=""):
self.singleIndent = (indent_size) * indent_char
self.indentLevel = 0
self.nestedLevel = 0
@ -104,6 +105,8 @@ class Beautifier:
import jsbeautifier.core.acorn as acorn
self.lineBreak = acorn.lineBreak
self.allLineBreaks = acorn.allLineBreaks
self.comment_pattern = re.compile(acorn.six.u(r"\/\/(?:[^\n\r\u2028\u2029]*)"))
self.block_comment_pattern = re.compile(r"\/\*(?:[\s\S]*?)((?:\*\/)|$)")
if not source_text:
source_text = ''
@ -116,7 +119,7 @@ class Beautifier:
self.opts = opts
self.indentSize = opts.indent_size
self.indentChar = opts.indent_char
self.pos = -1
self.input = None
self.ch = None
if self.opts.indent_with_tabs:
@ -150,97 +153,42 @@ class Beautifier:
m = re.search("^[\t ]*", self.source_text)
self.baseIndentString = m.group(0)
def next(self):
self.pos = self.pos + 1
if self.pos < len(self.source_text):
self.ch = self.source_text[self.pos]
else:
self.ch = ''
return self.ch
def peek(self,index=0):
result = ""
index += self.pos + 1
if index < len(self.source_text):
result = self.source_text[index]
return result
def peekIgnoreWhitespace(self,index=0):
start = self.pos
self.eatWhitespace()
result = self.peek(index)
self.pos = start - 1
self.next()
return result
def eatString(self, endChars):
start = self.pos
while self.next():
result = ''
self.ch = self.input.next()
while self.ch:
result += self.ch
if self.ch == "\\":
self.next()
elif self.ch in endChars:
result += self.input.next()
elif self.ch in endChars or self.ch == "\n":
break
elif self.ch == "\n":
break
return self.source_text[start:self.pos] + self.ch
def peekString(self, endChar):
start = self.pos
st = self.eatString(endChar)
self.pos = start - 1
self.next()
return st
self.ch = self.input.next()
return result
# Skips any white space in the source text from the current position.
# When allowAtLeastOneNewLine is true, will output new lines for each
# newline character found; if the user has preserve_newlines off, only
# the first newline will be output
def eatWhitespace(self, allowAtLeastOneNewLine=False):
result = WHITE_RE.search(self.peek()) is not None
result = whitespaceChar.search(self.input.peek() or '') is not None
isFirstNewLine = True
while WHITE_RE.search(self.peek()) is not None:
self.next()
while whitespaceChar.search(self.input.peek() or '') is not None:
self.ch = self.input.next()
if allowAtLeastOneNewLine and self.ch == "\n":
if self.opts.preserve_newlines or isFirstNewLine:
isFirstNewLine = False
self.output.add_new_line(True)
return result
def skipWhitespace(self):
result = ''
if self.ch and WHITE_RE.search(self.ch):
result = self.ch
while WHITE_RE.search(self.next()) is not None:
result += self.ch
return result
def eatComment(self, singleLine):
start = self.pos
if not singleLine:
self.next()
while self.next():
if not singleLine and self.ch == "*" and self.peek() == "/":
self.next()
break
elif singleLine and self.peek() == "\n":
break
return self.source_text[start:self.pos] + self.ch
def lookBack(self, string):
past = self.source_text[self.pos - len(string):self.pos]
return past.lower() == string
# Nested pseudo-class if we are insideRule
# and the next special character found opens
# a new block
def foundNestedPseudoClass(self):
i = self.pos + 1
openParen = 0
while i < len(self.source_text):
ch = self.source_text[i]
i = 1
ch = self.input.peek(i)
while ch:
if ch == "{":
return True
elif ch == "(":
@ -253,42 +201,42 @@ class Beautifier:
elif ch == ";" or ch == "}":
return False
i += 1
ch = self.input.peek(i)
return False
def beautify(self):
printer = Printer(self, self.indentChar, self.indentSize, self.baseIndentString)
printer = Printer(self.indentChar, self.indentSize, self.baseIndentString)
self.output = printer.output
output = self.output
self.input = InputScanner(self.source_text)
output = self.output
input = self.input
self.pos = -1
self.ch = None
insideRule = False
insidePropertyValue = False
enteringConditionalGroup = False
insideAtExtend = False
top_ch = ''
last_top_ch = ''
parenLevel = 0
while True:
whitespace = self.skipWhitespace()
whitespace = input.readWhile(whitespacePattern)
isAfterSpace = whitespace != ''
isAfterNewline = '\n' in whitespace
last_top_ch = top_ch
top_ch = self.ch
self.ch = input.next()
if not self.ch:
break
elif self.ch == '/' and self.peek() == '*':
elif self.ch == '/' and input.peek() == '*':
# /* css comment */
# Always start block comments on a new line.
# This handles scenarios where a block comment immediately
# follows a property definition on the same line or where
# minified code is being beautified.
output.add_new_line()
printer.print_string(self.eatComment(False))
input.back()
printer.print_string(input.readWhile(self.block_comment_pattern))
# Ensures any new lines following the comment are preserved
self.eatWhitespace(True)
@ -296,12 +244,13 @@ class Beautifier:
# Block comments are followed by a new line so they don't
# share a line with other properties
output.add_new_line()
elif self.ch == '/' and self.peek() == '/':
elif self.ch == '/' and input.peek() == '/':
# // single line comment
# Preserves the space before a comment
# on the same line as a rule
output.space_before_token = True
printer.print_string(self.eatComment(True))
input.back()
printer.print_string(input.readWhile(self.comment_pattern))
# Ensures any new lines following the comment are preserved
self.eatWhitespace(True)
@ -309,16 +258,15 @@ class Beautifier:
printer.preserveSingleSpace(isAfterSpace)
# deal with less propery mixins @{...}
if self.peek() == '{':
printer.print_string(self.eatString('}'))
if input.peek() == '{':
printer.print_string(self.ch + self.eatString('}'))
else:
printer.print_string(self.ch)
# strip trailing space, if present, for hash property check
variableOrRule = self.peekString(": ,;{}()[]/='\"")
variableOrRule = input.peekUntilAfter(re.compile(r"[: ,;{}()[\]\/='\"]"))
if variableOrRule[-1] in ": ":
# wwe have a variable or pseudo-class, add it and insert one space before continuing
self.next()
variableOrRule = self.eatString(": ")
if variableOrRule[-1].isspace():
variableOrRule = variableOrRule[:-1]
@ -336,13 +284,11 @@ class Beautifier:
printer.nestedLevel += 1
if variableOrRule in self.CONDITIONAL_GROUP_RULE:
enteringConditionalGroup = True
elif self.ch == '#' and self.peek() == '{':
elif self.ch == '#' and input.peek() == '{':
printer.preserveSingleSpace(isAfterSpace)
printer.print_string(self.eatString('}'))
printer.print_string(self.ch + self.eatString('}'))
elif self.ch == '{':
if self.peekIgnoreWhitespace() == '}':
self.eatWhitespace()
self.next()
if input.match(re.compile("[\t\n ]*}")) is not None:
output.space_before_token = True
printer.print_string("{}")
self.eatWhitespace(True)
@ -379,10 +325,9 @@ class Beautifier:
if self.opts.newline_between_rules and printer.indentLevel == 0 and not output.just_added_blankline():
output.add_new_line(True)
elif self.ch == ":":
self.eatWhitespace()
if (insideRule or enteringConditionalGroup) and \
not (self.lookBack('&') or self.foundNestedPseudoClass()) and \
not self.lookBack('(') and not insideAtExtend:
not (input.lookBack('&') or self.foundNestedPseudoClass()) and \
not input.lookBack('(') and not insideAtExtend:
# 'property: value' delimiter
# which could be in a conditional group query
printer.print_string(":")
@ -395,18 +340,18 @@ class Beautifier:
# sass nested pseudo-class don't use a space
# preserve space before pseudoclasses/pseudoelements, as it means "in any child"
if self.lookBack(' '):
if input.lookBack(' '):
output.space_before_token = True
if self.peek() == ":":
if input.peek() == ":":
# pseudo-element
self.next()
self.ch = input.next()
printer.print_string("::")
else:
# pseudo-element
printer.print_string(":")
elif self.ch == '"' or self.ch == '\'':
printer.preserveSingleSpace(isAfterSpace)
printer.print_string(self.eatString(self.ch))
printer.print_string(self.ch + self.eatString(self.ch))
elif self.ch == ';':
insidePropertyValue = False
insideAtExtend = False
@ -417,19 +362,20 @@ class Beautifier:
# line. Block comments are also affected, but
# a new line is always output before one inside
# that section
if self.peek() is not '/':
if input.peek() is not '/':
output.add_new_line()
elif self.ch == '(':
# may be a url
if self.lookBack("url"):
if input.lookBack("url"):
printer.print_string(self.ch)
self.eatWhitespace()
if self.next():
self.ch = input.next()
if self.ch:
if self.ch is not ')' and self.ch is not '"' \
and self.ch is not '\'':
printer.print_string(self.eatString(')'))
printer.print_string(self.ch + self.eatString(')'))
else:
self.pos -= 1
input.back()
parenLevel += 1
else:
parenLevel += 1
@ -457,7 +403,7 @@ class Beautifier:
printer.print_string(self.ch)
self.eatWhitespace()
# squash extra whitespace
if self.ch and WHITE_RE.search(self.ch):
if self.ch and bool(whitespaceChar.search(self.ch)):
self.ch = ''
elif self.ch == ']':
printer.print_string(self.ch)
@ -468,7 +414,7 @@ class Beautifier:
# no whitespace before or after
self.eatWhitespace()
printer.print_string('=')
if WHITE_RE.search(self.ch):
if bool(whitespaceChar.search(self.ch)):
self.ch = ''
elif self.ch == '!': # !important
printer.print_string(' ')

View File

@ -42,6 +42,9 @@ class CSSBeautifierTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
pass
def reset_options(self):
false = False
true = True
@ -61,10 +64,7 @@ class CSSBeautifierTest(unittest.TestCase):
default_options.preserve_newlines = false
default_options.space_around_selector_separator = false
cls.default_options = default_options
def reset_options(self):
self.options = copy.copy(self.default_options)
self.options = copy.copy(default_options)
def testGenerated(self):
self.reset_options()
@ -77,7 +77,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# End With Newline - (eof = "\n")
self.reset_options();
self.reset_options()
self.options.end_with_newline = true
test_fragment('', '\n')
test_fragment(' .tabs{}', ' .tabs {}\n')
@ -93,7 +93,7 @@ class CSSBeautifierTest(unittest.TestCase):
test_fragment('\n')
# End With Newline - (eof = "")
self.reset_options();
self.reset_options()
self.options.end_with_newline = false
test_fragment('')
test_fragment(' .tabs{}', ' .tabs {}')
@ -111,7 +111,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Empty braces
self.reset_options();
self.reset_options()
t('.tabs{}', '.tabs {}')
t('.tabs { }', '.tabs {}')
t('.tabs { }', '.tabs {}')
@ -126,7 +126,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
#
self.reset_options();
self.reset_options()
t(
'#cboxOverlay {\n' +
'\tbackground: url(images/overlay.png) repeat 0 0;\n' +
@ -143,7 +143,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Support simple language specific option inheritance/overriding - (c = " ")
self.reset_options();
self.reset_options()
self.options.indent_char = ' '
self.options.indent_size = 4
self.options.js = { 'indent_size': 3 }
@ -154,7 +154,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Support simple language specific option inheritance/overriding - (c = " ")
self.reset_options();
self.reset_options()
self.options.indent_char = ' '
self.options.indent_size = 4
self.options.html = { 'js': { 'indent_size': 3 }, 'css': { 'indent_size': 5 } }
@ -164,7 +164,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Support simple language specific option inheritance/overriding - (c = " ")
self.reset_options();
self.reset_options()
self.options.indent_char = ' '
self.options.indent_size = 9
self.options.html = { 'js': { 'indent_size': 3 }, 'css': { 'indent_size': 8 }, 'indent_size': 2}
@ -178,7 +178,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Space Around Combinator - (space = " ")
self.reset_options();
self.reset_options()
self.options.space_around_combinator = true
t('a>b{}', 'a > b {}')
t('a~b{}', 'a ~ b {}')
@ -214,7 +214,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Space Around Combinator - (space = "")
self.reset_options();
self.reset_options()
self.options.space_around_combinator = false
t('a>b{}', 'a>b {}')
t('a~b{}', 'a~b {}')
@ -250,7 +250,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Space Around Combinator - (space = " ")
self.reset_options();
self.reset_options()
self.options.space_around_selector_separator = true
t('a>b{}', 'a > b {}')
t('a~b{}', 'a ~ b {}')
@ -288,7 +288,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Selector Separator - (separator = " ", separator1 = " ")
self.reset_options();
self.reset_options()
self.options.selector_separator_newline = false
self.options.selector_separator = " "
t(
@ -326,7 +326,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Selector Separator - (separator = " ", separator1 = " ")
self.reset_options();
self.reset_options()
self.options.selector_separator_newline = false
self.options.selector_separator = " "
t(
@ -364,7 +364,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Selector Separator - (separator = "\n", separator1 = "\n\t")
self.reset_options();
self.reset_options()
self.options.selector_separator_newline = true
self.options.selector_separator = " "
t(
@ -402,7 +402,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Selector Separator - (separator = "\n", separator1 = "\n\t")
self.reset_options();
self.reset_options()
self.options.selector_separator_newline = true
self.options.selector_separator = " "
t(
@ -442,7 +442,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Preserve Newlines - (separator_input = "\n\n", separator_output = "\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
t('.div {}\n\n.span {}')
t(
@ -456,7 +456,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Preserve Newlines - (separator_input = "\n\n", separator_output = "\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = false
t('.div {}\n\n.span {}', '.div {}\n.span {}')
t(
@ -472,7 +472,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Preserve Newlines and newline_between_rules
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
self.options.newline_between_rules = true
t(
@ -582,7 +582,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Preserve Newlines and add tabs
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
t(
'.tool-tip {\n' +
@ -620,7 +620,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Newline Between Rules - (new_rule = "\n\n")
self.reset_options();
self.reset_options()
self.options.newline_between_rules = true
t(
'.div {}\n' +
@ -796,7 +796,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Newline Between Rules - (new_rule = "\n")
self.reset_options();
self.reset_options()
self.options.newline_between_rules = false
t(
'.div {}\n' +
@ -942,7 +942,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Functions braces
self.reset_options();
self.reset_options()
t('.tabs(){}', '.tabs() {}')
t('.tabs (){}', '.tabs () {}')
t(
@ -976,7 +976,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Comments - (i = "", i1 = "\n", o = "\n", new_rule = "\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = false
self.options.newline_between_rules = false
t('/* header comment newlines on */')
@ -1272,7 +1272,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n\n\n", i1 = "\n\n\n", o = "\n", new_rule = "\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = false
self.options.newline_between_rules = false
t('/* header comment newlines on */')
@ -1932,7 +1932,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n\t\t\n \n", i1 = "\n\t\t\t\n \n", o = "\n", new_rule = "\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = false
self.options.newline_between_rules = false
t('/* header comment newlines on */')
@ -2592,7 +2592,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "", i1 = "\n", o = "\n", new_rule = "\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
self.options.newline_between_rules = false
t('/* header comment newlines on */')
@ -2888,7 +2888,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n", i1 = "\n", o = "\n", new_rule = "\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
self.options.newline_between_rules = false
t('/* header comment newlines on */')
@ -3270,7 +3270,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n\t\t\n \n", i1 = "\n\t\t\t\n \n", o = "\n\n\n", new_rule = "\n\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
self.options.newline_between_rules = false
t('/* header comment newlines on */')
@ -4182,7 +4182,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n\n\n", i1 = "\n\n\n", o = "\n\n\n", new_rule = "\n\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
self.options.newline_between_rules = false
t('/* header comment newlines on */')
@ -5044,7 +5044,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "", i1 = "\n", o = "\n", new_rule = "\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = false
self.options.newline_between_rules = true
t('/* header comment newlines on */')
@ -5352,7 +5352,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n\n\n", i1 = "\n\n\n", o = "\n", new_rule = "\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = false
self.options.newline_between_rules = true
t('/* header comment newlines on */')
@ -6024,7 +6024,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n\t\t\n \n", i1 = "\n\t\t\t\n \n", o = "\n", new_rule = "\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = false
self.options.newline_between_rules = true
t('/* header comment newlines on */')
@ -6696,7 +6696,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "", i1 = "\n", o = "\n", new_rule = "\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
self.options.newline_between_rules = true
t('/* header comment newlines on */')
@ -7004,7 +7004,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n", i1 = "\n", o = "\n", new_rule = "\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
self.options.newline_between_rules = true
t('/* header comment newlines on */')
@ -7416,7 +7416,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n\n\n", i1 = "\n\n\n", o = "\n\n\n", new_rule = "\n\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
self.options.newline_between_rules = true
t('/* header comment newlines on */')
@ -8278,7 +8278,7 @@ class CSSBeautifierTest(unittest.TestCase):
'}')
# Comments - (i = "\n\t\t\n \n", i1 = "\n\t\t\t\n \n", o = "\n\n\n", new_rule = "\n\n\n")
self.reset_options();
self.reset_options()
self.options.preserve_newlines = true
self.options.newline_between_rules = true
t('/* header comment newlines on */')
@ -9192,7 +9192,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Handle LESS property name interpolation
self.reset_options();
self.reset_options()
t(
'tag {\n' +
'\t@{prop}: none;\n' +
@ -9231,7 +9231,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Handle LESS property name interpolation, test #631
self.reset_options();
self.reset_options()
t(
'.generate-columns(@n, @i: 1) when (@i =< @n) {\n' +
'\t.column-@{i} {\n' +
@ -9252,7 +9252,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Handle LESS function parameters
self.reset_options();
self.reset_options()
t(
'div{.px2rem(width,12);}',
# -- output --
@ -9268,7 +9268,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Psuedo-classes vs Variables
self.reset_options();
self.reset_options()
t('@page :first {}')
# Assume the colon goes with the @name. If we're in LESS, this is required regardless of the at-string.
@ -9278,7 +9278,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# SASS/SCSS
self.reset_options();
self.reset_options()
# Basic Interpolation
t(
@ -9306,7 +9306,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Proper handling of colon in selectors
self.reset_options();
self.reset_options()
self.options.selector_separator_newline = false
t('a :b {}')
t('a ::b {}')
@ -9332,7 +9332,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Regresssion Tests
self.reset_options();
self.reset_options()
self.options.selector_separator_newline = false
t(
'@media(min-width:768px) {\n' +
@ -9351,7 +9351,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Extend Tests
self.reset_options();
self.reset_options()
t(
'.btn-group-radios {\n' +
'\t.btn:hover {\n' +
@ -9372,7 +9372,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
# Important
self.reset_options();
self.reset_options()
t(
'a {\n' +
'\tcolor: blue !important;\n' +
@ -9397,7 +9397,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
#
self.reset_options();
self.reset_options()
@ -9415,7 +9415,7 @@ class CSSBeautifierTest(unittest.TestCase):
self.reset_options()
t = self.decodesto
self.reset_options();
self.reset_options()
#============================================================
t(None, "")
@ -9432,7 +9432,7 @@ class CSSBeautifierTest(unittest.TestCase):
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
"}")
# may not eat the space before "["
t('html.js [data-custom="123"] {\n\topacity: 1.00;\n}')
@ -9526,18 +9526,18 @@ class CSSBeautifierTest(unittest.TestCase):
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
'}')
# import
t('@import "test";');
t('@import "test";')
# don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}")
# handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}")
# nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}")
@ -9566,7 +9566,7 @@ class CSSBeautifierTest(unittest.TestCase):
cssbeautifier.beautify(expectation, self.options), expectation)
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
self.options.eol = '\r\\n'
expectation = expectation.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)

View File

@ -23,10 +23,17 @@ six = __import__("six")
# code point above 128.
_nonASCIIwhitespace = re.compile(six.u("[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]"))
_baseASCIIidentifierStartChars = six.u("\x24\x40\x41-\x5a\x5f\x61-\x7a")
_nonASCIIidentifierStartChars = six.u("\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc")
_baseASCIIidentifierChars = six.u("\x24\x30-\x39\x41-\x5a\x5f\x61-\x7a")
_nonASCIIidentifierChars = six.u("\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f")
_nonASCIIidentifierStart = re.compile("[" + _nonASCIIidentifierStartChars + "]")
_nonASCIIidentifier = re.compile("[" + _nonASCIIidentifierStartChars + _nonASCIIidentifierChars + "]")
#_nonASCIIidentifierStart = re.compile("[" + _nonASCIIidentifierStartChars + "]")
#_nonASCIIidentifier = re.compile("[" + _nonASCIIidentifierStartChars + _nonASCIIidentifierChars + "]")
_identifierStart = re.compile("[" + _baseASCIIidentifierStartChars + _nonASCIIidentifierStartChars + "]")
_identifierChars = re.compile("[" + _baseASCIIidentifierChars + _nonASCIIidentifierStartChars + _nonASCIIidentifierChars + "]")
identifier = re.compile("[" + _baseASCIIidentifierStartChars + _nonASCIIidentifierStartChars + "][" + _baseASCIIidentifierChars + _nonASCIIidentifierStartChars + _nonASCIIidentifierChars + "]*")
# Whether a single character denotes a newline.
@ -43,28 +50,30 @@ allLineBreaks = lineBreak
# Test whether a given character code starts an identifier.
def isIdentifierStart(code):
if code < 65:
return code in [36, 64] # permit $ (36) and @ (64). @ is used in ES7 decorators.
if code < 91:
return True # 65 through 91 are uppercase letters
if code < 97:
return code == 95 # permit _ (95)
if code < 123:
return True # 97 through 123 are lowercase letters
return code >= 0xaa and _nonASCIIidentifierStart.match(six.unichr(code)) != None
# if code < 65:
# return code in [36, 64] # permit $ (36) and @ (64). @ is used in ES7 decorators.
# if code < 91:
# return True # 65 through 91 are uppercase letters
# if code < 97:
# return code == 95 # permit _ (95)
# if code < 123:
# return True # 97 through 123 are lowercase letters
# return code >= 0xaa and _nonASCIIidentifierStart.match(six.unichr(code)) != None
return bool(_identifierStart.match(six.unichr(code)))
# Test whether a given character is part of an identifier.
def isIdentifierChar(code):
if code < 48:
return code == 36
if code < 58:
return True
if code < 65:
return False
if code < 91:
return True
if code < 97:
return code == 95
if code < 123:
return True
return code >= 0xaa and _nonASCIIidentifier.match(six.unichr(code)) != None
# if code < 48:
# return code == 36
# if code < 58:
# return True
# if code < 65:
# return False
# if code < 91:
# return True
# if code < 97:
# return code == 95
# if code < 123:
# return True
# return code >= 0xaa and _nonASCIIidentifier.match(six.unichr(code)) != None
return bool(_identifierChars.match(six.unichr(code)))

View File

@ -44,28 +44,19 @@ class InputScanner:
val = self.__input[self.__position]
self.__position += 1
return val;
return val
def peek(self, index = 0):
val = None
index += self.__position;
index += self.__position
if index >= 0 and index < self.__input_length:
val = self.__input[index];
return val;
def peekCharCode(self, index = 0):
# basically here for acorn
val = 0
index += self.__position;
if index >= 0 and index < self.__input_length:
val = ord(self.__input[index])
val = self.__input[index]
return val
def test(self, pattern, index = 0):
index += self.__position;
return index >= 0 and index < self.__input_length and pattern.match(self.__input, index)
index += self.__position
return index >= 0 and index < self.__input_length and bool(pattern.match(self.__input, index))
def testChar(self, pattern, index = 0):
# test one character regex match
@ -76,7 +67,57 @@ class InputScanner:
pattern_match = None
if self.hasNext():
pattern_match = pattern.match(self.__input, self.__position)
if pattern_match:
self.__position += len(pattern_match.group(0));
if bool(pattern_match):
self.__position = pattern_match.end(0)
return pattern_match
def readWhile(self, pattern):
val = ''
pattern_match = self.match(pattern)
if bool(pattern_match):
val = pattern_match.group(0)
return val
def readUntil(self, pattern):
val = ''
pattern_match = None
match_index = self.__position
if self.hasNext():
pattern_match = pattern.search(self.__input, self.__position)
if bool(pattern_match):
match_index = pattern_match.start(0)
else:
match_index = self.__input_length
val = self.__input[self.__position:match_index]
self.__position = match_index
return val
def readUntilAfter(self, pattern):
val = ''
pattern_match = None
match_index = self.__position
if self.hasNext():
pattern_match = pattern.search(self.__input, self.__position)
if bool(pattern_match):
match_index = pattern_match.end(0)
else:
match_index = self.__input_length
val = self.__input[self.__position:match_index]
self.__position = match_index
return val
# css beautifier legacy helpers
def peekUntilAfter(self, pattern):
start = self.__position
val = self.readUntilAfter(pattern)
self.__position = start
return val
def lookBack(self, testVal):
start = self.__position - 1
return start >= len(testVal) and \
self.__input[start - len(testVal):start].lower() == testVal

View File

@ -32,7 +32,7 @@ import copy
def mergeOpts(options, childFieldName):
finalOpts = copy.copy(options)
local = getattr(finalOpts, childFieldName)
local = getattr(finalOpts, childFieldName, None)
if (local):
delattr(finalOpts, childFieldName)
for key in local:

View File

@ -503,7 +503,7 @@ class Beautifier:
if self.flags.last_text == ';' or self.last_type == 'TK_START_BLOCK':
self.print_newline()
elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.flags.last_text == '.':
elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK', 'TK_COMMA'] or self.flags.last_text == '.':
# do nothing on (( and )( and ][ and ]( and .(
# TODO: Consider whether forcing this is required. Review failing tests when removed.
self.allow_wrap_or_preserved_newline(current_token, current_token.wanted_newline)
@ -1059,6 +1059,9 @@ class Beautifier:
space_before = self.last_type == 'TK_START_BLOCK'
space_after = False
elif current_token.text in ['--', '++', '!', '~'] or isUnary:
if self.last_type == 'TK_COMMA' or self.last_type == 'TK_START_EXPR':
self.allow_wrap_or_preserved_newline(current_token)
space_before = False
space_after = False

View File

@ -28,12 +28,14 @@ from ..core.token import Token
class Tokenizer:
whitespace = ["\n", "\r", "\t", " "]
digit = re.compile('[0-9]')
digit_bin = re.compile('[01]')
digit_oct = re.compile('[01234567]')
digit_hex = re.compile('[0123456789abcdefABCDEF]')
startXmlRegExp = re.compile('<()([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*(\'[^\']*\'|"[^"]*"|{[\s\S]+?}))*\s*(/?)\s*>')
xmlRegExp = re.compile('[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*(\'[^\']*\'|"[^"]*"|{[\s\S]+?}))*\s*(/?)\s*>')
positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ')
punct = (positionable_operators +
# non-positionable operators - these do not follow operator position settings
@ -46,6 +48,7 @@ class Tokenizer:
def __init__ (self, input_string, opts, indent_string):
import jsbeautifier.core.acorn as acorn
self.acorn = acorn
self.input = InputScanner(input_string)
self.opts = opts
self.indent_string = indent_string
@ -61,6 +64,9 @@ class Tokenizer:
self.template_pattern = re.compile('((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)')
self.whitespacePattern = re.compile(self.acorn.six.u('[\n\r\u2028\u2029\t ]+'))
self.newlinePattern = re.compile(self.acorn.six.u('([\t ]*)(\r\n|[\n\r\u2028\u2029])?'))
def tokenize(self):
self.in_html_comment = False
self.tokens = []
@ -119,37 +125,45 @@ class Tokenizer:
def __tokenize_next(self):
whitespace_on_this_line = []
self.n_newlines = 0
self.whitespace_before_token = ''
c = self.input.next()
if c == None:
return '', 'TK_EOF'
if len(self.tokens) > 0:
last_token = self.tokens[-1]
else:
# For the sake of tokenizing we can pretend that there was on open brace to start
last_token = Token('TK_START_BLOCK', '{')
while c in self.whitespace:
if self.acorn.newline.match(c):
# treat \r\n as one newline
if not (c == '\n' and self.input.peek(-2) == '\r'):
self.n_newlines += 1
whitespace_on_this_line = []
resulting_string = self.input.readWhile(self.whitespacePattern)
if not resulting_string == '':
if resulting_string == ' ':
self.whitespace_before_token = resulting_string
else:
whitespace_on_this_line.append(c)
for nextMatch in self.newlinePattern.findall(resulting_string):
if nextMatch[1] != '':
self.n_newlines += 1
else:
self.whitespace_before_token = nextMatch[0]
break
c = self.input.next()
resulting_string = self.input.readWhile(self.acorn.identifier)
if not resulting_string == '':
if not (last_token.type == 'TK_DOT' \
or (last_token.type == 'TK_RESERVED' and last_token.text in ['set', 'get'])) \
and resulting_string in self.reserved_words:
if resulting_string == 'in' or resulting_string == 'of': # in and of are operators, need to hack
return resulting_string, 'TK_OPERATOR'
if c == None:
return '', 'TK_EOF'
return resulting_string, 'TK_RESERVED'
if len(whitespace_on_this_line) != 0:
self.whitespace_before_token = ''.join(whitespace_on_this_line)
return resulting_string, 'TK_WORD'
c = self.input.next()
if c == None:
return '', 'TK_EOF'
if self.digit.match(c) or (c == '.' and self.input.testChar(self.digit)):
allow_decimal = True
@ -202,23 +216,6 @@ class Tokenizer:
return c, 'TK_WORD'
if self.acorn.isIdentifierStart(self.input.peekCharCode(-1)):
if self.input.hasNext():
while self.acorn.isIdentifierChar(self.input.peekCharCode()):
c += self.input.next()
if not self.input.hasNext():
break
if not (last_token.type == 'TK_DOT' \
or (last_token.type == 'TK_RESERVED' and last_token.text in ['set', 'get'])) \
and c in self.reserved_words:
if c == 'in' or c == 'of': # in and of are operators, need to hack
return c, 'TK_OPERATOR'
return c, 'TK_RESERVED'
return c, 'TK_WORD'
if c in '([':
return c, 'TK_START_EXPR'
@ -236,7 +233,6 @@ class Tokenizer:
if c == '/':
comment = ''
inline_comment = True
if self.input.peek() == '*': # peek /* .. */ comment
self.input.next()
comment_match = self.input.match(self.block_comment_pattern)
@ -255,10 +251,6 @@ class Tokenizer:
comment = '//' + comment_match.group(0)
return comment, 'TK_COMMENT'
startXmlRegExp = re.compile('<()([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*(\'[^\']*\'|"[^"]*"|{[\s\S]+?}))*\s*(/?)\s*>')
xmlRegExp = re.compile('[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*(\'[^\']*\'|"[^"]*"|{[\s\S]+?}))*\s*(/?)\s*>')
def allowRegExOrXML(self):
return (last_token.type == 'TK_RESERVED' and last_token.text in ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield']) or \
(last_token.type == 'TK_END_EXPR' and last_token.text == ')' and \
@ -270,7 +262,7 @@ class Tokenizer:
isString = (c == '`' or c == "'" or c == '"')
isRegExp = (c == '/' and allowRegExOrXML(self))
isXML = (self.opts.e4x and c == "<" and self.input.test(startXmlRegExp, -1) and allowRegExOrXML(self))
isXML = (self.opts.e4x and c == "<" and self.input.test(self.startXmlRegExp, -1) and allowRegExOrXML(self))
sep = c
esc = False
@ -287,11 +279,11 @@ class Tokenizer:
while self.input.hasNext():
current_char = self.input.peek()
if not (esc or (current_char != delimiter and
(allow_unescaped_newlines or not self.acorn.newline.match(current_char)))):
(allow_unescaped_newlines or not bool(self.acorn.newline.match(current_char))))):
break
# Handle \r\n linebreaks after escapes or in template strings
if (esc or allow_unescaped_newlines) and self.acorn.newline.match(current_char):
if (esc or allow_unescaped_newlines) and bool(self.acorn.newline.match(current_char)):
if current_char == '\r' and self.input.peek(1) == '\n':
self.input.next()
current_char = self.input.peek()
@ -346,13 +338,13 @@ class Tokenizer:
# handle e4x xml literals
self.input.back()
xmlStr = ""
match = self.input.match(xmlRegExp)
match = self.input.match(self.xmlRegExp)
if match:
rootTag = match.group(2)
rootTag = re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', rootTag))
isCurlyRoot = rootTag.startswith('{')
depth = 0
while (match):
while bool(match):
isEndTag = match.group(1)
tagName = match.group(2)
isSingletonTag = (match.groups()[-1] != "") or (match.group(2)[0:8] == "![CDATA[")
@ -367,7 +359,7 @@ class Tokenizer:
if depth <= 0:
break
match = self.input.match(xmlRegExp)
match = self.input.match(self.xmlRegExp)
# if we didn't close correctly, keep unformatted.
if not match:
@ -386,8 +378,7 @@ class Tokenizer:
if sep == '/':
# regexps may have modifiers /regexp/MOD, so fetch those too
# Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
while self.input.hasNext() and self.acorn.isIdentifierStart(self.input.peekCharCode()):
resulting_string += self.input.next()
resulting_string += self.input.readWhile(self.acorn.identifier)
resulting_string = re.sub(self.acorn.allLineBreaks, '\n', resulting_string)

View File

@ -43,18 +43,22 @@ class TestJSBeautifier(unittest.TestCase):
@classmethod
def setUpClass(cls):
pass
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
def reset_options(self):
true = True
false = False
default_options = jsbeautifier.default_options()
default_options.indent_size = 4
default_options.indent_char = ' '
default_options.preserve_newlines = True
default_options.jslint_happy = False
default_options.keep_array_indentation = False
default_options.preserve_newlines = true
default_options.jslint_happy = false
default_options.keep_array_indentation = true
default_options.brace_style = 'collapse'
default_options.indent_level = 0
default_options.break_chained_methods = False
default_options.break_chained_methods = false
default_options.eol = '\n'
default_options.indent_size = 4
@ -65,11 +69,7 @@ class TestJSBeautifier(unittest.TestCase):
default_options.brace_style = 'collapse'
default_options.operator_position = 'before-newline'
cls.default_options = default_options
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
def reset_options(self):
self.options = copy.copy(self.default_options)
self.options = copy.copy(default_options)
def test_unescape(self):
# Test cases contributed by <chrisjshull on GitHub.com>
@ -96,17 +96,17 @@ class TestJSBeautifier(unittest.TestCase):
self.options.unescape_strings = True
bt('"\\x41\\x42\\x43\\x01"', '"ABC\\x01"')
test_fragment('"\\x20\\x40\\x4a"', '" @J"');
test_fragment('"\\xff\\x40\\x4a"');
test_fragment('"\\u0072\\u016B\\u0137\\u012B\\u0074\\u0069\\u0073"', six.u('"\u0072\u016B\u0137\u012B\u0074\u0069\u0073"'));
test_fragment('"\\x20\\x40\\x4a"', '" @J"')
test_fragment('"\\xff\\x40\\x4a"')
test_fragment('"\\u0072\\u016B\\u0137\\u012B\\u0074\\u0069\\u0073"', six.u('"\u0072\u016B\u0137\u012B\u0074\u0069\u0073"'))
bt('a = /\s+/')
test_fragment('"\\x22\\x27",\'\\x22\\x27\',"\\x5c",\'\\x5c\',"\\xff","unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff"',
'"\\"\\\'", \'\\"\\\'\', "\\\\", \'\\\\\', "\\xff", "unicode \\u0000 \\" \\\' \\\\ ' + unicode_char(0xffff) + '"');
'"\\"\\\'", \'\\"\\\'\', "\\\\", \'\\\\\', "\\xff", "unicode \\u0000 \\" \\\' \\\\ ' + unicode_char(0xffff) + '"')
# For error case, return the string unchanged
test_fragment('"\\x22\\x27",\'\\x22\\x27\',"\\x5c",\'\\x5c\',"\\xff and \\xzz","unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"',
'"\\"\\\'", \'\\"\\\'\', "\\\\", \'\\\\\', "\\xff and \\xzz", "unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"');
'"\\"\\\'", \'\\"\\\'\', "\\\\", \'\\\\\', "\\xff and \\xzz", "unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"')
self.options.unescape_strings = False
@ -123,7 +123,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Unicode Support
self.reset_options();
self.reset_options()
bt('var ' + unicode_char(3232) + '_' + unicode_char(3232) + ' = "hi";')
bt(
'var ' + unicode_char(228) + 'x = {\n' +
@ -133,7 +133,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Test template and continuation strings
self.reset_options();
self.reset_options()
bt('`This is a ${template} string.`')
bt(
'`This\n' +
@ -172,7 +172,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# ES7 Decorators
self.reset_options();
self.reset_options()
bt('@foo')
bt('@foo(bar)')
bt(
@ -183,14 +183,14 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# ES7 exponential
self.reset_options();
self.reset_options()
bt('x ** 2')
bt('x ** -2')
#============================================================
# Spread operator
self.reset_options();
self.reset_options()
self.options.brace_style = "collapse,preserve-inline"
bt('const m = { ...item, c: 3 };')
bt(
@ -205,7 +205,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Object literal shorthand functions
self.reset_options();
self.reset_options()
bt(
'return {\n' +
' foo() {\n' +
@ -255,7 +255,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# End With Newline - (eof = "\n")
self.reset_options();
self.reset_options()
self.options.end_with_newline = true
test_fragment('', '\n')
test_fragment(' return .5', ' return .5\n')
@ -271,7 +271,7 @@ class TestJSBeautifier(unittest.TestCase):
test_fragment('\n')
# End With Newline - (eof = "")
self.reset_options();
self.reset_options()
self.options.end_with_newline = false
test_fragment('')
test_fragment(' return .5')
@ -289,7 +289,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Support simple language specific option inheritance/overriding - (j = " ")
self.reset_options();
self.reset_options()
self.options.js = { 'indent_size': 3 }
self.options.css = { 'indent_size': 5 }
bt(
@ -298,7 +298,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# Support simple language specific option inheritance/overriding - (j = " ")
self.reset_options();
self.reset_options()
self.options.html = { 'js': { 'indent_size': 3 }, 'css': { 'indent_size': 5 } }
bt(
'if (a == b) {\n' +
@ -306,7 +306,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# Support simple language specific option inheritance/overriding - (j = " ")
self.reset_options();
self.reset_options()
self.options.indent_size = 9
self.options.html = { 'js': { 'indent_size': 3 }, 'css': { 'indent_size': 5 }, 'indent_size': 2}
self.options.js = { 'indent_size': 4 }
@ -319,7 +319,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Brace style permutations - (ibo = "", iao = "", ibc = "", iac = "", obo = " ", oao = " ", obc = " ", oac = " ")
self.reset_options();
self.reset_options()
self.options.brace_style = 'collapse,preserve-inline'
bt(
'var a ={a: 2};\n' +
@ -341,7 +341,7 @@ class TestJSBeautifier(unittest.TestCase):
bt('try{a();}catch(b){c();}catch(d){}finally{e();}', 'try { a(); } catch (b) { c(); } catch (d) {} finally { e(); }')
# Brace style permutations - (ibo = "\n", iao = "\n", ibc = "\n", iac = "\n", obo = " ", oao = "\n ", obc = "\n", oac = " ")
self.reset_options();
self.reset_options()
self.options.brace_style = 'collapse,preserve-inline'
bt(
'var a =\n' +
@ -413,7 +413,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# Brace style permutations - (ibo = "", iao = "", ibc = "", iac = "", obo = " ", oao = "\n ", obc = "\n", oac = " ")
self.reset_options();
self.reset_options()
self.options.brace_style = 'collapse'
bt(
'var a ={a: 2};\n' +
@ -455,7 +455,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# Brace style permutations - (ibo = "\n", iao = "\n", ibc = "\n", iac = "\n", obo = " ", oao = "\n ", obc = "\n", oac = " ")
self.reset_options();
self.reset_options()
self.options.brace_style = 'collapse'
bt(
'var a =\n' +
@ -529,7 +529,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Comma-first option - (c0 = ",\n", c1 = ",\n ", c2 = ",\n ", c3 = ",\n ", f1 = " ,\n ")
self.reset_options();
self.reset_options()
self.options.comma_first = false
bt(
'{a:1, b:2}',
@ -661,7 +661,7 @@ class TestJSBeautifier(unittest.TestCase):
');')
# Comma-first option - (c0 = "\n, ", c1 = "\n , ", c2 = "\n , ", c3 = "\n , ", f1 = ", ")
self.reset_options();
self.reset_options()
self.options.comma_first = true
bt(
'{a:1, b:2}',
@ -798,7 +798,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Unindent chained functions - ()
self.reset_options();
self.reset_options()
self.options.unindent_chained_methods = true
bt(
'f().f().f()\n' +
@ -854,7 +854,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Space in parens tests - (s = "", e = "")
self.reset_options();
self.reset_options()
self.options.space_in_paren = false
self.options.space_in_empty_paren = false
bt('if(p) foo(a,b);', 'if (p) foo(a, b);')
@ -904,7 +904,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# Space in parens tests - (s = "", e = "")
self.reset_options();
self.reset_options()
self.options.space_in_paren = false
self.options.space_in_empty_paren = true
bt('if(p) foo(a,b);', 'if (p) foo(a, b);')
@ -954,7 +954,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# Space in parens tests - (s = " ", e = "")
self.reset_options();
self.reset_options()
self.options.space_in_paren = true
self.options.space_in_empty_paren = false
bt('if(p) foo(a,b);', 'if ( p ) foo( a, b );')
@ -1004,7 +1004,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# Space in parens tests - (s = " ", e = " ")
self.reset_options();
self.reset_options()
self.options.space_in_paren = true
self.options.space_in_empty_paren = true
bt('if(p) foo(a,b);', 'if ( p ) foo( a, b );')
@ -1056,7 +1056,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# operator_position option - ensure no neswlines if preserve_newlines is false - ()
self.reset_options();
self.reset_options()
self.options.operator_position = 'before-newline'
self.options.preserve_newlines = false
bt(
@ -1105,7 +1105,7 @@ class TestJSBeautifier(unittest.TestCase):
'ac + -ad')
# operator_position option - ensure no neswlines if preserve_newlines is false - ()
self.reset_options();
self.reset_options()
self.options.operator_position = 'after-newline'
self.options.preserve_newlines = false
bt(
@ -1154,7 +1154,7 @@ class TestJSBeautifier(unittest.TestCase):
'ac + -ad')
# operator_position option - ensure no neswlines if preserve_newlines is false - ()
self.reset_options();
self.reset_options()
self.options.operator_position = 'preserve-newline'
self.options.preserve_newlines = false
bt(
@ -1205,7 +1205,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# operator_position option - set to 'before-newline' (default value)
self.reset_options();
self.reset_options()
# comprehensive, various newlines
bt(
@ -1327,7 +1327,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# operator_position option - set to 'after_newline'
self.reset_options();
self.reset_options()
self.options.operator_position = 'after-newline'
# comprehensive, various newlines
@ -1449,7 +1449,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# operator_position option - set to 'preserve-newline'
self.reset_options();
self.reset_options()
self.options.operator_position = 'preserve-newline'
# comprehensive, various newlines
@ -1557,7 +1557,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Yield tests
self.reset_options();
self.reset_options()
bt('yield /foo\\//;')
bt('result = yield pgClient.query_(queryString);')
bt('yield [1, 2]')
@ -1573,7 +1573,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Async / await tests
self.reset_options();
self.reset_options()
bt('async function foo() {}')
bt('let w = async function foo() {}')
bt(
@ -1644,7 +1644,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# e4x - Test that e4x literals passed through when e4x-option is enabled
self.reset_options();
self.reset_options()
self.options.e4x = true
bt(
'xml=<a b="c"><d/><e>\n' +
@ -2016,12 +2016,12 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
#
self.reset_options();
self.reset_options()
#============================================================
# e4x disabled
self.reset_options();
self.reset_options()
self.options.e4x = false
bt(
'xml=<a b="c"><d/><e>\n' +
@ -2033,7 +2033,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Multiple braces
self.reset_options();
self.reset_options()
bt(
'{{}/z/}',
# -- output --
@ -2045,7 +2045,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Space before conditional - (s = "")
self.reset_options();
self.reset_options()
self.options.space_before_conditional = false
bt('if(a) b()')
bt('while(a) b()')
@ -2077,7 +2077,7 @@ class TestJSBeautifier(unittest.TestCase):
bt('return ();')
# Space before conditional - (s = " ")
self.reset_options();
self.reset_options()
self.options.space_before_conditional = true
bt('if (a) b()')
bt('while (a) b()')
@ -2111,7 +2111,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Beautify preserve formatting
self.reset_options();
self.reset_options()
bt(
'/* beautify preserve:start */\n' +
'/* beautify preserve:end */')
@ -2416,7 +2416,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Comments and tests
self.reset_options();
self.reset_options()
# #913
bt(
@ -2499,7 +2499,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Template Formatting
self.reset_options();
self.reset_options()
bt('<?=$view["name"]; ?>')
bt('a = <?= external() ?>;')
bt(
@ -2514,7 +2514,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# jslint and space after anon function - (f = " ", c = "")
self.reset_options();
self.reset_options()
self.options.jslint_happy = true
self.options.space_after_anon_function = true
bt(
@ -2606,7 +2606,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# jslint and space after anon function - (f = " ", c = "")
self.reset_options();
self.reset_options()
self.options.jslint_happy = true
self.options.space_after_anon_function = false
bt(
@ -2698,7 +2698,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# jslint and space after anon function - (f = " ", c = " ")
self.reset_options();
self.reset_options()
self.options.jslint_happy = false
self.options.space_after_anon_function = true
bt(
@ -2790,7 +2790,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# jslint and space after anon function - (f = "", c = " ")
self.reset_options();
self.reset_options()
self.options.jslint_happy = false
self.options.space_after_anon_function = false
bt(
@ -2885,7 +2885,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Regression tests
self.reset_options();
self.reset_options()
# Issue 241
bt(
@ -3269,6 +3269,39 @@ class TestJSBeautifier(unittest.TestCase):
' new Date().getTime()\n' +
'].join("-");')
# Issue 1374 - Parameters starting with ! or [ merged into single line
bt(
'fn(\n' +
' 1,\n' +
' !1,\n' +
' 1,\n' +
' [1]\n' +
')')
# Issue 1288 - Negative numbers remove newlines in array
bt(
'var array = [\n' +
' -1,\n' +
' 0,\n' +
' "a",\n' +
' -2,\n' +
' 1,\n' +
' -3,\n' +
'];')
# Issue 1229 - Negated expressions in array
bt(
'a = [\n' +
' true && 1,\n' +
' true && 1,\n' +
' true && 1\n' +
']\n' +
'a = [\n' +
' !true && 1,\n' +
' !true && 1,\n' +
' !true && 1\n' +
']')
# Issue #996 - Input ends with backslash throws exception
test_fragment(
'sd = 1;\n' +
@ -3330,7 +3363,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Test non-positionable-ops
self.reset_options();
self.reset_options()
bt('a += 2;')
bt('a -= 2;')
bt('a *= 2;')
@ -3346,7 +3379,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
#
self.reset_options();
self.reset_options()
# exponent literals
bt('a = 1e10')
@ -3452,7 +3485,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# brace_style ,preserve-inline tests - (obo = " ", obot = "", oao = "\n", oaot = " ", obc = "\n", oac = " ", oact = "")
self.reset_options();
self.reset_options()
self.options.brace_style = 'collapse,preserve-inline'
bt('import { asdf } from "asdf";')
bt('import { get } from "asdf";')
@ -3496,7 +3529,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# brace_style ,preserve-inline tests - (obo = "\n", obot = " ", oao = "\n", oaot = " ", obc = "\n", oac = "\n", oact = " ")
self.reset_options();
self.reset_options()
self.options.brace_style = 'expand,preserve-inline'
bt('import { asdf } from "asdf";')
bt('import { get } from "asdf";')
@ -3558,7 +3591,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# brace_style ,preserve-inline tests - (obo = " ", obot = "", oao = "\n", oaot = " ", obc = "\n", oac = "\n", oact = " ")
self.reset_options();
self.reset_options()
self.options.brace_style = 'end-expand,preserve-inline'
bt('import { asdf } from "asdf";')
bt('import { get } from "asdf";')
@ -3606,7 +3639,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# brace_style ,preserve-inline tests - (obo = " ", obot = "", oao = "\n", oaot = " ", obc = "\n", oac = " ", oact = "")
self.reset_options();
self.reset_options()
self.options.brace_style = 'none,preserve-inline'
bt('import { asdf } from "asdf";')
bt('import { get } from "asdf";')
@ -3650,7 +3683,7 @@ class TestJSBeautifier(unittest.TestCase):
'}')
# brace_style ,preserve-inline tests - (obo = " ", obot = "", oao = "\n", oaot = " ", obc = "\n", oac = " ", oact = "")
self.reset_options();
self.reset_options()
self.options.brace_style = 'collapse-preserve-inline'
bt('import { asdf } from "asdf";')
bt('import { get } from "asdf";')
@ -3696,7 +3729,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Destructured and related
self.reset_options();
self.reset_options()
self.options.brace_style = 'collapse,preserve-inline'
# Issue 382 - import destructured
@ -3831,7 +3864,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
# Old tests
self.reset_options();
self.reset_options()
bt('')
test_fragment(' return .5')
test_fragment(
@ -5097,39 +5130,39 @@ class TestJSBeautifier(unittest.TestCase):
test_fragment = self.decodesto
bt = self.bt
self.reset_options();
self.reset_options()
#============================================================
bt(None, "")
self.reset_options();
self.reset_options()
#============================================================
self.options.indent_size = 1;
self.options.indent_char = ' ';
self.options.indent_size = 1
self.options.indent_char = ' '
bt('{ one_char() }', "{\n one_char()\n}")
bt('var a,b=1,c=2', 'var a, b = 1,\n c = 2')
self.options.indent_size = 4;
self.options.indent_char = ' ';
self.options.indent_size = 4
self.options.indent_char = ' '
bt('{ one_char() }', "{\n one_char()\n}")
self.options.indent_size = 1;
self.options.indent_char = "\t";
self.options.indent_size = 1
self.options.indent_char = "\t"
bt('{ one_char() }', "{\n\tone_char()\n}")
bt('x = a ? b : c; x;', 'x = a ? b : c;\nx;')
#set to something else than it should change to, but with tabs on, should override
self.options.indent_size = 5;
self.options.indent_char = ' ';
self.options.indent_with_tabs = True;
self.options.indent_size = 5
self.options.indent_char = ' '
self.options.indent_with_tabs = True
bt('{ one_char() }', "{\n\tone_char()\n}")
bt('x = a ? b : c; x;', 'x = a ? b : c;\nx;')
self.reset_options();
self.reset_options()
#============================================================
self.options.preserve_newlines = False;
self.options.preserve_newlines = False
bt('var\na=dont_preserve_newlines;', 'var a = dont_preserve_newlines;')
# make sure the blank line between function definitions stays
@ -5143,14 +5176,14 @@ class TestJSBeautifier(unittest.TestCase):
)
self.options.preserve_newlines = True;
self.options.preserve_newlines = True
bt('var\na=do_preserve_newlines;', 'var\n a = do_preserve_newlines;')
bt('if (foo) // comment\n{\n bar();\n}')
self.reset_options();
self.reset_options()
#============================================================
self.options.keep_array_indentation = False;
self.options.keep_array_indentation = False
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f']",
"a = ['a', 'b', 'c',\n 'd', 'e', 'f'\n]")
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f',\n 'g', 'h', 'i']",
@ -5233,7 +5266,7 @@ class TestJSBeautifier(unittest.TestCase):
" }];\n" +
"}")
self.options.keep_array_indentation = True;
self.options.keep_array_indentation = True
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f']")
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f',\n 'g', 'h', 'i']")
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f',\n 'g', 'h', 'i']")
@ -5289,7 +5322,7 @@ class TestJSBeautifier(unittest.TestCase):
# " ];\n" +
# "}")
self.reset_options();
self.reset_options()
#============================================================
bt('a = //comment\n /regex/;')
@ -5298,14 +5331,14 @@ class TestJSBeautifier(unittest.TestCase):
bt('var a = new function();')
test_fragment('new function')
self.reset_options();
self.reset_options()
#============================================================
# START tests for brace positioning
# If this is ever supported, update tests for each brace style.
# test_fragment('return\n{', 'return\n{') # can't support this?, but that's an improbable and extreme case anyway.
self.options.brace_style = 'expand';
self.options.brace_style = 'expand'
bt('//case 1\nif (a == 1)\n{}\n//case 2\nelse if (a == 2)\n{}')
bt('if(1){2}else{3}', "if (1)\n{\n 2\n}\nelse\n{\n 3\n}")
@ -5398,7 +5431,7 @@ class TestJSBeautifier(unittest.TestCase):
" 'Value2': '2'\n" +
" });")
self.options.brace_style = 'collapse';
self.options.brace_style = 'collapse'
bt('//case 1\nif (a == 1) {}\n//case 2\nelse if (a == 2) {}')
bt('if(1){2}else{3}', "if (1) {\n 2\n} else {\n 3\n}")
@ -5489,7 +5522,7 @@ class TestJSBeautifier(unittest.TestCase):
" 'Value2': '2'\n" +
" });")
self.options.brace_style = "end-expand";
self.options.brace_style = "end-expand"
bt('//case 1\nif (a == 1) {}\n//case 2\nelse if (a == 2) {}')
bt('if(1){2}else{3}', "if (1) {\n 2\n}\nelse {\n 3\n}")
@ -5580,7 +5613,7 @@ class TestJSBeautifier(unittest.TestCase):
" 'Value2': '2'\n" +
" });")
self.options.brace_style = 'none';
self.options.brace_style = 'none'
bt('//case 1\nif (a == 1)\n{}\n//case 2\nelse if (a == 2)\n{}')
bt('if(1){2}else{3}', "if (1) {\n 2\n} else {\n 3\n}")
@ -5673,14 +5706,14 @@ class TestJSBeautifier(unittest.TestCase):
" });")
# END tests for brace position
self.reset_options();
self.reset_options()
#============================================================
test_fragment('roo = {\n /*\n ****\n FOO\n ****\n */\n BAR: 0\n};')
test_fragment("if (zz) {\n // ....\n}\n(function")
self.reset_options();
self.reset_options()
#============================================================
self.options.preserve_newlines = True;
self.options.preserve_newlines = True
bt('var a = 42; // foo\n\nvar b;')
bt('var a = 42; // foo\n\n\nvar b;')
bt("var a = 'foo' +\n 'bar';")
@ -5722,7 +5755,7 @@ class TestJSBeautifier(unittest.TestCase):
bt('/* foo */\n"x"')
self.reset_options();
self.reset_options()
#============================================================
self.options.break_chained_methods = False
self.options.preserve_newlines = False
@ -5760,7 +5793,7 @@ class TestJSBeautifier(unittest.TestCase):
bt('this.something.xxx = foo.moo.bar()')
bt('this\n.something\n.xxx = foo.moo\n.bar()', 'this\n .something\n .xxx = foo.moo\n .bar()')
self.reset_options();
self.reset_options()
#============================================================
# Line wrap test intputs
#..............---------1---------2---------3---------4---------5---------6---------7
@ -6037,7 +6070,7 @@ class TestJSBeautifier(unittest.TestCase):
' }\n'+
'}')
self.reset_options();
self.reset_options()
#============================================================
self.options.preserve_newlines = False
bt('if (foo) // comment\n bar();')
@ -6208,11 +6241,11 @@ class TestJSBeautifier(unittest.TestCase):
# this beavior differs between js and python, defaults to unlimited in js, 10 in python
bt('a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;',
'a = 1;\n\n\n\n\n\n\n\n\n\nb = 2;')
self.options.max_preserve_newlines = 8;
self.options.max_preserve_newlines = 8
bt('a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;',
'a = 1;\n\n\n\n\n\n\n\nb = 2;')
self.reset_options();
self.reset_options()
#============================================================
@ -6230,7 +6263,7 @@ class TestJSBeautifier(unittest.TestCase):
jsbeautifier.beautify(expectation, self.options), expectation)
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
self.options.eol = '\r\\n'
expectation = expectation.replace('\n', '\r\n')
self.assertMultiLineEqual(
jsbeautifier.beautify(input, self.options), expectation)
@ -6255,7 +6288,7 @@ class TestJSBeautifier(unittest.TestCase):
# If we set raw, input should be unchanged
self.options.test_output_raw = True
if self.options.end_with_newline:
elf.decodesto(input, input)
self.decodesto(input, input)
self.options.test_output_raw = False
current_indent_size = None
@ -6273,7 +6306,7 @@ class TestJSBeautifier(unittest.TestCase):
# If we set raw, input should be unchanged
self.options.test_output_raw = True
if self.options.end_with_newline:
elf.decodesto(wrapped_input, wrapped_input)
self.decodesto(wrapped_input, wrapped_input)
self.options.test_output_raw = False

View File

@ -39,6 +39,9 @@ class CSSBeautifierTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
pass
def reset_options(self):
false = False
true = True
@ -52,10 +55,7 @@ class CSSBeautifierTest(unittest.TestCase):
{{#default_options}} default_options.{{name}} = {{&value}}
{{/default_options}}
cls.default_options = default_options
def reset_options(self):
self.options = copy.copy(self.default_options)
self.options = copy.copy(default_options)
def testGenerated(self):
self.reset_options()
@ -69,7 +69,7 @@ class CSSBeautifierTest(unittest.TestCase):
#============================================================
{{^matrix}}
# {{&name}}
self.reset_options();
self.reset_options()
{{#options}}
self.options.{{name}} = {{&value}}
{{/options}}
@ -80,7 +80,7 @@ class CSSBeautifierTest(unittest.TestCase):
{{/matrix}}
{{#matrix}}
# {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
self.reset_options();
self.reset_options()
{{#options}}
self.options.{{name}} = {{&value}}
{{/options}}
@ -105,7 +105,7 @@ class CSSBeautifierTest(unittest.TestCase):
self.reset_options()
t = self.decodesto
self.reset_options();
self.reset_options()
#============================================================
t(None, "")
@ -122,7 +122,7 @@ class CSSBeautifierTest(unittest.TestCase):
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
"}")
# may not eat the space before "["
t('html.js [data-custom="123"] {\n\topacity: 1.00;\n}')
@ -216,18 +216,18 @@ class CSSBeautifierTest(unittest.TestCase):
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
'}')
# import
t('@import "test";');
t('@import "test";')
# don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}")
# handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}")
# nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}")
@ -256,7 +256,7 @@ class CSSBeautifierTest(unittest.TestCase):
cssbeautifier.beautify(expectation, self.options), expectation)
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
self.options.eol = '\r\\n'
expectation = expectation.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)

View File

@ -55,7 +55,7 @@ exports.test_data = {
{ fragment: true, input: ' .tabs{}', output: ' .tabs {}{{eof}}' },
{ fragment: true, input: ' \n\n.tabs{}\n\n\n\n', output: ' .tabs {}{{eof}}' },
{ fragment: true, input: '\n', output: '{{eof}}' }
],
]
}, {
name: "Empty braces",
description: "",
@ -65,14 +65,14 @@ exports.test_data = {
{ input: '.tabs { }', output: '.tabs {}' },
// When we support preserving newlines this will need to change
{ input: '.tabs \n{\n \n }', output: '.tabs {}' }
],
]
}, {
name: "",
description: "",
tests: [{
input: '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity = 90);\n}',
output: '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity=90);\n}'
}, ],
}]
}, {
name: "Support simple language specific option inheritance/overriding",
description: "Support simple language specific option inheritance/overriding",
@ -83,7 +83,7 @@ exports.test_data = {
{ name: "js", value: "{ 'indent_size': 3 }" },
{ name: "css", value: "{ 'indent_size': 5 }" }
],
c: ' ',
c: ' '
},
{
options: [
@ -217,18 +217,18 @@ exports.test_data = {
{ name: "preserve_newlines", value: "true" }
],
separator_input: '\\n\\n',
separator_output: '\\n\\n',
separator_output: '\\n\\n'
}, {
options: [
{ name: "preserve_newlines", value: "false" }
],
separator_input: '\\n\\n',
separator_output: '\\n',
separator_output: '\\n'
}],
tests: [
{ input: '.div {}{{separator_input}}.span {}', output: '.div {}{{separator_output}}.span {}' },
{ input: '#bla, #foo{\n\tcolor:black;{{separator_input}}\tfont-size: 12px;\n}', output: '#bla,\n#foo {\n\tcolor: black;{{separator_output}}\tfont-size: 12px;\n}' }
],
]
},
{
name: "Preserve Newlines and newline_between_rules",
@ -247,8 +247,8 @@ exports.test_data = {
{ unchanged: 'html {}\n\n/*this is a comment*/' },
{ unchanged: '.div {\n\ta: 1;\n\n\n\tb: 2;\n}\n\n\n\n.span {\n\ta: 1;\n}' },
{ unchanged: '.div {\n\n\n\ta: 1;\n\n\n\tb: 2;\n}\n\n\n\n.span {\n\ta: 1;\n}' },
{ unchanged: '@media screen {\n\t.div {\n\t\ta: 1;\n\n\n\t\tb: 2;\n\t}\n\n\n\n\t.span {\n\t\ta: 1;\n\t}\n}\n\n.div {}\n\n.span {}' },
],
{ unchanged: '@media screen {\n\t.div {\n\t\ta: 1;\n\n\n\t\tb: 2;\n\t}\n\n\n\n\t.span {\n\t\ta: 1;\n\t}\n}\n\n.div {}\n\n.span {}' }
]
}, {
name: "Preserve Newlines and add tabs",
options: [{ name: "preserve_newlines", value: "true" }],
@ -256,7 +256,7 @@ exports.test_data = {
tests: [{
input: '.tool-tip {\n\tposition: relative;\n\n\t\t\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\t\t\n\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}',
output: '.tool-tip {\n\tposition: relative;\n\n\n\t.tool-tip-content {\n\t\t&>* {\n\t\t\tmargin-top: 0;\n\t\t}\n\\n\\n\t\t.mixin-box-shadow(.2rem .2rem .5rem rgba(0, 0, 0, .15));\n\t\tpadding: 1rem;\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t}\n}'
}],
}]
}, {
name: "Newline Between Rules",
description: "",
@ -285,8 +285,8 @@ exports.test_data = {
{ input: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', output: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}', output: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}{{new_rule}}@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}' },
{ input: 'a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}{{new_rule}}.div {\n\theight: 15px;\n}' },
{ input: 'a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}{{new_rule}}.div {\n\theight: 15px;\n}' },
],
{ input: 'a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}{{new_rule}}.div {\n\theight: 15px;\n}' }
]
}, {
name: "Functions braces",
description: "",
@ -299,7 +299,7 @@ exports.test_data = {
{ input: '.tabs( ) { }', output: '.tabs() {}' },
{ input: '.tabs (t, t2) \n{\n key: val(p1 ,p2); \n }', output: '.tabs (t, t2) {\n\tkey: val(p1, p2);\n}' },
{ unchanged: '.box-shadow(@shadow: 0 1px 3px rgba(0, 0, 0, .25)) {\n\t-webkit-box-shadow: @shadow;\n\t-moz-box-shadow: @shadow;\n\tbox-shadow: @shadow;\n}' }
],
]
},
{
name: "Comments",
@ -542,9 +542,8 @@ exports.test_data = {
{
input: 'a:first-child{<i>color:red;<i>div:not(.peq){<i>color:black;<i>}<i>}<i>.div{<i>height:15px;<i>}',
output: 'a:first-child {<o>\tcolor: red;<o>\tdiv:not(.peq) {<o>\t\tcolor: black;<o>\t}<o>}<new_rule>.div {<o>\theight: 15px;<o>}'
},
],
}
]
},
{
name: "Handle LESS property name interpolation",
@ -558,8 +557,8 @@ exports.test_data = {
unchanged: 'tag {\n\tdynamic-@{prop}: none;\n}'
},
{ input: 'tag{dynamic-@{prop}:none;}', output: 'tag {\n\tdynamic-@{prop}: none;\n}' },
{ input: 'tag{ dynamic-@{prop}: none;}', output: 'tag {\n\tdynamic-@{prop}: none;\n}' },
],
{ input: 'tag{ dynamic-@{prop}: none;}', output: 'tag {\n\tdynamic-@{prop}: none;\n}' }
]
}, {
name: "Handle LESS property name interpolation, test #631",
description: "",
@ -569,7 +568,7 @@ exports.test_data = {
input: '.generate-columns(@n,@i:1) when (@i =< @n){.column-@{i}{width:(@i * 100% / @n);}.generate-columns(@n,(@i + 1));}',
output: '.generate-columns(@n, @i: 1) when (@i =< @n) {\n\t.column-@{i} {\n\t\twidth: (@i * 100% / @n);\n\t}\n\t.generate-columns(@n, (@i + 1));\n}'
}
],
]
}, {
name: "Handle LESS function parameters",
description: "",
@ -577,7 +576,7 @@ exports.test_data = {
{ input: 'div{.px2rem(width,12);}', output: 'div {\n\t.px2rem(width, 12);\n}' },
//mixin next to 'background: url("...")' should not add a linebreak after the comma
{ unchanged: 'div {\n\tbackground: url("//test.com/dummy.png");\n\t.px2rem(width, 12);\n}' }
],
]
}, {
name: "Psuedo-classes vs Variables",
description: "",
@ -588,7 +587,7 @@ exports.test_data = {
output: '@page: first {}'
},
{ unchanged: '@page: first {}' }
],
]
}, {
name: "SASS/SCSS",
description: "",
@ -614,11 +613,11 @@ exports.test_data = {
'\t&:not(:first-of-type) {',
'\t\tbackground: red;',
'\t}',
'}',
'}'
]
}
],
]
}, {
name: "Proper handling of colon in selectors",
description: "Space before a colon in a selector must be preserved, as it means pseudoclass/pseudoelement on any child",

View File

@ -72,7 +72,7 @@ exports.test_data = {
{ fragment: true, input: '<div></div>', output: '<div></div>{{eof}}' },
// { fragment: true, input: ' \n\n<div></div>\n\n\n\n', output: ' <div></div>{{eof}}' },
{ fragment: true, input: '\n', output: '{{eof}}' }
],
]
}, {
name: "Custom Extra Liners (empty)",
description: "",
@ -80,14 +80,14 @@ exports.test_data = {
options: [
{ name: "extra_liners", value: "[]" }
]
},
}
],
tests: [{
fragment: true,
input: '<html><head><meta></head><body><div><p>x</p></div></body></html>',
output: '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n <p>x</p>\n </div>\n</body>\n</html>'
}],
}]
}, {
name: "Custom Extra Liners (default)",
description: "",
@ -95,14 +95,14 @@ exports.test_data = {
options: [
{ name: "extra_liners", value: "null" }
]
},
}
],
tests: [{
fragment: true,
input: '<html><head></head><body></body></html>',
output: '<html>\n\n<head></head>\n\n<body></body>\n\n</html>'
}],
}]
}, {
name: "Custom Extra Liners (p, string)",
description: "",
@ -110,14 +110,14 @@ exports.test_data = {
options: [
{ name: "extra_liners", value: "'p,/p'" }
]
},
}
],
tests: [{
fragment: true,
input: '<html><head><meta></head><body><div><p>x</p></div></body></html>',
output: '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n\n <p>x\n\n </p>\n </div>\n</body>\n</html>'
}],
}]
}, {
name: "Custom Extra Liners (p)",
description: "",
@ -125,14 +125,14 @@ exports.test_data = {
options: [
{ name: "extra_liners", value: "['p', '/p']" }
]
},
}
],
tests: [{
fragment: true,
input: '<html><head><meta></head><body><div><p>x</p></div></body></html>',
output: '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n\n <p>x\n\n </p>\n </div>\n</body>\n</html>'
}],
}]
}, {
name: "Tests for script and style types (issue 453, 821)",
description: "Only format recognized script types",
@ -277,9 +277,9 @@ exports.test_data = {
' }',
'</style>'
]
},
}
],
]
}, {
name: "Attribute Wrap alignment with spaces",
description: "Ensure attributes are internally aligned with spaces when the indent_character is set to tab",
@ -426,7 +426,7 @@ exports.test_data = {
indent_over80: '\n '
}, {
options: [
{ name: "wrap_attributes", value: "'aligned-multiple'" },
{ name: "wrap_attributes", value: "'aligned-multiple'" }
],
indent_attr: ' ',
indent_attr_first: ' ',
@ -758,7 +758,7 @@ exports.test_data = {
fragment: true,
unchanged: '<span>{{condition1 && condition2 && condition3 && condition4 < 0 ? "resForTrue" : "resForFalse"}}</span>'
}
],
]
}, {
name: "Handlebars Else tag indenting",
description: "Handlebar Else tags should be newlined after formatted tags",
@ -813,7 +813,7 @@ exports.test_data = {
{ fragment: true, unchanged: '<p>\n <a href="/test/"><img src="test.jpg" /></a><a href="/test/"><img src="test.jpg" /></a>\n</p>' },
{ fragment: true, unchanged: '<p>\n <a href="/test/"><img src="test.jpg" /></a><a href="/test/"><img src="test.jpg" /></a><a href="/test/"><img src="test.jpg" /></a><a href="/test/"><img src="test.jpg" /></a>\n</p>' },
{ fragment: true, unchanged: '<p>\n <span>image: <img src="test.jpg" /></span><span>image: <img src="test.jpg" /></span>\n</p>' },
{ fragment: true, unchanged: '<p>\n <strong>image: <img src="test.jpg" /></strong><strong>image: <img src="test.jpg" /></strong>\n</p>' },
{ fragment: true, unchanged: '<p>\n <strong>image: <img src="test.jpg" /></strong><strong>image: <img src="test.jpg" /></strong>\n</p>' }
]
}, {
name: "File starting with comment",
@ -831,7 +831,7 @@ exports.test_data = {
'',
'</html>'
]
}, ]
}]
}, {
name: "Single line comment after closing tag",
description: "Keep single line comments as they are after closing tags",
@ -866,7 +866,7 @@ exports.test_data = {
' <!-- /.row -->',
'</div> <!-- /.col -->'
]
}, ]
}]
}, {
name: "Regression Tests",
description: "Regression Tests",
@ -878,7 +878,7 @@ exports.test_data = {
},
{ fragment: true, unchanged: '<a ">9</a">' },
{ fragment: true, unchanged: '<a href="javascript:;" id="_h_url_paid_pro3" onmousedown="_h_url_click_paid_pro(this);" rel="nofollow" class="pro-title" itemprop="name">WA GlassKote</a>' },
{ fragment: true, unchanged: '<a href="/b/yergey-brewing-a-beer-has-no-name/1745600">"A Beer Has No Name"</a>' },
{ fragment: true, unchanged: '<a href="/b/yergey-brewing-a-beer-has-no-name/1745600">"A Beer Has No Name"</a>' }
]
}, {
name: "Php formatting",
@ -887,7 +887,7 @@ exports.test_data = {
tests: [{
fragment: true,
input: '<h1 class="content-page-header"><?=$view["name"]; ?></h1>',
output: '<h1 class="content-page-header">\n <?=$view["name"]; ?>\n</h1>',
output: '<h1 class="content-page-header">\n <?=$view["name"]; ?>\n</h1>'
}, {
fragment: true,
unchanged: [
@ -974,9 +974,9 @@ exports.test_data = {
'{{h}}{{h}}{{c}}font-size: 12px;',
'{{h}}{{h}}}',
'{{h}}</style>',
'</head>',
'</head>'
]
}, ]
}]
}, {
name: "underscore.js formatting",
description: "underscore.js templates (<% ... %>) treated as comments.",
@ -990,7 +990,7 @@ exports.test_data = {
' </textarea>',
'</div>'
]
}, ]
}]
}, {
name: "Linewrap length",
description: "",
@ -1104,7 +1104,8 @@ exports.test_data = {
template: "^^^ $$$",
tests: [{
fragment: true,
unchanged: '<div><span></span></div><span><div></div></span>'
input: '<div><span></span></div><span><div></div></span>',
output: '<div><span></span></div><span>\n <div></div>\n</span>'
}, {
fragment: true,
input: '<div><div><span><span>Nested spans</span></span></div></div>',
@ -1249,7 +1250,7 @@ exports.test_data = {
'</div>',
'<p>',
' <p>But not me</p>',
'</p>',
'</p>'
]
}, {
fragment: true,
@ -1338,5 +1339,5 @@ exports.test_data = {
}]
}, {
name: "New Test Suite"
}],
}]
};

View File

@ -40,28 +40,28 @@ class TestJSBeautifier(unittest.TestCase):
@classmethod
def setUpClass(cls):
pass
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
def reset_options(self):
true = True
false = False
default_options = jsbeautifier.default_options()
default_options.indent_size = 4
default_options.indent_char = ' '
default_options.preserve_newlines = True
default_options.jslint_happy = False
default_options.keep_array_indentation = False
default_options.preserve_newlines = true
default_options.jslint_happy = false
default_options.keep_array_indentation = true
default_options.brace_style = 'collapse'
default_options.indent_level = 0
default_options.break_chained_methods = False
default_options.break_chained_methods = false
default_options.eol = '\n'
{{#default_options}} default_options.{{name}} = {{&value}}
{{/default_options}}
cls.default_options = default_options
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
def reset_options(self):
self.options = copy.copy(self.default_options)
self.options = copy.copy(default_options)
def test_unescape(self):
# Test cases contributed by <chrisjshull on GitHub.com>
@ -88,17 +88,17 @@ class TestJSBeautifier(unittest.TestCase):
self.options.unescape_strings = True
bt('"\\x41\\x42\\x43\\x01"', '"ABC\\x01"')
test_fragment('"\\x20\\x40\\x4a"', '" @J"');
test_fragment('"\\xff\\x40\\x4a"');
test_fragment('"\\u0072\\u016B\\u0137\\u012B\\u0074\\u0069\\u0073"', six.u('"\u0072\u016B\u0137\u012B\u0074\u0069\u0073"'));
test_fragment('"\\x20\\x40\\x4a"', '" @J"')
test_fragment('"\\xff\\x40\\x4a"')
test_fragment('"\\u0072\\u016B\\u0137\\u012B\\u0074\\u0069\\u0073"', six.u('"\u0072\u016B\u0137\u012B\u0074\u0069\u0073"'))
bt('a = /\s+/')
test_fragment('"\\x22\\x27",\'\\x22\\x27\',"\\x5c",\'\\x5c\',"\\xff","unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff"',
'"\\"\\\'", \'\\"\\\'\', "\\\\", \'\\\\\', "\\xff", "unicode \\u0000 \\" \\\' \\\\ ' + unicode_char(0xffff) + '"');
'"\\"\\\'", \'\\"\\\'\', "\\\\", \'\\\\\', "\\xff", "unicode \\u0000 \\" \\\' \\\\ ' + unicode_char(0xffff) + '"')
# For error case, return the string unchanged
test_fragment('"\\x22\\x27",\'\\x22\\x27\',"\\x5c",\'\\x5c\',"\\xff and \\xzz","unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"',
'"\\"\\\'", \'\\"\\\'\', "\\\\", \'\\\\\', "\\xff and \\xzz", "unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"');
'"\\"\\\'", \'\\"\\\'\', "\\\\", \'\\\\\', "\\xff and \\xzz", "unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"')
self.options.unescape_strings = False
@ -116,7 +116,7 @@ class TestJSBeautifier(unittest.TestCase):
#============================================================
{{^matrix}}
# {{&name}}
self.reset_options();
self.reset_options()
{{#options}}
self.options.{{name}} = {{&value}}
{{/options}}
@ -127,7 +127,7 @@ class TestJSBeautifier(unittest.TestCase):
{{/matrix}}
{{#matrix}}
# {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
self.reset_options();
self.reset_options()
{{#options}}
self.options.{{name}} = {{&value}}
{{/options}}
@ -142,39 +142,39 @@ class TestJSBeautifier(unittest.TestCase):
test_fragment = self.decodesto
bt = self.bt
self.reset_options();
self.reset_options()
#============================================================
bt(None, "")
self.reset_options();
self.reset_options()
#============================================================
self.options.indent_size = 1;
self.options.indent_char = ' ';
self.options.indent_size = 1
self.options.indent_char = ' '
bt('{ one_char() }', "{\n one_char()\n}")
bt('var a,b=1,c=2', 'var a, b = 1,\n c = 2')
self.options.indent_size = 4;
self.options.indent_char = ' ';
self.options.indent_size = 4
self.options.indent_char = ' '
bt('{ one_char() }', "{\n one_char()\n}")
self.options.indent_size = 1;
self.options.indent_char = "\t";
self.options.indent_size = 1
self.options.indent_char = "\t"
bt('{ one_char() }', "{\n\tone_char()\n}")
bt('x = a ? b : c; x;', 'x = a ? b : c;\nx;')
#set to something else than it should change to, but with tabs on, should override
self.options.indent_size = 5;
self.options.indent_char = ' ';
self.options.indent_with_tabs = True;
self.options.indent_size = 5
self.options.indent_char = ' '
self.options.indent_with_tabs = True
bt('{ one_char() }', "{\n\tone_char()\n}")
bt('x = a ? b : c; x;', 'x = a ? b : c;\nx;')
self.reset_options();
self.reset_options()
#============================================================
self.options.preserve_newlines = False;
self.options.preserve_newlines = False
bt('var\na=dont_preserve_newlines;', 'var a = dont_preserve_newlines;')
# make sure the blank line between function definitions stays
@ -188,14 +188,14 @@ class TestJSBeautifier(unittest.TestCase):
)
self.options.preserve_newlines = True;
self.options.preserve_newlines = True
bt('var\na=do_preserve_newlines;', 'var\n a = do_preserve_newlines;')
bt('if (foo) // comment\n{\n bar();\n}')
self.reset_options();
self.reset_options()
#============================================================
self.options.keep_array_indentation = False;
self.options.keep_array_indentation = False
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f']",
"a = ['a', 'b', 'c',\n 'd', 'e', 'f'\n]")
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f',\n 'g', 'h', 'i']",
@ -278,7 +278,7 @@ class TestJSBeautifier(unittest.TestCase):
" }];\n" +
"}")
self.options.keep_array_indentation = True;
self.options.keep_array_indentation = True
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f']")
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f',\n 'g', 'h', 'i']")
bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f',\n 'g', 'h', 'i']")
@ -334,7 +334,7 @@ class TestJSBeautifier(unittest.TestCase):
# " ];\n" +
# "}")
self.reset_options();
self.reset_options()
#============================================================
bt('a = //comment\n /regex/;')
@ -343,14 +343,14 @@ class TestJSBeautifier(unittest.TestCase):
bt('var a = new function();')
test_fragment('new function')
self.reset_options();
self.reset_options()
#============================================================
# START tests for brace positioning
# If this is ever supported, update tests for each brace style.
# test_fragment('return\n{', 'return\n{') # can't support this?, but that's an improbable and extreme case anyway.
self.options.brace_style = 'expand';
self.options.brace_style = 'expand'
bt('//case 1\nif (a == 1)\n{}\n//case 2\nelse if (a == 2)\n{}')
bt('if(1){2}else{3}', "if (1)\n{\n 2\n}\nelse\n{\n 3\n}")
@ -443,7 +443,7 @@ class TestJSBeautifier(unittest.TestCase):
" 'Value2': '2'\n" +
" });")
self.options.brace_style = 'collapse';
self.options.brace_style = 'collapse'
bt('//case 1\nif (a == 1) {}\n//case 2\nelse if (a == 2) {}')
bt('if(1){2}else{3}', "if (1) {\n 2\n} else {\n 3\n}")
@ -534,7 +534,7 @@ class TestJSBeautifier(unittest.TestCase):
" 'Value2': '2'\n" +
" });")
self.options.brace_style = "end-expand";
self.options.brace_style = "end-expand"
bt('//case 1\nif (a == 1) {}\n//case 2\nelse if (a == 2) {}')
bt('if(1){2}else{3}', "if (1) {\n 2\n}\nelse {\n 3\n}")
@ -625,7 +625,7 @@ class TestJSBeautifier(unittest.TestCase):
" 'Value2': '2'\n" +
" });")
self.options.brace_style = 'none';
self.options.brace_style = 'none'
bt('//case 1\nif (a == 1)\n{}\n//case 2\nelse if (a == 2)\n{}')
bt('if(1){2}else{3}', "if (1) {\n 2\n} else {\n 3\n}")
@ -718,14 +718,14 @@ class TestJSBeautifier(unittest.TestCase):
" });")
# END tests for brace position
self.reset_options();
self.reset_options()
#============================================================
test_fragment('roo = {\n /*\n ****\n FOO\n ****\n */\n BAR: 0\n};')
test_fragment("if (zz) {\n // ....\n}\n(function")
self.reset_options();
self.reset_options()
#============================================================
self.options.preserve_newlines = True;
self.options.preserve_newlines = True
bt('var a = 42; // foo\n\nvar b;')
bt('var a = 42; // foo\n\n\nvar b;')
bt("var a = 'foo' +\n 'bar';")
@ -767,7 +767,7 @@ class TestJSBeautifier(unittest.TestCase):
bt('/* foo */\n"x"')
self.reset_options();
self.reset_options()
#============================================================
self.options.break_chained_methods = False
self.options.preserve_newlines = False
@ -805,7 +805,7 @@ class TestJSBeautifier(unittest.TestCase):
bt('this.something.xxx = foo.moo.bar()')
bt('this\n.something\n.xxx = foo.moo\n.bar()', 'this\n .something\n .xxx = foo.moo\n .bar()')
self.reset_options();
self.reset_options()
#============================================================
# Line wrap test intputs
#..............---------1---------2---------3---------4---------5---------6---------7
@ -1082,7 +1082,7 @@ class TestJSBeautifier(unittest.TestCase):
' }\n'+
'}')
self.reset_options();
self.reset_options()
#============================================================
self.options.preserve_newlines = False
bt('if (foo) // comment\n bar();')
@ -1253,11 +1253,11 @@ class TestJSBeautifier(unittest.TestCase):
# this beavior differs between js and python, defaults to unlimited in js, 10 in python
bt('a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;',
'a = 1;\n\n\n\n\n\n\n\n\n\nb = 2;')
self.options.max_preserve_newlines = 8;
self.options.max_preserve_newlines = 8
bt('a = 1;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 2;',
'a = 1;\n\n\n\n\n\n\n\nb = 2;')
self.reset_options();
self.reset_options()
#============================================================
@ -1275,7 +1275,7 @@ class TestJSBeautifier(unittest.TestCase):
jsbeautifier.beautify(expectation, self.options), expectation)
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
self.options.eol = '\r\\n'
expectation = expectation.replace('\n', '\r\n')
self.assertMultiLineEqual(
jsbeautifier.beautify(input, self.options), expectation)
@ -1300,7 +1300,7 @@ class TestJSBeautifier(unittest.TestCase):
# If we set raw, input should be unchanged
self.options.test_output_raw = True
if self.options.end_with_newline:
elf.decodesto(input, input)
self.decodesto(input, input)
self.options.test_output_raw = False
current_indent_size = None
@ -1318,7 +1318,7 @@ class TestJSBeautifier(unittest.TestCase):
# If we set raw, input should be unchanged
self.options.test_output_raw = True
if self.options.end_with_newline:
elf.decodesto(wrapped_input, wrapped_input)
self.decodesto(wrapped_input, wrapped_input)
self.options.test_output_raw = False

View File

@ -186,7 +186,7 @@ exports.test_data = {
{ fragment: true, input: ' return .5', output: ' return .5{{eof}}' },
{ fragment: true, input: ' \n\nreturn .5\n\n\n\n', output: ' return .5{{eof}}' },
{ fragment: true, input: '\n', output: '{{eof}}' }
],
]
}, {
name: "Support simple language specific option inheritance/overriding",
description: "Support simple language specific option inheritance/overriding",
@ -219,7 +219,7 @@ exports.test_data = {
'{{j}}test();',
'}'
]
}, ]
}]
}, {
name: "Brace style permutations",
description: "",
@ -279,7 +279,7 @@ exports.test_data = {
oao: '\n ',
obc: '\n',
oac: ' '
},
}
],
tests: [{
input: 'var a =<ibo>{<iao>a: 2<ibc>}<iac>;\nvar a =<ibo>{<iao>a: 2<ibc>}<iac>;',
@ -309,7 +309,7 @@ exports.test_data = {
'catch (d)<obo>{}<oac>' +
'finally<obo>{<oao>e();<obc>}'
}
],
]
}, {
name: "Comma-first option",
description: "Put commas at the start of lines instead of the end",
@ -403,8 +403,8 @@ exports.test_data = {
' }',
');'
]
},
],
}
]
}, {
name: "Unindent chained functions",
description: "Don't indent chained functions if unindent_chained_functions is true",
@ -416,7 +416,7 @@ exports.test_data = {
tests: [{
input: [
'f().f().f()',
' .f().f();',
' .f().f();'
],
output: [
'f().f().f()',
@ -477,40 +477,40 @@ exports.test_data = {
' .f();',
'});'
]
},
}
],
]
}, {
name: "Space in parens tests",
description: "put space inside parens",
matrix: [{
options: [
{ name: "space_in_paren", value: "false" },
{ name: "space_in_empty_paren", value: "false" },
{ name: "space_in_empty_paren", value: "false" }
],
s: '',
e: '',
e: ''
}, {
options: [
{ name: "space_in_paren", value: "false" },
{ name: "space_in_empty_paren", value: "true" },
{ name: "space_in_empty_paren", value: "true" }
],
s: '',
e: '',
e: ''
}, {
options: [
{ name: "space_in_paren", value: "true" },
{ name: "space_in_empty_paren", value: "false" },
{ name: "space_in_empty_paren", value: "false" }
],
s: ' ',
e: '',
e: ''
}, {
options: [
{ name: "space_in_paren", value: "true" },
{ name: "space_in_empty_paren", value: "true" },
{ name: "space_in_empty_paren", value: "true" }
],
s: ' ',
e: ' ',
e: ' '
}],
tests: [{
input: 'if(p) foo(a,b);',
@ -564,9 +564,9 @@ exports.test_data = {
' dest: "www/gui/build"',
' }{{s}}]',
'}'
],
},
],
]
}
]
}, {
name: "operator_position option - ensure no neswlines if preserve_newlines is false",
matrix: [{
@ -589,7 +589,7 @@ exports.test_data = {
unchanged: inputlib.operator_position.sanity
}, {
input: inputlib.operator_position.comprehensive,
output: inputlib.operator_position.sanity,
output: inputlib.operator_position.sanity
}]
}, {
name: "operator_position option - set to 'before-newline' (default value)",
@ -941,7 +941,7 @@ exports.test_data = {
' return <ListItem item={return <tag>{item}</tag>} key={item.id} />;',
' });',
' }',
'});',
'});'
]
},
{
@ -962,7 +962,7 @@ exports.test_data = {
' return <div {someAttr}>Hello {this.props.name}</div>;',
' }',
'});',
'React.render(<HelloMessage name="John" />, mountNode);',
'React.render(<HelloMessage name="John" />, mountNode);'
]
},
{
@ -1127,7 +1127,7 @@ exports.test_data = {
' </Nav>',
' );',
'var qwer = <DropDown> A dropdown list <Menu> <MenuItem>Do Something</MenuItem> <MenuItem>Do Something Fun!</MenuItem> <MenuItem>Do Something Else</MenuItem> </Menu> </DropDown>;',
'render(dropdown);',
'render(dropdown);'
],
output: [
'var content = (',
@ -1143,7 +1143,7 @@ exports.test_data = {
' </Nav>',
');',
'var qwer = <DropDown> A dropdown list <Menu> <MenuItem>Do Something</MenuItem> <MenuItem>Do Something Fun!</MenuItem> <MenuItem>Do Something Else</MenuItem> </Menu> </DropDown>;',
'render(dropdown);',
'render(dropdown);'
]
},
{
@ -1314,12 +1314,12 @@ exports.test_data = {
options: [
{ name: "space_before_conditional", value: "false" }
],
s: '',
s: ''
}, {
options: [
{ name: "space_before_conditional", value: "true" }
],
s: ' ',
s: ' '
}],
tests: [
{ unchanged: 'if{{s}}(a) b()' },
@ -1338,7 +1338,7 @@ exports.test_data = {
output: 'do\n c();\nwhile{{s}}(a);'
},
{ unchanged: 'return [];' },
{ unchanged: 'return ();' },
{ unchanged: 'return ();' }
]
}, {
name: "Beautify preserve formatting",
@ -1646,7 +1646,7 @@ exports.test_data = {
' eleven: 11',
'};'
]
},
}
]
}, {
name: "Comments and tests",
@ -1676,7 +1676,7 @@ exports.test_data = {
' if (i % 3) {',
' console.log(i);',
' }',
'console.log("done");',
'console.log("done");'
]
},
{
@ -1686,7 +1686,7 @@ exports.test_data = {
' k: 0',
'}',
'// ...',
'foo(o)',
'foo(o)'
]
},
{
@ -1695,7 +1695,7 @@ exports.test_data = {
'Meteor.call("foo", bar, function(err, result) {',
' Session.set("baz", result.lorem)',
'})',
'//blah blah',
'//blah blah'
]
},
{
@ -1707,7 +1707,7 @@ exports.test_data = {
'',
'const foo = 5',
'// comment',
'bar()',
'bar()'
]
},
{
@ -1740,9 +1740,9 @@ exports.test_data = {
' // rounding up using integer arithmetic only',
' if (i % modulus)',
' i += modulus - (i % modulus);',
'// behavior of comments should be different for single statements vs block statements/expressions',
'// behavior of comments should be different for single statements vs block statements/expressions'
]
},
}
]
}, {
@ -1843,7 +1843,7 @@ exports.test_data = {
output: 'var o2 = $.extend(a);\n\nfunction{{f}}() {\n alert(x);\n}'
},
{ input: 'function*() {\n yield 1;\n}', output: 'function*{{f}}() {\n yield 1;\n}' },
{ unchanged: 'function* x() {\n yield 1;\n}' },
{ unchanged: 'function* x() {\n yield 1;\n}' }
]
}, {
name: "Regression tests",
@ -2333,6 +2333,47 @@ exports.test_data = {
'].join("-");'
]
},
{
comment: "Issue 1374 - Parameters starting with ! or [ merged into single line",
unchanged: [
'fn(',
' 1,',
' !1,',
' 1,',
' [1]',
')'
]
},
{
comment: "Issue 1288 - Negative numbers remove newlines in array",
unchanged: [
'var array = [',
' -1,',
' 0,',
' "a",',
' -2,',
' 1,',
' -3,',
'];'
]
},
{
comment: "Issue 1229 - Negated expressions in array",
unchanged: [
'a = [',
' true && 1,',
' true && 1,',
' true && 1',
']',
'a = [',
' !true && 1,',
' !true && 1,',
' !true && 1',
']'
]
},
{
comment: "Issue #996 - Input ends with backslash throws exception",
fragment: true,
@ -2420,7 +2461,7 @@ exports.test_data = {
{ unchanged: 'a |= 2;' },
{ unchanged: 'a **= 2;' },
{ unchanged: 'a <<= 2;' },
{ unchanged: 'a >>= 2;' },
{ unchanged: 'a >>= 2;' }
]
}, {
tests: [{
@ -2534,7 +2575,7 @@ exports.test_data = {
{ input: 'a=.0n', output: 'a = .0 n' },
{ input: 'a=1.0n', output: 'a = 1.0 n' },
{ input: 'a=1e0n', output: 'a = 1e0 n' },
{ input: 'a=0n11a+4', output: 'a = 0n 11 a + 4' },
{ input: 'a=0n11a+4', output: 'a = 0n 11 a + 4' }
]
}, {
//Relies on the tab being four spaces as default for the tests
@ -2797,7 +2838,7 @@ exports.test_data = {
' self.emit(eventName, event, meta);',
' });',
'}',
'["logged_in", "logged_out", "signed_up", "updated_user"].forEach(bindAuthEvent);',
'["logged_in", "logged_out", "signed_up", "updated_user"].forEach(bindAuthEvent);'
]
},
{
@ -3284,9 +3325,9 @@ exports.test_data = {
{ unchanged: 'if (1 + foo() && bar(baz()) / 2) one();\ntwo();\nthree();' },
{ input: 'var a=1,b={bang:2},c=3;', output: 'var a = 1,\n b = {\n bang: 2\n },\n c = 3;' },
{ input: 'var a={bing:1},b=2,c=3;', output: 'var a = {\n bing: 1\n },\n b = 2,\n c = 3;' },
{ input: 'var a={bing:1},b=2,c=3;', output: 'var a = {\n bing: 1\n },\n b = 2,\n c = 3;' }
],
]
}
],
examples: [{

View File

@ -9,8 +9,11 @@ build_js()
echo Building javascript...
cd $PROJECT_DIR
# jshint
$PROJECT_DIR/node_modules/.bin/jshint . || exit 1
# generate lib files
$PROJECT_DIR/node_modules/.bin/webpack
$PROJECT_DIR/node_modules/.bin/webpack || exit 1
mkdir -p ./js/lib/unpackers
cp -r ./js/src/unpackers ./js/lib/
@ -30,11 +33,6 @@ build_js()
cat ./dist/legacy_beautify_html.js >> ./js/lib/beautify-html.js
cat ./tools/template/beautify-html.end.js >> ./js/lib/beautify-html.js
cp ./dist/beautifier.js ./js/lib/
cp ./dist/beautifier.min.js ./js/lib/
# jshint
$PROJECT_DIR/node_modules/.bin/jshint 'js/src' 'test' || exit 1
}
build_beautify()
@ -67,9 +65,6 @@ build_beautify()
# html not ready yet
# $PROJECT_DIR/js/bin/html-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r index.html
# jshint again to make sure things haven't changed
$PROJECT_DIR/node_modules/.bin/jshint 'js/src' 'test' || exit 1
build_js
}

View File

@ -1,3 +1,4 @@
/*exported run_tests, read_settings_from_cookie, beautify, submitIssue */
var the = {
use_codemirror: !window.location.href.match(/without-codemirror/),
beautifier_file: window.location.href.match(/debug/) ? 'beautifier' : './beautifier.min',
@ -10,7 +11,7 @@ requirejs.config({
//By default load any module IDs from js/lib
baseUrl: 'js/lib',
paths: {
'beautifier': the.beautifier_file,
'beautifier': the.beautifier_file
}
});
@ -119,7 +120,7 @@ function unpacker_filter(source) {
for (var i = 0; i < unpackers.length; i++) {
if (unpackers[i].detect(source)) {
unpacked = unpackers[i].unpack(source);
if (unpacked != source) {
if (unpacked !== source) {
source = unpacker_filter(unpacked);
}
}
@ -130,7 +131,9 @@ function unpacker_filter(source) {
function beautify() {
if (the.beautify_in_progress) return;
if (the.beautify_in_progress) {
return;
}
store_settings_to_cookie();
@ -147,7 +150,7 @@ function beautify() {
the.language = $('#language option:selected').text();
opts.indent_size = $('#tabsize').val();
opts.indent_char = opts.indent_size == 1 ? '\t' : ' ';
opts.indent_char = opts.indent_size === 1 ? '\t' : ' ';
opts.max_preserve_newlines = $('#max-preserve-newlines').val();
opts.preserve_newlines = opts.max_preserve_newlines !== "-1";
opts.keep_array_indentation = $('#keep-array-indentation').prop('checked');

View File

@ -19,13 +19,13 @@ $(function() {
the.editor.setValue(default_text);
$('.CodeMirror').click(function() {
if (the.editor.getValue() == default_text) {
if (the.editor.getValue() === default_text) {
the.editor.setValue('');
}
});
} else {
$('#source').val(default_text).bind('click focus', function() {
if ($(this).val() == default_text) {
if ($(this).val() === default_text) {
$(this).val('');
}
}).bind('blur', function() {
@ -37,10 +37,11 @@ $(function() {
$(window).bind('keydown', function(e) {
if (e.ctrlKey && e.keyCode == 13) {
if (e.ctrlKey && e.keyCode === 13) {
beautify();
}
})
});
$('.submit').click(beautify);
$('select').change(beautify);
$(':checkbox').change(beautify);

View File

@ -23,12 +23,15 @@ var dist_full = {
resolve: {
modules: [ path.resolve(__dirname, "js/src") ]
},
devtool: 'source-map',
output: {
library: 'beautifier',
libraryTarget: 'umd',
umdNamedDefine: true,
filename: 'beautifier.js',
path: path.resolve(__dirname, 'dist')
path: path.resolve(__dirname, 'js/lib'),
// Workaround for https://github.com/webpack/webpack/issues/6525
globalObject: "typeof self !== 'undefined' ? self : typeof windows !== 'undefined' ? window : typeof global !== 'undefined' ? global : this"
}
};
@ -38,12 +41,15 @@ var dist_prod = {
resolve: {
modules: [ path.resolve(__dirname, "js/src") ]
},
devtool: 'source-map',
output: {
library: 'beautifier',
libraryTarget: 'umd',
umdNamedDefine: true,
filename: 'beautifier.min.js',
path: path.resolve(__dirname, 'dist')
path: path.resolve(__dirname, 'js/lib'),
// Workaround for https://github.com/webpack/webpack/issues/6525
globalObject: "typeof self !== 'undefined' ? self : typeof windows !== 'undefined' ? window : typeof global !== 'undefined' ? global : this"
}
};