Mercurial > public > mercurial-scm > hg-stable
diff mercurial/filesetlang.py @ 38918:e79a69af1593
fileset: insert hints where status should be computed
This will allow us to compute status against a narrowed set of files.
For example, "path:build/ & (unknown() + missing())" is rewritten as
"path:build/ & <withstatus>(unknown() + missing(), 'unknown missing')",
and the status call can be narrowed by the left-hand-side matcher,
"path:build/".
mctx.buildstatus() calls will be solely processed by getmatchwithstatus().
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sat, 21 Jul 2018 20:27:53 +0900 |
parents | 899b4c74209c |
children | 2372284d9457 |
line wrap: on
line diff
--- a/mercurial/filesetlang.py Sun Jul 22 11:12:55 2018 +0900 +++ b/mercurial/filesetlang.py Sat Jul 21 20:27:53 2018 +0900 @@ -171,6 +171,82 @@ return (op, x[1], ta) raise error.ProgrammingError('invalid operator %r' % op) +def _insertstatushints(x): + """Insert hint nodes where status should be calculated (first path) + + This works in bottom-up way, summing up status names and inserting hint + nodes at 'and' and 'or' as needed. Thus redundant hint nodes may be left. + + Returns (status-names, new-tree) at the given subtree, where status-names + is a sum of status names referenced in the given subtree. + """ + if x is None: + return (), x + + op = x[0] + if op in {'string', 'symbol', 'kindpat'}: + return (), x + if op == 'not': + h, t = _insertstatushints(x[1]) + return h, (op, t) + if op == 'and': + ha, ta = _insertstatushints(x[1]) + hb, tb = _insertstatushints(x[2]) + hr = ha + hb + if ha and hb: + return hr, ('withstatus', (op, ta, tb), ('string', ' '.join(hr))) + return hr, (op, ta, tb) + if op == 'or': + hs, ts = zip(*(_insertstatushints(y) for y in x[1:])) + hr = sum(hs, ()) + if sum(bool(h) for h in hs) > 1: + return hr, ('withstatus', (op,) + ts, ('string', ' '.join(hr))) + return hr, (op,) + ts + if op == 'list': + hs, ts = zip(*(_insertstatushints(y) for y in x[1:])) + return sum(hs, ()), (op,) + ts + if op == 'func': + f = getsymbol(x[1]) + # don't propagate 'ha' crossing a function boundary + ha, ta = _insertstatushints(x[2]) + if getattr(symbols.get(f), '_callstatus', False): + return (f,), ('withstatus', (op, x[1], ta), ('string', f)) + return (), (op, x[1], ta) + raise error.ProgrammingError('invalid operator %r' % op) + +def _mergestatushints(x, instatus): + """Remove redundant status hint nodes (second path) + + This is the top-down path to eliminate inner hint nodes. + """ + if x is None: + return x + + op = x[0] + if op == 'withstatus': + if instatus: + # drop redundant hint node + return _mergestatushints(x[1], instatus) + t = _mergestatushints(x[1], instatus=True) + return (op, t, x[2]) + if op in {'string', 'symbol', 'kindpat'}: + return x + if op == 'not': + t = _mergestatushints(x[1], instatus) + return (op, t) + if op == 'and': + ta = _mergestatushints(x[1], instatus) + tb = _mergestatushints(x[2], instatus) + return (op, ta, tb) + if op in {'list', 'or'}: + ts = tuple(_mergestatushints(y, instatus) for y in x[1:]) + return (op,) + ts + if op == 'func': + # don't propagate 'instatus' crossing a function boundary + ta = _mergestatushints(x[2], instatus=False) + return (op, x[1], ta) + raise error.ProgrammingError('invalid operator %r' % op) + def analyze(x): """Transform raw parsed tree to evaluatable tree which can be fed to optimize() or getmatch() @@ -178,7 +254,9 @@ All pseudo operations should be mapped to real operations or functions defined in methods or symbols table respectively. """ - return _analyze(x) + t = _analyze(x) + _h, t = _insertstatushints(t) + return _mergestatushints(t, instatus=False) def _optimizeandops(op, ta, tb): if tb is not None and tb[0] == 'not': @@ -205,6 +283,9 @@ return 0, x op = x[0] + if op == 'withstatus': + w, t = _optimize(x[1]) + return w, (op, t, x[2]) if op in {'string', 'symbol'}: return WEIGHT_CHECK_FILENAME, x if op == 'kindpat':