#!/usr/bin/python
# copyright Pat Beirne 2003
# GNU license
# pretty printer for Java/C/C++/Python
# align assignment statements, member declarations and function parameter lists
# usage:
# pretty_assign.py file_in.c
# pretty_assign -
# pretty_assign -h
# Ver 2: added leading space counter
# changes this:
"""
public void foo(int a,
StringBuffer b,
char c) {
int d = 12;
String e = new String(b);
int f;
DatagramSocket g = new DatagramSocket();
f = 23;
d = 12;
reallyLongVarName = 14;
"""
# into this
"""
public void foo(int a,
StringBuffer b,
char c) {
int d = 12;
String e = new String(b);
int f;
DatagramSocket g = new DatagramSocket();
f = 23;
d = 12;
reallyLongVarName = 14;
"""
VERSION = "1.1"
# type 1 function decl, with or without closing )
# type 2 member decl
# type 3 simple assignments
import sys, os
import re
##################PATTERNS#################
# no (, ending with ) or ,
# second & last line
# ^space-----not(-space-----notspace¬(-----.or)$
type1re = re.compile(r"^(\s*)([^\(]+ )([^ \t\(]+(,|\)).*)$")
# a ( early, no terminal )
# ^space-----any(any-space------not)-spaces-comma-not)$
type1are = re.compile(r"^(\s*)(.+\(.+ )([^\),]+,[^\)\n]*)$") # first line
# has at least a type, and an =
type2re = re.compile(r"^(\s*)([^\(\)]+ )(\S+ +)(=[^=].*)$")
# has a = and just a simple name on the left
type3re = re.compile(r"^(\s*)(\S+\s*)(=[^=].*)$")
# in each case, the group[1] is the indent space on the left
# group[1] is the left hand line, after the indents
# group[2] starts with the variable name or equal sign
# type 2 lines also have a group[3], which is the equal sign
############FUNCTIONS##################
def lineType(str):
"""classify a string"""
type = 0
m = type1re.match(str)
if m:
type = 1
else:
m = type1are.match(str)
if m:
type = 1
else:
m = type3re.match(str)
if m:
type = 3
else:
m = type2re.match(str)
if m:
type = 2
return type, m
############### the major subroutine ###################
def pa(data, fout):
accum = ""
accum_list = []
last_line_type = 0
last_line_indent = -1
for lin in data:
# classify
type, matches = lineType(lin)
if matches:
line_indent = len(matches.group(1))
#print type, matches and `matches.groups()`+'\n' or lin,
# printing
# a change of type or indent triggers a print
if accum_list and (last_line_type!=type or line_indent!=last_line_indent):
min = 0
min2 = 0
# generate alignment gaps
for a in accum_list:
if min<len(a[0]):
min = len(a[0])
if last_line_type==2:
for a in accum_list:
if min2<len(a[1]):
min2 = len(a[1])
for a in accum_list:
# print a
# accum += `last_line_type`+ a[0]+('@'*(min-len(a[0])))+a[1]
accum += a[0]+(' '*(min-len(a[0]))) + a[1]
if last_line_type==2:
accum += (' '*(min2-len(a[1]))) + a[2]
accum += '\n'
print >>fout,accum,
accum= ""
accum_list = []
# print non-affected files
if type==0:
print >>fout,lin,
line_indent = -1
# build the accumulator
# NOTE: the elements of accum_list[] have the first two matches glued together
if type==1 or type==3:
accum_list.append( (matches.group(1) + matches.group(2), matches.group(3)) )
if type==2:
accum_list.append( (matches.group(1) + matches.group(2),
matches.group(3), matches.group(4)) )
last_line_type = type
last_line_indent = line_indent
##############main###################
if len(sys.argv)==1:
print "pretty_align usage:"
print " pretty_align [-n] filename [other_filename....]"
print " or pretty_align -"
print " or pretty_align -h"
print ""
print " -n means don't make backup file (defaults to filename~)"
print " - means take input from stdin, output to stdout"
print " -h print help"
sys.exit(1)
if sys.argv[1] == "-h":
print "Pretty Align"
print "Pat Beirne, 2003, version " + VERSION
print ""
print "Synopsis:"
print " Adds small amounts of white space to code files, to align"
print " variables and declaration types."
print ""
print "Invoking:"
print " pretty_align file1 file2 file3...."
print ""
print " As each file is modified, a backup is made (filename~)"
print ""
print "Comments:"
print " There are three types of lines that are modified:"
print " function parameter lists that span multiple lines"
print " static, class members and function local declarations"
print " small clusters of simple assignment statements"
print ""
print " This app works well on Java, Python, C and C++ code"
print ""
print " This app does not make any changes to the left hand side"
print " of the lines, so your indenting is preserved, tabs and all"
print " This app is not aware of strings and comments, so, unfortunately"
print " it operates on strings and comments. Check your results before"
print " accepting its results"
sys.exit(1)
if sys.argv[1]=='-':
fin = sys.stdin
fout = sys.stdout
d = fin.readlines()
pa(d,fout)
sys.exit(0)
bMakeBackup = 1
nFirstArg = 1
if sys.argv[1]=='-n':
bMakeBackup = 0
nFirstArg = 2
for f in sys.argv[nFirstArg:]:
fin = open(f)
data = fin.readlines()
fin.close()
if bMakeBackup:
os.rename(f,f+'~')
fout = open(f,"w")
pa(data,fout)