Added option to ignore existing whitespace.

For ugly javascript i guess i'm not the only one who needs a
possibility to throw away existing whitespace and reformat everything
nicely. For some reason I didn't have this feature, and nobody told
anything about it. Now there is one, and the world will be slightly
better place.
This commit is contained in:
Einars Lielmanis 2008-12-23 17:19:28 +02:00
parent f649089c84
commit f5ec6a9159
4 changed files with 120 additions and 80 deletions

View File

@ -7,11 +7,11 @@
Written by Nochum Sossonko, (nsossonko@hotmail.com)
$Date$
$Revision$
Based on code initially developed by: Einars "elfz" Lielmanis, <elfz@laacz.lv>
Based on code initially developed by: Einars "elfz" Lielmanis, <elfz@laacz.lv>
http://elfz.laacz.lv/beautify/
You are free to use this in any way you want, in case you find this useful or working for you.
Usage:
@ -21,11 +21,11 @@
function style_html(html_source, indent_size, indent_character, max_char) {
//Wrapper function to invoke all the necessary constructors and deal with the output.
var Parser, multi_parser;
function Parser() {
this.pos = 0; //Parser position
this.token = '';
this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
@ -37,7 +37,7 @@ function style_html(html_source, indent_size, indent_character, max_char) {
this.tag_type = '';
this.token_text = this.last_token = this.last_text = this.token_type = '';
this.Utils = { //Uilities made available to the various functions
whitespace: "\n\r\t ".split(''),
single_token: 'br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed'.split(','), //all the single tags for HTML
@ -51,9 +51,9 @@ function style_html(html_source, indent_size, indent_character, max_char) {
return false;
}
}
this.get_content = function () { //function to capture regular content between tags
var char = '';
var content = [];
var space = false; //if a space is needed
@ -61,12 +61,12 @@ function style_html(html_source, indent_size, indent_character, max_char) {
if (this.pos >= this.input.length) {
return content.length?content.join(''):['', 'TK_EOF'];
}
char = this.input.charAt(this.pos);
this.pos++;
this.line_char_count++;
if (this.Utils.in_array(char, this.Utils.whitespace)) {
if (content.length) {
space = true;
@ -92,9 +92,9 @@ function style_html(html_source, indent_size, indent_character, max_char) {
}
return content.length?content.join(''):'';
}
this.get_script = function () { //get the full content of a script to pass to js_beautify
var char = '';
var content = [];
var reg_match = new RegExp('\<\/script' + '\>', 'igm');
@ -105,16 +105,16 @@ function style_html(html_source, indent_size, indent_character, max_char) {
if (this.pos >= this.input.length) {
return content.length?content.join(''):['', 'TK_EOF'];
}
char = this.input.charAt(this.pos);
this.pos++;
content.push(char);
}
return content.length?content.join(''):''; //we might not have any content at all
}
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']++;
@ -127,7 +127,7 @@ function style_html(html_source, indent_size, indent_character, max_char) {
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.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.
@ -151,7 +151,7 @@ function style_html(html_source, indent_size, indent_character, max_char) {
}
}
}
this.get_tag = function () { //function to get a full tag and parse its type
var char = '';
var content = [];
@ -161,28 +161,28 @@ function style_html(html_source, indent_size, indent_character, max_char) {
if (this.pos >= this.input.length) {
return content.length?content.join(''):['', 'TK_EOF'];
}
char = this.input.charAt(this.pos);
this.pos++;
this.line_char_count++;
if (this.Utils.in_array(char, this.Utils.whitespace)) { //don't want to insert unnecessary space
space = true;
this.line_char_count--;
continue;
}
if (char === "'" || char === '"') {
if (!content[1] || content[1] !== '!') { //if we're in a comment strings don't get treated specially
char += this.get_unformatted(char);
space = true;
}
}
if (char === '=') { //no space before =
space = false;
}
if (content.length && content[content.length-1] !== '=' && char !== '>'
&& space) { //no space after = or before >
if (this.line_char_count >= this.max_char) {
@ -197,7 +197,7 @@ function style_html(html_source, indent_size, indent_character, max_char) {
}
content.push(char); //inserts character at-a-time (or string)
} while (char !== '>');
var tag_complete = content.join('');
var tag_index;
if (tag_complete.indexOf(' ') != -1) { //if there's whitespace, thats where the tag name ends
@ -257,9 +257,9 @@ function style_html(html_source, indent_size, indent_character, max_char) {
}
return content.join(''); //returns fully formatted tag
}
this.get_unformatted = function (delimiter, orig_tag) { //function to return unformatted content in its entirety
if (orig_tag && orig_tag.indexOf(delimiter) != -1) {
return '';
}
@ -267,11 +267,11 @@ function style_html(html_source, indent_size, indent_character, max_char) {
var content = '';
var space = true;
do {
char = this.input.charAt(this.pos);
this.pos++
if (this.Utils.in_array(char, this.Utils.whitespace)) {
if (!space) {
this.line_char_count--;
@ -290,21 +290,22 @@ function style_html(html_source, indent_size, indent_character, max_char) {
content += char;
this.line_char_count++;
space = true;
} while (content.indexOf(delimiter) == -1);
return content;
}
this.get_token = function () { //initial handler for token-retrieval
var token;
if (this.last_token === 'TK_TAG_SCRIPT') { //check if we need to format javascript
var temp_token = this.get_script();
if (typeof temp_token !== 'string') {
return temp_token;
}
token = js_beautify(temp_token, this.indent_size, this.indent_character, this.indent_level); //call the JS Beautifier
token = js_beautify(temp_token,
{indent_size: this.indent_size, indent_char: this.indent_character, indent_level: this.indent_level}); //call the JS Beautifier
return [token, 'TK_CONTENT'];
}
if (this.current_mode === 'CONTENT') {
@ -316,7 +317,7 @@ function style_html(html_source, indent_size, indent_character, max_char) {
return [token, 'TK_CONTENT'];
}
}
if(this.current_mode === 'TAG') {
token = this.get_tag();
if (typeof token !== 'string') {
@ -328,9 +329,9 @@ function style_html(html_source, indent_size, indent_character, max_char) {
}
}
}
this.printer = function (js_source, indent_character, indent_size, max_char) { //handles input/output and some other printing functions
this.input = js_source || ''; //gets the input for the Parser
this.output = [];
this.indent_character = indent_character || ' ';
@ -339,11 +340,11 @@ function style_html(html_source, indent_size, indent_character, max_char) {
this.indent_level = 0;
this.max_char = max_char || 70; //maximum amount of characters per line
this.line_char_count = 0; //count to see if max_char was exceeded
for (var i=0; i<this.indent_size; i++) {
this.indent_string += this.indent_character;
}
this.print_newline = function (ignore, arr) {
this.line_char_count = 0;
if (!arr || !arr.length) {
@ -359,16 +360,16 @@ function style_html(html_source, indent_size, indent_character, max_char) {
arr.push(this.indent_string);
}
}
this.print_token = function (text) {
this.output.push(text);
}
this.indent = function () {
this.indent_level++;
}
this.unindent = function () {
if (this.indent_level > 0) {
this.indent_level--;
@ -377,25 +378,25 @@ function style_html(html_source, indent_size, indent_character, max_char) {
}
return this;
}
/*_____________________--------------------_____________________*/
multi_parser = new Parser(); //wrapping functions Parser
multi_parser.printer(html_source, indent_character, indent_size); //initialize starting values
while (true) {
var t = multi_parser.get_token();
multi_parser.token_text = t[0];
multi_parser.token_type = t[1];
if (multi_parser.token_type === 'TK_EOF') {
break;
}
switch (multi_parser.token_type) {
case 'TK_TAG_START': case 'TK_TAG_SCRIPT': case 'TK_TAG_STYLE':

View File

@ -5,6 +5,7 @@ var test_result = '';
var indent_size = 4;
var indent_char = ' ';
var preserve_newlines = true;
function lazy_escape(str)
{
@ -15,7 +16,7 @@ function bt(input, expected)
{
expected = expected || input;
result = js_beautify(input, indent_size, indent_char);
result = js_beautify(input, {indent_size:indent_size, indent_char:indent_char, preserve_newlines:preserve_newlines});
if (result != expected) {
test_result +=
@ -145,14 +146,21 @@ function test_js_beautify()
indent_size = 1;
indent_char = ' ';
bt('{ one_char() }', "{\n one_char()\n}")
bt('{ one_char() }', "{\n one_char()\n}");
indent_size = 4;
indent_char = ' ';
bt('{ one_char() }', "{\n one_char()\n}")
bt('{ one_char() }', "{\n one_char()\n}");
indent_size = 1;
indent_char = "\t";
bt('{ one_char() }', "{\n\tone_char()\n}")
bt('{ one_char() }', "{\n\tone_char()\n}");
preserve_newlines = false;
bt('var\na=dont_preserve_newlines', 'var a = dont_preserve_newlines');
preserve_newlines = true;
bt('var\na=do_preserve_newlines', 'var\na = do_preserve_newlines');
return results();
}

View File

@ -6,7 +6,7 @@
$Revision$
Written by Einars "elfz" Lielmanis, <elfz@laacz.lv>
Written by Einars Lielmanis, <einars@gmail.com>
http://elfz.laacz.lv/beautify/
Originally converted to javascript by Vital, <vital76@gmail.com>
@ -17,16 +17,39 @@
Usage:
js_beautify(js_source_text);
js_beautify(js_source_text, options);
The options are:
indent_size (default 4) indentation size,
indent_char (default space) character to indent with,
preserve_newlines (default true) whether existing line breaks should be preserved,
indent_level (default 0) initial indentation level, you probably won't need this ever,
e.g
js_beautify(js_source_text, {indent_size: 1, indent_char: '\t'});
*/
function js_beautify(js_source_text, indent_size, indent_character, indent_level)
function js_beautify(js_source_text, options)
{
var input, output, token_text, last_type, last_text, last_word, current_mode, modes, indent_string;
var whitespace, wordchar, punct, parser_pos, line_starters, in_case;
var prefix, token_type, do_block_just_closed, var_line, var_line_tainted, if_line_flag;
var indent_level;
var options = options || {};
var opt_indent_size = options['indent_size'] || 4;
var opt_indent_char = options['indent_char'] || ' ';
var opt_preserve_newlines =
typeof options['preserve_newlines'] === 'undefined' ? true : options['preserve_newlines'];
var opt_indent_level = options['indent_level'] || 0; // starting indentation
function trim_output()
{
@ -138,12 +161,16 @@ function js_beautify(js_source_text, indent_size, indent_character, indent_level
}
while (in_array(c, whitespace));
if (n_newlines > 1) {
for (var i = 0; i < 2; i++) {
print_newline(i === 0);
var wanted_newline = false;
if (opt_preserve_newlines) {
if (n_newlines > 1) {
for (var i = 0; i < 2; i++) {
print_newline(i === 0);
}
}
wanted_newline = (n_newlines === 1);
}
var wanted_newline = (n_newlines === 1);
if (in_array(c, wordchar)) {
@ -287,14 +314,13 @@ function js_beautify(js_source_text, indent_size, indent_character, indent_level
//----------------------------------
indent_character = indent_character || ' ';
indent_size = indent_size || 4;
indent_string = '';
while (indent_size--) {
indent_string += indent_character;
while (opt_indent_size--) {
indent_string += opt_indent_char;
}
indent_level = opt_indent_level;
input = js_source_text;
last_word = ''; // last 'TK_WORD' passed
@ -303,8 +329,8 @@ function js_beautify(js_source_text, indent_size, indent_character, indent_level
output = [];
do_block_just_closed = false;
var_line = false;
var_line_tainted = false;
var_line = false; // currently drawing var .... ;
var_line_tainted = false; // false: var a = 5; true: var a = 5, b = 6
whitespace = "\n\r\t ".split('');
wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split('');
@ -318,8 +344,7 @@ function js_beautify(js_source_text, indent_size, indent_character, indent_level
current_mode = 'BLOCK';
modes = [current_mode];
indent_level = indent_level || 0;
parser_pos = 0; // parser position
parser_pos = 0;
in_case = false; // flag for parser that case/default has been processed, and next colon needs special attention
while (true) {
var t = get_next_token(parser_pos);

View File

@ -18,17 +18,21 @@ window.onload = function() {
function do_js_beautify()
{
document.getElementById('beautify').disabled = true;
js_source = document.getElementById('content').value.replace(/^\s+/, '');
tabsize = document.getElementById('tabsize').value;
tabchar = ' ';
if (tabsize == 1) {
tabchar = '\t';
var js_source = document.getElementById('content').value.replace(/^\s+/, '');
var indent_size = document.getElementById('tabsize').value;
var indent_char = ' ';
var preserve_newlines = document.getElementById('preserve-newlines').checked;
if (indent_size == 1) {
indent_char = '\t';
}
if (js_source && js_source[0] === '<') {
document.getElementById('content').value = style_html(js_source, tabsize, tabchar, 80);
document.getElementById('content').value = style_html(js_source, tabsize, indent_char, 80);
} else {
document.getElementById('content').value = js_beautify(js_source, tabsize, tabchar);
document.getElementById('content').value =
js_beautify(js_source, {indent_size: indent_size, indent_char: indent_char, preserve_newlines:preserve_newlines});
}
document.getElementById('beautify').disabled = false;
@ -66,6 +70,8 @@ var latest_changes=new Object({'2008-12-04':'Moved to github.com','2008-02-22':'
<option value="4" selected="selected">indent with 4 spaces</option>
<option value="8">indent with 8 spaces</option>
</select>
<input type="checkbox" id="preserve-newlines" /><label for="preserve-newlines"> Preserve existing line
breaks?</label>
<p>You can always see the latest version of the code in github — <a
href="http://github.com/einars/js-beautify">http://github.com/einars/js-beautify</a>.</p>
<p>If you're writing javascript, <a href="http://jslint.com/">JSLint</a> is a really fine piece of software, too. You should at least understand what and why it says about your code &mdash; to be a better person. Even if it hurts your feelings.</p>