用JAVA写了一个简单的JS代码格式化工具
Section I: 为什么写这个小玩意
1. 手上有些JS代码,因为某种原因,起代码中没有任何回车、制
符。在编辑器中打开时,显示的就是一行。而我实现想研究和学习这些写得比较漂亮的JS 代码,
2. 可是好想没有哪个编辑器自带了JS格式化的功能。它们都可以给JS着色,但就是不能控制代码格式,真是遗憾。
3. 我是我去网上搜看有没有前人做过这个东东,发现有个JS写的JS代码格式化工具,相当不错,可以给代码格式化,还可以着色。只不过结果都是显示在网页上,复制下来的时候那些制表符都没有了,所以贴到编辑器中时,代码还是比较混乱;而且这个每次只能格式话一个文件,还得自己手动去Copy & paste;而且因为是JS写的,速度也受到了不小的限制;
基于以上原因,我准备自己些一个小工具来格式化我需要的代码:
Section II: 功能
1. 因为很多编辑器都有JS的着色功能,所一我需要的仅仅是一个格式化功能而已,那就是主要将那种故意将代码中的空白符去掉的的JS加上换行、制表符等等。
大概有一下几种情况:
1). add /n/n before "function";
2). add /n after ";" (Except "for", and ";" between " " which is part of a string in javascript. ) and /t before the next line;
3). add "/n" and "/t" after "{";
4). add "/n" and "/t" before "}".
2. 不仅可以格式话一个给定的文件,还可以格式化给定的一个文件夹里的所有的JS文件。这个就是通过传给Main函数的参数来实现的。参数可以如下:
1)格式化一个文件:
-f path/file.js
或者:
--file path/file.js
2) 格式化给定的一个文件夹里的所有的JS文件
-d path/directory
或者:
--directory path/directory
举例:要格式化/home/newweapon/js/ 这个文件夹中的所有JS文件,可一使用如下命令:
Jsformatter -d /home/newweapon/js
Section III 缺陷
目前写的这个程序还是一个比较脆弱的程序。有很多情况没有考虑到,我在注释中把我想到的需要提高的基本都提了一下。但是因为我写这个小玩意主要是想把
我自己下的一些JS代码格式化一下,而这个对我来说已经够用了,再加上自己的水平有限,所以没有将它写得很完美。等以后有时间了,再对这个程序做一些更新。
Section IV 代码
/**
* cn.newweapon.Jsformatter
*/
package cn.newweapon;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.list;
import java.util.regex.Pattern;
/**
* @author Newweapon @ ustc [Email: newweapon111 (at) gmail.com]
* @version 0.0.1
* @Created 20071015
*
* @TODO 1. user can specify the formatted file name;
* 2. User can located the formatted files to a specified fo lder.
* 3. If a file is formatted partly already. Delete all blan k characters first, and then format the file.
*/
public class JsFormatter {
/* ============ constants begin =================== */
/** Usage */
public static String USAGE = "Usage: JsFormatter -d path/direct ory JsFormatter -f path/filename.js";
/** Type: Directory = "0"; File = "1" */
public interface Type {
/** Directory: 0 */
public static String DIRECTORY = "0";
/** File: 1 */
public static String FILE = "1";
}
/* ============ constants end =================== */
/**
* Entry point of the project.
*
* @param args Like "-d /path/directory" or "-f /path/file.js" */
public static void main(String[] args) {
String startMsg = "Processing...";
String finishMsg = "Finished.";
// Parameters check.
if(args.length != 2) {
System.err.println(USAGE);
return;
}
// get the two parameters.
String type = args[0];
String path = args[1];
// Parameters check.
if("-d".equals(type) || "--directory".equals(type)) {
type = Type.DIRECTORY;
} else if("-f".equals(type) || "--file".equals(type)) {
type = Type.FILE;
} else {
System.err.println(USAGE);
return;
}
// Check file type
if(Type.FILE.equals(type)) {
if(path.length() <= 3 || !isJsFile(path)) {
System.err.println("The file must be a JS file.");
return;
}
}
// Start message
System.out.println(startMsg);
// format file(s)
try {
if(Type.FILE.equals(type)) {
formatFile(path);
} else {
List
jsFilelist = getJsFileList(path);
Iterator it = jsFilelist.iterator();
while(it.hasNext()) {
formatFile(it.next());
}
finishMsg += " (" + jsFileList.size() + " file(s) fo rmatted)";
}
} catch (Exception e) {
e.printStackTrace();
return;
}
// Finish message
System.out.println(finishMsg);
}
/**
* format a JS file.
*
* @param fileName The file name of the file which is to be form atted.
* @return String The formatted string.
* @throws IOException Exception when open, read and write file. */
private static void formatFile(String fileName) throws IOExcepti on {
String formattedFileName = fileName + ".formatted";
FileReader fr = new FileReader(fileName);
FileWriter fw = new FileWriter(formattedFileName);
String lastWord = "";
int forCount = 0;
int quoteCount = 0;
int sigleQuoteCount = 0;
int bracketCount = 0;
int thisChar = 0;
int lastChar = 0;
int nextChar = 0;
thisChar = fr.read();
if(thisChar != -1) {
nextChar = fr.read();
}
while(thisChar != -1) {
// find and replace
switch(thisChar) {
// 2. add after ";" (Except "for", and ";" between " " which is part of a string in javascript. ) and before the next li ne
case ';':
// If the ";" is in quote or in "for", then not print " "
if(quoteCount > 0 || sigleQuoteCount > 0 || forC ount > 0) {
fw.write(';');
if(forCount > 0) {
forCount--;
}
// Add " " after ";"
} else {
fw.write(';');
if(' ' != nextChar && ' ' != nextChar) {
fw.write(' ');
fillTableChar(fw, bracketCount);
}
}
break;
case '{': // 3. add " " and " " after "{"
bracketCount++;
fw.write('{');
if(' ' != nextChar && '
' != nextChar) { // If the file is already formatted, don't add af ter {.
fw.write(' ');
fillTableChar(fw, bracketCount);
}
break;
case '}': // 4. add " " and " " before "}"
bracketCount--;
fw.write(' ');
fillTableChar(fw, bracketCount);
fw.write('}');
if(';' != nextChar && '}' != nextChar && '
' != nextChar && ' ' != nextChar) {
fw.write(' ');
fillTableChar(fw, bracketCount);
}
break;
case ''':
fw.write(''');
if(quoteCount == 0) { //When ' is not between "" , change its state.
sigleQuoteCount = sigleQuoteCount == 0 ? 1 : 0;
}
break;
case '"':
fw.write('"');
if(sigleQuoteCount == 0) { //When ' is not betwe en "", change its state.
quoteCount = quoteCount == 0 ? 1 :0;
}
break;
case 'f': // 1. add before "function"
if(nextChar == 'u' && lastChar != '=') { // TODO This is a very weak way to determine whether this coming word is "f unction", so it is need to be fixed.
fw.write(' ');
fw.write(' ');
}
fw.write('f');
break;
default:
fw.write(thisChar);
break;
}
if(isAlpha(thisChar)) {
if(!isAlpha(lastChar)) {
lastWord = "";
}
lastWord += String.valueOf(thisChar);
} else {
if(isAlpha(lastChar)) {
if("102111114".equals(lastWord)) { // "for"
forCount = 2;
}
//TODO Whether is is suitable here to determine "function" and add " " before it?
} else {
lastWord = String.valueOf(thisChar);
}
}
lastChar = thisChar;
thisChar = nextChar;
if(thisChar != -1) {
nextChar = fr.read();
}
}
// close the files
fw.close();
fr.close();
}
/**
* Find all JS files in the specified directory.
*
* @param directory The directory in which the files to be Liste d.
* @return list The JS file list.
*/
private static List getJsFileList(String directory) {
List jsFileList = new ArrayList();
list(directory, jsFileList);
return jsFileList;
}
/**
* List all the JS files in the specified directory recursively. *
* @param path The path to be recursively searched for JS files. * @param result The path and file list
*/
private static void list(String path, List result) {
File f = new File(path);
if(f.isDirectory()) {
File[] fileList = f.listFiles();
for(int i = 0; i < fileList.length; i++) {
List(filelist[i].getPath(), result);
}
} else {
if(isJsFile(f.getName())) {
result.add(f.getPath());
}
}
}
/**
* Determine whether the the specified file is a JS file.
*
* @param fileName
* @return True: is a JS file; False: not a JS file.
*/
private static boolean isJsFile(String fileName) {
//TODO use pattern!!!
return ".js".equals(fileName.substring(fileName.length() - 3 ));
}
/**
* List all JS files in the specified directory(Not in their sub -directory).
*
* @param dir The specified directory.
* @return String[] The JS file list.
*/
private static String[] getSingleDirJsFileList(final String dir) {
String[] jsFileList;
File path = new File(dir);
jsFileList = path.list(new FilenameFilter() {
private Pattern pattern = Pattern.compile(".js");
public boolean accept(File dir, String name) {
return pattern.matcher(new File(name).getName()).mat ches();
}
});
return jsFileList;
}
/**
* Check whether the character is an alpha char.
* Actually, the words exist in a function name would no
t be limit among those we List below.
* This need to be fixed.
*
* @param c The char to be checked.
* @return boolean True: is alpha char; False: is not alpha char. */
private static boolean isAlpha(int c) {
return ((c > 'a' && c < 'z') || (c > 'A' && c < 'Z') || (c > '0' && c < '9'));
}
/**
* Fill specified number of ' '
*
* @param fw FileWriter
* @param charNum Specified number of ' '
* @throws IOException Exception when writing file
*/
private static void fillTableChar(FileWriter fw, int charNum) th rows IOException {
for(int i = 0; i < charNum; i++) {
fw.write(' ');
}
}
}