style: move style verifiers into classes

This commit is contained in:
Nathan Binkert 2011-04-15 10:43:47 -07:00
parent 2d5e1de4d9
commit 5add771e85

View file

@ -1,6 +1,6 @@
#! /usr/bin/env python #! /usr/bin/env python
# Copyright (c) 2006 The Regents of The University of Michigan # Copyright (c) 2006 The Regents of The University of Michigan
# Copyright (c) 2007 The Hewlett-Packard Development Company # Copyright (c) 2007,2011 The Hewlett-Packard Development Company
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
@ -28,23 +28,59 @@
# #
# Authors: Nathan Binkert # Authors: Nathan Binkert
import re import heapq
import os import os
import re
import sys import sys
sys.path.insert(0, os.path.dirname(__file__)) from os.path import dirname, join as joinpath
from itertools import count
from mercurial import bdiff, mdiff
current_dir = dirname(__file__)
sys.path.insert(0, current_dir)
sys.path.insert(1, joinpath(dirname(current_dir), 'src', 'python'))
from m5.util import neg_inf, pos_inf, Region, Regions
from file_types import lang_type from file_types import lang_type
all_regions = Region(neg_inf, pos_inf)
tabsize = 8 tabsize = 8
lead = re.compile(r'^([ \t]+)') lead = re.compile(r'^([ \t]+)')
trail = re.compile(r'([ \t]+)$') trail = re.compile(r'([ \t]+)$')
any_control = re.compile(r'\b(if|while|for)[ \t]*[(]') any_control = re.compile(r'\b(if|while|for)[ \t]*[(]')
good_control = re.compile(r'\b(if|while|for) [(]') good_control = re.compile(r'\b(if|while|for) [(]')
whitespace_types = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons'))
format_types = set(('C', 'C++')) format_types = set(('C', 'C++'))
def modified_regions(old_data, new_data):
regions = Regions()
beg = None
for pbeg, pend, fbeg, fend in bdiff.blocks(old_data, new_data):
if beg is not None and beg != fbeg:
regions.append(beg, fbeg)
beg = fend
return regions
def modregions(wctx, fname):
fctx = wctx.filectx(fname)
pctx = fctx.parents()
file_data = fctx.data()
lines = mdiff.splitnewlines(file_data)
if len(pctx) in (1, 2):
mod_regions = modified_regions(pctx[0].data(), file_data)
if len(pctx) == 2:
m2 = modified_regions(pctx[1].data(), file_data)
# only the lines that are new in both
mod_regions &= m2
else:
mod_regions = Regions()
mod_regions.add(0, len(lines))
return mod_regions
class UserInterface(object): class UserInterface(object):
def __init__(self, verbose=False, auto=False): def __init__(self, verbose=False, auto=False):
self.auto = auto self.auto = auto
@ -77,67 +113,106 @@ class StdioUI(UserInterface):
def write(self, string): def write(self, string):
sys.stdout.write(string) sys.stdout.write(string)
def checkwhite_line(line): class Region(object):
match = lead.search(line) def __init__(self, asdf):
if match and match.group(1).find('\t') != -1: self.regions = Foo
return False
match = trail.search(line) class Verifier(object):
if match: def __init__(self, ui, repo=None):
return False self.ui = ui
self.repo = repo
if repo is None:
self.wctx = None
return True def __getattr__(self, attr):
if attr in ('prompt', 'write'):
return getattr(self.ui, attr)
def checkwhite(filename): if attr == 'wctx':
if lang_type(filename) not in whitespace_types: try:
return wctx = repo.workingctx()
except:
from mercurial import context
wctx = context.workingctx(repo)
self.wctx = wctx
return wctx
try: raise AttributeError
f = file(filename, 'r+')
except OSError, msg:
print 'could not open file %s: %s' % (filename, msg)
return
for num,line in enumerate(f): def open(self, filename, mode):
if not checkwhite_line(line): if self.repo:
yield line,num + 1 filename = self.repo.wjoin(filename)
def fixwhite_line(line): try:
if lead.search(line): f = file(filename, mode)
newline = '' except OSError, msg:
for i,c in enumerate(line): print 'could not open file %s: %s' % (filename, msg)
if c == ' ': return None
newline += ' '
elif c == '\t':
newline += ' ' * (tabsize - len(newline) % tabsize)
else:
newline += line[i:]
break
line = newline return f
return line.rstrip() + '\n' def skip(self, filename):
return lang_type(filename) not in self.languages
def fixwhite(filename, fixonly=None): def check(self, filename, regions=all_regions):
if lang_type(filename) not in whitespace_types: f = self.open(filename, 'r')
return
try: errors = 0
f = file(filename, 'r+') for num,line in enumerate(f):
except OSError, msg: if num not in regions:
print 'could not open file %s: %s' % (filename, msg) continue
return if not self.check_line(line):
self.write("invalid %s in %s:%d\n" % \
(self.test_name, filename, num + 1))
if self.ui.verbose:
self.write(">>%s<<\n" % line[-1])
errors += 1
return errors
lines = list(f) def fix(self, filename, regions=all_regions):
f = self.open(filename, 'r+')
f.seek(0) lines = list(f)
f.truncate()
for i,line in enumerate(lines): f.seek(0)
if fixonly is None or i in fixonly: f.truncate()
line = fixwhite_line(line)
print >>f, line, for i,line in enumerate(lines):
if i in regions:
line = self.fix_line(line)
f.write(line)
f.close()
class Whitespace(Verifier):
languages = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons'))
test_name = 'whitespace'
def check_line(self, line):
match = lead.search(line)
if match and match.group(1).find('\t') != -1:
return False
match = trail.search(line)
if match:
return False
return True
def fix_line(self, line):
if lead.search(line):
newline = ''
for i,c in enumerate(line):
if c == ' ':
newline += ' '
elif c == '\t':
newline += ' ' * (tabsize - len(newline) % tabsize)
else:
newline += line[i:]
break
line = newline
return line.rstrip() + '\n'
def linelen(line): def linelen(line):
tabs = line.count('\t') tabs = line.count('\t')
@ -241,22 +316,6 @@ def validate(filename, stats, verbose, exit_code):
msg(i, line, 'improper spacing after %s' % match.group(1)) msg(i, line, 'improper spacing after %s' % match.group(1))
bad() bad()
def modified_lines(old_data, new_data, max_lines):
from itertools import count
from mercurial import bdiff, mdiff
modified = set()
counter = count()
for pbeg, pend, fbeg, fend in bdiff.blocks(old_data, new_data):
for i in counter:
if i < fbeg:
modified.add(i)
elif i + 1 >= fend:
break
elif i > max_lines:
break
return modified
def do_check_style(hgui, repo, *files, **args): def do_check_style(hgui, repo, *files, **args):
"""check files for proper m5 style guidelines""" """check files for proper m5 style guidelines"""
from mercurial import mdiff, util from mercurial import mdiff, util
@ -272,30 +331,26 @@ def do_check_style(hgui, repo, *files, **args):
def skip(name): def skip(name):
return files and name in files return files and name in files
def prompt(name, func, fixonly=None): def prompt(name, func, regions=all_regions):
result = ui.prompt("(a)bort, (i)gnore, or (f)ix?", 'aif', 'a') result = ui.prompt("(a)bort, (i)gnore, or (f)ix?", 'aif', 'a')
if result == 'a': if result == 'a':
return True return True
elif result == 'f': elif result == 'f':
func(repo.wjoin(name), fixonly) func(repo.wjoin(name), regions)
return False return False
modified, added, removed, deleted, unknown, ignore, clean = repo.status() modified, added, removed, deleted, unknown, ignore, clean = repo.status()
whitespace = Whitespace(ui)
for fname in added: for fname in added:
if skip(fname): if skip(fname) or whitespace.skip(fname):
continue continue
ok = True errors = whitespace.check(fname)
for line,num in checkwhite(repo.wjoin(fname)): if errors:
ui.write("invalid whitespace in %s:%d\n" % (fname, num)) print errors
if ui.verbose: if prompt(fname, whitespace.fix):
ui.write(">>%s<<\n" % line[-1])
ok = False
if not ok:
if prompt(fname, fixwhite):
return True return True
try: try:
@ -305,41 +360,14 @@ def do_check_style(hgui, repo, *files, **args):
wctx = context.workingctx(repo) wctx = context.workingctx(repo)
for fname in modified: for fname in modified:
if skip(fname): if skip(fname) or whitespace.skip(fname):
continue continue
if lang_type(fname) not in whitespace_types: regions = modregions(wctx, fname)
continue
fctx = wctx.filectx(fname) errors = whitespace.check(fname, regions)
pctx = fctx.parents() if errors:
if prompt(fname, whitespace.fix, regions):
file_data = fctx.data()
lines = mdiff.splitnewlines(file_data)
if len(pctx) in (1, 2):
mod_lines = modified_lines(pctx[0].data(), file_data, len(lines))
if len(pctx) == 2:
m2 = modified_lines(pctx[1].data(), file_data, len(lines))
# only the lines that are new in both
mod_lines = mod_lines & m2
else:
mod_lines = xrange(0, len(lines))
fixonly = set()
for i,line in enumerate(lines):
if i not in mod_lines:
continue
if checkwhite_line(line):
continue
ui.write("invalid whitespace: %s:%d\n" % (fname, i+1))
if ui.verbose:
ui.write(">>%s<<\n" % line[:-1])
fixonly.add(i)
if fixonly:
if prompt(fname, fixwhite, fixonly):
return True return True
def do_check_format(hgui, repo, **args): def do_check_format(hgui, repo, **args):