##// END OF EJS Templates
py3: fix bad escapes of sub() replacement pattern in test-template-basic.t...
Yuya Nishihara -
r40978:e48fdeb0 default
parent child Browse files
Show More
@@ -1,1089 +1,1089
1 1 Test template syntax and basic functionality
2 2 ============================================
3 3
4 4 $ hg init a
5 5 $ cd a
6 6 $ echo a > a
7 7 $ hg add a
8 8 $ echo line 1 > b
9 9 $ echo line 2 >> b
10 10 $ hg commit -l b -d '1000000 0' -u 'User Name <user@hostname>'
11 11
12 12 $ hg add b
13 13 $ echo other 1 > c
14 14 $ echo other 2 >> c
15 15 $ echo >> c
16 16 $ echo other 3 >> c
17 17 $ hg commit -l c -d '1100000 0' -u 'A. N. Other <other@place>'
18 18
19 19 $ hg add c
20 20 $ hg commit -m 'no person' -d '1200000 0' -u 'other@place'
21 21 $ echo c >> c
22 22 $ hg commit -m 'no user, no domain' -d '1300000 0' -u 'person'
23 23
24 24 $ echo foo > .hg/branch
25 25 $ hg commit -m 'new branch' -d '1400000 0' -u 'person'
26 26
27 27 $ hg co -q 3
28 28 $ echo other 4 >> d
29 29 $ hg add d
30 30 $ hg commit -m 'new head' -d '1500000 0' -u 'person'
31 31
32 32 $ hg merge -q foo
33 33 $ hg commit -m 'merge' -d '1500001 0' -u 'person'
34 34
35 35 Test arithmetic operators have the right precedence:
36 36
37 37 $ hg log -l 1 -T '{date(date, "%Y") + 5 * 10} {date(date, "%Y") - 2 * 3}\n'
38 38 2020 1964
39 39 $ hg log -l 1 -T '{date(date, "%Y") * 5 + 10} {date(date, "%Y") * 3 - 2}\n'
40 40 9860 5908
41 41
42 42 Test division:
43 43
44 44 $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n'
45 45 (template
46 46 (/
47 47 (integer '5')
48 48 (integer '2'))
49 49 (string ' ')
50 50 (func
51 51 (symbol 'mod')
52 52 (list
53 53 (integer '5')
54 54 (integer '2')))
55 55 (string '\n'))
56 56 * keywords:
57 57 * functions: mod
58 58 2 1
59 59 $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n'
60 60 (template
61 61 (/
62 62 (integer '5')
63 63 (negate
64 64 (integer '2')))
65 65 (string ' ')
66 66 (func
67 67 (symbol 'mod')
68 68 (list
69 69 (integer '5')
70 70 (negate
71 71 (integer '2'))))
72 72 (string '\n'))
73 73 * keywords:
74 74 * functions: mod
75 75 -3 -1
76 76 $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n'
77 77 (template
78 78 (/
79 79 (negate
80 80 (integer '5'))
81 81 (integer '2'))
82 82 (string ' ')
83 83 (func
84 84 (symbol 'mod')
85 85 (list
86 86 (negate
87 87 (integer '5'))
88 88 (integer '2')))
89 89 (string '\n'))
90 90 * keywords:
91 91 * functions: mod
92 92 -3 1
93 93 $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n'
94 94 (template
95 95 (/
96 96 (negate
97 97 (integer '5'))
98 98 (negate
99 99 (integer '2')))
100 100 (string ' ')
101 101 (func
102 102 (symbol 'mod')
103 103 (list
104 104 (negate
105 105 (integer '5'))
106 106 (negate
107 107 (integer '2'))))
108 108 (string '\n'))
109 109 * keywords:
110 110 * functions: mod
111 111 2 -1
112 112
113 113 Filters bind closer than arithmetic:
114 114
115 115 $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n'
116 116 (template
117 117 (-
118 118 (|
119 119 (func
120 120 (symbol 'revset')
121 121 (string '.'))
122 122 (symbol 'count'))
123 123 (integer '1'))
124 124 (string '\n'))
125 125 * keywords:
126 126 * functions: count, revset
127 127 0
128 128
129 129 But negate binds closer still:
130 130
131 131 $ hg debugtemplate -r0 -v '{1-3|stringify}\n'
132 132 (template
133 133 (-
134 134 (integer '1')
135 135 (|
136 136 (integer '3')
137 137 (symbol 'stringify')))
138 138 (string '\n'))
139 139 * keywords:
140 140 * functions: stringify
141 141 hg: parse error: arithmetic only defined on integers
142 142 [255]
143 143 $ hg debugtemplate -r0 -v '{-3|stringify}\n'
144 144 (template
145 145 (|
146 146 (negate
147 147 (integer '3'))
148 148 (symbol 'stringify'))
149 149 (string '\n'))
150 150 * keywords:
151 151 * functions: stringify
152 152 -3
153 153
154 154 Filters bind as close as map operator:
155 155
156 156 $ hg debugtemplate -r0 -v '{desc|splitlines % "{line}\n"}'
157 157 (template
158 158 (%
159 159 (|
160 160 (symbol 'desc')
161 161 (symbol 'splitlines'))
162 162 (template
163 163 (symbol 'line')
164 164 (string '\n'))))
165 165 * keywords: desc, line
166 166 * functions: splitlines
167 167 line 1
168 168 line 2
169 169
170 170 Keyword arguments:
171 171
172 172 $ hg debugtemplate -r0 -v '{foo=bar|baz}'
173 173 (template
174 174 (keyvalue
175 175 (symbol 'foo')
176 176 (|
177 177 (symbol 'bar')
178 178 (symbol 'baz'))))
179 179 * keywords: bar, foo
180 180 * functions: baz
181 181 hg: parse error: can't use a key-value pair in this context
182 182 [255]
183 183
184 184 $ hg debugtemplate '{pad("foo", width=10, left=true)}\n'
185 185 foo
186 186
187 187 Call function which takes named arguments by filter syntax:
188 188
189 189 $ hg debugtemplate '{" "|separate}'
190 190 $ hg debugtemplate '{("not", "an", "argument", "list")|separate}'
191 191 hg: parse error: can't use a list in this context
192 192 (check place of comma and parens)
193 193 [255]
194 194
195 195 Second branch starting at nullrev:
196 196
197 197 $ hg update null
198 198 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
199 199 $ echo second > second
200 200 $ hg add second
201 201 $ hg commit -m second -d '1000000 0' -u 'User Name <user@hostname>'
202 202 created new head
203 203
204 204 $ echo third > third
205 205 $ hg add third
206 206 $ hg mv second fourth
207 207 $ hg commit -m third -d "2020-01-01 10:01"
208 208
209 209 $ hg log --template '{join(file_copies, ",\n")}\n' -r .
210 210 fourth (second)
211 211 $ hg log -T '{file_copies % "{source} -> {name}\n"}' -r .
212 212 second -> fourth
213 213 $ hg log -T '{rev} {ifcontains("fourth", file_copies, "t", "f")}\n' -r .:7
214 214 8 t
215 215 7 f
216 216
217 217 Internal resources shouldn't be exposed (issue5699):
218 218
219 219 $ hg log -r. -T '{cache}{ctx}{repo}{revcache}{templ}{ui}'
220 220
221 221 Never crash on internal resource not available:
222 222
223 223 $ hg --cwd .. debugtemplate '{"c0bebeef"|shortest}\n'
224 224 abort: template resource not available: repo
225 225 [255]
226 226
227 227 $ hg config -T '{author}'
228 228
229 229 Quoting for ui.logtemplate
230 230
231 231 $ hg tip --config "ui.logtemplate={rev}\n"
232 232 8
233 233 $ hg tip --config "ui.logtemplate='{rev}\n'"
234 234 8
235 235 $ hg tip --config 'ui.logtemplate="{rev}\n"'
236 236 8
237 237 $ hg tip --config 'ui.logtemplate=n{rev}\n'
238 238 n8
239 239
240 240 Check that recursive reference does not fall into RuntimeError (issue4758):
241 241
242 242 common mistake:
243 243
244 244 $ cat << EOF > issue4758
245 245 > changeset = '{changeset}\n'
246 246 > EOF
247 247 $ hg log --style ./issue4758
248 248 abort: recursive reference 'changeset' in template
249 249 [255]
250 250
251 251 circular reference:
252 252
253 253 $ cat << EOF > issue4758
254 254 > changeset = '{foo}'
255 255 > foo = '{changeset}'
256 256 > EOF
257 257 $ hg log --style ./issue4758
258 258 abort: recursive reference 'foo' in template
259 259 [255]
260 260
261 261 buildmap() -> gettemplate(), where no thunk was made:
262 262
263 263 $ cat << EOF > issue4758
264 264 > changeset = '{files % changeset}\n'
265 265 > EOF
266 266 $ hg log --style ./issue4758
267 267 abort: recursive reference 'changeset' in template
268 268 [255]
269 269
270 270 not a recursion if a keyword of the same name exists:
271 271
272 272 $ cat << EOF > issue4758
273 273 > changeset = '{tags % rev}'
274 274 > rev = '{rev} {tag}\n'
275 275 > EOF
276 276 $ hg log --style ./issue4758 -r tip
277 277 8 tip
278 278
279 279 Set up phase:
280 280
281 281 $ hg phase -r 5 --public
282 282 $ hg phase -r 7 --secret --force
283 283
284 284 Add a dummy commit to make up for the instability of the above:
285 285
286 286 $ echo a > a
287 287 $ hg add a
288 288 $ hg ci -m future
289 289
290 290 Add a commit that does all possible modifications at once
291 291
292 292 $ echo modify >> third
293 293 $ touch b
294 294 $ hg add b
295 295 $ hg mv fourth fifth
296 296 $ hg rm a
297 297 $ hg ci -m "Modify, add, remove, rename"
298 298
299 299 Error on syntax:
300 300
301 301 $ cat <<EOF > t
302 302 > changeset = '{c}'
303 303 > c = q
304 304 > x = "f
305 305 > EOF
306 306 $ echo '[ui]' > .hg/hgrc
307 307 $ echo 'style = t' >> .hg/hgrc
308 308 $ hg log
309 309 hg: parse error at t:3: unmatched quotes
310 310 [255]
311 311
312 312 $ hg log -T '{date'
313 313 hg: parse error at 1: unterminated template expansion
314 314 ({date
315 315 ^ here)
316 316 [255]
317 317 $ hg log -T '{date(}'
318 318 hg: parse error at 6: not a prefix: end
319 319 ({date(}
320 320 ^ here)
321 321 [255]
322 322 $ hg log -T '{date)}'
323 323 hg: parse error at 5: invalid token
324 324 ({date)}
325 325 ^ here)
326 326 [255]
327 327 $ hg log -T '{date date}'
328 328 hg: parse error at 6: invalid token
329 329 ({date date}
330 330 ^ here)
331 331 [255]
332 332
333 333 $ hg log -T '{}'
334 334 hg: parse error at 1: not a prefix: end
335 335 ({}
336 336 ^ here)
337 337 [255]
338 338 $ hg debugtemplate -v '{()}'
339 339 (template
340 340 (group
341 341 None))
342 342 * keywords:
343 343 * functions:
344 344 hg: parse error: missing argument
345 345 [255]
346 346
347 347 Behind the scenes, this would throw TypeError without intype=bytes
348 348
349 349 $ hg log -l 3 --template '{date|obfuscate}\n'
350 350 &#48;&#46;&#48;&#48;
351 351 &#48;&#46;&#48;&#48;
352 352 &#49;&#53;&#55;&#55;&#56;&#55;&#50;&#56;&#54;&#48;&#46;&#48;&#48;
353 353
354 354 Behind the scenes, this will throw a ValueError
355 355
356 356 $ hg log -l 3 --template 'line: {desc|shortdate}\n'
357 357 hg: parse error: invalid date: 'Modify, add, remove, rename'
358 358 (template filter 'shortdate' is not compatible with keyword 'desc')
359 359 [255]
360 360
361 361 Behind the scenes, this would throw AttributeError without intype=bytes
362 362
363 363 $ hg log -l 3 --template 'line: {date|escape}\n'
364 364 line: 0.00
365 365 line: 0.00
366 366 line: 1577872860.00
367 367
368 368 $ hg log -l 3 --template 'line: {extras|localdate}\n'
369 369 hg: parse error: localdate expects a date information
370 370 [255]
371 371
372 372 Behind the scenes, this will throw ValueError
373 373
374 374 $ hg tip --template '{author|email|date}\n'
375 375 hg: parse error: date expects a date information
376 376 [255]
377 377
378 378 $ hg tip -T '{author|email|shortdate}\n'
379 379 hg: parse error: invalid date: 'test'
380 380 (template filter 'shortdate' is not compatible with keyword 'author')
381 381 [255]
382 382
383 383 $ hg tip -T '{get(extras, "branch")|shortdate}\n'
384 384 hg: parse error: invalid date: 'default'
385 385 (incompatible use of template filter 'shortdate')
386 386 [255]
387 387
388 388 Error in nested template:
389 389
390 390 $ hg log -T '{"date'
391 391 hg: parse error at 2: unterminated string
392 392 ({"date
393 393 ^ here)
394 394 [255]
395 395
396 396 $ hg log -T '{"foo{date|?}"}'
397 397 hg: parse error at 11: syntax error
398 398 ({"foo{date|?}"}
399 399 ^ here)
400 400 [255]
401 401
402 402 Thrown an error if a template function doesn't exist
403 403
404 404 $ hg tip --template '{foo()}\n'
405 405 hg: parse error: unknown function 'foo'
406 406 [255]
407 407
408 408 $ cd ..
409 409
410 410 Set up latesttag repository:
411 411
412 412 $ hg init latesttag
413 413 $ cd latesttag
414 414
415 415 $ echo a > file
416 416 $ hg ci -Am a -d '0 0'
417 417 adding file
418 418
419 419 $ echo b >> file
420 420 $ hg ci -m b -d '1 0'
421 421
422 422 $ echo c >> head1
423 423 $ hg ci -Am h1c -d '2 0'
424 424 adding head1
425 425
426 426 $ hg update -q 1
427 427 $ echo d >> head2
428 428 $ hg ci -Am h2d -d '3 0'
429 429 adding head2
430 430 created new head
431 431
432 432 $ echo e >> head2
433 433 $ hg ci -m h2e -d '4 0'
434 434
435 435 $ hg merge -q
436 436 $ hg ci -m merge -d '5 -3600'
437 437
438 438 $ hg tag -r 1 -m t1 -d '6 0' t1
439 439 $ hg tag -r 2 -m t2 -d '7 0' t2
440 440 $ hg tag -r 3 -m t3 -d '8 0' t3
441 441 $ hg tag -r 4 -m t4 -d '4 0' t4 # older than t2, but should not matter
442 442 $ hg tag -r 5 -m t5 -d '9 0' t5
443 443 $ hg tag -r 3 -m at3 -d '10 0' at3
444 444
445 445 $ cd ..
446 446
447 447 Test new-style inline templating:
448 448
449 449 $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
450 450 modified files: .hgtags
451 451
452 452
453 453 $ hg log -R latesttag -r tip -T '{rev % "a"}\n'
454 454 hg: parse error: 11 is not iterable of mappings
455 455 (keyword 'rev' does not support map operation)
456 456 [255]
457 457 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "a"}\n'
458 458 hg: parse error: None is not iterable of mappings
459 459 [255]
460 460 $ hg log -R latesttag -r tip -T '{extras % "{key}\n" % "{key}\n"}'
461 461 hg: parse error: list of strings is not mappable
462 462 [255]
463 463
464 464 Test new-style inline templating of non-list/dict type:
465 465
466 466 $ hg log -R latesttag -r tip -T '{manifest}\n'
467 467 11:2bc6e9006ce2
468 468 $ hg log -R latesttag -r tip -T 'string length: {manifest|count}\n'
469 469 string length: 15
470 470 $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
471 471 11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
472 472
473 473 $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
474 474 branch: default
475 475 $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
476 476 hg: parse error: None is not iterable of mappings
477 477 [255]
478 478 $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
479 479 branch: default
480 480 $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
481 481 0:ce3cec86e6c2
482 482 $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
483 483 9:fbc7cd862e9c
484 484
485 485 Test dot operator precedence:
486 486
487 487 $ hg debugtemplate -R latesttag -r0 -v '{manifest.node|short}\n'
488 488 (template
489 489 (|
490 490 (.
491 491 (symbol 'manifest')
492 492 (symbol 'node'))
493 493 (symbol 'short'))
494 494 (string '\n'))
495 495 * keywords: manifest, node, rev
496 496 * functions: formatnode, short
497 497 89f4071fec70
498 498
499 499 (the following examples are invalid, but seem natural in parsing POV)
500 500
501 501 $ hg debugtemplate -R latesttag -r0 -v '{foo|bar.baz}\n' 2> /dev/null
502 502 (template
503 503 (|
504 504 (symbol 'foo')
505 505 (.
506 506 (symbol 'bar')
507 507 (symbol 'baz')))
508 508 (string '\n'))
509 509 [255]
510 510 $ hg debugtemplate -R latesttag -r0 -v '{foo.bar()}\n' 2> /dev/null
511 511 (template
512 512 (.
513 513 (symbol 'foo')
514 514 (func
515 515 (symbol 'bar')
516 516 None))
517 517 (string '\n'))
518 518 * keywords: foo
519 519 * functions: bar
520 520 [255]
521 521
522 522 Test evaluation of dot operator:
523 523
524 524 $ hg log -R latesttag -l1 -T '{min(revset("0:9")).node}\n'
525 525 ce3cec86e6c26bd9bdfc590a6b92abc9680f1796
526 526 $ hg log -R latesttag -r0 -T '{extras.branch}\n'
527 527 default
528 528 $ hg log -R latesttag -r0 -T '{date.unixtime} {localdate(date, "+0200").tzoffset}\n'
529 529 0 -7200
530 530
531 531 $ hg log -R latesttag -l1 -T '{author.invalid}\n'
532 532 hg: parse error: 'test' is not a dictionary
533 533 (keyword 'author' does not support member operation)
534 534 [255]
535 535 $ hg log -R latesttag -l1 -T '{min("abc").invalid}\n'
536 536 hg: parse error: 'a' is not a dictionary
537 537 [255]
538 538
539 539 Test integer literal:
540 540
541 541 $ hg debugtemplate -v '{(0)}\n'
542 542 (template
543 543 (group
544 544 (integer '0'))
545 545 (string '\n'))
546 546 * keywords:
547 547 * functions:
548 548 0
549 549 $ hg debugtemplate -v '{(123)}\n'
550 550 (template
551 551 (group
552 552 (integer '123'))
553 553 (string '\n'))
554 554 * keywords:
555 555 * functions:
556 556 123
557 557 $ hg debugtemplate -v '{(-4)}\n'
558 558 (template
559 559 (group
560 560 (negate
561 561 (integer '4')))
562 562 (string '\n'))
563 563 * keywords:
564 564 * functions:
565 565 -4
566 566 $ hg debugtemplate '{(-)}\n'
567 567 hg: parse error at 3: not a prefix: )
568 568 ({(-)}\n
569 569 ^ here)
570 570 [255]
571 571 $ hg debugtemplate '{(-a)}\n'
572 572 hg: parse error: negation needs an integer argument
573 573 [255]
574 574
575 575 top-level integer literal is interpreted as symbol (i.e. variable name):
576 576
577 577 $ hg debugtemplate -D 1=one -v '{1}\n'
578 578 (template
579 579 (integer '1')
580 580 (string '\n'))
581 581 * keywords:
582 582 * functions:
583 583 one
584 584 $ hg debugtemplate -D 1=one -v '{if("t", "{1}")}\n'
585 585 (template
586 586 (func
587 587 (symbol 'if')
588 588 (list
589 589 (string 't')
590 590 (template
591 591 (integer '1'))))
592 592 (string '\n'))
593 593 * keywords:
594 594 * functions: if
595 595 one
596 596 $ hg debugtemplate -D 1=one -v '{1|stringify}\n'
597 597 (template
598 598 (|
599 599 (integer '1')
600 600 (symbol 'stringify'))
601 601 (string '\n'))
602 602 * keywords:
603 603 * functions: stringify
604 604 one
605 605
606 606 unless explicit symbol is expected:
607 607
608 608 $ hg log -Ra -r0 -T '{desc|1}\n'
609 609 hg: parse error: expected a symbol, got 'integer'
610 610 [255]
611 611 $ hg log -Ra -r0 -T '{1()}\n'
612 612 hg: parse error: expected a symbol, got 'integer'
613 613 [255]
614 614
615 615 Test string literal:
616 616
617 617 $ hg debugtemplate -Ra -r0 -v '{"string with no template fragment"}\n'
618 618 (template
619 619 (string 'string with no template fragment')
620 620 (string '\n'))
621 621 * keywords:
622 622 * functions:
623 623 string with no template fragment
624 624 $ hg debugtemplate -Ra -r0 -v '{"template: {rev}"}\n'
625 625 (template
626 626 (template
627 627 (string 'template: ')
628 628 (symbol 'rev'))
629 629 (string '\n'))
630 630 * keywords: rev
631 631 * functions:
632 632 template: 0
633 633 $ hg debugtemplate -Ra -r0 -v '{r"rawstring: {rev}"}\n'
634 634 (template
635 635 (string 'rawstring: {rev}')
636 636 (string '\n'))
637 637 * keywords:
638 638 * functions:
639 639 rawstring: {rev}
640 640 $ hg debugtemplate -Ra -r0 -v '{files % r"rawstring: {file}"}\n'
641 641 (template
642 642 (%
643 643 (symbol 'files')
644 644 (string 'rawstring: {file}'))
645 645 (string '\n'))
646 646 * keywords: files
647 647 * functions:
648 648 rawstring: {file}
649 649
650 650 Test string escaping:
651 651
652 652 $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
653 653 >
654 654 <>\n<[>
655 655 <>\n<]>
656 656 <>\n<
657 657
658 658 $ hg log -R latesttag -r 0 \
659 659 > --config ui.logtemplate='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
660 660 >
661 661 <>\n<[>
662 662 <>\n<]>
663 663 <>\n<
664 664
665 665 $ hg log -R latesttag -r 0 -T esc \
666 666 > --config templates.esc='>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
667 667 >
668 668 <>\n<[>
669 669 <>\n<]>
670 670 <>\n<
671 671
672 672 $ cat <<'EOF' > esctmpl
673 673 > changeset = '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n'
674 674 > EOF
675 675 $ hg log -R latesttag -r 0 --style ./esctmpl
676 676 >
677 677 <>\n<[>
678 678 <>\n<]>
679 679 <>\n<
680 680
681 681 Test string escaping of quotes:
682 682
683 683 $ hg log -Ra -r0 -T '{"\""}\n'
684 684 "
685 685 $ hg log -Ra -r0 -T '{"\\\""}\n'
686 686 \"
687 687 $ hg log -Ra -r0 -T '{r"\""}\n'
688 688 \"
689 689 $ hg log -Ra -r0 -T '{r"\\\""}\n'
690 690 \\\"
691 691
692 692
693 693 $ hg log -Ra -r0 -T '{"\""}\n'
694 694 "
695 695 $ hg log -Ra -r0 -T '{"\\\""}\n'
696 696 \"
697 697 $ hg log -Ra -r0 -T '{r"\""}\n'
698 698 \"
699 699 $ hg log -Ra -r0 -T '{r"\\\""}\n'
700 700 \\\"
701 701
702 702 Test exception in quoted template. single backslash before quotation mark is
703 703 stripped before parsing:
704 704
705 705 $ cat <<'EOF' > escquotetmpl
706 706 > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
707 707 > EOF
708 708 $ cd latesttag
709 709 $ hg log -r 2 --style ../escquotetmpl
710 710 " \" \" \\" head1
711 711
712 712 $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
713 713 valid
714 714 $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
715 715 valid
716 716
717 717 Test compatibility with 2.9.2-3.4 of escaped quoted strings in nested
718 718 _evalifliteral() templates (issue4733):
719 719
720 720 $ hg log -r 2 -T '{if(rev, "\"{rev}")}\n'
721 721 "2
722 722 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\"{rev}\")}")}\n'
723 723 "2
724 724 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\"{rev}\\\")}\")}")}\n'
725 725 "2
726 726
727 727 $ hg log -r 2 -T '{if(rev, "\\\"")}\n'
728 728 \"
729 729 $ hg log -r 2 -T '{if(rev, "{if(rev, \"\\\\\\\"\")}")}\n'
730 730 \"
731 731 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, \\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
732 732 \"
733 733
734 734 $ hg log -r 2 -T '{if(rev, r"\\\"")}\n'
735 735 \\\"
736 736 $ hg log -r 2 -T '{if(rev, "{if(rev, r\"\\\\\\\"\")}")}\n'
737 737 \\\"
738 738 $ hg log -r 2 -T '{if(rev, "{if(rev, \"{if(rev, r\\\"\\\\\\\\\\\\\\\"\\\")}\")}")}\n'
739 739 \\\"
740 740
741 741 escaped single quotes and errors:
742 742
743 743 $ hg log -r 2 -T "{if(rev, '{if(rev, \'foo\')}')}"'\n'
744 744 foo
745 745 $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n'
746 746 foo
747 747 $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n'
748 748 hg: parse error at 21: unterminated string
749 749 ({if(rev, "{if(rev, \")}")}\n
750 750 ^ here)
751 751 [255]
752 752 $ hg log -r 2 -T '{if(rev, \"\\"")}\n'
753 753 hg: parse error: trailing \ in string
754 754 [255]
755 755 $ hg log -r 2 -T '{if(rev, r\"\\"")}\n'
756 756 hg: parse error: trailing \ in string
757 757 [255]
758 758
759 759 $ cd ..
760 760
761 761 Test leading backslashes:
762 762
763 763 $ cd latesttag
764 764 $ hg log -r 2 -T '\{rev} {files % "\{file}"}\n'
765 765 {rev} {file}
766 766 $ hg log -r 2 -T '\\{rev} {files % "\\{file}"}\n'
767 767 \2 \head1
768 768 $ hg log -r 2 -T '\\\{rev} {files % "\\\{file}"}\n'
769 769 \{rev} \{file}
770 770 $ cd ..
771 771
772 772 Test leading backslashes in "if" expression (issue4714):
773 773
774 774 $ cd latesttag
775 775 $ hg log -r 2 -T '{if("1", "\{rev}")} {if("1", r"\{rev}")}\n'
776 776 {rev} \{rev}
777 777 $ hg log -r 2 -T '{if("1", "\\{rev}")} {if("1", r"\\{rev}")}\n'
778 778 \2 \\{rev}
779 779 $ hg log -r 2 -T '{if("1", "\\\{rev}")} {if("1", r"\\\{rev}")}\n'
780 780 \{rev} \\\{rev}
781 781 $ cd ..
782 782
783 783 "string-escape"-ed "\x5c\x786e" becomes r"\x6e" (once) or r"n" (twice)
784 784
785 785 $ hg log -R a -r 0 --template '{if("1", "\x5c\x786e", "NG")}\n'
786 786 \x6e
787 787 $ hg log -R a -r 0 --template '{if("1", r"\x5c\x786e", "NG")}\n'
788 788 \x5c\x786e
789 789 $ hg log -R a -r 0 --template '{if("", "NG", "\x5c\x786e")}\n'
790 790 \x6e
791 791 $ hg log -R a -r 0 --template '{if("", "NG", r"\x5c\x786e")}\n'
792 792 \x5c\x786e
793 793
794 794 $ hg log -R a -r 2 --template '{ifeq("no perso\x6e", desc, "\x5c\x786e", "NG")}\n'
795 795 \x6e
796 796 $ hg log -R a -r 2 --template '{ifeq(r"no perso\x6e", desc, "NG", r"\x5c\x786e")}\n'
797 797 \x5c\x786e
798 798 $ hg log -R a -r 2 --template '{ifeq(desc, "no perso\x6e", "\x5c\x786e", "NG")}\n'
799 799 \x6e
800 800 $ hg log -R a -r 2 --template '{ifeq(desc, r"no perso\x6e", "NG", r"\x5c\x786e")}\n'
801 801 \x5c\x786e
802 802
803 803 $ hg log -R a -r 8 --template '{join(files, "\n")}\n'
804 804 fourth
805 805 second
806 806 third
807 807 $ hg log -R a -r 8 --template '{join(files, r"\n")}\n'
808 808 fourth\nsecond\nthird
809 809
810 810 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", "htm\x6c")}'
811 811 <p>
812 812 1st
813 813 </p>
814 814 <p>
815 815 2nd
816 816 </p>
817 817 $ hg log -R a -r 2 --template '{rstdoc(r"1st\n\n2nd", "html")}'
818 818 <p>
819 819 1st\n\n2nd
820 820 </p>
821 821 $ hg log -R a -r 2 --template '{rstdoc("1st\n\n2nd", r"htm\x6c")}'
822 822 1st
823 823
824 824 2nd
825 825
826 826 $ hg log -R a -r 2 --template '{strip(desc, "\x6e")}\n'
827 827 o perso
828 828 $ hg log -R a -r 2 --template '{strip(desc, r"\x6e")}\n'
829 829 no person
830 830 $ hg log -R a -r 2 --template '{strip("no perso\x6e", "\x6e")}\n'
831 831 o perso
832 832 $ hg log -R a -r 2 --template '{strip(r"no perso\x6e", r"\x6e")}\n'
833 833 no perso
834 834
835 835 $ hg log -R a -r 2 --template '{sub("\\x6e", "\x2d", desc)}\n'
836 836 -o perso-
837 837 $ hg log -R a -r 2 --template '{sub(r"\\x6e", "-", desc)}\n'
838 838 no person
839 839 $ hg log -R a -r 2 --template '{sub("n", r"\\x2d", desc)}\n'
840 840 \x2do perso\x2d
841 841 $ hg log -R a -r 2 --template '{sub("n", "\x2d", "no perso\x6e")}\n'
842 842 -o perso-
843 843 $ hg log -R a -r 2 --template '{sub("n", r"\\x2d", r"no perso\x6e")}\n'
844 844 \x2do perso\x6e
845 845
846 846 $ hg log -R a -r 8 --template '{files % "{file}\n"}'
847 847 fourth
848 848 second
849 849 third
850 850
851 851 Test string escaping in nested expression:
852 852
853 853 $ hg log -R a -r 8 --template '{ifeq(r"\x6e", if("1", "\x5c\x786e"), join(files, "\x5c\x786e"))}\n'
854 854 fourth\x6esecond\x6ethird
855 855 $ hg log -R a -r 8 --template '{ifeq(if("1", r"\x6e"), "\x5c\x786e", join(files, "\x5c\x786e"))}\n'
856 856 fourth\x6esecond\x6ethird
857 857
858 858 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", "\x5c\x786e"))}\n'
859 859 fourth\x6esecond\x6ethird
860 860 $ hg log -R a -r 8 --template '{join(files, ifeq(branch, "default", r"\x5c\x786e"))}\n'
861 861 fourth\x5c\x786esecond\x5c\x786ethird
862 862
863 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\x5c\x786e", "\x5c\x786e"), desc)}\n'
863 $ hg log -R a -r 3:4 --template '{rev}:{sub(if("1", "\x6e"), ifeq(branch, "foo", r"\\x5c\\x786e", "\x5c\x5c\x786e"), desc)}\n'
864 864 3:\x6eo user, \x6eo domai\x6e
865 865 4:\x5c\x786eew bra\x5c\x786ech
866 866
867 867 Test quotes in nested expression are evaluated just like a $(command)
868 868 substitution in POSIX shells:
869 869
870 870 $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n'
871 871 8:95c24699272e
872 872 $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n'
873 873 {8} "95c24699272e"
874 874
875 875 Test recursive evaluation:
876 876
877 877 $ hg init r
878 878 $ cd r
879 879 $ echo a > a
880 880 $ hg ci -Am '{rev}'
881 881 adding a
882 882 $ hg log -r 0 --template '{if(rev, desc)}\n'
883 883 {rev}
884 884 $ hg log -r 0 --template '{if(rev, "{author} {rev}")}\n'
885 885 test 0
886 886
887 887 $ hg branch -q 'text.{rev}'
888 888 $ echo aa >> aa
889 889 $ hg ci -u '{node|short}' -m 'desc to be wrapped desc to be wrapped'
890 890
891 891 $ hg log -l1 --template '{fill(desc, "20", author, branch)}'
892 892 {node|short}desc to
893 893 text.{rev}be wrapped
894 894 text.{rev}desc to be
895 895 text.{rev}wrapped (no-eol)
896 896 $ hg log -l1 --template '{fill(desc, "20", "{node|short}:", "text.{rev}:")}'
897 897 bcc7ff960b8e:desc to
898 898 text.1:be wrapped
899 899 text.1:desc to be
900 900 text.1:wrapped (no-eol)
901 901 $ hg log -l1 -T '{fill(desc, date, "", "")}\n'
902 902 hg: parse error: fill expects an integer width
903 903 [255]
904 904
905 905 $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}'
906 906 {node|short} (no-eol)
907 907 $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}'
908 908 bcc-ff---b-e (no-eol)
909 909
910 910 $ cat >> .hg/hgrc <<EOF
911 911 > [extensions]
912 912 > color=
913 913 > [color]
914 914 > mode=ansi
915 915 > text.{rev} = red
916 916 > text.1 = green
917 917 > EOF
918 918 $ hg log --color=always -l 1 --template '{label(branch, "text\n")}'
919 919 \x1b[0;31mtext\x1b[0m (esc)
920 920 $ hg log --color=always -l 1 --template '{label("text.{rev}", "text\n")}'
921 921 \x1b[0;32mtext\x1b[0m (esc)
922 922
923 923 $ cd ..
924 924
925 925 Test bad template with better error message
926 926
927 927 $ hg log -Gv -R a --template '{desc|user()}'
928 928 hg: parse error: expected a symbol, got 'func'
929 929 [255]
930 930
931 931 Test broken string escapes:
932 932
933 933 $ hg log -T "bogus\\" -R a
934 934 hg: parse error: trailing \ in string
935 935 [255]
936 936 $ hg log -T "\\xy" -R a
937 937 hg: parse error: invalid \x escape* (glob)
938 938 [255]
939 939
940 940 Templater supports aliases of symbol and func() styles:
941 941
942 942 $ hg clone -q a aliases
943 943 $ cd aliases
944 944 $ cat <<EOF >> .hg/hgrc
945 945 > [templatealias]
946 946 > r = rev
947 947 > rn = "{r}:{node|short}"
948 948 > status(c, files) = files % "{c} {file}\n"
949 949 > utcdate(d) = localdate(d, "UTC")
950 950 > EOF
951 951
952 952 $ hg debugtemplate -vr0 '{rn} {utcdate(date)|isodate}\n'
953 953 (template
954 954 (symbol 'rn')
955 955 (string ' ')
956 956 (|
957 957 (func
958 958 (symbol 'utcdate')
959 959 (symbol 'date'))
960 960 (symbol 'isodate'))
961 961 (string '\n'))
962 962 * expanded:
963 963 (template
964 964 (template
965 965 (symbol 'rev')
966 966 (string ':')
967 967 (|
968 968 (symbol 'node')
969 969 (symbol 'short')))
970 970 (string ' ')
971 971 (|
972 972 (func
973 973 (symbol 'localdate')
974 974 (list
975 975 (symbol 'date')
976 976 (string 'UTC')))
977 977 (symbol 'isodate'))
978 978 (string '\n'))
979 979 * keywords: date, node, rev
980 980 * functions: isodate, localdate, short
981 981 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
982 982
983 983 $ hg debugtemplate -vr0 '{status("A", file_adds)}'
984 984 (template
985 985 (func
986 986 (symbol 'status')
987 987 (list
988 988 (string 'A')
989 989 (symbol 'file_adds'))))
990 990 * expanded:
991 991 (template
992 992 (%
993 993 (symbol 'file_adds')
994 994 (template
995 995 (string 'A')
996 996 (string ' ')
997 997 (symbol 'file')
998 998 (string '\n'))))
999 999 * keywords: file, file_adds
1000 1000 * functions:
1001 1001 A a
1002 1002
1003 1003 A unary function alias can be called as a filter:
1004 1004
1005 1005 $ hg debugtemplate -vr0 '{date|utcdate|isodate}\n'
1006 1006 (template
1007 1007 (|
1008 1008 (|
1009 1009 (symbol 'date')
1010 1010 (symbol 'utcdate'))
1011 1011 (symbol 'isodate'))
1012 1012 (string '\n'))
1013 1013 * expanded:
1014 1014 (template
1015 1015 (|
1016 1016 (func
1017 1017 (symbol 'localdate')
1018 1018 (list
1019 1019 (symbol 'date')
1020 1020 (string 'UTC')))
1021 1021 (symbol 'isodate'))
1022 1022 (string '\n'))
1023 1023 * keywords: date
1024 1024 * functions: isodate, localdate
1025 1025 1970-01-12 13:46 +0000
1026 1026
1027 1027 Aliases should be applied only to command arguments and templates in hgrc.
1028 1028 Otherwise, our stock styles and web templates could be corrupted:
1029 1029
1030 1030 $ hg log -r0 -T '{rn} {utcdate(date)|isodate}\n'
1031 1031 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
1032 1032
1033 1033 $ hg log -r0 --config ui.logtemplate='"{rn} {utcdate(date)|isodate}\n"'
1034 1034 0:1e4e1b8f71e0 1970-01-12 13:46 +0000
1035 1035
1036 1036 $ cat <<EOF > tmpl
1037 1037 > changeset = 'nothing expanded:{rn}\n'
1038 1038 > EOF
1039 1039 $ hg log -r0 --style ./tmpl
1040 1040 nothing expanded:
1041 1041
1042 1042 Aliases in formatter:
1043 1043
1044 1044 $ hg branches -T '{pad(branch, 7)} {rn}\n'
1045 1045 default 6:d41e714fe50d
1046 1046 foo 4:bbe44766e73d
1047 1047
1048 1048 Aliases should honor HGPLAIN:
1049 1049
1050 1050 $ HGPLAIN= hg log -r0 -T 'nothing expanded:{rn}\n'
1051 1051 nothing expanded:
1052 1052 $ HGPLAINEXCEPT=templatealias hg log -r0 -T '{rn}\n'
1053 1053 0:1e4e1b8f71e0
1054 1054
1055 1055 Unparsable alias:
1056 1056
1057 1057 $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}'
1058 1058 (template
1059 1059 (symbol 'bad'))
1060 1060 abort: bad definition of template alias "bad": at 2: not a prefix: end
1061 1061 [255]
1062 1062 $ hg log --config templatealias.bad='x(' -T '{bad}'
1063 1063 abort: bad definition of template alias "bad": at 2: not a prefix: end
1064 1064 [255]
1065 1065
1066 1066 $ cd ..
1067 1067
1068 1068 Test that template function in extension is registered as expected
1069 1069
1070 1070 $ cd a
1071 1071
1072 1072 $ cat <<EOF > $TESTTMP/customfunc.py
1073 1073 > from mercurial import registrar
1074 1074 >
1075 1075 > templatefunc = registrar.templatefunc()
1076 1076 >
1077 1077 > @templatefunc(b'custom()')
1078 1078 > def custom(context, mapping, args):
1079 1079 > return b'custom'
1080 1080 > EOF
1081 1081 $ cat <<EOF > .hg/hgrc
1082 1082 > [extensions]
1083 1083 > customfunc = $TESTTMP/customfunc.py
1084 1084 > EOF
1085 1085
1086 1086 $ hg log -r . -T "{custom()}\n" --config customfunc.enabled=true
1087 1087 custom
1088 1088
1089 1089 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now