391 raise error.ParseError(_("filter %s expects one argument") % n) |
392 raise error.ParseError(_("filter %s expects one argument") % n) |
392 f = context._filters[n] |
393 f = context._filters[n] |
393 return (runfilter, (args[0], f)) |
394 return (runfilter, (args[0], f)) |
394 raise error.ParseError(_("unknown function '%s'") % n) |
395 raise error.ParseError(_("unknown function '%s'") % n) |
395 |
396 |
|
397 # dict of template built-in functions |
|
398 funcs = {} |
|
399 |
|
400 templatefunc = registrar.templatefunc(funcs) |
|
401 |
|
402 @templatefunc('date(date[, fmt])') |
396 def date(context, mapping, args): |
403 def date(context, mapping, args): |
397 """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting |
404 """Format a date. See :hg:`help dates` for formatting |
398 strings. The default is a Unix date format, including the timezone: |
405 strings. The default is a Unix date format, including the timezone: |
399 "Mon Sep 04 15:13:13 2006 0700".""" |
406 "Mon Sep 04 15:13:13 2006 0700".""" |
400 if not (1 <= len(args) <= 2): |
407 if not (1 <= len(args) <= 2): |
401 # i18n: "date" is a keyword |
408 # i18n: "date" is a keyword |
402 raise error.ParseError(_("date expects one or two arguments")) |
409 raise error.ParseError(_("date expects one or two arguments")) |
412 return util.datestr(date, fmt) |
419 return util.datestr(date, fmt) |
413 except (TypeError, ValueError): |
420 except (TypeError, ValueError): |
414 # i18n: "date" is a keyword |
421 # i18n: "date" is a keyword |
415 raise error.ParseError(_("date expects a date information")) |
422 raise error.ParseError(_("date expects a date information")) |
416 |
423 |
|
424 @templatefunc('diff([includepattern [, excludepattern]])') |
417 def diff(context, mapping, args): |
425 def diff(context, mapping, args): |
418 """:diff([includepattern [, excludepattern]]): Show a diff, optionally |
426 """Show a diff, optionally |
419 specifying files to include or exclude.""" |
427 specifying files to include or exclude.""" |
420 if len(args) > 2: |
428 if len(args) > 2: |
421 # i18n: "diff" is a keyword |
429 # i18n: "diff" is a keyword |
422 raise error.ParseError(_("diff expects zero, one, or two arguments")) |
430 raise error.ParseError(_("diff expects zero, one, or two arguments")) |
423 |
431 |
431 ctx = mapping['ctx'] |
439 ctx = mapping['ctx'] |
432 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1))) |
440 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1))) |
433 |
441 |
434 return ''.join(chunks) |
442 return ''.join(chunks) |
435 |
443 |
|
444 @templatefunc('fill(text[, width[, initialident[, hangindent]]])') |
436 def fill(context, mapping, args): |
445 def fill(context, mapping, args): |
437 """:fill(text[, width[, initialident[, hangindent]]]): Fill many |
446 """Fill many |
438 paragraphs with optional indentation. See the "fill" filter.""" |
447 paragraphs with optional indentation. See the "fill" filter.""" |
439 if not (1 <= len(args) <= 4): |
448 if not (1 <= len(args) <= 4): |
440 # i18n: "fill" is a keyword |
449 # i18n: "fill" is a keyword |
441 raise error.ParseError(_("fill expects one to four arguments")) |
450 raise error.ParseError(_("fill expects one to four arguments")) |
442 |
451 |
454 except IndexError: |
463 except IndexError: |
455 pass |
464 pass |
456 |
465 |
457 return templatefilters.fill(text, width, initindent, hangindent) |
466 return templatefilters.fill(text, width, initindent, hangindent) |
458 |
467 |
|
468 @templatefunc('pad(text, width[, fillchar=\' \'[, right=False]])') |
459 def pad(context, mapping, args): |
469 def pad(context, mapping, args): |
460 """:pad(text, width[, fillchar=' '[, right=False]]): Pad text with a |
470 """Pad text with a |
461 fill character.""" |
471 fill character.""" |
462 if not (2 <= len(args) <= 4): |
472 if not (2 <= len(args) <= 4): |
463 # i18n: "pad" is a keyword |
473 # i18n: "pad" is a keyword |
464 raise error.ParseError(_("pad() expects two to four arguments")) |
474 raise error.ParseError(_("pad() expects two to four arguments")) |
465 |
475 |
479 if right: |
489 if right: |
480 return text.rjust(width, fillchar) |
490 return text.rjust(width, fillchar) |
481 else: |
491 else: |
482 return text.ljust(width, fillchar) |
492 return text.ljust(width, fillchar) |
483 |
493 |
|
494 @templatefunc('indent(text, indentchars[, firstline])') |
484 def indent(context, mapping, args): |
495 def indent(context, mapping, args): |
485 """:indent(text, indentchars[, firstline]): Indents all non-empty lines |
496 """Indents all non-empty lines |
486 with the characters given in the indentchars string. An optional |
497 with the characters given in the indentchars string. An optional |
487 third parameter will override the indent for the first line only |
498 third parameter will override the indent for the first line only |
488 if present.""" |
499 if present.""" |
489 if not (2 <= len(args) <= 3): |
500 if not (2 <= len(args) <= 3): |
490 # i18n: "indent" is a keyword |
501 # i18n: "indent" is a keyword |
499 firstline = indent |
510 firstline = indent |
500 |
511 |
501 # the indent function doesn't indent the first line, so we do it here |
512 # the indent function doesn't indent the first line, so we do it here |
502 return templatefilters.indent(firstline + text, indent) |
513 return templatefilters.indent(firstline + text, indent) |
503 |
514 |
|
515 @templatefunc('get(dict, key)') |
504 def get(context, mapping, args): |
516 def get(context, mapping, args): |
505 """:get(dict, key): Get an attribute/key from an object. Some keywords |
517 """Get an attribute/key from an object. Some keywords |
506 are complex types. This function allows you to obtain the value of an |
518 are complex types. This function allows you to obtain the value of an |
507 attribute on these types.""" |
519 attribute on these types.""" |
508 if len(args) != 2: |
520 if len(args) != 2: |
509 # i18n: "get" is a keyword |
521 # i18n: "get" is a keyword |
510 raise error.ParseError(_("get() expects two arguments")) |
522 raise error.ParseError(_("get() expects two arguments")) |
515 raise error.ParseError(_("get() expects a dict as first argument")) |
527 raise error.ParseError(_("get() expects a dict as first argument")) |
516 |
528 |
517 key = evalfuncarg(context, mapping, args[1]) |
529 key = evalfuncarg(context, mapping, args[1]) |
518 return dictarg.get(key) |
530 return dictarg.get(key) |
519 |
531 |
|
532 @templatefunc('if(expr, then[, else])') |
520 def if_(context, mapping, args): |
533 def if_(context, mapping, args): |
521 """:if(expr, then[, else]): Conditionally execute based on the result of |
534 """Conditionally execute based on the result of |
522 an expression.""" |
535 an expression.""" |
523 if not (2 <= len(args) <= 3): |
536 if not (2 <= len(args) <= 3): |
524 # i18n: "if" is a keyword |
537 # i18n: "if" is a keyword |
525 raise error.ParseError(_("if expects two or three arguments")) |
538 raise error.ParseError(_("if expects two or three arguments")) |
526 |
539 |
528 if test: |
541 if test: |
529 yield args[1][0](context, mapping, args[1][1]) |
542 yield args[1][0](context, mapping, args[1][1]) |
530 elif len(args) == 3: |
543 elif len(args) == 3: |
531 yield args[2][0](context, mapping, args[2][1]) |
544 yield args[2][0](context, mapping, args[2][1]) |
532 |
545 |
|
546 @templatefunc('ifcontains(search, thing, then[, else])') |
533 def ifcontains(context, mapping, args): |
547 def ifcontains(context, mapping, args): |
534 """:ifcontains(search, thing, then[, else]): Conditionally execute based |
548 """Conditionally execute based |
535 on whether the item "search" is in "thing".""" |
549 on whether the item "search" is in "thing".""" |
536 if not (3 <= len(args) <= 4): |
550 if not (3 <= len(args) <= 4): |
537 # i18n: "ifcontains" is a keyword |
551 # i18n: "ifcontains" is a keyword |
538 raise error.ParseError(_("ifcontains expects three or four arguments")) |
552 raise error.ParseError(_("ifcontains expects three or four arguments")) |
539 |
553 |
543 if item in items: |
557 if item in items: |
544 yield args[2][0](context, mapping, args[2][1]) |
558 yield args[2][0](context, mapping, args[2][1]) |
545 elif len(args) == 4: |
559 elif len(args) == 4: |
546 yield args[3][0](context, mapping, args[3][1]) |
560 yield args[3][0](context, mapping, args[3][1]) |
547 |
561 |
|
562 @templatefunc('ifeq(expr1, expr2, then[, else])') |
548 def ifeq(context, mapping, args): |
563 def ifeq(context, mapping, args): |
549 """:ifeq(expr1, expr2, then[, else]): Conditionally execute based on |
564 """Conditionally execute based on |
550 whether 2 items are equivalent.""" |
565 whether 2 items are equivalent.""" |
551 if not (3 <= len(args) <= 4): |
566 if not (3 <= len(args) <= 4): |
552 # i18n: "ifeq" is a keyword |
567 # i18n: "ifeq" is a keyword |
553 raise error.ParseError(_("ifeq expects three or four arguments")) |
568 raise error.ParseError(_("ifeq expects three or four arguments")) |
554 |
569 |
557 if test == match: |
572 if test == match: |
558 yield args[2][0](context, mapping, args[2][1]) |
573 yield args[2][0](context, mapping, args[2][1]) |
559 elif len(args) == 4: |
574 elif len(args) == 4: |
560 yield args[3][0](context, mapping, args[3][1]) |
575 yield args[3][0](context, mapping, args[3][1]) |
561 |
576 |
|
577 @templatefunc('join(list, sep)') |
562 def join(context, mapping, args): |
578 def join(context, mapping, args): |
563 """:join(list, sep): Join items in a list with a delimiter.""" |
579 """Join items in a list with a delimiter.""" |
564 if not (1 <= len(args) <= 2): |
580 if not (1 <= len(args) <= 2): |
565 # i18n: "join" is a keyword |
581 # i18n: "join" is a keyword |
566 raise error.ParseError(_("join expects one or two arguments")) |
582 raise error.ParseError(_("join expects one or two arguments")) |
567 |
583 |
568 joinset = args[0][0](context, mapping, args[0][1]) |
584 joinset = args[0][0](context, mapping, args[0][1]) |
580 first = False |
596 first = False |
581 else: |
597 else: |
582 yield joiner |
598 yield joiner |
583 yield x |
599 yield x |
584 |
600 |
|
601 @templatefunc('label(label, expr)') |
585 def label(context, mapping, args): |
602 def label(context, mapping, args): |
586 """:label(label, expr): Apply a label to generated content. Content with |
603 """Apply a label to generated content. Content with |
587 a label applied can result in additional post-processing, such as |
604 a label applied can result in additional post-processing, such as |
588 automatic colorization.""" |
605 automatic colorization.""" |
589 if len(args) != 2: |
606 if len(args) != 2: |
590 # i18n: "label" is a keyword |
607 # i18n: "label" is a keyword |
591 raise error.ParseError(_("label expects two arguments")) |
608 raise error.ParseError(_("label expects two arguments")) |
596 # etc. don't need to be quoted |
613 # etc. don't need to be quoted |
597 label = evalstringliteral(context, mapping, args[0]) |
614 label = evalstringliteral(context, mapping, args[0]) |
598 |
615 |
599 return ui.label(thing, label) |
616 return ui.label(thing, label) |
600 |
617 |
|
618 @templatefunc('latesttag([pattern])') |
601 def latesttag(context, mapping, args): |
619 def latesttag(context, mapping, args): |
602 """:latesttag([pattern]): The global tags matching the given pattern on the |
620 """The global tags matching the given pattern on the |
603 most recent globally tagged ancestor of this changeset.""" |
621 most recent globally tagged ancestor of this changeset.""" |
604 if len(args) > 1: |
622 if len(args) > 1: |
605 # i18n: "latesttag" is a keyword |
623 # i18n: "latesttag" is a keyword |
606 raise error.ParseError(_("latesttag expects at most one argument")) |
624 raise error.ParseError(_("latesttag expects at most one argument")) |
607 |
625 |
609 if len(args) == 1: |
627 if len(args) == 1: |
610 pattern = evalstring(context, mapping, args[0]) |
628 pattern = evalstring(context, mapping, args[0]) |
611 |
629 |
612 return templatekw.showlatesttags(pattern, **mapping) |
630 return templatekw.showlatesttags(pattern, **mapping) |
613 |
631 |
|
632 @templatefunc('localdate(date[, tz])') |
614 def localdate(context, mapping, args): |
633 def localdate(context, mapping, args): |
615 """:localdate(date[, tz]): Converts a date to the specified timezone. |
634 """Converts a date to the specified timezone. |
616 The default is local date.""" |
635 The default is local date.""" |
617 if not (1 <= len(args) <= 2): |
636 if not (1 <= len(args) <= 2): |
618 # i18n: "localdate" is a keyword |
637 # i18n: "localdate" is a keyword |
619 raise error.ParseError(_("localdate expects one or two arguments")) |
638 raise error.ParseError(_("localdate expects one or two arguments")) |
620 |
639 |
637 raise error.ParseError(_("localdate expects a timezone")) |
656 raise error.ParseError(_("localdate expects a timezone")) |
638 else: |
657 else: |
639 tzoffset = util.makedate()[1] |
658 tzoffset = util.makedate()[1] |
640 return (date[0], tzoffset) |
659 return (date[0], tzoffset) |
641 |
660 |
|
661 @templatefunc('revset(query[, formatargs...])') |
642 def revset(context, mapping, args): |
662 def revset(context, mapping, args): |
643 """:revset(query[, formatargs...]): Execute a revision set query. See |
663 """Execute a revision set query. See |
644 :hg:`help revset`.""" |
664 :hg:`help revset`.""" |
645 if not len(args) > 0: |
665 if not len(args) > 0: |
646 # i18n: "revset" is a keyword |
666 # i18n: "revset" is a keyword |
647 raise error.ParseError(_("revset expects one or more arguments")) |
667 raise error.ParseError(_("revset expects one or more arguments")) |
648 |
668 |
667 revs = list(revs) |
687 revs = list(revs) |
668 revsetcache[raw] = revs |
688 revsetcache[raw] = revs |
669 |
689 |
670 return templatekw.showrevslist("revision", revs, **mapping) |
690 return templatekw.showrevslist("revision", revs, **mapping) |
671 |
691 |
|
692 @templatefunc('rstdoc(text, style)') |
672 def rstdoc(context, mapping, args): |
693 def rstdoc(context, mapping, args): |
673 """:rstdoc(text, style): Format ReStructuredText.""" |
694 """Format ReStructuredText.""" |
674 if len(args) != 2: |
695 if len(args) != 2: |
675 # i18n: "rstdoc" is a keyword |
696 # i18n: "rstdoc" is a keyword |
676 raise error.ParseError(_("rstdoc expects two arguments")) |
697 raise error.ParseError(_("rstdoc expects two arguments")) |
677 |
698 |
678 text = evalstring(context, mapping, args[0]) |
699 text = evalstring(context, mapping, args[0]) |
679 style = evalstring(context, mapping, args[1]) |
700 style = evalstring(context, mapping, args[1]) |
680 |
701 |
681 return minirst.format(text, style=style, keep=['verbose']) |
702 return minirst.format(text, style=style, keep=['verbose']) |
682 |
703 |
|
704 @templatefunc('shortest(node, minlength=4)') |
683 def shortest(context, mapping, args): |
705 def shortest(context, mapping, args): |
684 """:shortest(node, minlength=4): Obtain the shortest representation of |
706 """Obtain the shortest representation of |
685 a node.""" |
707 a node.""" |
686 if not (1 <= len(args) <= 2): |
708 if not (1 <= len(args) <= 2): |
687 # i18n: "shortest" is a keyword |
709 # i18n: "shortest" is a keyword |
688 raise error.ParseError(_("shortest() expects one or two arguments")) |
710 raise error.ParseError(_("shortest() expects one or two arguments")) |
689 |
711 |
732 else: |
754 else: |
733 length += 1 |
755 length += 1 |
734 if len(shortest) <= length: |
756 if len(shortest) <= length: |
735 return shortest |
757 return shortest |
736 |
758 |
|
759 @templatefunc('strip(text[, chars])') |
737 def strip(context, mapping, args): |
760 def strip(context, mapping, args): |
738 """:strip(text[, chars]): Strip characters from a string. By default, |
761 """Strip characters from a string. By default, |
739 strips all leading and trailing whitespace.""" |
762 strips all leading and trailing whitespace.""" |
740 if not (1 <= len(args) <= 2): |
763 if not (1 <= len(args) <= 2): |
741 # i18n: "strip" is a keyword |
764 # i18n: "strip" is a keyword |
742 raise error.ParseError(_("strip expects one or two arguments")) |
765 raise error.ParseError(_("strip expects one or two arguments")) |
743 |
766 |
745 if len(args) == 2: |
768 if len(args) == 2: |
746 chars = evalstring(context, mapping, args[1]) |
769 chars = evalstring(context, mapping, args[1]) |
747 return text.strip(chars) |
770 return text.strip(chars) |
748 return text.strip() |
771 return text.strip() |
749 |
772 |
|
773 @templatefunc('sub(pattern, replacement, expression)') |
750 def sub(context, mapping, args): |
774 def sub(context, mapping, args): |
751 """:sub(pattern, replacement, expression): Perform text substitution |
775 """Perform text substitution |
752 using regular expressions.""" |
776 using regular expressions.""" |
753 if len(args) != 3: |
777 if len(args) != 3: |
754 # i18n: "sub" is a keyword |
778 # i18n: "sub" is a keyword |
755 raise error.ParseError(_("sub expects three arguments")) |
779 raise error.ParseError(_("sub expects three arguments")) |
756 |
780 |
766 yield patre.sub(rpl, src) |
790 yield patre.sub(rpl, src) |
767 except re.error: |
791 except re.error: |
768 # i18n: "sub" is a keyword |
792 # i18n: "sub" is a keyword |
769 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl) |
793 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl) |
770 |
794 |
|
795 @templatefunc('startswith(pattern, text)') |
771 def startswith(context, mapping, args): |
796 def startswith(context, mapping, args): |
772 """:startswith(pattern, text): Returns the value from the "text" argument |
797 """Returns the value from the "text" argument |
773 if it begins with the content from the "pattern" argument.""" |
798 if it begins with the content from the "pattern" argument.""" |
774 if len(args) != 2: |
799 if len(args) != 2: |
775 # i18n: "startswith" is a keyword |
800 # i18n: "startswith" is a keyword |
776 raise error.ParseError(_("startswith expects two arguments")) |
801 raise error.ParseError(_("startswith expects two arguments")) |
777 |
802 |
779 text = evalstring(context, mapping, args[1]) |
804 text = evalstring(context, mapping, args[1]) |
780 if text.startswith(patn): |
805 if text.startswith(patn): |
781 return text |
806 return text |
782 return '' |
807 return '' |
783 |
808 |
784 |
809 @templatefunc('word(number, text[, separator])') |
785 def word(context, mapping, args): |
810 def word(context, mapping, args): |
786 """:word(number, text[, separator]): Return the nth word from a string.""" |
811 """Return the nth word from a string.""" |
787 if not (2 <= len(args) <= 3): |
812 if not (2 <= len(args) <= 3): |
788 # i18n: "word" is a keyword |
813 # i18n: "word" is a keyword |
789 raise error.ParseError(_("word expects two or three arguments, got %d") |
814 raise error.ParseError(_("word expects two or three arguments, got %d") |
790 % len(args)) |
815 % len(args)) |
791 |
816 |
818 } |
843 } |
819 |
844 |
820 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"}) |
845 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"}) |
821 methods = exprmethods.copy() |
846 methods = exprmethods.copy() |
822 methods["integer"] = exprmethods["symbol"] # '{1}' as variable |
847 methods["integer"] = exprmethods["symbol"] # '{1}' as variable |
823 |
|
824 funcs = { |
|
825 "date": date, |
|
826 "diff": diff, |
|
827 "fill": fill, |
|
828 "get": get, |
|
829 "if": if_, |
|
830 "ifcontains": ifcontains, |
|
831 "ifeq": ifeq, |
|
832 "indent": indent, |
|
833 "join": join, |
|
834 "label": label, |
|
835 "latesttag": latesttag, |
|
836 "localdate": localdate, |
|
837 "pad": pad, |
|
838 "revset": revset, |
|
839 "rstdoc": rstdoc, |
|
840 "shortest": shortest, |
|
841 "startswith": startswith, |
|
842 "strip": strip, |
|
843 "sub": sub, |
|
844 "word": word, |
|
845 } |
|
846 |
848 |
847 # template engine |
849 # template engine |
848 |
850 |
849 stringify = templatefilters.stringify |
851 stringify = templatefilters.stringify |
850 |
852 |