style: move style verifiers into classes
This commit is contained in:
parent
2d5e1de4d9
commit
5add771e85
1 changed files with 139 additions and 111 deletions
250
util/style.py
250
util/style.py
|
@ -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):
|
||||||
|
|
Loading…
Reference in a new issue