public JsBeautify(string js_source_text, JsBeautifyOptions options) { opt_indent_size = options.indent_size ?? 4; opt_indent_char = options.indent_char ?? ' '; opt_indent_level = options.indent_level ?? 0; opt_preserve_newlines = options.preserve_newlines ?? true; output = new StringBuilder(); modes = new Stack <string>(); indent_string = ""; while (opt_indent_size > 0) { indent_string += opt_indent_char; opt_indent_size -= 1; } indent_level = opt_indent_level; input = js_source_text.Replace("<script type=\"text/javascript\">", "").Replace("</script>", ""); if (input.Length != js_source_text.Length) { output.AppendLine("<script type=\"text/javascript\">"); add_script_tags = true; } last_word = ""; // last 'TK_WORD' passed last_type = "TK_START_EXPR"; // last token type last_text = ""; // last token text do_block_just_closed = 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 "; wordchar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$"; digits = "0123456789"; // <!-- is a special case (ok, it's a minor hack actually) punct = "+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::".Split(' '); // words which should always start on new line. line_starters = "continue,try,throw,return,var,if,switch,case,default,for,while,break,function".Split(','); // states showing if we are currently in expression (i.e. "if" case) - 'EXPRESSION', or in usual block (like, procedure), 'BLOCK'. // some formatting depends on that. current_mode = "BLOCK"; modes.Push(current_mode); parser_pos = 0; in_case = false; while (true) { var t = get_next_token(ref parser_pos); token_text = t[0]; token_type = t[1]; if (token_type == "TK_EOF") { break; } switch (token_type) { case "TK_START_EXPR": var_line = false; set_mode("EXPRESSION"); if ((last_text == ";") || (last_type == "TK_START_BLOCK")) { print_newline(null); } else if ((last_type == "TK_END_EXPR") || (last_type == "TK_START_EXPR")) { // do nothing on (( and )( and ][ and ]( .. } else if ((last_type != "TK_WORD") && (last_type != "TK_OPERATOR")) { print_space(); } else if (line_starters.Contains(last_word)) { print_space(); } print_token(); break; case "TK_END_EXPR": print_token(); restore_mode(); break; case "TK_START_BLOCK": if (last_word == "do") { set_mode("DO_BLOCK"); } else { set_mode("BLOCK"); } if ((last_type != "TK_OPERATOR") && (last_type != "TK_START_EXPR")) { if (last_type == "TK_START_BLOCK") { print_newline(null); } else { print_space(); } } print_token(); indent(); break; case "TK_END_BLOCK": if (last_type == "TK_START_BLOCK") { // nothing trim_output(); unindent(); } else { unindent(); print_newline(null); } print_token(); restore_mode(); break; case "TK_WORD": if (do_block_just_closed) { // do {} ## while () print_space(); print_token(); print_space(); do_block_just_closed = false; break; } if ((token_text == "case") || (token_text == "default")) { if (last_text == ":") { // switch cases following one another remove_indent(); } else { // case statement starts in the same line where switch unindent(); print_newline(null); indent(); } print_token(); in_case = true; break; } prefix = "NONE"; if (last_type == "TK_END_BLOCK") { if (!(new string[] { "else", "catch", "finally" }).Contains(token_text.ToLower())) { prefix = "NEWLINE"; } else { prefix = "SPACE"; print_space(); } } else if ((last_type == "TK_SEMICOLON") && ((current_mode == "BLOCK") || (current_mode == "DO_BLOCK"))) { prefix = "NEWLINE"; } else if ((last_type == "TK_SEMICOLON") && (current_mode == "EXPRESSION")) { prefix = "SPACE"; } else if (last_type == "TK_STRING") { prefix = "NEWLINE"; } else if (last_type == "TK_WORD") { prefix = "SPACE"; } else if (last_type == "TK_START_BLOCK") { prefix = "NEWLINE"; } else if (last_type == "TK_END_EXPR") { print_space(); prefix = "NEWLINE"; } if ((last_type != "TK_END_BLOCK") && ((new string[] { "else", "catch", "finally" }).Contains(token_text.ToLower()))) { print_newline(null); } else if ((line_starters.Contains(token_text)) || (prefix == "NEWLINE")) { if (last_text == "else") { // no need to force newline on else break print_space(); } else if (((last_type == "TK_START_EXPR") || (last_text == "=") || (last_text == ",")) && (token_text == "function")) { // no need to force newline on "function": (function // DONOTHING } else if ((last_type == "TK_WORD") && ((last_text == "return") || (last_text == "throw"))) { // no newline between "return nnn" print_space(); } else if (last_type != "TK_END_EXPR") { if (((last_type != "TK_START_EXPR") || (token_text != "var")) && (last_text != ":")) { // no need to force newline on "var": for (var x = 0...) if ((token_text == "if") && (last_type == "TK_WORD") && (last_word == "else")) { // no newline for } else if { print_space(); } else { print_newline(null); } } } else { if ((line_starters.Contains(token_text)) && (last_text != ")")) { print_newline(null); } } } else if (prefix == "SPACE") { print_space(); } print_token(); last_word = token_text; if (token_text == "var") { var_line = true; var_line_tainted = false; } if (token_text == "if" || token_text == "else") { if_line_flag = true; } break; case "TK_SEMICOLON": print_token(); var_line = false; break; case "TK_STRING": if ((last_type == "TK_START_BLOCK") || (last_type == "TK_END_BLOCK") || (last_type == "TK_SEMICOLON")) { print_newline(null); } else if (last_type == "TK_WORD") { print_space(); } print_token(); break; case "TK_OPERATOR": var start_delim = true; var end_delim = true; if (var_line && (token_text != ",")) { var_line_tainted = true; if (token_text == ":") { var_line = false; } } if (var_line && (token_text == ",") && (current_mode == "EXPRESSION")) { // do not break on comma, for(var a = 1, b = 2) var_line_tainted = false; } if (token_text == ":" && in_case) { print_token(); // colon really asks for separate treatment print_newline(null); in_case = false; break; } if (token_text == "::") { // no spaces around exotic namespacing syntax operator print_token(); break; } if (token_text == ",") { if (var_line) { if (var_line_tainted) { print_token(); print_newline(null); var_line_tainted = false; } else { print_token(); print_space(); } } else if (last_type == "TK_END_BLOCK") { print_token(); print_newline(null); } else { if (current_mode == "BLOCK") { print_token(); print_newline(null); } else { // EXPR od DO_BLOCK print_token(); print_space(); } } break; } else if ((token_text == "--") || (token_text == "++")) { // unary operators special case if (last_text == ";") { if (current_mode == "BLOCK") { // { foo; --i } print_newline(null); start_delim = true; end_delim = false; } else { // space for (;; ++i) start_delim = true; end_delim = false; } } else { if (last_text == "{") { // {--i print_newline(null); } start_delim = false; end_delim = false; } } else if (((token_text == "!") || (token_text == "+") || (token_text == "-")) && ((last_text == "return") || (last_text == "case"))) { start_delim = true; end_delim = false; } else if (((token_text == "!") || (token_text == "+") || (token_text == "-")) && (last_type == "TK_START_EXPR")) { // special case handling: if (!a) start_delim = false; end_delim = false; } else if (last_type == "TK_OPERATOR") { start_delim = false; end_delim = false; } else if (last_type == "TK_END_EXPR") { start_delim = true; end_delim = true; } else if (token_text == ".") { // decimal digits or object.property start_delim = false; end_delim = false; } else if (token_text == ":") { if (is_ternary_op()) { start_delim = true; } else { start_delim = false; } } if (start_delim) { print_space(); } print_token(); if (end_delim) { print_space(); } break; case "TK_BLOCK_COMMENT": print_newline(null); print_token(); print_newline(null); break; case "TK_COMMENT": // print_newline(); print_space(); print_token(); print_newline(null); break; case "TK_UNKNOWN": print_token(); break; } last_type = token_type; last_text = token_text; } }
public JsBeautify(string js_source_text, JsBeautifyOptions options) { opt_indent_size = options.indent_size ?? 4; opt_indent_char = options.indent_char ?? ' '; opt_indent_level = options.indent_level ?? 0; opt_preserve_newlines = options.preserve_newlines ?? true; output = new StringBuilder(); modes = new Stack<string>(); indent_string = ""; while (opt_indent_size > 0) { indent_string += opt_indent_char; opt_indent_size -= 1; } indent_level = opt_indent_level; input = js_source_text.Replace("<script type=\"text/javascript\">", "").Replace("</script>", ""); if (input.Length != js_source_text.Length) { output.AppendLine("<script type=\"text/javascript\">"); add_script_tags = true; } last_word = ""; // last 'TK_WORD' passed last_type = "TK_START_EXPR"; // last token type last_text = ""; // last token text do_block_just_closed = 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 "; wordchar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$"; digits = "0123456789"; // <!-- is a special case (ok, it's a minor hack actually) punct = "+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::".Split(' '); // words which should always start on new line. line_starters = "continue,try,throw,return,var,if,switch,case,default,for,while,break,function".Split(','); // states showing if we are currently in expression (i.e. "if" case) - 'EXPRESSION', or in usual block (like, procedure), 'BLOCK'. // some formatting depends on that. current_mode = "BLOCK"; modes.Push(current_mode); parser_pos = 0; in_case = false; while (true) { var t = get_next_token(ref parser_pos); token_text = t[0]; token_type = t[1]; if (token_type == "TK_EOF") { break; } switch (token_type) { case "TK_START_EXPR": var_line = false; set_mode("EXPRESSION"); if ((last_text == ";") || (last_type == "TK_START_BLOCK")) { print_newline(null); } else if ((last_type == "TK_END_EXPR") || (last_type == "TK_START_EXPR")) { // do nothing on (( and )( and ][ and ]( .. } else if ((last_type != "TK_WORD") && (last_type != "TK_OPERATOR")) { print_space(); } else if (line_starters.Contains(last_word)) { print_space(); } print_token(); break; case "TK_END_EXPR": print_token(); restore_mode(); break; case "TK_START_BLOCK": if (last_word == "do") { set_mode("DO_BLOCK"); } else { set_mode("BLOCK"); } if ((last_type != "TK_OPERATOR") && (last_type != "TK_START_EXPR")) { if (last_type == "TK_START_BLOCK") { print_newline(null); } else { print_space(); } } print_token(); indent(); break; case "TK_END_BLOCK": if (last_type == "TK_START_BLOCK") { // nothing trim_output(); unindent(); } else { unindent(); print_newline(null); } print_token(); restore_mode(); break; case "TK_WORD": if (do_block_just_closed) { // do {} ## while () print_space(); print_token(); print_space(); do_block_just_closed = false; break; } if ((token_text == "case") || (token_text == "default")) { if (last_text == ":") { // switch cases following one another remove_indent(); } else { // case statement starts in the same line where switch unindent(); print_newline(null); indent(); } print_token(); in_case = true; break; } prefix = "NONE"; if (last_type == "TK_END_BLOCK") { if (!(new string[] { "else", "catch", "finally" }).Contains(token_text.ToLower())) { prefix = "NEWLINE"; } else { prefix = "SPACE"; print_space(); } } else if ((last_type == "TK_SEMICOLON") && ((current_mode == "BLOCK") || (current_mode == "DO_BLOCK"))) { prefix = "NEWLINE"; } else if ((last_type == "TK_SEMICOLON") && (current_mode == "EXPRESSION")) { prefix = "SPACE"; } else if (last_type == "TK_STRING") { prefix = "NEWLINE"; } else if (last_type == "TK_WORD") { prefix = "SPACE"; } else if (last_type == "TK_START_BLOCK") { prefix = "NEWLINE"; } else if (last_type == "TK_END_EXPR") { print_space(); prefix = "NEWLINE"; } if ((last_type != "TK_END_BLOCK") && ((new string[] { "else", "catch", "finally" }).Contains(token_text.ToLower()))) { print_newline(null); } else if ((line_starters.Contains(token_text)) || (prefix == "NEWLINE")) { if (last_text == "else") { // no need to force newline on else break print_space(); } else if (((last_type == "TK_START_EXPR") || (last_text == "=") || (last_text == ",")) && (token_text == "function")) { // no need to force newline on "function": (function // DONOTHING } else if ((last_type == "TK_WORD") && ((last_text == "return") || (last_text == "throw"))) { // no newline between "return nnn" print_space(); } else if (last_type != "TK_END_EXPR") { if (((last_type != "TK_START_EXPR") || (token_text != "var")) && (last_text != ":")) { // no need to force newline on "var": for (var x = 0...) if ((token_text == "if") && (last_type == "TK_WORD") && (last_word == "else")) { // no newline for } else if { print_space(); } else { print_newline(null); } } } else { if ((line_starters.Contains(token_text)) && (last_text != ")")) { print_newline(null); } } } else if (prefix == "SPACE") { print_space(); } print_token(); last_word = token_text; if (token_text == "var") { var_line = true; var_line_tainted = false; } if (token_text == "if" || token_text == "else") { if_line_flag = true; } break; case "TK_SEMICOLON": print_token(); var_line = false; break; case "TK_STRING": if ((last_type == "TK_START_BLOCK") || (last_type == "TK_END_BLOCK") || (last_type == "TK_SEMICOLON")) { print_newline(null); } else if (last_type == "TK_WORD") { print_space(); } print_token(); break; case "TK_OPERATOR": var start_delim = true; var end_delim = true; if (var_line && (token_text != ",")) { var_line_tainted = true; if (token_text == ":") { var_line = false; } } if (var_line && (token_text == ",") && (current_mode == "EXPRESSION")) { // do not break on comma, for(var a = 1, b = 2) var_line_tainted = false; } if (token_text == ":" && in_case) { print_token(); // colon really asks for separate treatment print_newline(null); in_case = false; break; } if (token_text == "::") { // no spaces around exotic namespacing syntax operator print_token(); break; } if (token_text == ",") { if (var_line) { if (var_line_tainted) { print_token(); print_newline(null); var_line_tainted = false; } else { print_token(); print_space(); } } else if (last_type == "TK_END_BLOCK") { print_token(); print_newline(null); } else { if (current_mode == "BLOCK") { print_token(); print_newline(null); } else { // EXPR od DO_BLOCK print_token(); print_space(); } } break; } else if ((token_text == "--") || (token_text == "++")) { // unary operators special case if (last_text == ";") { if (current_mode == "BLOCK") { // { foo; --i } print_newline(null); start_delim = true; end_delim = false; } else { // space for (;; ++i) start_delim = true; end_delim = false; } } else { if (last_text == "{") { // {--i print_newline(null); } start_delim = false; end_delim = false; } } else if (((token_text == "!") || (token_text == "+") || (token_text == "-")) && ((last_text == "return") || (last_text == "case"))) { start_delim = true; end_delim = false; } else if (((token_text == "!") || (token_text == "+") || (token_text == "-")) && (last_type == "TK_START_EXPR")) { // special case handling: if (!a) start_delim = false; end_delim = false; } else if (last_type == "TK_OPERATOR") { start_delim = false; end_delim = false; } else if (last_type == "TK_END_EXPR") { start_delim = true; end_delim = true; } else if (token_text == ".") { // decimal digits or object.property start_delim = false; end_delim = false; } else if (token_text == ":") { if (is_ternary_op()) { start_delim = true; } else { start_delim = false; } } if (start_delim) { print_space(); } print_token(); if (end_delim) { print_space(); } break; case "TK_BLOCK_COMMENT": print_newline(null); print_token(); print_newline(null); break; case "TK_COMMENT": // print_newline(); print_space(); print_token(); print_newline(null); break; case "TK_UNKNOWN": print_token(); break; } last_type = token_type; last_text = token_text; } }