UNPKG

coffee-fmt

Version:

a `gofmt` inspired Coffeescript formatter/beautifier.

238 lines (231 loc) 6.94 kB
### # Just nobody look. # At this, what a disaster! # I blame Coffeescript. ### {Lexer} = require('./lexer') INDENT = "INDENT" OUTDENT = "OUTDENT" TERMINATOR = "TERMINATOR" HERECOMMENT = "HERECOMMENT" COMMENT = "COMMENT" IDENTIFIER = "IDENTIFIER" NUMBER = "NUMBER" STRING = "STRING" PADDED_LR_TYPES = [ "UNARY", "LOGIC", "SHIFT", "COMPARE", "COMPOUND_ASSIGN", "MATH", "RELATION", "FORIN", "FOROF", "INSTANCEOF", "UNLESS", "IF", "THEN", "ELSE", "OWN", "WHEN", "LEADING_WHEN", "=", "->", "FOR", "BY", "RETURN", "UNDEFINED" "NULL" "THROW", "WHILE", "SWITCH", "+", "CLASS" ":" "-" ] fmt = (code, options) -> lexer = new Lexer() tokens = lexer.tokenize code, { rewrite: false, verbose: options.verbose } formatted_code = "" CURR_INDENT = "" CURR_LINE = 0 INDENT_SIZE = options.tab.length comments = [] tokens.unshift [ 'PROGRAM', '' , first_line: 0 first_column: 0 last_line: 0 last_column: 0 ] for i in [0..tokens.length - 1] by 1 do (i) -> # console.log tokens[i], i # FOO BAR BAZ BEEP BOOP XXX XXX XXX ABCDEFGHIJKLMNOPQRSTUVWXZY 01234567890 ABCDEFGHIJKLMNOPQRSTUVWXYZ 01234567890 token = tokens[i] CURR_INDENT = if token[0] is INDENT CURR_INDENT + options.tab else if token[0] is OUTDENT CURR_INDENT.slice(0, -INDENT_SIZE) else CURR_INDENT # console.log("CURR_INDENT IS:" + CURR_INDENT.length); if token[0] is INDENT or token[0] is OUTDENT return if token[0] is HERECOMMENT if tokens[i - 1][1] is "\n" or token[2].first_line isnt token[2].last_line comments.push { type: HERECOMMENT text: token[1].trim() token: token index: i } CURR_LINE = token[2].first_line return else if formatted_code.length and not (formatted_code.charAt(formatted_code.length - 1).match(/\s/)) formatted_code += " " else formatted_code = formatted_code.trim() formatted_code += " " formatted_code += "### " token[1] = token[1].trim() + " ###" if token[0] is COMMENT # if token[1][0] is "\n" if true if (token[1].split("#")[0].indexOf("\n") is -1) and formatted_code.length # console.log true inline = token[1].split("\n").shift() if formatted_code.length and not (formatted_code.charAt(formatted_code.length - 1).match(/\s/)) formatted_code += " " else formatted_code = formatted_code.trim() formatted_code += " " formatted_code += inline.trim().replace(/^#(\s)*/, "# ") token[1] = token[1].split("\n").slice(1).join("\n") comments.push { type: COMMENT text: token[1] token: token index: i } CURR_LINE = token[2].first_line return if token[2].first_line > (CURR_LINE) formatted_code += "\n" + CURR_INDENT CURR_LINE = token[2].first_line if token.generated return if token[0] is TERMINATOR return if token[0] is "PROGRAM" return # console.log(comments) for j in [0..comments.length - 1] by 1 do (j) -> # console.log ("$" + comments[j].text + "$") if comments[j].text.length is 0 return if comments[j].type is COMMENT tmp = comments[j].text.split("\n") if (tmp.length) if formatted_code.slice(-2) != "\n\n" formatted_code += "\n" + CURR_INDENT bool = false lastLineWasBlank = false tmp.forEach (line, index) -> text = line.split("#"); text = text[1] or text[0]; bool = bool or text.trim() if bool isBlankLine = not text.trim() if lastLineWasBlank and isBlankLine formatted_code = formatted_code.slice(0, formatted_code.lastIndexOf("\n")) formatted_code = formatted_code.slice(0, formatted_code.lastIndexOf("\n")+1) if true formatted_code += "# " + text.trim() formatted_code += "\n" + CURR_INDENT else formatted_code += "\n" + CURR_INDENT lastLineWasBlank = isBlankLine if lastLineWasBlank formatted_code = formatted_code.slice(0, formatted_code.lastIndexOf("\n")) formatted_code = formatted_code.slice(0, formatted_code.lastIndexOf("\n")+1) formatted_code += CURR_INDENT else if comments[j].type is HERECOMMENT if comments[j].token.first_line is comments[j].token.last_line and comments[j].token.first_line is tokens[comments[j].index - 1].last_line and tokens[comments[j].index - 1][1] isnt "\n" formatted_code = formatted_code.slice(0, formatted_code.lastIndexOf("\n")) if formatted_code.length and not (formatted_code.charAt(formatted_code.length - 1).match(/\s/)) formatted_code += " " formatted_code += "###\n" + CURR_INDENT tmp = comments[j].text.split("\n") tmp.forEach (line) -> formatted_code += CURR_INDENT + line.trim() + "\n" + CURR_INDENT formatted_code += "###\n" + CURR_INDENT comments = [] tmp = "" # Tokens that should always* have whitespace pads on left and right if token[0] in PADDED_LR_TYPES if not formatted_code.charAt(formatted_code.length - 1).match(/\s/) tmp += " " if token[1] is "==" tmp += "is" else if token[1] is "!=" tmp += "isnt" else if token[1] is "!" tmp += "not" else if token[1] is "&&" tmp += "and" else if token[1] is "||" tmp += "or" else if token[1] is "||=" tmp += "or=" else if token[1] is "&&=" tmp += "and=" else tmp += token[1] else if token[0] in ["@"] if not formatted_code.charAt(formatted_code.length - 1).match(/\s/) tmp += " " tmp += token[1] else if token[0] is "," if tokens[i - 1][0] is "IDENTIFIER" or tokens[i - 1][0] is "NUMBER" formatted_code = formatted_code.trim() tmp += token[1] else if token[0] is "IDENTIFIER" if tokens[i - 1][0] is "," or tokens[i - 1][0] is "IDENTIFIER" or formatted_code.charAt(formatted_code.length - 1) is ")" tmp += " " tmp += token[1] else if token[0] is "STRING" if tokens[i - 1][0] is "," or tokens[i - 1][0] is "IDENTIFIER" tmp += " " tmp += token[1] else if token[0] is "NUMBER" if tokens[i - 1][0] is "," or tokens[i - 1][0] is "IDENTIFIER" tmp += " " tmp += token[1] # else if token[0] is ":" # if tokens[i - 1][0] is IDENTIFIER or tokens[i - 1][0] is STRING # tmp += token[1] + " " else tmp = token[1] if tokens[i - 1][0] in PADDED_LR_TYPES if not formatted_code.charAt(formatted_code.length - 1).match(/\s/) and not tmp.charAt(0).match(/\s/) tmp = " " + tmp if tokens[i - 1][0] is "," and tmp.charAt(0) isnt " " tmp = " " + tmp # console.log "tmp ", tmp formatted_code += tmp if true and formatted_code.slice(-options.newLine.length) isnt options.newLine formatted_code += options.newLine return formatted_code module.exports.format = fmt