comparison mercurial/logcmdutil.py @ 36003:fcde8946c553

logcmdutil: hold makefilematcher/makehunksfilter() by changesetpriner (API) This merges self.matchfn and self.show(matchfn) into self._makefilematcher, and does the same for hunksfilter. Because changesetprinter seems to have too many optional arguments, makefilematcher() and makehunksfilter() will be packed into one object by later patch.
author Yuya Nishihara <yuya@tcha.org>
date Sun, 21 Jan 2018 14:07:52 +0900
parents f8ad57d24252
children d4c210ee894f
comparison
equal deleted inserted replaced
36002:f8ad57d24252 36003:fcde8946c553
133 return ' '.join(labels) 133 return ' '.join(labels)
134 134
135 class changesetprinter(object): 135 class changesetprinter(object):
136 '''show changeset information when templating not requested.''' 136 '''show changeset information when templating not requested.'''
137 137
138 def __init__(self, ui, repo, matchfn=None, diffopts=None, buffered=False): 138 def __init__(self, ui, repo, makefilematcher=None, makehunksfilter=None,
139 diffopts=None, buffered=False):
139 self.ui = ui 140 self.ui = ui
140 self.repo = repo 141 self.repo = repo
141 self.buffered = buffered 142 self.buffered = buffered
142 self.matchfn = matchfn 143 self._makefilematcher = makefilematcher or (lambda ctx: None)
144 self._makehunksfilter = makehunksfilter or (lambda ctx: None)
143 self.diffopts = diffopts or {} 145 self.diffopts = diffopts or {}
144 self.header = {} 146 self.header = {}
145 self.hunk = {} 147 self.hunk = {}
146 self.lastheader = None 148 self.lastheader = None
147 self.footer = None 149 self.footer = None
161 163
162 def close(self): 164 def close(self):
163 if self.footer: 165 if self.footer:
164 self.ui.write(self.footer) 166 self.ui.write(self.footer)
165 167
166 def show(self, ctx, copies=None, matchfn=None, hunksfilterfn=None, 168 def show(self, ctx, copies=None, **props):
167 **props):
168 props = pycompat.byteskwargs(props) 169 props = pycompat.byteskwargs(props)
169 if self.buffered: 170 if self.buffered:
170 self.ui.pushbuffer(labeled=True) 171 self.ui.pushbuffer(labeled=True)
171 self._show(ctx, copies, matchfn, hunksfilterfn, props) 172 self._show(ctx, copies, props)
172 self.hunk[ctx.rev()] = self.ui.popbuffer() 173 self.hunk[ctx.rev()] = self.ui.popbuffer()
173 else: 174 else:
174 self._show(ctx, copies, matchfn, hunksfilterfn, props) 175 self._show(ctx, copies, props)
175 176
176 def _show(self, ctx, copies, matchfn, hunksfilterfn, props): 177 def _show(self, ctx, copies, props):
177 '''show a single changeset or file revision''' 178 '''show a single changeset or file revision'''
178 changenode = ctx.node() 179 changenode = ctx.node()
179 rev = ctx.rev() 180 rev = ctx.rev()
180 181
181 if self.ui.quiet: 182 if self.ui.quiet:
262 else: 263 else:
263 self.ui.write(columns['summary'] % description.splitlines()[0], 264 self.ui.write(columns['summary'] % description.splitlines()[0],
264 label='log.summary') 265 label='log.summary')
265 self.ui.write("\n") 266 self.ui.write("\n")
266 267
267 self._showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn) 268 self._showpatch(ctx)
268 269
269 def _showobsfate(self, ctx): 270 def _showobsfate(self, ctx):
270 obsfate = templatekw.showobsfate(repo=self.repo, ctx=ctx, ui=self.ui) 271 obsfate = templatekw.showobsfate(repo=self.repo, ctx=ctx, ui=self.ui)
271 272
272 if obsfate: 273 if obsfate:
276 277
277 def _exthook(self, ctx): 278 def _exthook(self, ctx):
278 '''empty method used by extension as a hook point 279 '''empty method used by extension as a hook point
279 ''' 280 '''
280 281
281 def _showpatch(self, ctx, matchfn, hunksfilterfn=None): 282 def _showpatch(self, ctx):
282 if not matchfn: 283 matchfn = self._makefilematcher(ctx)
283 matchfn = self.matchfn 284 hunksfilterfn = self._makehunksfilter(ctx)
284 if matchfn: 285 if matchfn:
285 stat = self.diffopts.get('stat') 286 stat = self.diffopts.get('stat')
286 diff = self.diffopts.get('patch') 287 diff = self.diffopts.get('patch')
287 diffopts = patch.diffallopts(self.ui, self.diffopts) 288 diffopts = patch.diffallopts(self.ui, self.diffopts)
288 node = ctx.node() 289 node = ctx.node()
301 self.ui.write("\n") 302 self.ui.write("\n")
302 303
303 class jsonchangeset(changesetprinter): 304 class jsonchangeset(changesetprinter):
304 '''format changeset information.''' 305 '''format changeset information.'''
305 306
306 def __init__(self, ui, repo, matchfn=None, diffopts=None, buffered=False): 307 def __init__(self, ui, repo, makefilematcher=None, makehunksfilter=None,
307 changesetprinter.__init__(self, ui, repo, matchfn, diffopts, buffered) 308 diffopts=None, buffered=False):
309 changesetprinter.__init__(self, ui, repo, makefilematcher,
310 makehunksfilter, diffopts, buffered)
308 self.cache = {} 311 self.cache = {}
309 self._first = True 312 self._first = True
310 313
311 def close(self): 314 def close(self):
312 if not self._first: 315 if not self._first:
313 self.ui.write("\n]\n") 316 self.ui.write("\n]\n")
314 else: 317 else:
315 self.ui.write("[]\n") 318 self.ui.write("[]\n")
316 319
317 def _show(self, ctx, copies, matchfn, hunksfilterfn, props): 320 def _show(self, ctx, copies, props):
318 '''show a single changeset or file revision''' 321 '''show a single changeset or file revision'''
319 rev = ctx.rev() 322 rev = ctx.rev()
320 if rev is None: 323 if rev is None:
321 jrev = jnode = 'null' 324 jrev = jnode = 'null'
322 else: 325 else:
377 if copies: 380 if copies:
378 self.ui.write((',\n "copies": {%s}') % 381 self.ui.write((',\n "copies": {%s}') %
379 ", ".join('"%s": "%s"' % (j(k), j(v)) 382 ", ".join('"%s": "%s"' % (j(k), j(v))
380 for k, v in copies)) 383 for k, v in copies))
381 384
382 matchfn = self.matchfn 385 matchfn = self._makefilematcher(ctx)
383 if matchfn: 386 if matchfn:
384 stat = self.diffopts.get('stat') 387 stat = self.diffopts.get('stat')
385 diff = self.diffopts.get('patch') 388 diff = self.diffopts.get('patch')
386 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True) 389 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
387 node, prev = ctx.node(), ctx.p1().node() 390 node, prev = ctx.node(), ctx.p1().node()
408 functions that use changesest_templater. 411 functions that use changesest_templater.
409 ''' 412 '''
410 413
411 # Arguments before "buffered" used to be positional. Consider not 414 # Arguments before "buffered" used to be positional. Consider not
412 # adding/removing arguments before "buffered" to not break callers. 415 # adding/removing arguments before "buffered" to not break callers.
413 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None, 416 def __init__(self, ui, repo, tmplspec, makefilematcher=None,
414 buffered=False): 417 makehunksfilter=None, diffopts=None, buffered=False):
415 changesetprinter.__init__(self, ui, repo, matchfn, diffopts, buffered) 418 changesetprinter.__init__(self, ui, repo, makefilematcher,
419 makehunksfilter, diffopts, buffered)
416 tres = formatter.templateresources(ui, repo) 420 tres = formatter.templateresources(ui, repo)
417 self.t = formatter.loadtemplater(ui, tmplspec, 421 self.t = formatter.loadtemplater(ui, tmplspec,
418 defaults=templatekw.keywords, 422 defaults=templatekw.keywords,
419 resources=tres, 423 resources=tres,
420 cache=templatekw.defaulttempl) 424 cache=templatekw.defaulttempl)
453 if not self.footer: 457 if not self.footer:
454 self.footer = "" 458 self.footer = ""
455 self.footer += templater.stringify(self.t(self._parts['docfooter'])) 459 self.footer += templater.stringify(self.t(self._parts['docfooter']))
456 return super(changesettemplater, self).close() 460 return super(changesettemplater, self).close()
457 461
458 def _show(self, ctx, copies, matchfn, hunksfilterfn, props): 462 def _show(self, ctx, copies, props):
459 '''show a single changeset or file revision''' 463 '''show a single changeset or file revision'''
460 props = props.copy() 464 props = props.copy()
461 props['ctx'] = ctx 465 props['ctx'] = ctx
462 props['index'] = index = next(self._counter) 466 props['index'] = index = next(self._counter)
463 props['revcache'] = {'copies': copies} 467 props['revcache'] = {'copies': copies}
480 self.ui.write(h) 484 self.ui.write(h)
481 485
482 # write changeset metadata, then patch if requested 486 # write changeset metadata, then patch if requested
483 key = self._parts[self._tref] 487 key = self._parts[self._tref]
484 self.ui.write(templater.stringify(self.t(key, **props))) 488 self.ui.write(templater.stringify(self.t(key, **props)))
485 self._showpatch(ctx, matchfn, hunksfilterfn=hunksfilterfn) 489 self._showpatch(ctx)
486 490
487 if self._parts['footer']: 491 if self._parts['footer']:
488 if not self.footer: 492 if not self.footer:
489 self.footer = templater.stringify( 493 self.footer = templater.stringify(
490 self.t(self._parts['footer'], **props)) 494 self.t(self._parts['footer'], **props))
527 """Create a changesettemplater from a literal template 'tmpl' 531 """Create a changesettemplater from a literal template 'tmpl'
528 byte-string.""" 532 byte-string."""
529 spec = templatespec(tmpl, None) 533 spec = templatespec(tmpl, None)
530 return changesettemplater(ui, repo, spec, buffered=buffered) 534 return changesettemplater(ui, repo, spec, buffered=buffered)
531 535
532 def changesetdisplayer(ui, repo, opts, buffered=False): 536 def changesetdisplayer(ui, repo, opts, makefilematcher=None,
537 makehunksfilter=None, buffered=False):
533 """show one changeset using template or regular display. 538 """show one changeset using template or regular display.
534 539
535 Display format will be the first non-empty hit of: 540 Display format will be the first non-empty hit of:
536 1. option 'template' 541 1. option 'template'
537 2. option 'style' 542 2. option 'style'
539 4. [ui] setting 'style' 544 4. [ui] setting 'style'
540 If all of these values are either the unset or the empty string, 545 If all of these values are either the unset or the empty string,
541 regular display via changesetprinter() is done. 546 regular display via changesetprinter() is done.
542 """ 547 """
543 # options 548 # options
544 match = None 549 if not makefilematcher and (opts.get('patch') or opts.get('stat')):
545 if opts.get('patch') or opts.get('stat'): 550 def makefilematcher(ctx):
546 match = scmutil.matchall(repo) 551 return scmutil.matchall(repo)
547 552
553 postargs = (makefilematcher, makehunksfilter, opts, buffered)
548 if opts.get('template') == 'json': 554 if opts.get('template') == 'json':
549 return jsonchangeset(ui, repo, match, opts, buffered) 555 return jsonchangeset(ui, repo, *postargs)
550 556
551 spec = _lookuptemplate(ui, opts.get('template'), opts.get('style')) 557 spec = _lookuptemplate(ui, opts.get('template'), opts.get('style'))
552 558
553 if not spec.ref and not spec.tmpl and not spec.mapfile: 559 if not spec.ref and not spec.tmpl and not spec.mapfile:
554 return changesetprinter(ui, repo, match, opts, buffered) 560 return changesetprinter(ui, repo, *postargs)
555 561
556 return changesettemplater(ui, repo, spec, match, opts, buffered) 562 return changesettemplater(ui, repo, spec, *postargs)
557 563
558 def _makematcher(repo, revs, pats, opts): 564 def _makematcher(repo, revs, pats, opts):
559 """Build matcher and expanded patterns from log options 565 """Build matcher and expanded patterns from log options
560 566
561 If --follow, revs are the revisions to follow from. 567 If --follow, revs are the revisions to follow from.
859 def formatnode(repo, ctx): 865 def formatnode(repo, ctx):
860 props = {'ctx': ctx, 'repo': repo, 'revcache': {}} 866 props = {'ctx': ctx, 'repo': repo, 'revcache': {}}
861 return templ.render(props) 867 return templ.render(props)
862 return formatnode 868 return formatnode
863 869
864 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None, 870 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None, props=None):
865 filematcher=None, props=None):
866 props = props or {} 871 props = props or {}
867 formatnode = _graphnodeformatter(ui, displayer) 872 formatnode = _graphnodeformatter(ui, displayer)
868 state = graphmod.asciistate() 873 state = graphmod.asciistate()
869 styles = state['styles'] 874 styles = state['styles']
870 875
895 copies = [] 900 copies = []
896 for fn in ctx.files(): 901 for fn in ctx.files():
897 rename = getrenamed(fn, ctx.rev()) 902 rename = getrenamed(fn, ctx.rev())
898 if rename: 903 if rename:
899 copies.append((fn, rename[0])) 904 copies.append((fn, rename[0]))
900 revmatchfn = None
901 if filematcher is not None:
902 revmatchfn = filematcher(ctx)
903 edges = edgefn(type, char, state, rev, parents) 905 edges = edgefn(type, char, state, rev, parents)
904 firstedge = next(edges) 906 firstedge = next(edges)
905 width = firstedge[2] 907 width = firstedge[2]
906 displayer.show(ctx, copies=copies, matchfn=revmatchfn, 908 displayer.show(ctx, copies=copies,
907 _graphwidth=width, **pycompat.strkwargs(props)) 909 _graphwidth=width, **pycompat.strkwargs(props))
908 lines = displayer.hunk.pop(rev).split('\n') 910 lines = displayer.hunk.pop(rev).split('\n')
909 if not lines[-1]: 911 if not lines[-1]:
910 del lines[-1] 912 del lines[-1]
911 displayer.flush(ctx) 913 displayer.flush(ctx)
924 if opts.get('rev'): 926 if opts.get('rev'):
925 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1 927 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
926 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev) 928 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
927 929
928 ui.pager('log') 930 ui.pager('log')
929 displayer = changesetdisplayer(ui, repo, opts, buffered=True) 931 displayer = changesetdisplayer(ui, repo, opts, makefilematcher=filematcher,
930 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed, 932 buffered=True)
931 filematcher) 933 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed)
932 934
933 def checkunsupportedgraphflags(pats, opts): 935 def checkunsupportedgraphflags(pats, opts):
934 for op in ["newest_first"]: 936 for op in ["newest_first"]:
935 if op in opts and opts[op]: 937 if op in opts and opts[op]:
936 raise error.Abort(_("-G/--graph option is incompatible with --%s") 938 raise error.Abort(_("-G/--graph option is incompatible with --%s")