import subprocess
import comment

class SourceFile(object):
    __slots__ = ['path', 'relpath', 'env', 'filetype', 'lines']

    def __init__(self, path, relpath, env, filetype):
        self.path = path
        self.relpath = relpath
        self.env = env
        self.filetype = filetype
        with open(path, 'r') as fp:
            self.lines = fp.readlines()

    def run_filters(self):
        objs = []
        for filter in self.filters():
            filter1 = getattr(self, filter + '_filter1')
            filter2 = getattr(self, filter + '_filter2')
            obj = filter1()
            objs.append((filter2, obj))
        objs.reverse()
        for filter2, obj in objs:
            filter2(obj)

    def filters(self):
        yield 'shebang'
        yield 'copyright'
        if self.filetype.name in ('h', 'hxx'):
            yield 'headerguard'

    def show_diff(self):
        text = ''.join(self.lines)
        proc = subprocess.Popen(
            ['diff', '-u', '--', self.path, '-'],
            stdin=subprocess.PIPE)
        proc.communicate(text)
        if proc.returncode not in (0, 1):
            raise Exception('diff returned {}'.format(proc.returncode))

    def shebang_filter1(self):
        if not self.lines or not self.lines[0].startswith('#!'):
            return None
        return self.lines.pop(0)

    def shebang_filter2(self, shebang):
        if shebang is not None:
            self.lines.insert(0, shebang)

    def headerguard_filter1(self):
        head, body = comment.extract_lead_comments(self.lines, self.filetype)
        pre, body, post = comment.remove_blank_lines(body)
        if len(body) < 3:
            return None
        if (not body[0].startswith('#ifndef') or
            not body[1].startswith('#define') or
            not body[-1].startswith('#endif')):
            return None
        self.lines = body[2:-1]
        return head

    def headerguard_filter2(self, comments):
        head = comments or []
        tail = []
        guardname = self.env['guardname']
        if self.env['guards'] and guardname:
            head.extend(['#ifndef {}\n'.format(guardname),
                         '#define {}\n'.format(guardname)])
            tail.append('#endif\n')
        if head or tail:
            self.lines = head + self.lines + tail

    def copyright_filter1(self):
        head, body = comment.extract_lead_comment(self.lines, self.filetype)
        for pre, lbody, post in head:
            if 'COPYRIGHT' in lbody.upper():
                break
        else:
            return None
        self.lines = body
        return head

    def copyright_filter2(self, val):
        if val is None:
            return
        head = [pre + lbody + post for pre, lbody, post in val]
        self.lines = head + self.lines

@@ -709,16 +709,746 @@
 2(obj)%0A%0A
+    def fix_whitespace(self):%0A        %22%22%22Fix minor whitespace issues.%0A%0A        Removes extra blank lines, trailing whitespace, and ensures%0A        that there is a line break at the end of the file.%0A        %22%22%22%0A        lines = %5B%5D%0A        blank = False%0A        for line in self.lines:%0A            line = line.rstrip()%0A            if line:%0A                if blank and lines:%0A                    lines.append('%5Cn')%0A                lines.append(line + '%5Cn')%0A                blank = False%0A            else:%0A                blank = True%0A        self.lines = lines%0A%0A    def expand_tabs(self):%0A        %22%22%22Convert tabs to spaces.%22%22%22%0A        width = self.env%5B'tabsize'%5D%0A        self.lines = %5Bline.expandtabs(width) for line in self.lines%5D%0A%0A
     def 

Add methods for fixing whitespace problems