Compare commits
1031 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd7af1a40e | ||
|
|
9d1d7839b0 | ||
|
|
477c5fd6ab | ||
|
|
4cd984b7b1 | ||
|
|
7993776f86 | ||
|
|
88cc48f40e | ||
| 2051981a24 | |||
| 9af5a53333 | |||
| cce24d2f46 | |||
| 9d7828a714 | |||
|
|
3aab541d9c | ||
| 3682bbc1a9 | |||
|
|
ccfc96f1cb | ||
|
|
7e1c8a3b5e | ||
|
|
5fe78f5627 | ||
|
|
e3f81cf0e4 | ||
|
|
7400e6ed6b | ||
|
|
93394e7054 | ||
|
|
f030154ef6 | ||
|
|
10a2225aae | ||
|
|
de793e5902 | ||
|
|
1fc1884017 | ||
|
|
75ce386b44 | ||
|
|
e60fdafdaa | ||
|
|
d541e0da65 | ||
|
|
59a1729a05 | ||
|
|
112ac54a57 | ||
|
|
0cc2de6179 | ||
|
|
28e268bd26 | ||
|
|
7321febe4f | ||
|
|
96d6c1df9f | ||
|
|
cb058f2e4b | ||
|
|
c564bbc0b7 | ||
|
|
b066a17ebe | ||
|
|
277ce0e226 | ||
|
|
65830dd705 | ||
|
|
53127bf844 | ||
|
|
2ea6dd627e | ||
|
|
db26a28bf9 | ||
|
|
6e9ad1048d | ||
|
|
e98b61f2b5 | ||
|
|
4fa1c0bcfb | ||
|
|
e3dd91d45e | ||
|
|
e768e2c100 | ||
|
|
ac24ec1387 | ||
|
|
3a61289f6c | ||
|
|
9f149a7f35 | ||
|
|
70f83b34ef | ||
|
|
8d3953183a | ||
|
|
2812a8c77a | ||
|
|
3af5ceadc1 | ||
|
|
0d9e402d3e | ||
|
|
dede40bc4e | ||
|
|
3a00956144 | ||
|
|
01add8a34a | ||
|
|
a09712d5d2 | ||
|
|
8074e509ba | ||
|
|
af10774aec | ||
|
|
d54c7e090e | ||
|
|
3937f1efb0 | ||
|
|
3e8190831c | ||
|
|
3c2b74a36f | ||
|
|
0558b6c7aa | ||
|
|
774f6df210 | ||
|
|
1e1c0e8f04 | ||
|
|
caf0f9db3e | ||
|
|
da43078eba | ||
|
|
6fae705b43 | ||
|
|
e4f1e05c1d | ||
|
|
4559287d07 | ||
|
|
2d4776586d | ||
|
|
ed4b932530 | ||
|
|
f0f704fc99 | ||
|
|
403c44b1fa | ||
|
|
493f36ecdd | ||
|
|
5d8f8dac35 | ||
|
|
f5dce013ce | ||
|
|
531881f651 | ||
|
|
7de681aa3b | ||
|
|
04ef785eea | ||
|
|
e1cfb91d42 | ||
|
|
72930708ec | ||
|
|
58763fd49f | ||
|
|
a89c875c49 | ||
|
|
e0f598f91c | ||
|
|
a696b6b155 | ||
|
|
d8770133f0 | ||
|
|
5c32372d42 | ||
|
|
4d7c7531d0 | ||
|
|
c99dab0dae | ||
|
|
3dee86c997 | ||
|
|
df75d1ce43 | ||
|
|
2469943c98 | ||
|
|
072b38d326 | ||
|
|
dc81bf5fb5 | ||
|
|
8e7401d2f4 | ||
|
|
6a79e614da | ||
|
|
4287b33796 | ||
|
|
3b37dc2480 | ||
|
|
dec99b0deb | ||
|
|
3840c4e768 | ||
|
|
793b23411a | ||
|
|
fd549c764b | ||
|
|
339a4ddb51 | ||
|
|
3dbad09f72 | ||
|
|
d480f6a0db | ||
|
|
e21ae0d7d8 | ||
|
|
f6d9837b07 | ||
|
|
d5a026d91a | ||
|
|
1d4baca0d9 | ||
|
|
887b04f7f4 | ||
|
|
d4a9ce614b | ||
|
|
db76a15ba5 | ||
|
|
64ea936d7f | ||
|
|
c010855bac | ||
|
|
2f47226f91 | ||
|
|
d26f79a458 | ||
|
|
ce6ff451b0 | ||
|
|
5466f19441 | ||
|
|
0db0ab1b3c | ||
|
|
fd34737330 | ||
|
|
f48f89fbd9 | ||
|
|
7de0a3afa4 | ||
|
|
c5aba94233 | ||
|
|
b7500776fe | ||
|
|
d29ddec34f | ||
|
|
83f71520d2 | ||
|
|
ee15bfa1db | ||
|
|
f2abee3606 | ||
|
|
c6ec416e30 | ||
|
|
9a193e26ec | ||
|
|
2657880ff3 | ||
|
|
497262d23c | ||
|
|
1b745bc505 | ||
|
|
2620714b43 | ||
|
|
4ebf611b91 | ||
|
|
862ae0b969 | ||
|
|
b634dea7ab | ||
|
|
2931be613b | ||
|
|
6d2ecaa15c | ||
|
|
a8c215774b | ||
|
|
0ae2f2a5fd | ||
|
|
9f5a38df60 | ||
|
|
5e30a65689 | ||
|
|
c1de01d594 | ||
|
|
2dd21de678 | ||
|
|
b0c17d1163 | ||
|
|
36d13508d4 | ||
|
|
fdc290c546 | ||
|
|
cd1d42bc9d | ||
|
|
643166a337 | ||
|
|
4c714e10a3 | ||
|
|
b6910b9428 | ||
|
|
4f14b8923f | ||
|
|
1417be626f | ||
|
|
cad9a4f834 | ||
|
|
ca1e2a5a6b | ||
|
|
79e076c3c0 | ||
|
|
7d567c3ac9 | ||
|
|
351965a87b | ||
|
|
d1b867f7b5 | ||
|
|
52c49169b4 | ||
|
|
720c561b75 | ||
|
|
041248e7cc | ||
|
|
3855ba9136 | ||
|
|
4729876714 | ||
|
|
4ae73ba871 | ||
|
|
10b64adb58 | ||
|
|
cf45f28cc0 | ||
|
|
64243e6ec1 | ||
|
|
e186d375dc | ||
|
|
04bc1a7998 | ||
|
|
82ed5d35b7 | ||
|
|
e1998346e1 | ||
|
|
f04d66e10b | ||
|
|
da49dbdd84 | ||
|
|
ac97523275 | ||
|
|
fb97883bef | ||
|
|
415ce36861 | ||
|
|
18395a8dc0 | ||
|
|
2ac1787dae | ||
|
|
62944f3c7d | ||
|
|
f06eb12d10 | ||
|
|
0e58226f3b | ||
|
|
2821c5ac44 | ||
|
|
b0c9bf6c8d | ||
|
|
b87703eb39 | ||
|
|
863038905c | ||
|
|
2eb488359d | ||
|
|
9030d90894 | ||
|
|
473eb628bc | ||
|
|
672611435a | ||
|
|
3c73af7964 | ||
|
|
e8f183b403 | ||
|
|
3313e1f6cc | ||
|
|
9fd12498ed | ||
|
|
49a5937ace | ||
|
|
bf8fa9a504 | ||
|
|
a6405e8935 | ||
|
|
d457bca59a | ||
|
|
8f8a04fdb5 | ||
|
|
a4ce40add4 | ||
|
|
b8347821c4 | ||
|
|
3f97a034c7 | ||
|
|
74949bb049 | ||
|
|
9e7f259d03 | ||
|
|
b8b06e6680 | ||
|
|
928befeea2 | ||
|
|
e171c0aa9b | ||
|
|
2140298af2 | ||
|
|
adece30891 | ||
|
|
012430cfb6 | ||
|
|
0b20a7eef8 | ||
|
|
82102f59ba | ||
|
|
539dae2148 | ||
|
|
d3d7055103 | ||
|
|
ea2c4d96b4 | ||
|
|
9518cdc1e9 | ||
|
|
290fb4380c | ||
|
|
420892d7a9 | ||
|
|
7153a03a5f | ||
|
|
53b5e5c8ee | ||
|
|
d748650691 | ||
|
|
e850823809 | ||
|
|
d19b3aa837 | ||
|
|
534303795a | ||
|
|
d543b4c625 | ||
|
|
4fdb042ad4 | ||
|
|
276039cf35 | ||
|
|
0ef837bc19 | ||
|
|
f6e7ac2e5e | ||
|
|
cf9f6e958c | ||
|
|
c2e1a9216d | ||
|
|
a1b3cfece4 | ||
|
|
b5cf4ca56e | ||
|
|
a071694324 | ||
|
|
3c4b27608e | ||
|
|
493bbadb18 | ||
|
|
df70bb0173 | ||
|
|
78033ea2d3 | ||
|
|
08d985f40c | ||
|
|
376d336525 | ||
|
|
edfa8ed4ea | ||
|
|
09f9b9e625 | ||
|
|
27055ea1b9 | ||
|
|
9fefe4a378 | ||
|
|
c5ac2e2d00 | ||
|
|
75ad38f244 | ||
|
|
1133f02df7 | ||
|
|
ce924349c1 | ||
|
|
24a68b2ad8 | ||
|
|
06521a1b55 | ||
|
|
3594e1886f | ||
|
|
aacf75d414 | ||
|
|
511a235215 | ||
|
|
6f19a3fbae | ||
|
|
c6267c526d | ||
|
|
90cc2a441d | ||
|
|
286fca3935 | ||
|
|
9bfc8c2ff6 | ||
|
|
e3dab66ad1 | ||
|
|
eabda340ef | ||
|
|
d0bbe2a0e8 | ||
|
|
7441e6cf74 | ||
|
|
a87dfe62ad | ||
|
|
02470c5647 | ||
|
|
c7edf914e1 | ||
|
|
f98084bbd3 | ||
|
|
ef538ef9da | ||
|
|
f492d423dd | ||
|
|
abc00ab6ca | ||
|
|
243f613444 | ||
|
|
8fdd18f897 | ||
|
|
822909d046 | ||
|
|
95becc4f6f | ||
|
|
11a281b862 | ||
|
|
b97e1400e6 | ||
|
|
a8c0646d18 | ||
|
|
98175bac4b | ||
|
|
81e65b43bf | ||
|
|
23d63ccb44 | ||
|
|
59342bc10f | ||
|
|
ea81976ee9 | ||
|
|
53088dae65 | ||
|
|
2623d101e7 | ||
|
|
4f7ca3f2d1 | ||
|
|
0411fd1ddc | ||
|
|
eed9664173 | ||
|
|
7410b01cad | ||
|
|
a9a60c4f9c | ||
|
|
3a79bd908a | ||
|
|
573ad560b6 | ||
|
|
e09699a029 | ||
|
|
a6833c62f8 | ||
|
|
3585d21a41 | ||
|
|
f34b2e23cb | ||
|
|
0e38cdc4a9 | ||
|
|
683141d14a | ||
|
|
36fac4efe1 | ||
|
|
ea4a23654f | ||
|
|
cf42496970 | ||
|
|
6db06524b1 | ||
|
|
a4c72238d0 | ||
|
|
ae27a1b343 | ||
|
|
c7f2739dda | ||
|
|
3d7c681ed0 | ||
|
|
0086a51311 | ||
|
|
ea26369f80 | ||
|
|
49dedfbc86 | ||
|
|
2c89688b46 | ||
|
|
d5a0b616e1 | ||
|
|
e0dbf4b691 | ||
|
|
41491843de | ||
|
|
34c5a68635 | ||
|
|
7be5b86618 | ||
|
|
1ea9721e90 | ||
|
|
0ffadafe3c | ||
|
|
a9adf06d98 | ||
|
|
540484f92f | ||
|
|
238f81f621 | ||
|
|
20508c45f2 | ||
|
|
ef58767834 | ||
|
|
0ef1166b0b | ||
|
|
6e3c556d0b | ||
|
|
02f786694e | ||
|
|
4b485ad570 | ||
|
|
3df06e6482 | ||
|
|
c94f481f2a | ||
|
|
41f2133693 | ||
|
|
2872b44b45 | ||
|
|
5c9d96d5a8 | ||
|
|
3d8be2119e | ||
|
|
569522b007 | ||
|
|
dccb8081bc | ||
|
|
df5cf4ff9c | ||
|
|
03ede837e4 | ||
|
|
e6fd4efd08 | ||
|
|
1cd810f8f6 | ||
|
|
ce1c98ec52 | ||
|
|
42a9352f48 | ||
|
|
a7b915299a | ||
|
|
4819584714 | ||
|
|
d58184efa1 | ||
|
|
1f833dcae2 | ||
|
|
c66e8917c8 | ||
|
|
247355f15b | ||
|
|
5112479372 | ||
|
|
5dd5de0d76 | ||
|
|
0339feecda | ||
|
|
2098b0166f | ||
|
|
28d60d8b10 | ||
|
|
a730a482c3 | ||
|
|
bd836d763c | ||
|
|
a7d75c9071 | ||
|
|
fdc045a230 | ||
|
|
faf6ecceda | ||
|
|
3a2b5df05d | ||
|
|
6c4e861eac | ||
|
|
a21a523896 | ||
|
|
4b77ee5c25 | ||
|
|
8badebca26 | ||
|
|
b5388cb3a4 | ||
|
|
4b75865e47 | ||
|
|
9640f5a02b | ||
|
|
881c38e301 | ||
|
|
20b9ead82a | ||
|
|
fb62263871 | ||
|
|
c5f24f6481 | ||
|
|
c2fad1fa90 | ||
|
|
02f3ce74d7 | ||
|
|
489515968c | ||
|
|
76b7dc3820 | ||
|
|
7f23fe3f64 | ||
|
|
e269626982 | ||
|
|
92adb55c7a | ||
|
|
8e01baa49a | ||
|
|
375294a56c | ||
|
|
765c12a2d1 | ||
|
|
5d08b64c23 | ||
|
|
86f260672b | ||
|
|
98f02b62b2 | ||
|
|
44a94b0b50 | ||
|
|
12171e3318 | ||
|
|
cafe1478e6 | ||
|
|
bbc9c0bb47 | ||
|
|
df7967c6f8 | ||
|
|
7c5dca0553 | ||
|
|
b3aa531f4a | ||
|
|
e830176d13 | ||
|
|
23cc9b23e3 | ||
|
|
dbf76874b9 | ||
|
|
cf1395afd8 | ||
|
|
7b2e45692e | ||
|
|
0e2cb5866c | ||
|
|
c44a6433fd | ||
|
|
a66fe6de73 | ||
|
|
026ac59326 | ||
|
|
4e9267eed2 | ||
|
|
0daaf05782 | ||
|
|
9244828618 | ||
|
|
3d1a430b62 | ||
|
|
4f08212415 | ||
|
|
58ff0e74f4 | ||
|
|
61a377cc29 | ||
|
|
a3bf98deff | ||
|
|
083efc265b | ||
|
|
bbb00ac7ef | ||
|
|
78c2c1c2da | ||
|
|
7123784734 | ||
|
|
47ef45ee38 | ||
|
|
ead5322340 | ||
|
|
67c5bc97ce | ||
|
|
5301e98942 | ||
|
|
cece9201bb | ||
|
|
1857882d9e | ||
|
|
e1fda18164 | ||
|
|
977e2a3c35 | ||
|
|
92ecc4d7fd | ||
|
|
a432cec468 | ||
|
|
f27ddea013 | ||
|
|
e2ffcbd3d6 | ||
|
|
e65f1ecffe | ||
|
|
51241c5a95 | ||
|
|
9925b4e3a9 | ||
|
|
cdb1326219 | ||
|
|
d8bd32aee8 | ||
|
|
7b3d2e5cd6 | ||
|
|
58dce57b7e | ||
|
|
c0281b21f0 | ||
|
|
a43ec0189a | ||
|
|
f6d2fe6093 | ||
|
|
d56487e509 | ||
|
|
4763b1da6e | ||
|
|
8eec5c82c2 | ||
|
|
2854aba5e3 | ||
|
|
b1a593d27e | ||
|
|
61d4cb2a6e | ||
|
|
72383a7d7f | ||
|
|
1fc7dc3951 | ||
|
|
8e87f3f969 | ||
|
|
bc90b9a4e1 | ||
|
|
ff02cf8613 | ||
|
|
7361431799 | ||
|
|
bd41582b97 | ||
|
|
d4edf577eb | ||
|
|
12865e3b85 | ||
|
|
f2e725b3c9 | ||
|
|
f1049b4a7a | ||
|
|
fe93e72f0b | ||
|
|
8d1f66cd41 | ||
|
|
06734487ee | ||
|
|
aefe06b296 | ||
|
|
3576e8f784 | ||
|
|
5cf7c12dc9 | ||
|
|
fc418d1195 | ||
|
|
b942cae98e | ||
|
|
3987828294 | ||
|
|
e25a64af9b | ||
|
|
4ce0113395 | ||
|
|
efff69bc49 | ||
|
|
487d504486 | ||
|
|
47e9aa9abb | ||
|
|
4ad3c48f2d | ||
|
|
bfcb3eac49 | ||
|
|
b30e19b23c | ||
|
|
3f14491e31 | ||
|
|
798cd5b658 | ||
|
|
f24ea5e56c | ||
|
|
216d4d63bc | ||
|
|
018770e894 | ||
|
|
2e2bdb0d0d | ||
|
|
3ad683a370 | ||
|
|
ad21e86f06 | ||
|
|
4e80d5d5fd | ||
|
|
f7dc21d24d | ||
|
|
5e93134ea4 | ||
|
|
8baf544040 | ||
|
|
ec31b93447 | ||
|
|
aefe16a6e2 | ||
|
|
9d26327ae4 | ||
|
|
f767ef2f19 | ||
|
|
3b46553f47 | ||
|
|
b3192b94b4 | ||
|
|
878256306c | ||
|
|
87bcbeda37 | ||
|
|
7465410c5a | ||
|
|
d987c9f5cd | ||
|
|
bb8092a5e4 | ||
|
|
f10d1432f3 | ||
|
|
1f8eae0071 | ||
|
|
9c5e3750c4 | ||
|
|
3c1157fe5d | ||
|
|
de0b54cfb6 | ||
|
|
7fc6d7c5bf | ||
|
|
bbd764d249 | ||
|
|
4c53da5be1 | ||
|
|
eba68ac2ff | ||
|
|
0742e5fbbd | ||
|
|
8bbc35560f | ||
|
|
0ea3215e7a | ||
|
|
d5a7769b4f | ||
|
|
ba372e106f | ||
|
|
f6566838f6 | ||
|
|
ee3e8c46dc | ||
|
|
4afe912f84 | ||
|
|
ac392561c1 | ||
|
|
a1bdb8f99d | ||
|
|
5e52acf63a | ||
|
|
d6930e89ef | ||
|
|
be594918a9 | ||
|
|
d81fb1d17f | ||
|
|
66e3dc8218 | ||
|
|
5a1b069b73 | ||
|
|
71ca9e8c4c | ||
|
|
9078ce5976 | ||
|
|
91b45925e9 | ||
|
|
45dda0c9fd | ||
|
|
79fc4f2d47 | ||
|
|
0d6fee4053 | ||
|
|
1bb34cb165 | ||
|
|
fdc37c037f | ||
|
|
7490663d9e | ||
|
|
0b4fa9472d | ||
|
|
203ee9f1c2 | ||
|
|
3b97209663 | ||
|
|
2929c46d68 | ||
|
|
f640dbb34e | ||
|
|
315c455314 | ||
|
|
9295fa49e4 | ||
|
|
8f2059a4c4 | ||
|
|
129fea0a76 | ||
|
|
dd1633f453 | ||
|
|
a9ff077046 | ||
|
|
2949a7e86a | ||
|
|
b9699bb93e | ||
|
|
1f9bd897a7 | ||
|
|
c1445b3859 | ||
|
|
44b3082234 | ||
|
|
3442e17f83 | ||
|
|
5d3f97819b | ||
|
|
9218462477 | ||
|
|
4c802a6ff7 | ||
|
|
35f4681929 | ||
|
|
943b73b287 | ||
|
|
15da5fc702 | ||
|
|
717e8c3be5 | ||
|
|
d6b989c837 | ||
|
|
6703992cf3 | ||
|
|
412e804b93 | ||
|
|
b162748e26 | ||
|
|
b6d65a434b | ||
|
|
78922d8433 | ||
|
|
14474170f0 | ||
|
|
23c6950692 | ||
|
|
55ba3b11cb | ||
|
|
67dc65a67d | ||
|
|
70ce4eef7e | ||
|
|
82935c585a | ||
|
|
60fc24ce2b | ||
|
|
b3c1d94e75 | ||
|
|
2dba81e460 | ||
|
|
99f92beac9 | ||
|
|
f06ca50df9 | ||
|
|
2485efa5f3 | ||
|
|
2e1e295ba4 | ||
|
|
ec3e14c8f6 | ||
|
|
9841ce1107 | ||
|
|
5c56f9008c | ||
|
|
6009e20aa4 | ||
|
|
850ef84b0b | ||
|
|
0143ced07c | ||
|
|
ce7f2e799e | ||
|
|
f030333766 | ||
|
|
1039320a9c | ||
|
|
d8526a388d | ||
|
|
06a78e1aff | ||
|
|
d6bbdb90b9 | ||
|
|
2e34d9ffd4 | ||
|
|
8269a3a235 | ||
|
|
c4b41734a9 | ||
|
|
23569ad5e7 | ||
|
|
d390b02be5 | ||
|
|
fa71f846ae | ||
|
|
8f3d6c575d | ||
|
|
d424a885bd | ||
|
|
f2483538d2 | ||
|
|
15ceb61d96 | ||
|
|
c5f5b4a1f0 | ||
|
|
c488e1d45c | ||
|
|
6f727d3a39 | ||
|
|
dfde9ba4b8 | ||
|
|
1571981b57 | ||
|
|
e20fb95a43 | ||
|
|
3421f5bc18 | ||
|
|
88968af20b | ||
|
|
42e0c17de7 | ||
|
|
4cbbaec833 | ||
|
|
180e018a36 | ||
|
|
113358fa50 | ||
|
|
37d65a6296 | ||
|
|
ecd0646f41 | ||
|
|
eb6cc1fa77 | ||
|
|
6dffdaa471 | ||
|
|
4dab89f7e6 | ||
|
|
16ac2c70ab | ||
|
|
e7d9aaec4e | ||
|
|
2297b2d3a1 | ||
|
|
de8d9d88ff | ||
|
|
e2e9725812 | ||
|
|
60e5c488ac | ||
|
|
146a72b331 | ||
|
|
65c351f50e | ||
|
|
18c8b41f3f | ||
|
|
f28b68661d | ||
|
|
1751bbaea6 | ||
|
|
b57a734625 | ||
|
|
00bbca275c | ||
|
|
e2271f9a03 | ||
|
|
3eff3d2704 | ||
|
|
bc517cecf3 | ||
|
|
2210528581 | ||
|
|
e458c17f4f | ||
|
|
3a69609ffd | ||
|
|
6641ca13a1 | ||
|
|
6d1857c253 | ||
|
|
29ec7d6bf3 | ||
|
|
fd45b7f93f | ||
|
|
3fb591714a | ||
|
|
c5e0577aee | ||
|
|
95ca400826 | ||
|
|
5125bb840d | ||
|
|
89fffbb4da | ||
|
|
a190fb447d | ||
|
|
06f91ed4b9 | ||
|
|
187ae4b938 | ||
|
|
a1abc46138 | ||
|
|
5c243b6c4c | ||
|
|
4f28f26626 | ||
|
|
0907fbf906 | ||
|
|
4a27ba8bce | ||
|
|
f2177e1254 | ||
|
|
be4ca80842 | ||
|
|
18001a9d2d | ||
|
|
7d2ce304c4 | ||
|
|
24538da200 | ||
|
|
0a2f6caba0 | ||
|
|
eb6af955c3 | ||
|
|
dbfab0a535 | ||
|
|
d2a1b9f18e | ||
|
|
80a3f74852 | ||
|
|
e934065cf8 | ||
|
|
7ef0287467 | ||
|
|
5b42cb37c7 | ||
|
|
a5d7f1093e | ||
|
|
01c095cac7 | ||
|
|
fa654473c9 | ||
|
|
1f0df70ac3 | ||
|
|
c5024ddea4 | ||
|
|
2e0c7418e4 | ||
|
|
a598dc4d3f | ||
|
|
77077fb5f4 | ||
|
|
84f371ca51 | ||
|
|
dd92f7004d | ||
|
|
adea891229 | ||
|
|
c4f060413e | ||
|
|
29f2bce64d | ||
|
|
5915d3834d | ||
|
|
8f7ec80666 | ||
|
|
0a6d606342 | ||
|
|
24882e4ce5 | ||
|
|
595fcef9bb | ||
|
|
98fd46549c | ||
|
|
45ecb72c16 | ||
|
|
b5aadd0899 | ||
|
|
acd61f14fd | ||
|
|
613911dc10 | ||
|
|
24d5be3e14 | ||
|
|
cd4e406dc9 | ||
|
|
6671861e54 | ||
|
|
bc2b8cca52 | ||
|
|
0aa7b4c3e3 | ||
|
|
9e28c62730 | ||
|
|
5806cddfd3 | ||
|
|
6828ce30f5 | ||
|
|
8ea7a88281 | ||
|
|
a206d9405b | ||
|
|
4a0c6995ed | ||
|
|
396e923f22 | ||
|
|
cefc287986 | ||
|
|
fe24e078a4 | ||
|
|
b2beb34e9a | ||
|
|
087cee5425 | ||
|
|
91132a42c8 | ||
|
|
9bf0bf7a39 | ||
|
|
4f6c3afa3e | ||
|
|
f337bb6458 | ||
|
|
e99786e726 | ||
|
|
819dbc5285 | ||
|
|
02efddca75 | ||
|
|
809a1ff357 | ||
|
|
952a299914 | ||
|
|
67af4137af | ||
|
|
9a88084edc | ||
|
|
95bac8b612 | ||
|
|
7894e16e43 | ||
|
|
0c19c3cde1 | ||
|
|
b2188abb5d | ||
|
|
8ace57bbef | ||
|
|
8b766894b7 | ||
|
|
5ecb00fe0e | ||
|
|
04453f5f24 | ||
|
|
dd54c26f9a | ||
|
|
fa3abfad2e | ||
|
|
28948fc7b2 | ||
|
|
792a4e1445 | ||
|
|
dc7efab0ab | ||
|
|
899547dcc8 | ||
|
|
728390b6e5 | ||
|
|
ffb7f94e2a | ||
|
|
e8517e748d | ||
|
|
02fde10c12 | ||
|
|
b0c60f1c08 | ||
|
|
9ccb83be57 | ||
|
|
424592b873 | ||
|
|
d772eb6add | ||
|
|
a9651883cd | ||
|
|
ff67f8c791 | ||
|
|
c690a3d58d | ||
|
|
3e7958619a | ||
|
|
54c1ca0786 | ||
|
|
241fbe0581 | ||
|
|
5ff5a5c1ae | ||
|
|
a60c373caf | ||
|
|
8dcf0d0c72 | ||
|
|
ce97fd5901 | ||
|
|
e71be41e72 | ||
|
|
ee49ce3165 | ||
|
|
b21e0baa47 | ||
|
|
444e16fdd2 | ||
|
|
5e98ac943b | ||
|
|
116d192584 | ||
|
|
3ec14a8844 | ||
|
|
bdd5bc90c4 | ||
|
|
e096462005 | ||
|
|
24ae19b8e5 | ||
|
|
61685714c6 | ||
|
|
53310d1ea0 | ||
|
|
12e30bf969 | ||
|
|
264cc7e2fe | ||
|
|
14e047e7c4 | ||
|
|
f5bcec66e5 | ||
|
|
9dd2365485 | ||
|
|
bb45ed3fce | ||
|
|
a951332367 | ||
|
|
f8578ecc28 | ||
|
|
8c0bfb030a | ||
|
|
04043d267f | ||
|
|
7e629ef30a | ||
|
|
fab2979b87 | ||
|
|
6c9660aee8 | ||
|
|
fdcb084df6 | ||
|
|
5fb7b24011 | ||
|
|
1fa467aa9f | ||
|
|
0fc6b46c4c | ||
|
|
e18b4b2063 | ||
|
|
f4b2589149 | ||
|
|
d99eb3c970 | ||
|
|
3fa0e7c1ad | ||
|
|
92e289cb3d | ||
|
|
0e4f11afc5 | ||
|
|
49d5a16db3 | ||
|
|
33d7ea6569 | ||
|
|
b55ef724f6 | ||
|
|
6057ecc83a | ||
|
|
0150df0b05 | ||
|
|
df8e951324 | ||
|
|
e2d9ae379b | ||
|
|
a2684a6a12 | ||
|
|
b8641d7d5e | ||
|
|
14b632f1a5 | ||
|
|
7470af24a9 | ||
|
|
b45e226db9 | ||
|
|
e1e21f30fe | ||
|
|
29e4b21a8b | ||
|
|
19e5ed6313 | ||
|
|
a91cd72829 | ||
|
|
56530a1f24 | ||
|
|
daabfcaf91 | ||
|
|
97400df68d | ||
|
|
79ffa53ace | ||
|
|
25e326c55c | ||
|
|
5dfc367420 | ||
|
|
e1deef327f | ||
|
|
7747cf07e7 | ||
|
|
5fc5fbc8e7 | ||
|
|
8470763c31 | ||
|
|
546357662f | ||
|
|
5f8de9fd2c | ||
|
|
1264ffa4b4 | ||
|
|
35421403cf | ||
|
|
13b8283666 | ||
|
|
1016dd4a40 | ||
|
|
88516acdaa | ||
|
|
ea0fa20579 | ||
|
|
778d2af36e | ||
|
|
934f6aeada | ||
|
|
72463f414c | ||
|
|
a67ce9db0e | ||
|
|
55854907a2 | ||
|
|
304a6f7a33 | ||
|
|
41234c4f97 | ||
|
|
40b8aab7d4 | ||
|
|
44522b11bc | ||
|
|
3c879e868a | ||
|
|
c64e019f29 | ||
|
|
afe6bdbfb3 | ||
|
|
73e373527f | ||
|
|
d8d5b14e66 | ||
|
|
70ad78d234 | ||
|
|
57f6e0c1a5 | ||
|
|
d455d2b9bf | ||
|
|
af4222d7d2 | ||
|
|
e15bf5105c | ||
|
|
c3d47f02d3 | ||
|
|
4c8456a86d | ||
|
|
29300ea9e1 | ||
|
|
cdac253556 | ||
|
|
7001e38c30 | ||
|
|
6c90de1974 | ||
|
|
17fc9e5a91 | ||
|
|
cc438b4411 | ||
|
|
d8d5a15321 | ||
|
|
9be1494357 | ||
|
|
2c07858bd3 | ||
|
|
74e50359b5 | ||
|
|
31eb477a0e | ||
|
|
142d097ef0 | ||
|
|
c69dfba6bb | ||
|
|
c1d4cea638 | ||
|
|
245b0c58c6 | ||
|
|
9042477415 | ||
|
|
d551dff4cf | ||
|
|
2ec89e68ba | ||
|
|
ff6dcee84c | ||
|
|
8db15d143b | ||
|
|
a4c2ec0440 | ||
|
|
cd2fe40a59 | ||
|
|
7a0707342b | ||
|
|
624ccd12fc | ||
|
|
a79e1d65a7 | ||
|
|
0721aae353 | ||
|
|
abac5bdf48 | ||
|
|
b463c56c93 | ||
|
|
2fb589b131 | ||
|
|
b53a585756 | ||
|
|
6b55c54af9 | ||
|
|
86d3ec5cee | ||
|
|
d26fa9b04b | ||
|
|
0467637500 | ||
|
|
1a4db03d5e | ||
|
|
28bcb7e491 | ||
|
|
c0c476e53d | ||
|
|
26aa2761c0 | ||
|
|
cf4bd29d44 | ||
|
|
12459e8c8b | ||
|
|
43d49d17d0 | ||
|
|
b344f95e97 | ||
|
|
6bfb3fab3c | ||
|
|
c571f6b4fb | ||
|
|
f684e821d4 | ||
|
|
06f177534d | ||
|
|
a6c8303839 | ||
|
|
ec84d0f372 | ||
|
|
026a8aa9a1 | ||
|
|
b4e4727887 | ||
|
|
14e3038571 | ||
|
|
9ae171f57c | ||
|
|
b9f55997ae | ||
|
|
ecd0ae1b16 | ||
|
|
98278eff2a | ||
|
|
f0988b52d0 | ||
|
|
4232f1a599 | ||
|
|
8462120c0a | ||
|
|
a79d4cb803 | ||
|
|
8c35f0a4ce | ||
|
|
232d266fb5 | ||
|
|
db0f8d33e1 | ||
|
|
f99fc16e14 | ||
|
|
4bbb5fadb3 | ||
|
|
488673ea9a | ||
|
|
060787a5db | ||
|
|
8a43f4902d | ||
|
|
0e56abd0f9 | ||
|
|
2e06bb6561 | ||
|
|
3d3ad553a6 | ||
|
|
a821c886b3 | ||
|
|
b323069419 | ||
|
|
992b41b82a | ||
|
|
3be7803d30 | ||
|
|
80ea2bb51d | ||
|
|
764ada3a17 | ||
|
|
570cd3d810 | ||
|
|
197f130593 | ||
|
|
e3d006f867 | ||
|
|
ae128f587d | ||
|
|
ff36a87551 | ||
|
|
14f1002680 | ||
|
|
89b8c9f198 | ||
|
|
c7cb2f26ff | ||
|
|
0849501ef5 | ||
|
|
ced7490df6 | ||
|
|
d5cb31922e | ||
|
|
d9c72e8f58 | ||
|
|
a50dc97332 | ||
|
|
c4909ac657 | ||
|
|
e1adf6766b | ||
|
|
772c68ff62 | ||
|
|
952171337c | ||
|
|
660cd61da7 | ||
|
|
cf66c2a191 | ||
|
|
9a85c45bf7 | ||
|
|
7ab0e9c722 | ||
|
|
4079fb2326 | ||
|
|
6bf05083bd | ||
|
|
a9061dc299 | ||
|
|
161ebc1166 | ||
|
|
6345b0c7de | ||
|
|
6ad736346f | ||
|
|
4792bc8c3a | ||
|
|
6c62617fd0 | ||
|
|
67feb5bab7 | ||
|
|
814d7d69fa | ||
|
|
35e518cbc2 | ||
|
|
99a7cfceac | ||
|
|
1ebf75e7d0 | ||
|
|
b3fe15dc41 | ||
|
|
67d4510e9b | ||
|
|
7ad546504c | ||
|
|
d9b528351c | ||
|
|
4f003cdb60 | ||
|
|
f04cc0c9e6 | ||
|
|
1fad357f26 | ||
|
|
43e1a2c991 | ||
|
|
2e11c3c104 | ||
|
|
b7e71a1c3c | ||
|
|
c5d32c58f9 | ||
|
|
98611888af | ||
|
|
e0aea4a5f5 | ||
|
|
1f2c5bfed7 | ||
|
|
04c068e514 | ||
|
|
555dc98f5b | ||
|
|
61763f278f | ||
|
|
b6ef010595 | ||
|
|
5de313272f | ||
|
|
d0251642bc | ||
|
|
1ca91b9e0c | ||
|
|
9a63bd5d6f | ||
|
|
a7900205e5 | ||
|
|
dbb22f506d | ||
|
|
3cf4f337e7 | ||
|
|
22baaecd14 | ||
|
|
db182f4612 | ||
|
|
d33a62ffa6 | ||
|
|
83ddb3f99f | ||
|
|
35ab335cb2 | ||
|
|
395329fb6c | ||
|
|
1efeea3884 | ||
|
|
54aaac9964 | ||
|
|
071adeeba4 | ||
|
|
6358b22289 | ||
|
|
edeac7e6a2 | ||
|
|
62944cdf6f | ||
|
|
8e8e0201f0 | ||
|
|
0820549b4c | ||
|
|
c99754391f | ||
|
|
271d55506a | ||
|
|
bac71c7670 | ||
|
|
0683e14777 | ||
|
|
7d2d06dbcd | ||
|
|
038126d59c | ||
|
|
f29583d81d | ||
|
|
a7153a082f | ||
|
|
079e2f3008 | ||
|
|
c6e190118f | ||
|
|
ea2faa0607 | ||
|
|
4395818e72 | ||
|
|
dde7e6781d | ||
|
|
300e7e125e | ||
|
|
8dfd71ccc6 | ||
|
|
084383a644 | ||
|
|
1ebc8034b4 | ||
|
|
81ac8e2be9 | ||
|
|
c44900bf8f | ||
|
|
ea8a499769 | ||
|
|
7f91e014a0 | ||
|
|
6d6bdebb57 | ||
|
|
1980df89c0 | ||
|
|
c42a73d572 | ||
|
|
8a50d8b6a9 | ||
|
|
75403c0ec1 | ||
|
|
726c31f8de | ||
|
|
dbec3ad33f | ||
|
|
3cb974de49 | ||
|
|
75b220dbc4 | ||
|
|
cae0f15245 | ||
|
|
46a36a10a5 | ||
|
|
2467200a3b | ||
|
|
181f098958 | ||
|
|
3e65c3af5e | ||
|
|
c04bdc48aa | ||
|
|
894eb3ab79 | ||
|
|
58cee899b0 | ||
|
|
1091cc23af | ||
|
|
233d8e3ebf | ||
|
|
ec1a7b4b40 | ||
|
|
ffce61da4b | ||
|
|
909b2e2713 | ||
|
|
70a0e256b0 | ||
|
|
f10a3d6232 | ||
|
|
cbdd04e506 | ||
|
|
bf4b08efd2 | ||
|
|
2980f5d3ce | ||
|
|
677c2e87b5 | ||
|
|
48167f53eb | ||
|
|
9e5a0f0e61 | ||
|
|
abefdd506c | ||
|
|
88bd2fc9e2 | ||
|
|
aa0db81300 | ||
|
|
a12fb177e2 | ||
|
|
2d77ad8aab | ||
|
|
9486eb2fde |
21
.gitea/workflows/release.yml
Normal file
21
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
on:
|
||||||
|
- push
|
||||||
|
jobs:
|
||||||
|
Release-Src:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: True
|
||||||
|
- name: Create Archive from full repo with submodule
|
||||||
|
run: |
|
||||||
|
mkdir /tmp/release
|
||||||
|
tar -czf /tmp/release/taskwarrior2-full.tar.gz *
|
||||||
|
ls /tmp/release
|
||||||
|
- name: Release Archive
|
||||||
|
uses: https://gitea.com/actions/gitea-release-action@v1
|
||||||
|
with:
|
||||||
|
files: /tmp/release/*
|
||||||
|
sha256sum: True
|
||||||
|
|
||||||
@@ -9,9 +9,6 @@ jobs:
|
|||||||
- name: "Centos 8"
|
- name: "Centos 8"
|
||||||
runner: ubuntu-latest
|
runner: ubuntu-latest
|
||||||
dockerfile: centos8
|
dockerfile: centos8
|
||||||
- name: "Fedora 31"
|
|
||||||
runner: ubuntu-latest
|
|
||||||
dockerfile: fedora31
|
|
||||||
- name: "Fedora 32"
|
- name: "Fedora 32"
|
||||||
runner: ubuntu-latest
|
runner: ubuntu-latest
|
||||||
dockerfile: fedora32
|
dockerfile: fedora32
|
||||||
@@ -21,6 +18,9 @@ jobs:
|
|||||||
- name: "Fedora 34"
|
- name: "Fedora 34"
|
||||||
runner: ubuntu-latest
|
runner: ubuntu-latest
|
||||||
dockerfile: fedora34
|
dockerfile: fedora34
|
||||||
|
- name: "Fedora 35"
|
||||||
|
runner: ubuntu-latest
|
||||||
|
dockerfile: fedora35
|
||||||
- name: "Debian Testing"
|
- name: "Debian Testing"
|
||||||
runner: ubuntu-latest
|
runner: ubuntu-latest
|
||||||
dockerfile: debiantesting
|
dockerfile: debiantesting
|
||||||
@@ -33,9 +33,12 @@ jobs:
|
|||||||
- name: "Ubuntu 21.04"
|
- name: "Ubuntu 21.04"
|
||||||
runner: ubuntu-latest
|
runner: ubuntu-latest
|
||||||
dockerfile: ubuntu2104
|
dockerfile: ubuntu2104
|
||||||
- name: "OpenSUSE 15.0"
|
- name: "Ubuntu 21.10"
|
||||||
runner: ubuntu-latest
|
runner: ubuntu-latest
|
||||||
dockerfile: opensuse1500
|
dockerfile: ubuntu2110
|
||||||
|
- name: "OpenSUSE 15"
|
||||||
|
runner: ubuntu-latest
|
||||||
|
dockerfile: opensuse15
|
||||||
- name: "Archlinux Base (Rolling)"
|
- name: "Archlinux Base (Rolling)"
|
||||||
runner: ubuntu-latest
|
runner: ubuntu-latest
|
||||||
dockerfile: arch
|
dockerfile: arch
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required (VERSION 3.0)
|
cmake_minimum_required (VERSION 3.5)
|
||||||
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
include (CheckFunctionExists)
|
include (CheckFunctionExists)
|
||||||
@@ -9,7 +9,7 @@ set (HAVE_CMAKE true)
|
|||||||
project (task)
|
project (task)
|
||||||
include (CXXSniffer)
|
include (CXXSniffer)
|
||||||
|
|
||||||
set (PROJECT_VERSION "2.6.0")
|
set (PROJECT_VERSION "2.6.2")
|
||||||
|
|
||||||
OPTION (ENABLE_WASM "Enable 'wasm' support" OFF)
|
OPTION (ENABLE_WASM "Enable 'wasm' support" OFF)
|
||||||
|
|
||||||
|
|||||||
48
ChangeLog
48
ChangeLog
@@ -1,4 +1,45 @@
|
|||||||
2.6.0 () -
|
------ current release ---------------------------
|
||||||
|
2.6.2 -
|
||||||
|
|
||||||
|
- TW #502 Sequence of IDs doesn't work with attribute "depends"
|
||||||
|
Thanks to Andreas Kalex and Reg for reporting.
|
||||||
|
- TW #2648 xdg-open is not available on Mac OS-X
|
||||||
|
Thanks to chapterjson for reporting.
|
||||||
|
- TW #2655 The bulk removal of depends: and tags: is ignored
|
||||||
|
Thanks to angelus2014 for reporting.
|
||||||
|
- TW #2664 Tag exclusion should be detected as invalid write context
|
||||||
|
Thanks to bentwitthold for reporting.
|
||||||
|
- TW #2671 The value of soww named date is incorrect
|
||||||
|
Thanks to Lennart Kill for reporting.
|
||||||
|
- TW #2689 Corruption of the depends attribute upon syncing with taskd 1.1.0
|
||||||
|
Thanks to Klaus Ethgen for reporting, Dusting J. Mitchell for
|
||||||
|
contributing.
|
||||||
|
- TW #2707 Assigning dependencies via ID ranges
|
||||||
|
Thanks to jaker-dotcom for reporting.
|
||||||
|
- TW #2748 Recurring report does not include parent tasks
|
||||||
|
Thanks to Klaus Ethgen for reporting.
|
||||||
|
|
||||||
|
2.6.1 (2021-10-19) - a696b6b155f9c8af87a6a496c0d298c58e6fe369
|
||||||
|
|
||||||
|
- TW #2619 Fish autocompletion fails when completing tag removal
|
||||||
|
Thanks to Alexandre Provencio for reporting, Tin Lai and Alexandre
|
||||||
|
Provencio for contributing.
|
||||||
|
- TW #2620 Old-style context definition should only be interpreted as read
|
||||||
|
context.
|
||||||
|
Thanks to bongoman and Tom Dörr for reporting.
|
||||||
|
- TW #2622 Tags and dependencies appear as orphaned UDAs.
|
||||||
|
Thanks to Scott Mcdermott for reporting.
|
||||||
|
- TW #2626 Waiting report lists deleted and/or completed tasks.
|
||||||
|
Thanks to Don Harper for reporting.
|
||||||
|
- TW #2629 New-style context definition should take precedence over old-style
|
||||||
|
even if the new-style is an empty string.
|
||||||
|
Thanks to Denis Kasak for reporting.
|
||||||
|
- TW #2632 Cannot build on Cygwin
|
||||||
|
Thanks to Michael Esemplare for contributing.
|
||||||
|
- TW #2639 Unable to use ranges for some tasks with IDs above 1000.
|
||||||
|
Thanks to Manuel Haussmann for reporting.
|
||||||
|
|
||||||
|
2.6.0 (2021-10-03) - 8174287f917a3b24c19a6601ba36fca9bdaa78c9
|
||||||
|
|
||||||
- TW #1654 "Due" parsing behaviour seems inconsistent
|
- TW #1654 "Due" parsing behaviour seems inconsistent
|
||||||
Thanks to Jim B for reporting.
|
Thanks to Jim B for reporting.
|
||||||
@@ -134,16 +175,13 @@
|
|||||||
Thanks to bharatvaj for contributing.
|
Thanks to bharatvaj for contributing.
|
||||||
- TW #2581 Config entry with a trailing comment cannot be modified
|
- TW #2581 Config entry with a trailing comment cannot be modified
|
||||||
|
|
||||||
|
------ old releases ------------------------------
|
||||||
------ current release ---------------------------
|
|
||||||
|
|
||||||
2.5.3 (2021-01-05) - 2f47226f91f0b02f7617912175274d9eed85924f
|
2.5.3 (2021-01-05) - 2f47226f91f0b02f7617912175274d9eed85924f
|
||||||
|
|
||||||
- #2375 task hangs then dies when certain tasks are present in a report
|
- #2375 task hangs then dies when certain tasks are present in a report
|
||||||
Thanks to Max Rossmannek, Tomáš Janoušek and Chad Phillips.
|
Thanks to Max Rossmannek, Tomáš Janoušek and Chad Phillips.
|
||||||
|
|
||||||
------ old releases ------------------------------
|
|
||||||
|
|
||||||
2.5.2 (2020-12-05) - b0c17d11639dc6e783befd89c8508f2abb9b4287
|
2.5.2 (2020-12-05) - b0c17d11639dc6e783befd89c8508f2abb9b4287
|
||||||
|
|
||||||
- TD-64 sync conflict deleted all annotations of the task
|
- TD-64 sync conflict deleted all annotations of the task
|
||||||
|
|||||||
12
DEVELOPER.md
12
DEVELOPER.md
@@ -11,7 +11,7 @@
|
|||||||
```
|
```
|
||||||
$ git clone --recursive https://github.com/GothenburgBitFactory/taskwarrior taskwarrior.git
|
$ git clone --recursive https://github.com/GothenburgBitFactory/taskwarrior taskwarrior.git
|
||||||
$ cd taskwarrior.git
|
$ cd taskwarrior.git
|
||||||
$ git checkout 2.6.0 # Latest dev branch
|
$ git checkout develop # Latest dev branch
|
||||||
$ git submodule init # This is now done by cmake as a test
|
$ git submodule init # This is now done by cmake as a test
|
||||||
$ git submodule update # Update the libhsared.git submodule
|
$ git submodule update # Update the libhsared.git submodule
|
||||||
$ cmake -DCMAKE_BUILD_TYPE=debug . # debug or release. Default: neither
|
$ cmake -DCMAKE_BUILD_TYPE=debug . # debug or release. Default: neither
|
||||||
@@ -144,13 +144,3 @@
|
|||||||
is possible that a patch may be rejected because it conflicts in some way with
|
is possible that a patch may be rejected because it conflicts in some way with
|
||||||
plans or upcoming changes. Check with us first, before sinking time and effort
|
plans or upcoming changes. Check with us first, before sinking time and effort
|
||||||
into a patch.
|
into a patch.
|
||||||
|
|
||||||
# Current Code Base Condition
|
|
||||||
|
|
||||||
**'master' branch**:
|
|
||||||
* 2.5.3 Current release, locked.
|
|
||||||
|
|
||||||
**'2.6.0' branch**:
|
|
||||||
* Current development branch no plans yet.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|||||||
4
NEWS
4
NEWS
@@ -39,8 +39,8 @@ New Features in Taskwarrior 2.6.0
|
|||||||
whole units like days, e.g. 'add test due:2021-07-17' would not match
|
whole units like days, e.g. 'add test due:2021-07-17' would not match
|
||||||
'due.before:tomorrow' (on the 16th), but would match 'due.by:tomorrow'.
|
'due.before:tomorrow' (on the 16th), but would match 'due.by:tomorrow'.
|
||||||
- Waiting is now an entirely "virtual" concept, based on a task's
|
- Waiting is now an entirely "virtual" concept, based on a task's
|
||||||
'wait' property and the current time. Task is consiered "waiting" if its
|
'wait' property and the current time. Task is considered "waiting" if its
|
||||||
wait attribute is in the future. TaskWarrior no longer explicitly
|
wait attribute is in the future. TaskWarrior no longer explicitly
|
||||||
"unwaits" a task (the wait attribute is not removed once its value is in
|
"unwaits" a task (the wait attribute is not removed once its value is in
|
||||||
the past), so the "unwait' verbosity token is no longer available.
|
the past), so the "unwait' verbosity token is no longer available.
|
||||||
This allows for filtering for tasks that were waiting in the past
|
This allows for filtering for tasks that were waiting in the past
|
||||||
|
|||||||
35
README.md
35
README.md
@@ -1,10 +1,9 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="https://avatars.githubusercontent.com/u/36100920?s=200&u=24da05914c20c4ccfe8485310f7b83049407fa9a&v=4"></br>
|
<img src="https://avatars.githubusercontent.com/u/36100920?s=200&u=24da05914c20c4ccfe8485310f7b83049407fa9a&v=4"></br>
|
||||||
|
|
||||||
[](https://github.com/GothenburgBitFactory/taskwarrior/actions)
|
[](https://github.com/GothenburgBitFactory/taskwarrior/actions)
|
||||||
[](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest)
|
[](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest)
|
||||||
[](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest)
|
[](https://github.com/GothenburgBitFactory/taskwarrior/releases/latest)
|
||||||

|
|
||||||
[](https://github.com/sponsors/GothenburgBitFactory/)
|
[](https://github.com/sponsors/GothenburgBitFactory/)
|
||||||
</br>
|
</br>
|
||||||
[](https://twitter.com/taskwarrior)
|
[](https://twitter.com/taskwarrior)
|
||||||
@@ -20,6 +19,14 @@ features](https://taskwarrior.org/docs/), developed as a portable open source pr
|
|||||||
with an active and quite vast [ecosystem of tools, hooks and
|
with an active and quite vast [ecosystem of tools, hooks and
|
||||||
extensions](https://taskwarrior.org/tools/).
|
extensions](https://taskwarrior.org/tools/).
|
||||||
|
|
||||||
|
### HEADS UP!
|
||||||
|
### This fork is to intended to preserve the taskwarrior 2.6.2 codebase, and to allow for further fixes and improvements!!
|
||||||
|
It's great to see that taskwarrior is still an active project and the active developers, working on version 3.x, have a vision that fuses taskchampion and an SQL database. It's cool, and in rust, and everything.. but they are decidedly uninterested in further work on the 2.x (data as text files) codebase.
|
||||||
|
|
||||||
|
It was the fact that task used a text-file to store data, that first hooked me to the project. I'm almost certainly first taskwarrior user, and technically the original taskwarrior Designer! I worked with the orginal programmer, Paul Beckingham, as he brilliantly implemented so many of my crazy ideas, like colors, urgency, reports, UDAs, attribute modifiers and SO much more!
|
||||||
|
|
||||||
|
Because I'm an old fart, set im my ways like that, and kind of squeamish about keeping my task data as a SQL database, this repo is somewhare I can try to apply fixes and cherry-pick from changes made after official 2.6.2 releases, and to curate some of the things that I think belong with it. That said, I'm a terrible programmer, a dubious developer, and would welcome input, ideas and contributions.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
[](https://archlinux.org/packages/community/x86_64/task/)
|
[](https://archlinux.org/packages/community/x86_64/task/)
|
||||||
[](https://packages.debian.org/search?keywords=task&searchon=names&suite=all§ion=all)
|
[](https://packages.debian.org/search?keywords=task&searchon=names&suite=all§ion=all)
|
||||||
@@ -64,13 +71,15 @@ For code contributions, please use pull requests, or alternately send your code
|
|||||||
|
|
||||||
We use the following branching model:
|
We use the following branching model:
|
||||||
|
|
||||||
* `master` is the stable branch. Building from here is the same as building
|
* `stable` is a branch containing the content of the latest release. Building
|
||||||
from the latest tarball, or installing a binary package. No development is
|
from here is the same as building from the latest tarball, or installing a
|
||||||
done on the `master` branch.
|
binary package. No development is done on the `stable` branch.
|
||||||
|
|
||||||
* `2.6.0` is the current development branch. All work is done here, and upon
|
* `develop` is the current development branch. All work is done here, and upon
|
||||||
release it will be merged to `master`. This development branch is not stable,
|
release it will be merged to `stable`. While development branch is not
|
||||||
and should be treated accordingly. Make backups.
|
stable, we utilize CI to ensure we're at least not merging improvements that
|
||||||
|
break existing tests, and hence should be relatively safe. We still recommend
|
||||||
|
making backups when using the development branch.
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
@@ -80,18 +89,18 @@ There are many binary packages available, but to install from source requires:
|
|||||||
* cmake
|
* cmake
|
||||||
* make
|
* make
|
||||||
* C++ compiler, currently gcc 7.1+ or clang 5.0+ for full C++17 support
|
* C++ compiler, currently gcc 7.1+ or clang 5.0+ for full C++17 support
|
||||||
* libuuid
|
* uuid-dev (was libuuid-dev)
|
||||||
* GnuTLS (optional, required for sync)
|
* GnuTLS (optional, required for sync)
|
||||||
|
|
||||||
Download the tarball, and expand it:
|
Download the tarball, and expand it:
|
||||||
|
|
||||||
$ curl -O https://taskwarrior.org/download/task-2.6.0.tar.gz
|
$ curl -O https://taskwarrior.org/download/task-2.6.2.tar.gz
|
||||||
$ tar xzf task-2.6.0.tar.gz
|
$ tar xzf task-2.6.2.tar.gz
|
||||||
$ cd task-2.6.0
|
$ cd task-2.6.2
|
||||||
|
|
||||||
Or clone this repository:
|
Or clone this repository:
|
||||||
|
|
||||||
$ git clone --recursive -b 2.6.0 https://github.com/GothenburgBitFactory/taskwarrior.git
|
$ git clone --recursive -b stable https://github.com/GothenburgBitFactory/taskwarrior.git
|
||||||
$ cd taskwarrior
|
$ cd taskwarrior
|
||||||
|
|
||||||
Then build:
|
Then build:
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "GNU")
|
|||||||
set (GNUHURD true)
|
set (GNUHURD true)
|
||||||
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN")
|
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN")
|
||||||
set (CYGWIN true)
|
set (CYGWIN true)
|
||||||
|
set (_CXX14_FLAGS "-std=gnu++17")
|
||||||
else (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
else (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
set (UNKNOWN true)
|
set (UNKNOWN true)
|
||||||
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
|
|||||||
@@ -16,14 +16,6 @@ services:
|
|||||||
security_opt:
|
security_opt:
|
||||||
- label=type:container_runtime_t
|
- label=type:container_runtime_t
|
||||||
tty: true
|
tty: true
|
||||||
test-fedora31:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: test/docker/fedora31
|
|
||||||
network_mode: "host"
|
|
||||||
security_opt:
|
|
||||||
- label=type:container_runtime_t
|
|
||||||
tty: true
|
|
||||||
test-fedora32:
|
test-fedora32:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
@@ -48,6 +40,14 @@ services:
|
|||||||
security_opt:
|
security_opt:
|
||||||
- label=type:container_runtime_t
|
- label=type:container_runtime_t
|
||||||
tty: true
|
tty: true
|
||||||
|
test-fedora35:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: test/docker/fedora35
|
||||||
|
network_mode: "host"
|
||||||
|
security_opt:
|
||||||
|
- label=type:container_runtime_t
|
||||||
|
tty: true
|
||||||
test-ubuntu1804:
|
test-ubuntu1804:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
@@ -72,6 +72,14 @@ services:
|
|||||||
security_opt:
|
security_opt:
|
||||||
- label=type:container_runtime_t
|
- label=type:container_runtime_t
|
||||||
tty: true
|
tty: true
|
||||||
|
test-ubuntu2110:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: test/docker/ubuntu2110
|
||||||
|
network_mode: "host"
|
||||||
|
security_opt:
|
||||||
|
- label=type:container_runtime_t
|
||||||
|
tty: true
|
||||||
test-debianstable:
|
test-debianstable:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
@@ -96,10 +104,10 @@ services:
|
|||||||
security_opt:
|
security_opt:
|
||||||
- label=type:container_runtime_t
|
- label=type:container_runtime_t
|
||||||
tty: true
|
tty: true
|
||||||
test-opensuse1500:
|
test-opensuse15:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: test/docker/opensuse1500
|
dockerfile: test/docker/opensuse15
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
security_opt:
|
security_opt:
|
||||||
- label=type:container_runtime_t
|
- label=type:container_runtime_t
|
||||||
|
|||||||
1
index.html
Normal file
1
index.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Taskwarrior Docs
|
||||||
@@ -107,7 +107,7 @@ function __fish.task.need_to_complete.attr_value
|
|||||||
or return 1
|
or return 1
|
||||||
# only start completion when there's a colon in attr_name
|
# only start completion when there's a colon in attr_name
|
||||||
set -l cmd (commandline -ct)
|
set -l cmd (commandline -ct)
|
||||||
string match -q "*:*" "$cmd[-1]"
|
string match -q -- "*:*" "$cmd[-1]"
|
||||||
end
|
end
|
||||||
|
|
||||||
function __fish.task.need_to_complete.command
|
function __fish.task.need_to_complete.command
|
||||||
@@ -251,11 +251,11 @@ function __fish.task.list.dates
|
|||||||
echo -e (string replace --all -r "^|\n" "\n$user_input_numeric" $__fish_task_static_reldates | string collect)
|
echo -e (string replace --all -r "^|\n" "\n$user_input_numeric" $__fish_task_static_reldates | string collect)
|
||||||
# special cases for 1st, 2nd and 3rd, and 4-0th
|
# special cases for 1st, 2nd and 3rd, and 4-0th
|
||||||
set -l suffix 'th' '4th, 5th, etc.'
|
set -l suffix 'th' '4th, 5th, etc.'
|
||||||
if string match -q "*1" $user_input_numeric
|
if string match -q -- "*1" $user_input_numeric
|
||||||
set suffix 'st' 'first'
|
set suffix 'st' 'first'
|
||||||
else if string match -q "*2" $user_input_numeric
|
else if string match -q -- "*2" $user_input_numeric
|
||||||
set suffix 'nd' 'second'
|
set suffix 'nd' 'second'
|
||||||
else if string match -q "*3" $user_input_numeric
|
else if string match -q -- "*3" $user_input_numeric
|
||||||
set suffix 'rd' 'third'
|
set suffix 'rd' 'third'
|
||||||
end
|
end
|
||||||
echo -e $user_input_numeric"$suffix[1]\t$suffix[2]"
|
echo -e $user_input_numeric"$suffix[1]\t$suffix[2]"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ FROM centos:8
|
|||||||
|
|
||||||
RUN dnf update -y
|
RUN dnf update -y
|
||||||
RUN yum install epel-release -y
|
RUN yum install epel-release -y
|
||||||
RUN dnf install python38 git gcc gcc-c++ cmake make gnutls-devel libuuid-devel libfaketime sudo man gdb -y
|
RUN dnf install python38 vim git gcc gcc-c++ cmake make gnutls-devel libuuid-devel libfaketime sudo man gdb -y
|
||||||
|
|
||||||
RUN useradd warrior
|
RUN useradd warrior
|
||||||
RUN echo warrior ALL=NOPASSWD:ALL > /etc/sudoers.d/warrior
|
RUN echo warrior ALL=NOPASSWD:ALL > /etc/sudoers.d/warrior
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
#include <shared.h>
|
#include <shared.h>
|
||||||
#include <format.h>
|
#include <format.h>
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#ifdef HAVE_COMMIT
|
#ifdef HAVE_COMMIT
|
||||||
#include <commit.h>
|
#include <commit.h>
|
||||||
@@ -115,7 +116,7 @@ std::string configurationDefaults =
|
|||||||
"expressions=infix # Prefer infix over postfix expressions\n"
|
"expressions=infix # Prefer infix over postfix expressions\n"
|
||||||
"json.array=1 # Enclose JSON output in [ ]\n"
|
"json.array=1 # Enclose JSON output in [ ]\n"
|
||||||
"abbreviation.minimum=2 # Shortest allowed abbreviation\n"
|
"abbreviation.minimum=2 # Shortest allowed abbreviation\n"
|
||||||
"news.version= # Latest version higlights read by the user\n"
|
"news.version= # Latest version highlights read by the user\n"
|
||||||
"\n"
|
"\n"
|
||||||
"# Dates\n"
|
"# Dates\n"
|
||||||
"dateformat=Y-M-D # Preferred input and display date format\n"
|
"dateformat=Y-M-D # Preferred input and display date format\n"
|
||||||
@@ -358,9 +359,9 @@ std::string configurationDefaults =
|
|||||||
"report.completed.context=1\n"
|
"report.completed.context=1\n"
|
||||||
"\n"
|
"\n"
|
||||||
"report.recurring.description=Recurring Tasks\n"
|
"report.recurring.description=Recurring Tasks\n"
|
||||||
"report.recurring.labels=ID,Active,Age,D,P,Project,Tags,Recur,Sch,Due,Until,Description,Urg\n"
|
"report.recurring.labels=ID,Active,Age,D,P,Parent,Project,Tags,Recur,Sch,Due,Until,Description,Urg\n"
|
||||||
"report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur,scheduled.countdown,due,until.remaining,description,urgency\n"
|
"report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,parent.short,project,tags,recur,scheduled.countdown,due,until.remaining,description,urgency\n"
|
||||||
"report.recurring.filter=status:pending and (+PARENT or +CHILD)\n"
|
"report.recurring.filter=(status:pending and +CHILD) or (status:recurring and +PARENT)\n"
|
||||||
"report.recurring.sort=due+,urgency-,entry+\n"
|
"report.recurring.sort=due+,urgency-,entry+\n"
|
||||||
"report.recurring.context=1\n"
|
"report.recurring.context=1\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -474,7 +475,7 @@ int Context::initialize (int argc, const char** argv)
|
|||||||
// [1] Load the correct config file.
|
// [1] Load the correct config file.
|
||||||
// - Default to ~/.taskrc (ctor).
|
// - Default to ~/.taskrc (ctor).
|
||||||
// - If no ~/.taskrc, use $XDG_CONFIG_HOME/task/taskrc if exists, or
|
// - If no ~/.taskrc, use $XDG_CONFIG_HOME/task/taskrc if exists, or
|
||||||
// ~/.config/task/taskrc if $XDG_DATA_HOME is unset
|
// ~/.config/task/taskrc if $XDG_CONFIG_HOME is unset
|
||||||
// - Allow $TASKRC override.
|
// - Allow $TASKRC override.
|
||||||
// - Allow command line override rc:<file>
|
// - Allow command line override rc:<file>
|
||||||
// - Load resultant file.
|
// - Load resultant file.
|
||||||
@@ -667,9 +668,13 @@ int Context::initialize (int argc, const char** argv)
|
|||||||
rc = 4;
|
rc = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
catch (const std::regex_error& e)
|
||||||
|
{
|
||||||
|
std::cout << "regex_error caught: " << e.what() << '\n';
|
||||||
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
error ("Unknown error. Please report.");
|
error ("knknown error. Please report.");
|
||||||
rc = 3;
|
rc = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -966,16 +971,19 @@ std::string Context::getTaskContext (const std::string& kind, std::string name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Figure out the context string for this kind (read/write)
|
// Figure out the context string for this kind (read/write)
|
||||||
std::string contextString = config.get ("context." + name + "." + kind);
|
std::string contextString = "";
|
||||||
if (contextString.empty ())
|
|
||||||
|
if (! config.has ("context." + name + "." + kind) && kind == "read")
|
||||||
{
|
{
|
||||||
debug ("Specific " + kind + " context for '" + name + "' not defined. ");
|
debug ("Specific " + kind + " context for '" + name + "' not defined. ");
|
||||||
if (fallback)
|
if (fallback)
|
||||||
{
|
{
|
||||||
debug ("Falling back on generic.");
|
debug ("Trying to interpret old-style context definition as read context.");
|
||||||
contextString = config.get ("context." + name);
|
contextString = config.get ("context." + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
contextString = config.get ("context." + name + "." + kind);
|
||||||
|
|
||||||
debug (format ("Detected context string: {1}", contextString.empty() ? "(empty)" : contextString));
|
debug (format ("Detected context string: {1}", contextString.empty() ? "(empty)" : contextString));
|
||||||
return contextString;
|
return contextString;
|
||||||
@@ -1320,6 +1328,12 @@ void Context::debugTiming (const std::string& details, const Timer& timer)
|
|||||||
debug (out.str ());
|
debug (out.str ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
CurrentTask Context::withCurrentTask (const Task *task)
|
||||||
|
{
|
||||||
|
return CurrentTask(*this, task);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// This capability is to answer the question of 'what did I just do to generate
|
// This capability is to answer the question of 'what did I just do to generate
|
||||||
// this output?'.
|
// this output?'.
|
||||||
@@ -1421,6 +1435,19 @@ void Context::debug (const std::string& input)
|
|||||||
debugMessages.push_back (input);
|
debugMessages.push_back (input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
CurrentTask::CurrentTask (Context &context, const Task *task)
|
||||||
|
: context {context}, previous {context.currentTask}
|
||||||
|
{
|
||||||
|
context.currentTask = task;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
CurrentTask::~CurrentTask ()
|
||||||
|
{
|
||||||
|
context.currentTask = previous;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// vim ts=2:sw=2
|
// vim ts=2:sw=2
|
||||||
|
|||||||
@@ -38,6 +38,8 @@
|
|||||||
#include <Timer.h>
|
#include <Timer.h>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
class CurrentTask;
|
||||||
|
|
||||||
class Context
|
class Context
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -73,6 +75,9 @@ public:
|
|||||||
void decomposeSortField (const std::string&, std::string&, bool&, bool&);
|
void decomposeSortField (const std::string&, std::string&, bool&, bool&);
|
||||||
void debugTiming (const std::string&, const Timer&);
|
void debugTiming (const std::string&, const Timer&);
|
||||||
|
|
||||||
|
CurrentTask withCurrentTask (const Task *);
|
||||||
|
friend class CurrentTask;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void staticInitialization ();
|
void staticInitialization ();
|
||||||
void createDefaultConfig ();
|
void createDefaultConfig ();
|
||||||
@@ -115,6 +120,25 @@ public:
|
|||||||
long time_sort_us {0};
|
long time_sort_us {0};
|
||||||
long time_render_us {0};
|
long time_render_us {0};
|
||||||
long time_hooks_us {0};
|
long time_hooks_us {0};
|
||||||
|
|
||||||
|
// the current task for DOM references, or NULL if there is no task
|
||||||
|
const Task * currentTask {NULL};
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CurrentTask resets Context::currentTask to previous context task on destruction; this ensures
|
||||||
|
// that this context value is restored when exiting the scope where the context was applied.
|
||||||
|
class CurrentTask {
|
||||||
|
public:
|
||||||
|
~CurrentTask();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CurrentTask(Context &context, const Task *previous);
|
||||||
|
|
||||||
|
Context &context;
|
||||||
|
const Task *previous;
|
||||||
|
|
||||||
|
friend class Context;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
132
src/DOM.cpp
132
src/DOM.cpp
@@ -239,22 +239,25 @@ bool getDOM (const std::string& name, Variant& value)
|
|||||||
//
|
//
|
||||||
// This code emphasizes speed, hence 'id' and 'urgency' being evaluated first
|
// This code emphasizes speed, hence 'id' and 'urgency' being evaluated first
|
||||||
// as special cases.
|
// as special cases.
|
||||||
bool getDOM (const std::string& name, const Task& task, Variant& value)
|
//
|
||||||
|
// If task is NULL, then the contextual task will be determined from the DOM
|
||||||
|
// string, if any exists.
|
||||||
|
bool getDOM (const std::string& name, const Task* task, Variant& value)
|
||||||
{
|
{
|
||||||
// Special case, blank refs cause problems.
|
// Special case, blank refs cause problems.
|
||||||
if (name == "")
|
if (name == "")
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Quickly deal with the most common cases.
|
// Quickly deal with the most common cases.
|
||||||
if (task.data.size () && name == "id")
|
if (task && name == "id")
|
||||||
{
|
{
|
||||||
value = Variant (static_cast<int> (task.id));
|
value = Variant (static_cast<int> (task->id));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.data.size () && name == "urgency")
|
if (task && name == "urgency")
|
||||||
{
|
{
|
||||||
value = Variant (task.urgency_c ());
|
value = Variant (task->urgency_c ());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,54 +265,55 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
|
|||||||
auto elements = split (name, '.');
|
auto elements = split (name, '.');
|
||||||
Task loaded_task;
|
Task loaded_task;
|
||||||
|
|
||||||
// Use a lambda to decide whether the reference is going to be the passed
|
// decide whether the reference is going to be the passed
|
||||||
// "task" or whether it's going to be a newly loaded task (if id/uuid was
|
// "task" or whether it's going to be a newly loaded task (if id/uuid was
|
||||||
// given).
|
// given).
|
||||||
const Task& ref = [&]() -> const Task&
|
const Task* ref = task;
|
||||||
|
Lexer lexer (elements[0]);
|
||||||
|
std::string token;
|
||||||
|
Lexer::Type type;
|
||||||
|
|
||||||
|
// If this can be ID/UUID reference (the name contains '.'),
|
||||||
|
// lex it to figure out. Otherwise don't lex, as lexing can be slow.
|
||||||
|
if ((elements.size() > 1) and lexer.token (token, type))
|
||||||
{
|
{
|
||||||
Lexer lexer (elements[0]);
|
bool reloaded = false;
|
||||||
std::string token;
|
|
||||||
Lexer::Type type;
|
|
||||||
|
|
||||||
// If this can be ID/UUID reference (the name contains '.'),
|
if (type == Lexer::Type::uuid &&
|
||||||
// lex it to figure out. Otherwise don't lex, as lexing can be slow.
|
token.length () == elements[0].length ())
|
||||||
if ((elements.size() > 1) and lexer.token (token, type))
|
|
||||||
{
|
{
|
||||||
bool reloaded = false;
|
if (!task || token != task->get ("uuid"))
|
||||||
|
|
||||||
if (type == Lexer::Type::uuid &&
|
|
||||||
token.length () == elements[0].length ())
|
|
||||||
{
|
{
|
||||||
if (token != task.get ("uuid"))
|
if (Context::getContext ().tdb2.get (token, loaded_task))
|
||||||
{
|
|
||||||
Context::getContext ().tdb2.get (token, loaded_task);
|
|
||||||
reloaded = true;
|
reloaded = true;
|
||||||
}
|
|
||||||
|
|
||||||
// Eat elements[0]/UUID.
|
|
||||||
elements.erase (elements.begin ());
|
|
||||||
}
|
|
||||||
else if (type == Lexer::Type::number &&
|
|
||||||
token.find ('.') == std::string::npos)
|
|
||||||
{
|
|
||||||
auto id = strtol (token.c_str (), nullptr, 10);
|
|
||||||
if (id && id != task.id)
|
|
||||||
{
|
|
||||||
Context::getContext ().tdb2.get (id, loaded_task);
|
|
||||||
reloaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eat elements[0]/ID.
|
|
||||||
elements.erase (elements.begin ());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reloaded)
|
// Eat elements[0]/UUID.
|
||||||
return loaded_task;
|
elements.erase (elements.begin ());
|
||||||
|
}
|
||||||
|
else if (type == Lexer::Type::number &&
|
||||||
|
token.find ('.') == std::string::npos)
|
||||||
|
{
|
||||||
|
auto id = strtol (token.c_str (), nullptr, 10);
|
||||||
|
if (id && (!task || id != task->id))
|
||||||
|
{
|
||||||
|
if (Context::getContext ().tdb2.get (id, loaded_task))
|
||||||
|
reloaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eat elements[0]/ID.
|
||||||
|
elements.erase (elements.begin ());
|
||||||
}
|
}
|
||||||
|
|
||||||
return task;
|
if (reloaded)
|
||||||
|
ref = &loaded_task;
|
||||||
|
}
|
||||||
|
|
||||||
} ();
|
|
||||||
|
// The remainder of this method requires a contextual task, so if we do not
|
||||||
|
// have one, delegate to the two-argument getDOM
|
||||||
|
if (!ref)
|
||||||
|
return getDOM (name, value);
|
||||||
|
|
||||||
auto size = elements.size ();
|
auto size = elements.size ();
|
||||||
|
|
||||||
@@ -318,31 +322,31 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
|
|||||||
{
|
{
|
||||||
// Now that 'ref' is the contextual task, and any ID/UUID is chopped off the
|
// Now that 'ref' is the contextual task, and any ID/UUID is chopped off the
|
||||||
// elements vector, DOM resolution is now simple.
|
// elements vector, DOM resolution is now simple.
|
||||||
if (ref.data.size () && size == 1 && canonical == "id")
|
if (size == 1 && canonical == "id")
|
||||||
{
|
{
|
||||||
value = Variant (static_cast<int> (ref.id));
|
value = Variant (static_cast<int> (ref->id));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref.data.size () && size == 1 && canonical == "urgency")
|
if (size == 1 && canonical == "urgency")
|
||||||
{
|
{
|
||||||
value = Variant (ref.urgency_c ());
|
value = Variant (ref->urgency_c ());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handling of status required for virtual waiting status
|
// Special handling of status required for virtual waiting status
|
||||||
// implementation. Remove in 3.0.0.
|
// implementation. Remove in 3.0.0.
|
||||||
if (ref.data.size () && size == 1 && canonical == "status")
|
if (size == 1 && canonical == "status")
|
||||||
{
|
{
|
||||||
value = Variant (ref.statusToText (ref.getStatus ()));
|
value = Variant (ref->statusToText (ref->getStatus ()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Column* column = Context::getContext ().columns[canonical];
|
Column* column = Context::getContext ().columns[canonical];
|
||||||
|
|
||||||
if (ref.data.size () && size == 1 && column)
|
if (size == 1 && column)
|
||||||
{
|
{
|
||||||
if (column->is_uda () && ! ref.has (canonical))
|
if (column->is_uda () && ! ref->has (canonical))
|
||||||
{
|
{
|
||||||
value = Variant ("");
|
value = Variant ("");
|
||||||
return true;
|
return true;
|
||||||
@@ -350,7 +354,7 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
|
|||||||
|
|
||||||
if (column->type () == "date")
|
if (column->type () == "date")
|
||||||
{
|
{
|
||||||
auto numeric = ref.get_date (canonical);
|
auto numeric = ref->get_date (canonical);
|
||||||
if (numeric == 0)
|
if (numeric == 0)
|
||||||
value = Variant ("");
|
value = Variant ("");
|
||||||
else
|
else
|
||||||
@@ -358,32 +362,32 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
|
|||||||
}
|
}
|
||||||
else if (column->type () == "duration" || canonical == "recur")
|
else if (column->type () == "duration" || canonical == "recur")
|
||||||
{
|
{
|
||||||
auto period = ref.get (canonical);
|
auto period = ref->get (canonical);
|
||||||
|
|
||||||
Duration iso;
|
Duration iso;
|
||||||
std::string::size_type cursor = 0;
|
std::string::size_type cursor = 0;
|
||||||
if (iso.parse (period, cursor))
|
if (iso.parse (period, cursor))
|
||||||
value = Variant (iso.toTime_t (), Variant::type_duration);
|
value = Variant (iso.toTime_t (), Variant::type_duration);
|
||||||
else
|
else
|
||||||
value = Variant (Duration (ref.get (canonical)).toTime_t (), Variant::type_duration);
|
value = Variant (Duration (ref->get (canonical)).toTime_t (), Variant::type_duration);
|
||||||
}
|
}
|
||||||
else if (column->type () == "numeric")
|
else if (column->type () == "numeric")
|
||||||
value = Variant (ref.get_float (canonical));
|
value = Variant (ref->get_float (canonical));
|
||||||
else
|
else
|
||||||
value = Variant (ref.get (canonical));
|
value = Variant (ref->get (canonical));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref.data.size () && size == 2 && canonical == "tags")
|
if (size == 2 && canonical == "tags")
|
||||||
{
|
{
|
||||||
value = Variant (ref.hasTag (elements[1]) ? elements[1] : "");
|
value = Variant (ref->hasTag (elements[1]) ? elements[1] : "");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref.data.size () && size == 2 && column && column->type () == "date")
|
if (size == 2 && column && column->type () == "date")
|
||||||
{
|
{
|
||||||
Datetime date (ref.get_date (canonical));
|
Datetime date (ref->get_date (canonical));
|
||||||
if (elements[1] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
|
if (elements[1] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
|
||||||
else if (elements[1] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
|
else if (elements[1] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
|
||||||
else if (elements[1] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
|
else if (elements[1] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
|
||||||
@@ -396,15 +400,15 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref.data.size () && size == 2 && elements[0] == "annotations" && elements[1] == "count")
|
if (size == 2 && elements[0] == "annotations" && elements[1] == "count")
|
||||||
{
|
{
|
||||||
value = Variant (static_cast<int> (ref.getAnnotationCount ()));
|
value = Variant (static_cast<int> (ref->getAnnotationCount ()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref.data.size () && size == 3 && elements[0] == "annotations")
|
if (size == 3 && elements[0] == "annotations")
|
||||||
{
|
{
|
||||||
auto annos = ref.getAnnotations ();
|
auto annos = ref->getAnnotations ();
|
||||||
|
|
||||||
int a = strtol (elements[1].c_str (), nullptr, 10);
|
int a = strtol (elements[1].c_str (), nullptr, 10);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@@ -430,9 +434,9 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref.data.size () && size == 4 && elements[0] == "annotations" && elements[2] == "entry")
|
if (size == 4 && elements[0] == "annotations" && elements[2] == "entry")
|
||||||
{
|
{
|
||||||
auto annos = ref.getAnnotations ();
|
auto annos = ref->getAnnotations ();
|
||||||
|
|
||||||
int a = strtol (elements[1].c_str (), nullptr, 10);
|
int a = strtol (elements[1].c_str (), nullptr, 10);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
// 2017-04-22 Deprecated, use DOM::get.
|
// 2017-04-22 Deprecated, use DOM::get.
|
||||||
bool getDOM (const std::string&, Variant&);
|
bool getDOM (const std::string&, Variant&);
|
||||||
bool getDOM (const std::string&, const Task&, Variant&);
|
bool getDOM (const std::string&, const Task*, Variant&);
|
||||||
|
|
||||||
class DOM
|
class DOM
|
||||||
{
|
{
|
||||||
|
|||||||
30
src/Eval.cpp
30
src/Eval.cpp
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include <cmake.h>
|
#include <cmake.h>
|
||||||
#include <Eval.h>
|
#include <Eval.h>
|
||||||
|
#include <DOM.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <Context.h>
|
#include <Context.h>
|
||||||
@@ -34,8 +35,6 @@
|
|||||||
#include <shared.h>
|
#include <shared.h>
|
||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
extern Task& contextTask;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Supported operators, borrowed from C++, particularly the precedence.
|
// Supported operators, borrowed from C++, particularly the precedence.
|
||||||
// Note: table is sorted by length of operator string, so searches match
|
// Note: table is sorted by length of operator string, so searches match
|
||||||
@@ -101,6 +100,19 @@ static bool namedConstants (const std::string& name, Variant& value)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Support for evaluating DOM references (add with `e.AddSource(domSource)`)
|
||||||
|
bool domSource (const std::string& identifier, Variant& value)
|
||||||
|
{
|
||||||
|
if (getDOM (identifier, Context::getContext ().currentTask, value))
|
||||||
|
{
|
||||||
|
value.source (identifier);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
Eval::Eval ()
|
Eval::Eval ()
|
||||||
{
|
{
|
||||||
@@ -278,6 +290,8 @@ void Eval::evaluatePostfixStack (
|
|||||||
Variant left = values.back ();
|
Variant left = values.back ();
|
||||||
values.pop_back ();
|
values.pop_back ();
|
||||||
|
|
||||||
|
auto contextTask = Context::getContext ().currentTask;
|
||||||
|
|
||||||
// Ordering these by anticipation frequency of use is a good idea.
|
// Ordering these by anticipation frequency of use is a good idea.
|
||||||
Variant result;
|
Variant result;
|
||||||
if (token.first == "and") result = left && right;
|
if (token.first == "and") result = left && right;
|
||||||
@@ -299,10 +313,14 @@ void Eval::evaluatePostfixStack (
|
|||||||
else if (token.first == "^") result = left ^ right;
|
else if (token.first == "^") result = left ^ right;
|
||||||
else if (token.first == "%") result = left % right;
|
else if (token.first == "%") result = left % right;
|
||||||
else if (token.first == "xor") result = left.operator_xor (right);
|
else if (token.first == "xor") result = left.operator_xor (right);
|
||||||
else if (token.first == "~") result = left.operator_match (right, contextTask);
|
else if (contextTask) {
|
||||||
else if (token.first == "!~") result = left.operator_nomatch (right, contextTask);
|
if (token.first == "~") result = left.operator_match (right, *contextTask);
|
||||||
else if (token.first == "_hastag_") result = left.operator_hastag (right, contextTask);
|
else if (token.first == "!~") result = left.operator_nomatch (right, *contextTask);
|
||||||
else if (token.first == "_notag_") result = left.operator_notag (right, contextTask);
|
else if (token.first == "_hastag_") result = left.operator_hastag (right, *contextTask);
|
||||||
|
else if (token.first == "_notag_") result = left.operator_notag (right, *contextTask);
|
||||||
|
else
|
||||||
|
throw format ("Unsupported operator '{1}'.", token.first);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
throw format ("Unsupported operator '{1}'.", token.first);
|
throw format ("Unsupported operator '{1}'.", token.first);
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
#include <Lexer.h>
|
#include <Lexer.h>
|
||||||
#include <Variant.h>
|
#include <Variant.h>
|
||||||
|
|
||||||
|
bool domSource (const std::string&, Variant&);
|
||||||
|
|
||||||
class Eval
|
class Eval
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -35,23 +35,6 @@
|
|||||||
#include <format.h>
|
#include <format.h>
|
||||||
#include <shared.h>
|
#include <shared.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Const iterator that can be derefenced into a Task by domSource.
|
|
||||||
static Task dummy;
|
|
||||||
Task& contextTask = dummy;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool domSource (const std::string& identifier, Variant& value)
|
|
||||||
{
|
|
||||||
if (getDOM (identifier, contextTask, value))
|
|
||||||
{
|
|
||||||
value.source (identifier);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Take an input set of tasks and filter into a subset.
|
// Take an input set of tasks and filter into a subset.
|
||||||
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output)
|
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output)
|
||||||
@@ -79,7 +62,7 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
|
|||||||
for (auto& task : input)
|
for (auto& task : input)
|
||||||
{
|
{
|
||||||
// Set up context for any DOM references.
|
// Set up context for any DOM references.
|
||||||
contextTask = task;
|
auto currentTask = Context::getContext ().withCurrentTask(&task);
|
||||||
|
|
||||||
Variant var;
|
Variant var;
|
||||||
eval.evaluateCompiledExpression (var);
|
eval.evaluateCompiledExpression (var);
|
||||||
@@ -131,7 +114,7 @@ void Filter::subset (std::vector <Task>& output)
|
|||||||
for (auto& task : pending)
|
for (auto& task : pending)
|
||||||
{
|
{
|
||||||
// Set up context for any DOM references.
|
// Set up context for any DOM references.
|
||||||
contextTask = task;
|
auto currentTask = Context::getContext ().withCurrentTask(&task);
|
||||||
|
|
||||||
Variant var;
|
Variant var;
|
||||||
eval.evaluateCompiledExpression (var);
|
eval.evaluateCompiledExpression (var);
|
||||||
@@ -150,7 +133,7 @@ void Filter::subset (std::vector <Task>& output)
|
|||||||
for (auto& task : completed)
|
for (auto& task : completed)
|
||||||
{
|
{
|
||||||
// Set up context for any DOM references.
|
// Set up context for any DOM references.
|
||||||
contextTask = task;
|
auto currentTask = Context::getContext ().withCurrentTask(&task);
|
||||||
|
|
||||||
Variant var;
|
Variant var;
|
||||||
eval.evaluateCompiledExpression (var);
|
eval.evaluateCompiledExpression (var);
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
#include <Task.h>
|
#include <Task.h>
|
||||||
#include <Variant.h>
|
#include <Variant.h>
|
||||||
|
|
||||||
bool domSource (const std::string&, Variant&);
|
|
||||||
|
|
||||||
class Filter
|
class Filter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
195
src/TDB2.cpp
195
src/TDB2.cpp
@@ -1025,203 +1025,24 @@ void TDB2::show_diff (
|
|||||||
Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : "");
|
Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : "");
|
||||||
Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : "");
|
Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : "");
|
||||||
|
|
||||||
|
auto before = prior == "" ? Task() : Task(prior);
|
||||||
|
auto after = Task(current);
|
||||||
|
|
||||||
if (Context::getContext ().config.get ("undo.style") == "side")
|
if (Context::getContext ().config.get ("undo.style") == "side")
|
||||||
{
|
{
|
||||||
|
Table view = before.diffForUndoSide(after);
|
||||||
|
|
||||||
std::cout << '\n'
|
std::cout << '\n'
|
||||||
<< format ("The last modification was made {1}", lastChange.toString ())
|
<< format ("The last modification was made {1}", lastChange.toString ())
|
||||||
<< '\n';
|
<< '\n'
|
||||||
|
<< '\n'
|
||||||
// Attributes are all there is, so figure the different attribute names
|
|
||||||
// between before and after.
|
|
||||||
Table view;
|
|
||||||
view.width (Context::getContext ().getWidth ());
|
|
||||||
view.intraPadding (2);
|
|
||||||
view.add ("");
|
|
||||||
view.add ("Prior Values");
|
|
||||||
view.add ("Current Values");
|
|
||||||
setHeaderUnderline (view);
|
|
||||||
|
|
||||||
Task after (current);
|
|
||||||
|
|
||||||
if (prior != "")
|
|
||||||
{
|
|
||||||
Task before (prior);
|
|
||||||
|
|
||||||
std::vector <std::string> beforeAtts;
|
|
||||||
for (auto& att : before.data)
|
|
||||||
beforeAtts.push_back (att.first);
|
|
||||||
|
|
||||||
std::vector <std::string> afterAtts;
|
|
||||||
for (auto& att : after.data)
|
|
||||||
afterAtts.push_back (att.first);
|
|
||||||
|
|
||||||
std::vector <std::string> beforeOnly;
|
|
||||||
std::vector <std::string> afterOnly;
|
|
||||||
listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly);
|
|
||||||
|
|
||||||
int row;
|
|
||||||
for (auto& name : beforeOnly)
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, name);
|
|
||||||
view.set (row, 1, renderAttribute (name, before.get (name)), color_red);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& att : before.data)
|
|
||||||
{
|
|
||||||
std::string priorValue = before.get (att.first);
|
|
||||||
std::string currentValue = after.get (att.first);
|
|
||||||
|
|
||||||
if (currentValue != "")
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, att.first);
|
|
||||||
view.set (row, 1, renderAttribute (att.first, priorValue),
|
|
||||||
(priorValue != currentValue ? color_red : Color ()));
|
|
||||||
view.set (row, 2, renderAttribute (att.first, currentValue),
|
|
||||||
(priorValue != currentValue ? color_green : Color ()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& name : afterOnly)
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, name);
|
|
||||||
view.set (row, 2, renderAttribute (name, after.get (name)), color_green);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int row;
|
|
||||||
for (auto& att : after.data)
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, att.first);
|
|
||||||
view.set (row, 2, renderAttribute (att.first, after.get (att.first)), color_green);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << '\n'
|
|
||||||
<< view.render ()
|
<< view.render ()
|
||||||
<< '\n';
|
<< '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This style looks like this:
|
|
||||||
// --- before 2009-07-04 00:00:25.000000000 +0200
|
|
||||||
// +++ after 2009-07-04 00:00:45.000000000 +0200
|
|
||||||
//
|
|
||||||
// - name: old // att deleted
|
|
||||||
// + name:
|
|
||||||
//
|
|
||||||
// - name: old // att changed
|
|
||||||
// + name: new
|
|
||||||
//
|
|
||||||
// - name:
|
|
||||||
// + name: new // att added
|
|
||||||
//
|
|
||||||
else if (Context::getContext ().config.get ("undo.style") == "diff")
|
else if (Context::getContext ().config.get ("undo.style") == "diff")
|
||||||
{
|
{
|
||||||
// Create reference tasks.
|
Table view = before.diffForUndoPatch(after, lastChange);
|
||||||
Task before;
|
|
||||||
if (prior != "")
|
|
||||||
before.parse (prior);
|
|
||||||
|
|
||||||
Task after (current);
|
|
||||||
|
|
||||||
// Generate table header.
|
|
||||||
Table view;
|
|
||||||
view.width (Context::getContext ().getWidth ());
|
|
||||||
view.intraPadding (2);
|
|
||||||
view.add ("");
|
|
||||||
view.add ("");
|
|
||||||
|
|
||||||
int row = view.addRow ();
|
|
||||||
view.set (row, 0, "--- previous state", color_red);
|
|
||||||
view.set (row, 1, "Undo will restore this state", color_red);
|
|
||||||
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, "+++ current state ", color_green);
|
|
||||||
view.set (row, 1, format ("Change made {1}",
|
|
||||||
lastChange.toString (Context::getContext ().config.get ("dateformat"))),
|
|
||||||
color_green);
|
|
||||||
|
|
||||||
view.addRow ();
|
|
||||||
|
|
||||||
// Add rows to table showing diffs.
|
|
||||||
std::vector <std::string> all = Context::getContext ().getColumns ();
|
|
||||||
|
|
||||||
// Now factor in the annotation attributes.
|
|
||||||
for (auto& it : before.data)
|
|
||||||
if (it.first.substr (0, 11) == "annotation_")
|
|
||||||
all.push_back (it.first);
|
|
||||||
|
|
||||||
for (auto& it : after.data)
|
|
||||||
if (it.first.substr (0, 11) == "annotation_")
|
|
||||||
all.push_back (it.first);
|
|
||||||
|
|
||||||
// Now render all the attributes.
|
|
||||||
std::sort (all.begin (), all.end ());
|
|
||||||
|
|
||||||
std::string before_att;
|
|
||||||
std::string after_att;
|
|
||||||
std::string last_att;
|
|
||||||
for (auto& a : all)
|
|
||||||
{
|
|
||||||
if (a != last_att) // Skip duplicates.
|
|
||||||
{
|
|
||||||
last_att = a;
|
|
||||||
|
|
||||||
before_att = before.get (a);
|
|
||||||
after_att = after.get (a);
|
|
||||||
|
|
||||||
// Don't report different uuid.
|
|
||||||
// Show nothing if values are the unchanged.
|
|
||||||
if (a == "uuid" ||
|
|
||||||
before_att == after_att)
|
|
||||||
{
|
|
||||||
// Show nothing - no point displaying that which did not change.
|
|
||||||
|
|
||||||
// row = view.addRow ();
|
|
||||||
// view.set (row, 0, *a + ":");
|
|
||||||
// view.set (row, 1, before_att);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute deleted.
|
|
||||||
else if (before_att != "" && after_att == "")
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, '-' + a + ':', color_red);
|
|
||||||
view.set (row, 1, before_att, color_red);
|
|
||||||
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, '+' + a + ':', color_green);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute added.
|
|
||||||
else if (before_att == "" && after_att != "")
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, '-' + a + ':', color_red);
|
|
||||||
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, '+' + a + ':', color_green);
|
|
||||||
view.set (row, 1, after_att, color_green);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute changed.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, '-' + a + ':', color_red);
|
|
||||||
view.set (row, 1, before_att, color_red);
|
|
||||||
|
|
||||||
row = view.addRow ();
|
|
||||||
view.set (row, 0, '+' + a + ':', color_green);
|
|
||||||
view.set (row, 1, after_att, color_green);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << '\n'
|
std::cout << '\n'
|
||||||
<< view.render ()
|
<< view.render ()
|
||||||
<< '\n';
|
<< '\n';
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#include <TLSClient.h>
|
#include <TLSClient.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
#include <shared.h>
|
#include <shared.h>
|
||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
|
#define HEADER_SIZE 4
|
||||||
#define MAX_BUF 16384
|
#define MAX_BUF 16384
|
||||||
|
|
||||||
#if GNUTLS_VERSION_NUMBER < 0x030406
|
#if GNUTLS_VERSION_NUMBER < 0x030406
|
||||||
@@ -469,24 +471,15 @@ void TLSClient::send (const std::string& data)
|
|||||||
packet[3] = l;
|
packet[3] = l;
|
||||||
|
|
||||||
unsigned int total = 0;
|
unsigned int total = 0;
|
||||||
unsigned int remaining = packet.length ();
|
|
||||||
|
|
||||||
while (total < packet.length ())
|
int status;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
int status;
|
status = gnutls_record_send (_session, packet.c_str () + total, packet.length () - total); // All
|
||||||
do
|
|
||||||
{
|
|
||||||
status = gnutls_record_send (_session, packet.c_str () + total, remaining); // All
|
|
||||||
}
|
|
||||||
while (errno == GNUTLS_E_INTERRUPTED ||
|
|
||||||
errno == GNUTLS_E_AGAIN);
|
|
||||||
|
|
||||||
if (status == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
total += (unsigned int) status;
|
|
||||||
remaining -= (unsigned int) status;
|
|
||||||
}
|
}
|
||||||
|
while ((status > 0 && (total += status) < packet.length ()) ||
|
||||||
|
status == GNUTLS_E_INTERRUPTED ||
|
||||||
|
status == GNUTLS_E_AGAIN);
|
||||||
|
|
||||||
if (_debug)
|
if (_debug)
|
||||||
std::cout << "c: INFO Sending 'XXXX"
|
std::cout << "c: INFO Sending 'XXXX"
|
||||||
@@ -500,18 +493,22 @@ void TLSClient::recv (std::string& data)
|
|||||||
{
|
{
|
||||||
data = ""; // No appending of data.
|
data = ""; // No appending of data.
|
||||||
int received = 0;
|
int received = 0;
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
// Get the encoded length.
|
// Get the encoded length.
|
||||||
unsigned char header[4] {};
|
unsigned char header[HEADER_SIZE] {};
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
received = gnutls_record_recv (_session, header, 4); // All
|
received = gnutls_record_recv (_session, header + total, HEADER_SIZE - total); // All
|
||||||
}
|
}
|
||||||
while (received > 0 &&
|
while ((received > 0 && (total += received) < HEADER_SIZE) ||
|
||||||
(errno == GNUTLS_E_INTERRUPTED ||
|
received == GNUTLS_E_INTERRUPTED ||
|
||||||
errno == GNUTLS_E_AGAIN));
|
received == GNUTLS_E_AGAIN);
|
||||||
|
|
||||||
int total = received;
|
if (total < HEADER_SIZE) {
|
||||||
|
throw std::string ("Failed to receive header: ") +
|
||||||
|
(received < 0 ? gnutls_strerror(received) : "connection lost?");
|
||||||
|
}
|
||||||
|
|
||||||
// Decode the length.
|
// Decode the length.
|
||||||
unsigned long expected = (header[0]<<24) |
|
unsigned long expected = (header[0]<<24) |
|
||||||
@@ -521,7 +518,11 @@ void TLSClient::recv (std::string& data)
|
|||||||
if (_debug)
|
if (_debug)
|
||||||
std::cout << "c: INFO expecting " << expected << " bytes.\n";
|
std::cout << "c: INFO expecting " << expected << " bytes.\n";
|
||||||
|
|
||||||
// TODO This would be a good place to assert 'expected < _limit'.
|
if (_limit && expected >= (unsigned long) _limit) {
|
||||||
|
std::ostringstream err_str;
|
||||||
|
err_str << "Expected message size " << expected << " is larger than allowed limit " << _limit;
|
||||||
|
throw err_str.str ();
|
||||||
|
}
|
||||||
|
|
||||||
// Arbitrary buffer size.
|
// Arbitrary buffer size.
|
||||||
char buffer[MAX_BUF];
|
char buffer[MAX_BUF];
|
||||||
@@ -531,13 +532,18 @@ void TLSClient::recv (std::string& data)
|
|||||||
// fits in the buffer.
|
// fits in the buffer.
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
int chunk_size = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
received = gnutls_record_recv (_session, buffer, MAX_BUF - 1); // All
|
received = gnutls_record_recv (_session, buffer + chunk_size, MAX_BUF - chunk_size); // All
|
||||||
|
if (received > 0) {
|
||||||
|
total += received;
|
||||||
|
chunk_size += received;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while (received > 0 &&
|
while ((received > 0 && (unsigned long) total < expected && chunk_size < MAX_BUF) ||
|
||||||
(errno == GNUTLS_E_INTERRUPTED ||
|
received == GNUTLS_E_INTERRUPTED ||
|
||||||
errno == GNUTLS_E_AGAIN));
|
received == GNUTLS_E_AGAIN);
|
||||||
|
|
||||||
// Other end closed the connection.
|
// Other end closed the connection.
|
||||||
if (received == 0)
|
if (received == 0)
|
||||||
@@ -548,17 +554,10 @@ void TLSClient::recv (std::string& data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Something happened.
|
// Something happened.
|
||||||
if (received < 0 && gnutls_error_is_fatal (received) == 0) // All
|
if (received < 0)
|
||||||
{
|
|
||||||
if (_debug)
|
|
||||||
std::cout << "c: WARNING " << gnutls_strerror (received) << '\n'; // All
|
|
||||||
}
|
|
||||||
else if (received < 0)
|
|
||||||
throw std::string (gnutls_strerror (received)); // All
|
throw std::string (gnutls_strerror (received)); // All
|
||||||
|
|
||||||
buffer [received] = '\0';
|
data.append (buffer, chunk_size);
|
||||||
data += buffer;
|
|
||||||
total += received;
|
|
||||||
|
|
||||||
// Stop at defined limit.
|
// Stop at defined limit.
|
||||||
if (_limit && total > _limit)
|
if (_limit && total > _limit)
|
||||||
|
|||||||
285
src/Task.cpp
285
src/Task.cpp
@@ -60,8 +60,6 @@
|
|||||||
|
|
||||||
#define APPROACHING_INFINITY 1000 // Close enough. This isn't rocket surgery.
|
#define APPROACHING_INFINITY 1000 // Close enough. This isn't rocket surgery.
|
||||||
|
|
||||||
extern Task& contextTask;
|
|
||||||
|
|
||||||
static const float epsilon = 0.000001;
|
static const float epsilon = 0.000001;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -114,6 +112,12 @@ bool Task::operator== (const Task& other)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool Task::operator!= (const Task& other)
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
Task::Task (const std::string& input)
|
Task::Task (const std::string& input)
|
||||||
{
|
{
|
||||||
@@ -363,6 +367,14 @@ Task::dateState Task::getDateState (const std::string& name) const
|
|||||||
return dateNotDue;
|
return dateNotDue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// An empty task is typically a "dummy", such as in DOM evaluation, which may or
|
||||||
|
// may not occur in the context of a task.
|
||||||
|
bool Task::is_empty () const
|
||||||
|
{
|
||||||
|
return data.size () == 0;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Ready means pending, not blocked and either not scheduled or scheduled before
|
// Ready means pending, not blocked and either not scheduled or scheduled before
|
||||||
// now.
|
// now.
|
||||||
@@ -546,9 +558,11 @@ bool Task::is_udaPresent () const
|
|||||||
bool Task::is_orphanPresent () const
|
bool Task::is_orphanPresent () const
|
||||||
{
|
{
|
||||||
for (auto& att : data)
|
for (auto& att : data)
|
||||||
if (att.first.compare (0, 11, "annotation_", 11) != 0)
|
if (! isAnnotationAttr (att.first) &&
|
||||||
if (Context::getContext ().columns.find (att.first) == Context::getContext ().columns.end ())
|
! isTagAttr (att.first) &&
|
||||||
return true;
|
! isDepAttr (att.first) &&
|
||||||
|
Context::getContext ().columns.find (att.first) == Context::getContext ().columns.end ())
|
||||||
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -576,12 +590,14 @@ bool Task::is_overdue () const
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Task is considered waiting if it's pending and the wait attribute is set as
|
||||||
|
// future datetime value.
|
||||||
|
// While this is not consistent with other attribute-based virtual tags, such
|
||||||
|
// as +BLOCKED, it is more backwards compatible with how +WAITING virtual tag
|
||||||
|
// behaved in the past, when waiting had a dedicated status value.
|
||||||
bool Task::is_waiting () const
|
bool Task::is_waiting () const
|
||||||
{
|
{
|
||||||
// note that is_waiting can return true for tasks in an actual status other
|
if (has ("wait") && get ("status") == "pending")
|
||||||
// than pending; in this case +WAITING will be set but the status will not be
|
|
||||||
// "waiting"
|
|
||||||
if (has ("wait"))
|
|
||||||
{
|
{
|
||||||
Datetime now;
|
Datetime now;
|
||||||
Datetime wait (get_date ("wait"));
|
Datetime wait (get_date ("wait"));
|
||||||
@@ -761,7 +777,25 @@ void Task::parseJSON (const json::object* root_obj)
|
|||||||
else if (i.first == "depends" && i.second->type() == json::j_string)
|
else if (i.first == "depends" && i.second->type() == json::j_string)
|
||||||
{
|
{
|
||||||
auto deps = (json::string*)i.second;
|
auto deps = (json::string*)i.second;
|
||||||
auto uuids = split (deps->_data, ',');
|
|
||||||
|
// Fix for issue#2689: taskserver sometimes encodes the depends
|
||||||
|
// property as a string of the format `[\"uuid\",\"uuid\"]`
|
||||||
|
// The string includes the backslash-escaped `"` characters, making
|
||||||
|
// it invalid JSON. Since we know the characters we're looking for,
|
||||||
|
// we'll just filter out everything else.
|
||||||
|
std::string deps_str = deps->_data;
|
||||||
|
if (deps_str.front () == '[' && deps_str.back () == ']') {
|
||||||
|
std::string filtered;
|
||||||
|
for (auto &c: deps_str) {
|
||||||
|
if ((c >= '0' && c <= '9') ||
|
||||||
|
(c >= 'a' && c <= 'f') ||
|
||||||
|
c == ',' || c == '-') {
|
||||||
|
filtered.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deps_str = filtered;
|
||||||
|
}
|
||||||
|
auto uuids = split (deps_str, ',');
|
||||||
|
|
||||||
for (const auto& uuid : uuids)
|
for (const auto& uuid : uuids)
|
||||||
addDependency (uuid);
|
addDependency (uuid);
|
||||||
@@ -1478,7 +1512,7 @@ void Task::fixTagsAttribute ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool Task::isTagAttr(const std::string& attr) const
|
bool Task::isTagAttr(const std::string& attr)
|
||||||
{
|
{
|
||||||
return attr.compare(0, 5, "tags_") == 0;
|
return attr.compare(0, 5, "tags_") == 0;
|
||||||
}
|
}
|
||||||
@@ -1512,7 +1546,7 @@ void Task::fixDependsAttribute ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool Task::isDepAttr(const std::string& attr) const
|
bool Task::isDepAttr(const std::string& attr)
|
||||||
{
|
{
|
||||||
return attr.compare(0, 4, "dep_") == 0;
|
return attr.compare(0, 4, "dep_") == 0;
|
||||||
}
|
}
|
||||||
@@ -1533,7 +1567,7 @@ const std::string Task::attr2Dep (const std::string& attr) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
bool Task::isAnnotationAttr(const std::string& attr) const
|
bool Task::isAnnotationAttr(const std::string& attr)
|
||||||
{
|
{
|
||||||
return attr.compare(0, 11, "annotation_") == 0;
|
return attr.compare(0, 11, "annotation_") == 0;
|
||||||
}
|
}
|
||||||
@@ -1541,7 +1575,7 @@ bool Task::isAnnotationAttr(const std::string& attr) const
|
|||||||
#ifdef PRODUCT_TASKWARRIOR
|
#ifdef PRODUCT_TASKWARRIOR
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// A UDA Orphan is an attribute that is not represented in context.columns.
|
// A UDA Orphan is an attribute that is not represented in context.columns.
|
||||||
std::vector <std::string> Task::getUDAOrphanUUIDs () const
|
std::vector <std::string> Task::getUDAOrphans () const
|
||||||
{
|
{
|
||||||
std::vector <std::string> orphans;
|
std::vector <std::string> orphans;
|
||||||
for (auto& it : data)
|
for (auto& it : data)
|
||||||
@@ -2253,6 +2287,10 @@ void Task::modify (modType type, bool text_required /* = false */)
|
|||||||
{
|
{
|
||||||
std::string label = " [1;37;43mMODIFICATION[0m ";
|
std::string label = " [1;37;43mMODIFICATION[0m ";
|
||||||
|
|
||||||
|
// while reading the parse tree, consider DOM references in the context of
|
||||||
|
// this task
|
||||||
|
auto currentTask = Context::getContext ().withCurrentTask(this);
|
||||||
|
|
||||||
// Need this for later comparison.
|
// Need this for later comparison.
|
||||||
auto originalStatus = getStatus ();
|
auto originalStatus = getStatus ();
|
||||||
|
|
||||||
@@ -2272,6 +2310,19 @@ void Task::modify (modType type, bool text_required /* = false */)
|
|||||||
value == "''" ||
|
value == "''" ||
|
||||||
value == "\"\"")
|
value == "\"\"")
|
||||||
{
|
{
|
||||||
|
// Special case: Handle bulk removal of 'tags' and 'depends" virtual
|
||||||
|
// attributes
|
||||||
|
if (name == "depends")
|
||||||
|
{
|
||||||
|
for (auto dep: getDependencyUUIDs ())
|
||||||
|
removeDependency(dep);
|
||||||
|
}
|
||||||
|
else if (name == "tags")
|
||||||
|
{
|
||||||
|
for (auto tag: getTags ())
|
||||||
|
removeTag(tag);
|
||||||
|
}
|
||||||
|
|
||||||
// ::composeF4 will skip if the value is blank, but the presence of
|
// ::composeF4 will skip if the value is blank, but the presence of
|
||||||
// the attribute will prevent ::validate from applying defaults.
|
// the attribute will prevent ::validate from applying defaults.
|
||||||
if ((has (name) && get (name) != "") ||
|
if ((has (name) && get (name) != "") ||
|
||||||
@@ -2400,7 +2451,8 @@ void Task::modify (modType type, bool text_required /* = false */)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Compare this task to another and summarize the differences for display
|
// Compare this task to another and summarize the differences for display, in
|
||||||
|
// the future tense ("Foo will be set to ..").
|
||||||
std::string Task::diff (const Task& after) const
|
std::string Task::diff (const Task& after) const
|
||||||
{
|
{
|
||||||
// Attributes are all there is, so figure the different attribute names
|
// Attributes are all there is, so figure the different attribute names
|
||||||
@@ -2645,3 +2697,206 @@ std::string Task::diffForInfo (
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Similar to diff, but formatted as a side-by-side table for an Undo preview
|
||||||
|
Table Task::diffForUndoSide (
|
||||||
|
const Task& after) const
|
||||||
|
{
|
||||||
|
// Set the colors.
|
||||||
|
Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : "");
|
||||||
|
Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : "");
|
||||||
|
|
||||||
|
// Attributes are all there is, so figure the different attribute names
|
||||||
|
// between before and after.
|
||||||
|
Table view;
|
||||||
|
view.width (Context::getContext ().getWidth ());
|
||||||
|
view.intraPadding (2);
|
||||||
|
view.add ("");
|
||||||
|
view.add ("Prior Values");
|
||||||
|
view.add ("Current Values");
|
||||||
|
setHeaderUnderline (view);
|
||||||
|
|
||||||
|
if (!is_empty ())
|
||||||
|
{
|
||||||
|
const Task &before = *this;
|
||||||
|
|
||||||
|
std::vector <std::string> beforeAtts;
|
||||||
|
for (auto& att : before.data)
|
||||||
|
beforeAtts.push_back (att.first);
|
||||||
|
|
||||||
|
std::vector <std::string> afterAtts;
|
||||||
|
for (auto& att : after.data)
|
||||||
|
afterAtts.push_back (att.first);
|
||||||
|
|
||||||
|
std::vector <std::string> beforeOnly;
|
||||||
|
std::vector <std::string> afterOnly;
|
||||||
|
listDiff (beforeAtts, afterAtts, beforeOnly, afterOnly);
|
||||||
|
|
||||||
|
int row;
|
||||||
|
for (auto& name : beforeOnly)
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, name);
|
||||||
|
view.set (row, 1, renderAttribute (name, before.get (name)), color_red);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& att : before.data)
|
||||||
|
{
|
||||||
|
std::string priorValue = before.get (att.first);
|
||||||
|
std::string currentValue = after.get (att.first);
|
||||||
|
|
||||||
|
if (currentValue != "")
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, att.first);
|
||||||
|
view.set (row, 1, renderAttribute (att.first, priorValue),
|
||||||
|
(priorValue != currentValue ? color_red : Color ()));
|
||||||
|
view.set (row, 2, renderAttribute (att.first, currentValue),
|
||||||
|
(priorValue != currentValue ? color_green : Color ()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& name : afterOnly)
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, name);
|
||||||
|
view.set (row, 2, renderAttribute (name, after.get (name)), color_green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int row;
|
||||||
|
for (auto& att : after.data)
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, att.first);
|
||||||
|
view.set (row, 2, renderAttribute (att.first, after.get (att.first)), color_green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Similar to diff, but formatted as a diff for an Undo preview
|
||||||
|
Table Task::diffForUndoPatch (
|
||||||
|
const Task& after,
|
||||||
|
const Datetime& lastChange) const
|
||||||
|
{
|
||||||
|
// This style looks like this:
|
||||||
|
// --- before 2009-07-04 00:00:25.000000000 +0200
|
||||||
|
// +++ after 2009-07-04 00:00:45.000000000 +0200
|
||||||
|
//
|
||||||
|
// - name: old // att deleted
|
||||||
|
// + name:
|
||||||
|
//
|
||||||
|
// - name: old // att changed
|
||||||
|
// + name: new
|
||||||
|
//
|
||||||
|
// - name:
|
||||||
|
// + name: new // att added
|
||||||
|
//
|
||||||
|
|
||||||
|
// Set the colors.
|
||||||
|
Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : "");
|
||||||
|
Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : "");
|
||||||
|
|
||||||
|
const Task &before = *this;
|
||||||
|
|
||||||
|
// Generate table header.
|
||||||
|
Table view;
|
||||||
|
view.width (Context::getContext ().getWidth ());
|
||||||
|
view.intraPadding (2);
|
||||||
|
view.add ("");
|
||||||
|
view.add ("");
|
||||||
|
|
||||||
|
int row = view.addRow ();
|
||||||
|
view.set (row, 0, "--- previous state", color_red);
|
||||||
|
view.set (row, 1, "Undo will restore this state", color_red);
|
||||||
|
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, "+++ current state ", color_green);
|
||||||
|
view.set (row, 1, format ("Change made {1}",
|
||||||
|
lastChange.toString (Context::getContext ().config.get ("dateformat"))),
|
||||||
|
color_green);
|
||||||
|
|
||||||
|
view.addRow ();
|
||||||
|
|
||||||
|
// Add rows to table showing diffs.
|
||||||
|
std::vector <std::string> all = Context::getContext ().getColumns ();
|
||||||
|
|
||||||
|
// Now factor in the annotation attributes.
|
||||||
|
for (auto& it : before.data)
|
||||||
|
if (it.first.substr (0, 11) == "annotation_")
|
||||||
|
all.push_back (it.first);
|
||||||
|
|
||||||
|
for (auto& it : after.data)
|
||||||
|
if (it.first.substr (0, 11) == "annotation_")
|
||||||
|
all.push_back (it.first);
|
||||||
|
|
||||||
|
// Now render all the attributes.
|
||||||
|
std::sort (all.begin (), all.end ());
|
||||||
|
|
||||||
|
std::string before_att;
|
||||||
|
std::string after_att;
|
||||||
|
std::string last_att;
|
||||||
|
for (auto& a : all)
|
||||||
|
{
|
||||||
|
if (a != last_att) // Skip duplicates.
|
||||||
|
{
|
||||||
|
last_att = a;
|
||||||
|
|
||||||
|
before_att = before.get (a);
|
||||||
|
after_att = after.get (a);
|
||||||
|
|
||||||
|
// Don't report different uuid.
|
||||||
|
// Show nothing if values are the unchanged.
|
||||||
|
if (a == "uuid" ||
|
||||||
|
before_att == after_att)
|
||||||
|
{
|
||||||
|
// Show nothing - no point displaying that which did not change.
|
||||||
|
|
||||||
|
// row = view.addRow ();
|
||||||
|
// view.set (row, 0, *a + ":");
|
||||||
|
// view.set (row, 1, before_att);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute deleted.
|
||||||
|
else if (before_att != "" && after_att == "")
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, '-' + a + ':', color_red);
|
||||||
|
view.set (row, 1, before_att, color_red);
|
||||||
|
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, '+' + a + ':', color_green);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute added.
|
||||||
|
else if (before_att == "" && after_att != "")
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, '-' + a + ':', color_red);
|
||||||
|
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, '+' + a + ':', color_green);
|
||||||
|
view.set (row, 1, after_att, color_green);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute changed.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, '-' + a + ':', color_red);
|
||||||
|
view.set (row, 1, before_att, color_red);
|
||||||
|
|
||||||
|
row = view.addRow ();
|
||||||
|
view.set (row, 0, '+' + a + ':', color_green);
|
||||||
|
view.set (row, 1, after_att, color_green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
20
src/Task.h
20
src/Task.h
@@ -33,6 +33,8 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <JSON.h>
|
#include <JSON.h>
|
||||||
|
#include <Table.h>
|
||||||
|
#include <Datetime.h>
|
||||||
|
|
||||||
class Task
|
class Task
|
||||||
{
|
{
|
||||||
@@ -60,6 +62,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
Task () = default;
|
Task () = default;
|
||||||
bool operator== (const Task&);
|
bool operator== (const Task&);
|
||||||
|
bool operator!= (const Task&);
|
||||||
Task (const std::string&);
|
Task (const std::string&);
|
||||||
Task (const json::object*);
|
Task (const json::object*);
|
||||||
|
|
||||||
@@ -74,7 +77,6 @@ public:
|
|||||||
enum dateState {dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday};
|
enum dateState {dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday};
|
||||||
|
|
||||||
// Public data.
|
// Public data.
|
||||||
std::map <std::string, std::string> data {};
|
|
||||||
int id {0};
|
int id {0};
|
||||||
float urgency_value {0.0};
|
float urgency_value {0.0};
|
||||||
bool recalc_urgency {true};
|
bool recalc_urgency {true};
|
||||||
@@ -100,6 +102,8 @@ public:
|
|||||||
void set (const std::string&, long long);
|
void set (const std::string&, long long);
|
||||||
void remove (const std::string&);
|
void remove (const std::string&);
|
||||||
|
|
||||||
|
bool is_empty () const;
|
||||||
|
|
||||||
#ifdef PRODUCT_TASKWARRIOR
|
#ifdef PRODUCT_TASKWARRIOR
|
||||||
bool is_ready () const;
|
bool is_ready () const;
|
||||||
bool is_due () const;
|
bool is_due () const;
|
||||||
@@ -113,6 +117,10 @@ public:
|
|||||||
bool is_overdue () const;
|
bool is_overdue () const;
|
||||||
bool is_udaPresent () const;
|
bool is_udaPresent () const;
|
||||||
bool is_orphanPresent () const;
|
bool is_orphanPresent () const;
|
||||||
|
|
||||||
|
static bool isTagAttr (const std::string&);
|
||||||
|
static bool isDepAttr (const std::string&);
|
||||||
|
static bool isAnnotationAttr (const std::string&);
|
||||||
#endif
|
#endif
|
||||||
bool is_waiting () const;
|
bool is_waiting () const;
|
||||||
|
|
||||||
@@ -150,7 +158,7 @@ public:
|
|||||||
std::vector <Task> getBlockedTasks () const;
|
std::vector <Task> getBlockedTasks () const;
|
||||||
std::vector <Task> getDependencyTasks () const;
|
std::vector <Task> getDependencyTasks () const;
|
||||||
|
|
||||||
std::vector <std::string> getUDAOrphanUUIDs () const;
|
std::vector <std::string> getUDAOrphans () const;
|
||||||
|
|
||||||
void substitute (const std::string&, const std::string&, const std::string&);
|
void substitute (const std::string&, const std::string&, const std::string&);
|
||||||
#endif
|
#endif
|
||||||
@@ -167,6 +175,8 @@ public:
|
|||||||
|
|
||||||
std::string diff (const Task& after) const;
|
std::string diff (const Task& after) const;
|
||||||
std::string diffForInfo (const Task& after, const std::string& dateformat, long& last_timestamp, const long current_timestamp) const;
|
std::string diffForInfo (const Task& after, const std::string& dateformat, long& last_timestamp, const long current_timestamp) const;
|
||||||
|
Table diffForUndoSide (const Task& after) const;
|
||||||
|
Table diffForUndoPatch (const Task& after, const Datetime& lastChange) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int determineVersion (const std::string&);
|
int determineVersion (const std::string&);
|
||||||
@@ -176,16 +186,16 @@ private:
|
|||||||
void validate_before (const std::string&, const std::string&);
|
void validate_before (const std::string&, const std::string&);
|
||||||
const std::string encode (const std::string&) const;
|
const std::string encode (const std::string&) const;
|
||||||
const std::string decode (const std::string&) const;
|
const std::string decode (const std::string&) const;
|
||||||
bool isTagAttr (const std::string&) const;
|
|
||||||
const std::string tag2Attr (const std::string&) const;
|
const std::string tag2Attr (const std::string&) const;
|
||||||
const std::string attr2Tag (const std::string&) const;
|
const std::string attr2Tag (const std::string&) const;
|
||||||
bool isDepAttr (const std::string&) const;
|
|
||||||
const std::string dep2Attr (const std::string&) const;
|
const std::string dep2Attr (const std::string&) const;
|
||||||
const std::string attr2Dep (const std::string&) const;
|
const std::string attr2Dep (const std::string&) const;
|
||||||
bool isAnnotationAttr (const std::string&) const;
|
|
||||||
void fixDependsAttribute ();
|
void fixDependsAttribute ();
|
||||||
void fixTagsAttribute ();
|
void fixTagsAttribute ();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::map <std::string, std::string> data {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float urgency_project () const;
|
float urgency_project () const;
|
||||||
float urgency_active () const;
|
float urgency_active () const;
|
||||||
|
|||||||
@@ -32,7 +32,9 @@
|
|||||||
#include <format.h>
|
#include <format.h>
|
||||||
#include <utf8.h>
|
#include <utf8.h>
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
|
#include <util.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#define STRING_COLUMN_LABEL_DEP "Depends"
|
#define STRING_COLUMN_LABEL_DEP "Depends"
|
||||||
|
|
||||||
@@ -152,20 +154,59 @@ void ColumnDepends::modify (Task& task, const std::string& value)
|
|||||||
// Apply or remove dendencies in turn.
|
// Apply or remove dendencies in turn.
|
||||||
for (auto& dep : split (value, ','))
|
for (auto& dep : split (value, ','))
|
||||||
{
|
{
|
||||||
|
bool removal = false;
|
||||||
if (dep[0] == '-')
|
if (dep[0] == '-')
|
||||||
{
|
{
|
||||||
if (dep.length () == 37)
|
removal = true;
|
||||||
task.removeDependency (dep.substr (1));
|
dep = dep.substr(1);
|
||||||
else
|
|
||||||
task.removeDependency (strtol (dep.substr (1).c_str (), nullptr, 10));
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
auto hyphen = dep.find ('-');
|
||||||
|
long lower, upper; // For ID ranges
|
||||||
|
std::regex valid_uuid ("[a-f0-9]{8}([a-f0-9-]{4,28})?"); // TODO: Make more precise
|
||||||
|
|
||||||
|
// UUID
|
||||||
|
if (dep.length () >= 8 && std::regex_match (dep, valid_uuid))
|
||||||
{
|
{
|
||||||
if (dep.length () == 36)
|
// Full UUID, can be added directly
|
||||||
task.addDependency (dep);
|
if (dep.length () == 36)
|
||||||
else
|
if (removal)
|
||||||
task.addDependency (strtol (dep.c_str (), nullptr, 10));
|
task.removeDependency (dep);
|
||||||
|
else
|
||||||
|
task.addDependency (dep);
|
||||||
|
|
||||||
|
// Short UUID, need to look up full form
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Task loaded_task;
|
||||||
|
if (Context::getContext ().tdb2.get (dep, loaded_task))
|
||||||
|
if (removal)
|
||||||
|
task.removeDependency (loaded_task.get ("uuid"));
|
||||||
|
else
|
||||||
|
task.addDependency (loaded_task.get ("uuid"));
|
||||||
|
else
|
||||||
|
throw format ("Dependency could not be set - task with UUID '{1}' does not exist.", dep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// ID range
|
||||||
|
else if (dep.find ('-') != std::string::npos &&
|
||||||
|
extractLongInteger (dep.substr (0, hyphen), lower) &&
|
||||||
|
extractLongInteger (dep.substr (hyphen + 1), upper))
|
||||||
|
{
|
||||||
|
for (long i = lower; i <= upper; i++)
|
||||||
|
if (removal)
|
||||||
|
task.removeDependency (i);
|
||||||
|
else
|
||||||
|
task.addDependency (i);
|
||||||
|
}
|
||||||
|
// Simple ID
|
||||||
|
else if (extractLongInteger (dep, lower))
|
||||||
|
if (removal)
|
||||||
|
task.removeDependency (lower);
|
||||||
|
else
|
||||||
|
task.addDependency (lower);
|
||||||
|
else
|
||||||
|
throw format ("Invalid dependency value: '{1}'", dep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,6 @@
|
|||||||
#include <utf8.h>
|
#include <utf8.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
|
|
||||||
extern Task& contextTask;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnProject::ColumnProject ()
|
ColumnProject::ColumnProject ()
|
||||||
{
|
{
|
||||||
@@ -121,7 +119,6 @@ void ColumnProject::modify (Task& task, const std::string& value)
|
|||||||
{
|
{
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (domSource);
|
e.addSource (domSource);
|
||||||
contextTask = task;
|
|
||||||
|
|
||||||
Variant v;
|
Variant v;
|
||||||
e.evaluateInfixExpression (value, v);
|
e.evaluateInfixExpression (value, v);
|
||||||
|
|||||||
@@ -36,8 +36,6 @@
|
|||||||
#include <format.h>
|
#include <format.h>
|
||||||
#include <utf8.h>
|
#include <utf8.h>
|
||||||
|
|
||||||
extern Task& contextTask;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnRecur::ColumnRecur ()
|
ColumnRecur::ColumnRecur ()
|
||||||
{
|
{
|
||||||
@@ -108,7 +106,6 @@ void ColumnRecur::modify (Task& task, const std::string& value)
|
|||||||
{
|
{
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (domSource);
|
e.addSource (domSource);
|
||||||
contextTask = task;
|
|
||||||
e.evaluateInfixExpression (value, evaluatedValue);
|
e.evaluateInfixExpression (value, evaluatedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,6 @@
|
|||||||
#include <utf8.h>
|
#include <utf8.h>
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
|
|
||||||
extern Task& contextTask;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnTags::ColumnTags ()
|
ColumnTags::ColumnTags ()
|
||||||
{
|
{
|
||||||
@@ -162,7 +160,6 @@ void ColumnTags::modify (Task& task, const std::string& value)
|
|||||||
{
|
{
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (domSource);
|
e.addSource (domSource);
|
||||||
contextTask = task;
|
|
||||||
|
|
||||||
Variant v;
|
Variant v;
|
||||||
e.evaluateInfixExpression (value, v);
|
e.evaluateInfixExpression (value, v);
|
||||||
|
|||||||
@@ -34,8 +34,6 @@
|
|||||||
#include <Filter.h>
|
#include <Filter.h>
|
||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
extern Task& contextTask;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnTypeDate::ColumnTypeDate ()
|
ColumnTypeDate::ColumnTypeDate ()
|
||||||
{
|
{
|
||||||
@@ -213,7 +211,6 @@ void ColumnTypeDate::modify (Task& task, const std::string& value)
|
|||||||
{
|
{
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (domSource);
|
e.addSource (domSource);
|
||||||
contextTask = task;
|
|
||||||
e.evaluateInfixExpression (value, evaluatedValue);
|
e.evaluateInfixExpression (value, evaluatedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
#include <Filter.h>
|
#include <Filter.h>
|
||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
extern Task& contextTask;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnTypeDuration::ColumnTypeDuration ()
|
ColumnTypeDuration::ColumnTypeDuration ()
|
||||||
{
|
{
|
||||||
@@ -55,7 +53,6 @@ void ColumnTypeDuration::modify (Task& task, const std::string& value)
|
|||||||
{
|
{
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (domSource);
|
e.addSource (domSource);
|
||||||
contextTask = task;
|
|
||||||
e.evaluateInfixExpression (value, evaluatedValue);
|
e.evaluateInfixExpression (value, evaluatedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
#include <Filter.h>
|
#include <Filter.h>
|
||||||
#include <format.h>
|
#include <format.h>
|
||||||
|
|
||||||
extern Task& contextTask;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnTypeNumeric::ColumnTypeNumeric ()
|
ColumnTypeNumeric::ColumnTypeNumeric ()
|
||||||
{
|
{
|
||||||
@@ -55,7 +53,6 @@ void ColumnTypeNumeric::modify (Task& task, const std::string& value)
|
|||||||
{
|
{
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (domSource);
|
e.addSource (domSource);
|
||||||
contextTask = task;
|
|
||||||
e.evaluateInfixExpression (value, evaluatedValue);
|
e.evaluateInfixExpression (value, evaluatedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,6 @@
|
|||||||
|
|
||||||
#define STRING_INVALID_MOD "The '{1}' attribute does not allow a value of '{2}'."
|
#define STRING_INVALID_MOD "The '{1}' attribute does not allow a value of '{2}'."
|
||||||
|
|
||||||
extern Task& contextTask;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
ColumnTypeString::ColumnTypeString ()
|
ColumnTypeString::ColumnTypeString ()
|
||||||
{
|
{
|
||||||
@@ -67,7 +65,6 @@ void ColumnTypeString::modify (Task& task, const std::string& value)
|
|||||||
{
|
{
|
||||||
Eval e;
|
Eval e;
|
||||||
e.addSource (domSource);
|
e.addSource (domSource);
|
||||||
contextTask = task;
|
|
||||||
|
|
||||||
Variant v;
|
Variant v;
|
||||||
e.evaluateInfixExpression (value, v);
|
e.evaluateInfixExpression (value, v);
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ int CmdAdd::execute (std::string& output)
|
|||||||
{
|
{
|
||||||
// Apply the command line modifications to the new task.
|
// Apply the command line modifications to the new task.
|
||||||
Task task;
|
Task task;
|
||||||
|
|
||||||
|
// the task is empty, but DOM references can refer to earlier parts of the
|
||||||
|
// command line, e.g., `task add due:20110101 wait:due`.
|
||||||
task.modify (Task::modReplace, true);
|
task.modify (Task::modReplace, true);
|
||||||
Context::getContext ().tdb2.add (task);
|
Context::getContext ().tdb2.add (task);
|
||||||
|
|
||||||
|
|||||||
@@ -110,33 +110,43 @@ std::string CmdContext::joinWords (const std::vector <std::string>& words, unsig
|
|||||||
// Validate the context as valid for writing and fail the write context definition
|
// Validate the context as valid for writing and fail the write context definition
|
||||||
// A valid write context:
|
// A valid write context:
|
||||||
// - does not contain any operators except AND
|
// - does not contain any operators except AND
|
||||||
// - does not use modifiers
|
// - does not contain tag exclusion
|
||||||
|
// - does not use modifiers, except for 'equals' and 'is'
|
||||||
//
|
//
|
||||||
// Returns True if the context is a valid write context. If the context is
|
// Returns True if the context is a valid write context. If the context is
|
||||||
// invalid due to a wrong modifier use, the modifier string will contain the
|
// invalid due to a wrong modifier use, the modifier string will contain the
|
||||||
// first invalid modifier.
|
// first invalid modifier.
|
||||||
bool CmdContext::validateWriteContext (const std::vector <A2>& lexedArgs, std::string& modifier_token)
|
//
|
||||||
|
bool CmdContext::validateWriteContext (const std::vector <A2>& lexedArgs, std::string& reason)
|
||||||
{
|
{
|
||||||
bool contains_or = false;
|
|
||||||
bool contains_modifier = false;
|
|
||||||
|
|
||||||
for (auto &arg: lexedArgs) {
|
for (auto &arg: lexedArgs) {
|
||||||
if (arg._lextype == Lexer::Type::op)
|
if (arg._lextype == Lexer::Type::op)
|
||||||
if (arg.attribute ("raw") == "or")
|
if (arg.attribute ("raw") == "or")
|
||||||
contains_or = true;
|
{
|
||||||
|
reason = "contains the 'OR' operator";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (arg._lextype == Lexer::Type::pair) {
|
if (arg._lextype == Lexer::Type::pair) {
|
||||||
auto modifier = arg.attribute ("modifier");
|
auto modifier = arg.attribute ("modifier");
|
||||||
if (modifier != "" && modifier != "is" && modifier != "equals")
|
if (modifier != "" && modifier != "is" && modifier != "equals")
|
||||||
{
|
{
|
||||||
contains_modifier = true;
|
reason = format ("contains an attribute modifier '{1}'", arg.attribute ("raw"));
|
||||||
modifier_token = arg.attribute ("raw");
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg._lextype == Lexer::Type::tag) {
|
||||||
|
if (arg.attribute ("sign") == "-")
|
||||||
|
{
|
||||||
|
reason = format ("contains tag exclusion '{1}'", arg.attribute ("raw"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return not contains_or and not contains_modifier;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -211,14 +221,13 @@ void CmdContext::defineContext (const std::vector <std::string>& words, std::str
|
|||||||
! confirm (format ("The filter '{1}' matches 0 pending tasks. Do you wish to continue?", value)))
|
! confirm (format ("The filter '{1}' matches 0 pending tasks. Do you wish to continue?", value)))
|
||||||
throw std::string ("Context definition aborted.");
|
throw std::string ("Context definition aborted.");
|
||||||
|
|
||||||
std::string modifier_token = "";
|
std::string reason = "";
|
||||||
bool valid_write_context = CmdContext::validateWriteContext (lexedArgs, modifier_token);
|
bool valid_write_context = CmdContext::validateWriteContext (lexedArgs, reason);
|
||||||
|
|
||||||
if (! valid_write_context)
|
if (! valid_write_context)
|
||||||
{
|
{
|
||||||
std::stringstream warning;
|
std::stringstream warning;
|
||||||
warning << format ("The filter '{1}' is not a valid modification string, because it contains ", value)
|
warning << format ("The filter '{1}' is not a valid modification string, because it contains {2}.", value, reason)
|
||||||
<< ( modifier_token.empty () ? "the OR operator." : format ("an attribute modifier ({1}).", modifier_token) )
|
|
||||||
<< "\nAs such, value for the write context cannot be set (context will not apply on task add / task log).\n\n"
|
<< "\nAs such, value for the write context cannot be set (context will not apply on task add / task log).\n\n"
|
||||||
<< format ("Please use 'task config context.{1}.write <default mods>' to set default attribute values for new tasks in this context manually.\n\n", words[1]);
|
<< format ("Please use 'task config context.{1}.write <default mods>' to set default attribute values for new tasks in this context manually.\n\n", words[1]);
|
||||||
out << colorizeFootnote (warning.str ());
|
out << colorizeFootnote (warning.str ());
|
||||||
|
|||||||
@@ -249,20 +249,20 @@ int CmdCustom::execute (std::string& output)
|
|||||||
rc = 1;
|
rc = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inform user about the new release higlights if not presented yet
|
// Inform user about the new release highlights if not presented yet
|
||||||
if (Context::getContext ().config.get ("news.version") != "2.6.0")
|
if (Context::getContext ().config.get ("news.version") != "2.6.0")
|
||||||
{
|
{
|
||||||
std::random_device device;
|
std::random_device device;
|
||||||
std::mt19937 random_generator(device());
|
std::mt19937 random_generator(device());
|
||||||
std::uniform_int_distribution<std::mt19937::result_type> ten_percent(1, 10);
|
std::uniform_int_distribution<std::mt19937::result_type> twentyfive_percent(1, 4);
|
||||||
|
|
||||||
std::string NEWS_NOTICE = (
|
std::string NEWS_NOTICE = (
|
||||||
"Recently upgraded to 2.6.0. "
|
"Recently upgraded to 2.6.0. "
|
||||||
"Please run 'task news' to read higlights about the new release."
|
"Please run 'task news' to read highlights about the new release."
|
||||||
);
|
);
|
||||||
|
|
||||||
// 1 in 10 chance to display the message.
|
// 1 in 10 chance to display the message.
|
||||||
if (ten_percent(random_generator) == 10)
|
if (twentyfive_percent(random_generator) == 4)
|
||||||
{
|
{
|
||||||
if (Context::getContext ().verbose ("footnote"))
|
if (Context::getContext ().verbose ("footnote"))
|
||||||
Context::getContext ().footnote (NEWS_NOTICE);
|
Context::getContext ().footnote (NEWS_NOTICE);
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ int CmdDenotate::execute (std::string&)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (before.data != task.data)
|
if (before.getAnnotations () != task.getAnnotations ())
|
||||||
{
|
{
|
||||||
auto question = format ("Denotate task {1} '{2}'?",
|
auto question = format ("Denotate task {1} '{2}'?",
|
||||||
task.identifier (true),
|
task.identifier (true),
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ std::string CmdEdit::formatTask (Task task, const std::string& dateformat)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UDA orphans
|
// UDA orphans
|
||||||
auto orphans = task.getUDAOrphanUUIDs ();
|
auto orphans = task.getUDAOrphans ();
|
||||||
if (orphans.size ())
|
if (orphans.size ())
|
||||||
{
|
{
|
||||||
before << "# User Defined Attribute Orphans\n";
|
before << "# User Defined Attribute Orphans\n";
|
||||||
|
|||||||
@@ -64,9 +64,8 @@ int CmdGet::execute (std::string& output)
|
|||||||
{
|
{
|
||||||
case Lexer::Type::dom:
|
case Lexer::Type::dom:
|
||||||
{
|
{
|
||||||
Task t;
|
|
||||||
Variant result;
|
Variant result;
|
||||||
if (getDOM (arg.attribute ("raw"), t, result))
|
if (getDOM (arg.attribute ("raw"), NULL, result))
|
||||||
results.emplace_back (result);
|
results.emplace_back (result);
|
||||||
else
|
else
|
||||||
results.emplace_back ("");
|
results.emplace_back ("");
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ void CmdImport::importSingleTask (json::object* obj)
|
|||||||
if (hasGeneratedEnd)
|
if (hasGeneratedEnd)
|
||||||
task.set ("end", before.get ("end"));
|
task.set ("end", before.get ("end"));
|
||||||
|
|
||||||
if (before.data != task.data)
|
if (before != task)
|
||||||
{
|
{
|
||||||
CmdModify modHelper;
|
CmdModify modHelper;
|
||||||
modHelper.checkConsistency (before, task);
|
modHelper.checkConsistency (before, task);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ int CmdModify::execute (std::string&)
|
|||||||
Task before (task);
|
Task before (task);
|
||||||
task.modify (Task::modReplace);
|
task.modify (Task::modReplace);
|
||||||
|
|
||||||
if (before.data != task.data)
|
if (before != task)
|
||||||
{
|
{
|
||||||
// Abort if change introduces inconsistencies.
|
// Abort if change introduces inconsistencies.
|
||||||
checkConsistency(before, task);
|
checkConsistency(before, task);
|
||||||
|
|||||||
@@ -477,7 +477,7 @@ void CmdNews::version2_6_0 (std::vector<NewsItem>& items) {
|
|||||||
" hooks.location=$XDG_CONFIG_HOME/task/hooks/\n\n"
|
" hooks.location=$XDG_CONFIG_HOME/task/hooks/\n\n"
|
||||||
" Solutions in the past required symlinks or more cumbersome configuration overrides.",
|
" Solutions in the past required symlinks or more cumbersome configuration overrides.",
|
||||||
" If you configure your data.location and hooks.location as above, ensure\n"
|
" If you configure your data.location and hooks.location as above, ensure\n"
|
||||||
" that the XFG_DATA_HOME and XDG_CONFIG_HOME environment variables are set,\n"
|
" that the XDG_DATA_HOME and XDG_CONFIG_HOME environment variables are set,\n"
|
||||||
" otherwise they're going to expand to empty string. Alternatively you can\n"
|
" otherwise they're going to expand to empty string. Alternatively you can\n"
|
||||||
" hardcode the desired paths on your system."
|
" hardcode the desired paths on your system."
|
||||||
);
|
);
|
||||||
@@ -575,14 +575,15 @@ int CmdNews::execute (std::string& output)
|
|||||||
std::stringstream outro;
|
std::stringstream outro;
|
||||||
outro << underline.colorize (bold.colorize ("Taskwarrior crowdfunding\n"));
|
outro << underline.colorize (bold.colorize ("Taskwarrior crowdfunding\n"));
|
||||||
outro << format (
|
outro << format (
|
||||||
"Taskwarrior has been in development for {1} years and its continued survival\n"
|
"Taskwarrior has been in development for {1} years but its survival\n"
|
||||||
"depends on your support!\n\n"
|
"depends on your support!\n\n"
|
||||||
"Please consider joining our {2} fundraiser and visit crowdfunding page at:\n\n",
|
"Please consider joining our {2} fundraiser to help us fund maintenance\n"
|
||||||
|
"and development of new features:\n\n",
|
||||||
std::lround (static_cast<float>(development_time.days ()) / 365.25),
|
std::lround (static_cast<float>(development_time.days ()) / 365.25),
|
||||||
now.year ()
|
now.year ()
|
||||||
);
|
);
|
||||||
outro << bold.colorize(" https://github.com/sponsors/GothenburgBitFactory/\n\n");
|
outro << bold.colorize(" https://github.com/sponsors/GothenburgBitFactory/\n\n");
|
||||||
outro << "Interesting perks are available for our sponsors.\nSponsorship directly translates to more development time spent on the project.\n";
|
outro << "Perks are available for our sponsors.\n";
|
||||||
|
|
||||||
std::cout << outro.str ();
|
std::cout << outro.str ();
|
||||||
|
|
||||||
@@ -615,7 +616,11 @@ int CmdNews::execute (std::string& output)
|
|||||||
autoComplete (answer, options, matches, 1); // Hard-coded 1.
|
autoComplete (answer, options, matches, 1); // Hard-coded 1.
|
||||||
|
|
||||||
if (matches.size () == 1 && matches[0] == "yes")
|
if (matches.size () == 1 && matches[0] == "yes")
|
||||||
|
#if defined (DARWIN)
|
||||||
|
system ("open 'https://github.com/sponsors/GothenburgBitFactory/'");
|
||||||
|
#else
|
||||||
system ("xdg-open 'https://github.com/sponsors/GothenburgBitFactory/'");
|
system ("xdg-open 'https://github.com/sponsors/GothenburgBitFactory/'");
|
||||||
|
#endif
|
||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
@@ -624,7 +629,7 @@ int CmdNews::execute (std::string& output)
|
|||||||
|
|
||||||
if (! full_summary && major_items)
|
if (! full_summary && major_items)
|
||||||
Context::getContext ().footnote (format (
|
Context::getContext ().footnote (format (
|
||||||
"Only major higlights were displayed ({1} out of {2} total).\n"
|
"Only major highlights were displayed ({1} out of {2} total).\n"
|
||||||
"If you're interested in more release highlights, run 'task news {3} minor'.",
|
"If you're interested in more release highlights, run 'task news {3} minor'.",
|
||||||
items.size (),
|
items.size (),
|
||||||
total_highlights,
|
total_highlights,
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include <format.h>
|
#include <format.h>
|
||||||
#include <shared.h>
|
#include <shared.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
|
#include <Task.h>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
CmdUDAs::CmdUDAs ()
|
CmdUDAs::CmdUDAs ()
|
||||||
@@ -133,10 +134,8 @@ int CmdUDAs::execute (std::string& output)
|
|||||||
std::map <std::string, int> orphans;
|
std::map <std::string, int> orphans;
|
||||||
for (auto& i : filtered)
|
for (auto& i : filtered)
|
||||||
{
|
{
|
||||||
for (auto& att : i.data)
|
for (auto& att : i.getUDAOrphans ())
|
||||||
if (att.first.substr (0, 11) != "annotation_" &&
|
orphans[att]++;
|
||||||
Context::getContext ().columns.find (att.first) == Context::getContext ().columns.end ())
|
|
||||||
orphans[att.first]++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orphans.size ())
|
if (orphans.size ())
|
||||||
|
|||||||
Submodule src/libshared updated: 945d0fb9a1...a803061222
@@ -29,6 +29,7 @@
|
|||||||
#include <new>
|
#include <new>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <Context.h>
|
#include <Context.h>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
int main (int argc, const char** argv)
|
int main (int argc, const char** argv)
|
||||||
@@ -64,6 +65,11 @@ int main (int argc, const char** argv)
|
|||||||
status = -3;
|
status = -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
catch (const std::regex_error& e)
|
||||||
|
{
|
||||||
|
std::cout << "regex_error caught: " << e.what() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
std::cerr << "Unknown error. Please report.\n";
|
std::cerr << "Unknown error. Please report.\n";
|
||||||
|
|||||||
@@ -189,10 +189,9 @@ static void colorizeKeyword (Task& task, const std::string& rule, const Color& b
|
|||||||
// first match.
|
// first match.
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const auto& att : task.data)
|
for (const auto& att : task.getAnnotations ())
|
||||||
{
|
{
|
||||||
if (! att.first.compare (0, 11, "annotation_", 11) &&
|
if (find (att.second, rule.substr (14), sensitive) != std::string::npos)
|
||||||
find (att.second, rule.substr (14), sensitive) != std::string::npos)
|
|
||||||
{
|
{
|
||||||
applyColor (base, c, merge);
|
applyColor (base, c, merge);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include <cmake.h>
|
#include <cmake.h>
|
||||||
|
#include <format.h>
|
||||||
#include <shared.h>
|
#include <shared.h>
|
||||||
// If <iostream> is included, put it after <stdio.h>, because it includes
|
// If <iostream> is included, put it after <stdio.h>, because it includes
|
||||||
// <stdio.h>, and therefore would ignore the _WITH_GETLINE.
|
// <stdio.h>, and therefore would ignore the _WITH_GETLINE.
|
||||||
@@ -309,3 +310,10 @@ void setHeaderUnderline (Table& table)
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Perform strtol on a string and check if the extracted value matches.
|
||||||
|
//
|
||||||
|
bool extractLongInteger (const std::string& input, long& output)
|
||||||
|
{
|
||||||
|
output = strtol (input.c_str (), nullptr, 10);
|
||||||
|
return (format ("{1}", output) == input);
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const std::vector <std::string> extractParents (
|
|||||||
bool nontrivial (const std::string&);
|
bool nontrivial (const std::string&);
|
||||||
const char* optionalBlankLine ();
|
const char* optionalBlankLine ();
|
||||||
void setHeaderUnderline (Table&);
|
void setHeaderUnderline (Table&);
|
||||||
|
bool extractLongInteger (const std::string&, long&);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
1
test/.gitignore
vendored
1
test/.gitignore
vendored
@@ -34,5 +34,6 @@ variant_partial.t
|
|||||||
variant_subtract.t
|
variant_subtract.t
|
||||||
variant_xor.t
|
variant_xor.t
|
||||||
view.t
|
view.t
|
||||||
|
tw-2689.t
|
||||||
|
|
||||||
json_test
|
json_test
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ include_directories (${CMAKE_SOURCE_DIR}
|
|||||||
${CMAKE_SOURCE_DIR}/test
|
${CMAKE_SOURCE_DIR}/test
|
||||||
${TASK_INCLUDE_DIRS})
|
${TASK_INCLUDE_DIRS})
|
||||||
|
|
||||||
set (test_SRCS col.t dom.t eval.t lexer.t t.t tdb2.t util.t variant_add.t variant_and.t variant_cast.t variant_divide.t variant_equal.t variant_exp.t variant_gt.t variant_gte.t variant_inequal.t variant_lt.t variant_lte.t variant_match.t variant_math.t variant_modulo.t variant_multiply.t variant_nomatch.t variant_not.t variant_or.t variant_partial.t variant_subtract.t variant_xor.t view.t)
|
set (test_SRCS col.t dom.t eval.t lexer.t t.t tw-2689.t tdb2.t util.t variant_add.t variant_and.t variant_cast.t variant_divide.t variant_equal.t variant_exp.t variant_gt.t variant_gte.t variant_inequal.t variant_lt.t variant_lte.t variant_match.t variant_math.t variant_modulo.t variant_multiply.t variant_nomatch.t variant_not.t variant_or.t variant_partial.t variant_subtract.t variant_xor.t view.t)
|
||||||
|
|
||||||
add_custom_target (test ./run_all --verbose
|
add_custom_target (test ./run_all --verbose
|
||||||
DEPENDS ${test_SRCS} task_executable
|
DEPENDS ${test_SRCS} task_executable
|
||||||
|
|||||||
@@ -121,15 +121,15 @@ class ContextManagementTest(TestCase):
|
|||||||
self.assertEqual(self.t.taskrc_content.count(context_line), 1)
|
self.assertEqual(self.t.taskrc_content.count(context_line), 1)
|
||||||
|
|
||||||
# Assert the config does not contain write context definition
|
# Assert the config does not contain write context definition
|
||||||
context_line = 'context.work.write=due.before:today\n'
|
context_line = 'context.urgent.write=due.before:today\n'
|
||||||
self.assertNotIn(context_line, self.t.taskrc_content)
|
self.assertNotIn(context_line, self.t.taskrc_content)
|
||||||
|
|
||||||
# Assert that the write context was not set at all
|
# Assert that the write context was not set at all
|
||||||
self.assertNotIn('context.work.write=', self.t.taskrc_content)
|
self.assertNotIn('context.urgent.write=', self.t.taskrc_content)
|
||||||
|
|
||||||
# Assert that legacy style was not used
|
# Assert that legacy style was not used
|
||||||
# Assert the config contains read context definition
|
# Assert the config contains read context definition
|
||||||
context_line = 'context.work=due.before:today\n'
|
context_line = 'context.urgent=due.before:today\n'
|
||||||
self.assertNotIn(context_line, self.t.taskrc_content)
|
self.assertNotIn(context_line, self.t.taskrc_content)
|
||||||
|
|
||||||
def test_context_define_invalid_for_write_due_to_operator(self):
|
def test_context_define_invalid_for_write_due_to_operator(self):
|
||||||
@@ -146,15 +146,40 @@ class ContextManagementTest(TestCase):
|
|||||||
self.assertEqual(self.t.taskrc_content.count(context_line), 1)
|
self.assertEqual(self.t.taskrc_content.count(context_line), 1)
|
||||||
|
|
||||||
# Assert the config does not contain write context definition
|
# Assert the config does not contain write context definition
|
||||||
context_line = 'context.work.write=due:today or +next\n'
|
context_line = 'context.urgent.write=due:today or +next\n'
|
||||||
self.assertNotIn(context_line, self.t.taskrc_content)
|
self.assertNotIn(context_line, self.t.taskrc_content)
|
||||||
|
|
||||||
# Assert that the write context was not set at all
|
# Assert that the write context was not set at all
|
||||||
self.assertNotIn('context.work.write=', self.t.taskrc_content)
|
self.assertNotIn('context.urgent.write=', self.t.taskrc_content)
|
||||||
|
|
||||||
# Assert that legacy style was not used
|
# Assert that legacy style was not used
|
||||||
# Assert the config contains read context definition
|
# Assert the config contains read context definition
|
||||||
context_line = 'context.work=due:today or +next\n'
|
context_line = 'context.urgent=due:today or +next\n'
|
||||||
|
self.assertNotIn(context_line, self.t.taskrc_content)
|
||||||
|
|
||||||
|
def test_context_define_invalid_for_write_due_to_tag_exclusion(self):
|
||||||
|
"""Test definition of a context that is not a valid write context because it contains a tag exclusion."""
|
||||||
|
self.t.config("confirmation", "off")
|
||||||
|
code, out, err = self.t('context define nowork due:today -work')
|
||||||
|
self.assertIn("Context 'nowork' defined", out)
|
||||||
|
|
||||||
|
# Assert the config contains read context definition
|
||||||
|
context_line = 'context.nowork.read=due:today -work\n'
|
||||||
|
self.assertIn(context_line, self.t.taskrc_content)
|
||||||
|
|
||||||
|
# Assert that it contains the definition only once
|
||||||
|
self.assertEqual(self.t.taskrc_content.count(context_line), 1)
|
||||||
|
|
||||||
|
# Assert the config does not contain write context definition
|
||||||
|
context_line = 'context.nowork.write=due:today -work\n'
|
||||||
|
self.assertNotIn(context_line, self.t.taskrc_content)
|
||||||
|
|
||||||
|
# Assert that the write context was not set at all
|
||||||
|
self.assertNotIn('context.nowork.write=', self.t.taskrc_content)
|
||||||
|
|
||||||
|
# Assert that legacy style was not used
|
||||||
|
# Assert the config contains read context definition
|
||||||
|
context_line = 'context.nowork=due:today -work\n'
|
||||||
self.assertNotIn(context_line, self.t.taskrc_content)
|
self.assertNotIn(context_line, self.t.taskrc_content)
|
||||||
|
|
||||||
def test_context_delete(self):
|
def test_context_delete(self):
|
||||||
@@ -206,6 +231,16 @@ class ContextManagementTest(TestCase):
|
|||||||
self.assertEqual(len(list(filter(contains_work, out.splitlines()))), 1)
|
self.assertEqual(len(list(filter(contains_work, out.splitlines()))), 1)
|
||||||
self.assertEqual(len(list(filter(contains_home, out.splitlines()))), 1)
|
self.assertEqual(len(list(filter(contains_home, out.splitlines()))), 1)
|
||||||
|
|
||||||
|
def test_context_list_legacy(self):
|
||||||
|
"""Test the determination of legacy context definition."""
|
||||||
|
self.t('config context.old project:Old', input='y\n')
|
||||||
|
self.t('context old')
|
||||||
|
code, out, err = self.t('context list')
|
||||||
|
|
||||||
|
# Assert that "old" context has only the read component defined
|
||||||
|
self.assertRegex(out, r'read\s+project:Old\s+yes')
|
||||||
|
self.assertRegex(out, r'write\s+yes')
|
||||||
|
|
||||||
def test_context_initially_empty(self):
|
def test_context_initially_empty(self):
|
||||||
"""Test that no context is set initially."""
|
"""Test that no context is set initially."""
|
||||||
self.t('context define work project:Work', input='y\ny\n')
|
self.t('context define work project:Work', input='y\ny\n')
|
||||||
|
|||||||
@@ -145,6 +145,22 @@ class TestDependencies(TestCase):
|
|||||||
code, out, err = self.t("_get 1.tags.BLOCKED")
|
code, out, err = self.t("_get 1.tags.BLOCKED")
|
||||||
self.assertEqual("\n", out)
|
self.assertEqual("\n", out)
|
||||||
|
|
||||||
|
def test_dependency_bulk_removal(self):
|
||||||
|
"""2655: Check that one can bulk undepend a task"""
|
||||||
|
self.t("add three")
|
||||||
|
self.t("1 modify dep:2,3")
|
||||||
|
|
||||||
|
code, out, err = self.t("_get 1.tags.BLOCKED")
|
||||||
|
self.assertEqual("BLOCKED\n", out)
|
||||||
|
|
||||||
|
self.t("1 modify depends:")
|
||||||
|
|
||||||
|
code, out, err = self.t("_get 1.tags.BLOCKED")
|
||||||
|
self.assertEqual("\n", out)
|
||||||
|
|
||||||
|
code, out, err = self.t("_get 1.depends")
|
||||||
|
self.assertEqual("\n", out)
|
||||||
|
|
||||||
def test_chain_repair(self):
|
def test_chain_repair(self):
|
||||||
"""Check that a broken chain is repaired"""
|
"""Check that a broken chain is repaired"""
|
||||||
self.t("add three")
|
self.t("add three")
|
||||||
@@ -169,7 +185,6 @@ class TestDependencies(TestCase):
|
|||||||
self.assertNotIn("Would you like the dependency chain fixed?", out)
|
self.assertNotIn("Would you like the dependency chain fixed?", out)
|
||||||
self.assertIn("Deleted 1 task", out)
|
self.assertIn("Deleted 1 task", out)
|
||||||
|
|
||||||
@unittest.expectedFailure
|
|
||||||
def test_id_range_dep(self):
|
def test_id_range_dep(self):
|
||||||
"""Check that an ID range can be used for deps"""
|
"""Check that an ID range can be used for deps"""
|
||||||
self.t("add three")
|
self.t("add three")
|
||||||
@@ -178,7 +193,7 @@ class TestDependencies(TestCase):
|
|||||||
self.t("3 modify dep:1-2")
|
self.t("3 modify dep:1-2")
|
||||||
code, out, err = self.t("_get 1.tags.BLOCKING")
|
code, out, err = self.t("_get 1.tags.BLOCKING")
|
||||||
self.assertEqual("BLOCKING\n", out)
|
self.assertEqual("BLOCKING\n", out)
|
||||||
code, out, err = self.t("_get 2.tag.BLOCKING")
|
code, out, err = self.t("_get 2.tags.BLOCKING")
|
||||||
self.assertEqual("BLOCKING\n", out)
|
self.assertEqual("BLOCKING\n", out)
|
||||||
|
|
||||||
def test_id_uuid_dep(self):
|
def test_id_uuid_dep(self):
|
||||||
@@ -196,6 +211,22 @@ class TestDependencies(TestCase):
|
|||||||
code, out, err = self.t("3 modify dep:-1,-%s" % uuid)
|
code, out, err = self.t("3 modify dep:-1,-%s" % uuid)
|
||||||
self.assertIn("Modifying task 3 'three'.", out)
|
self.assertIn("Modifying task 3 'three'.", out)
|
||||||
|
|
||||||
|
def test_id_uuid_short_dep(self):
|
||||||
|
"""Check that short UUIDs are usable for deps"""
|
||||||
|
|
||||||
|
# Get 2.uuid
|
||||||
|
code, out, err = self.t("_get 2.uuid")
|
||||||
|
short_uuid = out.strip().split("-")[0]
|
||||||
|
|
||||||
|
# Add a mix of IDs and UUID
|
||||||
|
code, out, err = self.t("add three dep:%s" % short_uuid)
|
||||||
|
self.assertIn("Created task 3.", out)
|
||||||
|
|
||||||
|
# Remove a mix of IЅs and UUID
|
||||||
|
code, out, err = self.t("3 modify dep:-%s" % short_uuid)
|
||||||
|
self.assertIn("Modifying task 3 'three'.", out)
|
||||||
|
|
||||||
|
|
||||||
class TestBug697(TestCase):
|
class TestBug697(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Executed before each test in the class"""
|
"""Executed before each test in the class"""
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
FROM centos:8
|
FROM centos:8
|
||||||
|
|
||||||
|
# Fix missing repo metadata
|
||||||
|
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-*
|
||||||
|
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-Linux-*
|
||||||
|
|
||||||
RUN dnf update -y
|
RUN dnf update -y
|
||||||
RUN dnf install python3 git gcc gcc-c++ make gnutls-devel libuuid-devel glibc-langpack-en -y
|
RUN dnf install python3 git gcc gcc-c++ make gnutls-devel libuuid-devel glibc-langpack-en -y
|
||||||
RUN dnf install epel-release -y
|
RUN dnf install epel-release -y
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM fedora:31
|
FROM fedora:35
|
||||||
|
|
||||||
RUN dnf update -y
|
RUN dnf update -y
|
||||||
RUN dnf install python3 git gcc gcc-c++ cmake make gnutls-devel libuuid-devel libfaketime glibc-langpack-en -y
|
RUN dnf install python3 git gcc gcc-c++ cmake make gnutls-devel libuuid-devel libfaketime glibc-langpack-en -y
|
||||||
27
test/docker/ubuntu1604
Normal file
27
test/docker/ubuntu1604
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
FROM ubuntu:16.04
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y build-essential cmake git uuid-dev libgnutls28-dev faketime locales python3
|
||||||
|
|
||||||
|
# Setup language environment
|
||||||
|
RUN locale-gen en_US.UTF-8
|
||||||
|
ENV LC_ALL en_US.UTF-8
|
||||||
|
ENV LANG en_US.UTF-8
|
||||||
|
ENV LANGUAGE en_US.UTF-8
|
||||||
|
|
||||||
|
# Setup taskwarrior
|
||||||
|
ADD . /root/code/
|
||||||
|
WORKDIR /root/code/
|
||||||
|
RUN git clean -dfx
|
||||||
|
RUN git submodule init
|
||||||
|
RUN git submodule update
|
||||||
|
RUN cmake -DCMAKE_BUILD_TYPE=debug .
|
||||||
|
RUN make -j2
|
||||||
|
RUN make install
|
||||||
|
RUN task --version
|
||||||
|
|
||||||
|
# Setup tests
|
||||||
|
WORKDIR /root/code/test/
|
||||||
|
RUN make
|
||||||
|
|
||||||
|
CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"]
|
||||||
27
test/docker/ubuntu2110
Normal file
27
test/docker/ubuntu2110
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
FROM ubuntu:21.10
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN DEBIAN_FRONTEND="noninteractive" apt-get install -y build-essential cmake git uuid-dev libgnutls28-dev faketime locales python3
|
||||||
|
|
||||||
|
# Setup language environment
|
||||||
|
RUN locale-gen en_US.UTF-8
|
||||||
|
ENV LC_ALL en_US.UTF-8
|
||||||
|
ENV LANG en_US.UTF-8
|
||||||
|
ENV LANGUAGE en_US.UTF-8
|
||||||
|
|
||||||
|
# Setup taskwarrior
|
||||||
|
ADD . /root/code/
|
||||||
|
WORKDIR /root/code/
|
||||||
|
RUN git clean -dfx
|
||||||
|
RUN git submodule init
|
||||||
|
RUN git submodule update
|
||||||
|
RUN cmake -DCMAKE_BUILD_TYPE=debug .
|
||||||
|
RUN make -j8
|
||||||
|
RUN make install
|
||||||
|
RUN task --version
|
||||||
|
|
||||||
|
# Setup tests
|
||||||
|
WORKDIR /root/code/test/
|
||||||
|
RUN make -j8
|
||||||
|
|
||||||
|
CMD ["bash", "-c", "./run_all -v ; cat all.log | grep 'not ok' ; ./problems"]
|
||||||
48
test/ids.t
48
test/ids.t
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
# Ensure python finds the local simpletap module
|
# Ensure python finds the local simpletap module
|
||||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||||
@@ -117,6 +118,53 @@ class TestIDMisParse(TestCase):
|
|||||||
self.assertNotIn("three", out)
|
self.assertNotIn("three", out)
|
||||||
|
|
||||||
|
|
||||||
|
class TestIDRangeParsing(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
"""Executed before each test in the class"""
|
||||||
|
self.t = Task()
|
||||||
|
|
||||||
|
def generate_tasks(self, n):
|
||||||
|
"""Generates n tasks for testing purposes"""
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w') as f:
|
||||||
|
f.write('\n'.join([f'{{"description": "test task {i+1}"}}' for i in range(n)]))
|
||||||
|
f.flush()
|
||||||
|
code, out, err = self.t(f'import {f.name}')
|
||||||
|
|
||||||
|
def test_single_digit_range(self):
|
||||||
|
"""Test that parsing single digit ID range works"""
|
||||||
|
self.generate_tasks(20)
|
||||||
|
code, out, err = self.t("4-6 count")
|
||||||
|
self.assertEqual("3", out.strip())
|
||||||
|
|
||||||
|
def test_double_digit_range(self):
|
||||||
|
"""Test that parsing double digit ID range works"""
|
||||||
|
self.generate_tasks(30)
|
||||||
|
code, out, err = self.t("14-26 count")
|
||||||
|
self.assertEqual("13", out.strip())
|
||||||
|
|
||||||
|
def test_triple_digit_range(self):
|
||||||
|
"""Test that parsing triple digit ID range works"""
|
||||||
|
self.generate_tasks(150)
|
||||||
|
code, out, err = self.t("110-128 count")
|
||||||
|
self.assertEqual("19", out.strip())
|
||||||
|
|
||||||
|
def test_quadruple_digit_range(self):
|
||||||
|
"""Test that parsing four digit ID range works"""
|
||||||
|
self.generate_tasks(1200)
|
||||||
|
|
||||||
|
# Full range
|
||||||
|
code, out, err = self.t("1100-1189 count")
|
||||||
|
self.assertEqual("90", out.strip())
|
||||||
|
|
||||||
|
# Partially existing range, 1200 is the last ID
|
||||||
|
code, out, err = self.t("1100-1289 count")
|
||||||
|
self.assertEqual("101", out.strip())
|
||||||
|
|
||||||
|
# Also if the range resembles time
|
||||||
|
code, out, err = self.t("1100-1140 count")
|
||||||
|
self.assertEqual("41", out.strip())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from simpletap import TAPTestRunner
|
from simpletap import TAPTestRunner
|
||||||
unittest.main(testRunner=TAPTestRunner())
|
unittest.main(testRunner=TAPTestRunner())
|
||||||
|
|||||||
@@ -500,17 +500,9 @@ class TestBug1900(TestCase):
|
|||||||
|
|
||||||
def test_project_eval(self):
|
def test_project_eval(self):
|
||||||
"""1900: Project name can contain dashes"""
|
"""1900: Project name can contain dashes"""
|
||||||
self.t("add foo project:due-b")
|
self.t("add foo project:doo-bee")
|
||||||
code, out, err = self.t("_get 1.project")
|
code, out, err = self.t("_get 1.project")
|
||||||
self.assertEqual("due-b\n", out)
|
self.assertEqual("doo-bee\n", out)
|
||||||
|
|
||||||
self.t("add foo project:scheduled-home")
|
|
||||||
code, out, err = self.t("_get 2.project")
|
|
||||||
self.assertEqual("scheduled-home\n", out)
|
|
||||||
|
|
||||||
self.t("add foo project:entry-work")
|
|
||||||
code, out, err = self.t("_get 3.project")
|
|
||||||
self.assertEqual("entry-work\n", out)
|
|
||||||
|
|
||||||
|
|
||||||
class TestBug1904(TestCase):
|
class TestBug1904(TestCase):
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ class TestRecurrenceWeekdays(TestCase):
|
|||||||
# The due dates should be Friday and Monday, three days apart,
|
# The due dates should be Friday and Monday, three days apart,
|
||||||
# having skipped the weekend.
|
# having skipped the weekend.
|
||||||
# Note: On daylight savings in the fall, this '3' becomes '2.9583'.
|
# Note: On daylight savings in the fall, this '3' becomes '2.9583'.
|
||||||
self.assertTrue(int(monday.strip()) - int(friday.strip()) >= 2)
|
# Note: when monday is next year, friday+2 > 365
|
||||||
|
self.assertTrue(int(monday.strip()) >= (int(friday.strip()) + 2) % 365)
|
||||||
|
|
||||||
|
|
||||||
class TestRecurrenceUntil(TestCase):
|
class TestRecurrenceUntil(TestCase):
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ TODO Task::decode
|
|||||||
test.is (task.get ("three"), "four", "three=four");
|
test.is (task.get ("three"), "four", "three=four");
|
||||||
|
|
||||||
// Task::set
|
// Task::set
|
||||||
task.data.clear ();
|
task = Task();
|
||||||
task.set ("name", "value");
|
task.set ("name", "value");
|
||||||
test.is (task.composeF4 (), "[name:\"value\"]", "Task::set");
|
test.is (task.composeF4 (), "[name:\"value\"]", "Task::set");
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ TODO Task::decode
|
|||||||
test.is (task.composeF4 (), "[name:\"value\"]", "Task::remove");
|
test.is (task.composeF4 (), "[name:\"value\"]", "Task::remove");
|
||||||
|
|
||||||
// Task::all
|
// Task::all
|
||||||
test.is (task.data.size (), (size_t)1, "Task::all size");
|
test.is (task.all ().size (), (size_t)1, "Task::all size");
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
18
test/tag.t
18
test/tag.t
@@ -36,13 +36,10 @@ from basetest import Task, TestCase
|
|||||||
|
|
||||||
|
|
||||||
class TestTags(TestCase):
|
class TestTags(TestCase):
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
"""Executed once before any test in the class"""
|
|
||||||
cls.t = Task()
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Executed before each test in the class"""
|
"""Executed before each test in the class"""
|
||||||
|
self.t = Task()
|
||||||
|
|
||||||
def split_tags(self, tags):
|
def split_tags(self, tags):
|
||||||
return sorted(tags.strip().split(','))
|
return sorted(tags.strip().split(','))
|
||||||
@@ -81,6 +78,19 @@ class TestTags(TestCase):
|
|||||||
code, out, err = self.t("1 modify -missing")
|
code, out, err = self.t("1 modify -missing")
|
||||||
self.assertIn("Modified 0 tasks", out)
|
self.assertIn("Modified 0 tasks", out)
|
||||||
|
|
||||||
|
def test_tag_bulk_removal(self):
|
||||||
|
"""2655: Test bulk removal of tags"""
|
||||||
|
self.t("add +one This +two is a test +three")
|
||||||
|
code, out, err = self.t("_get 1.tags")
|
||||||
|
self.assertEqual(
|
||||||
|
sorted(["one", "two", "three"]),
|
||||||
|
self.split_tags(out))
|
||||||
|
|
||||||
|
# Remove all tags in bulk
|
||||||
|
self.t("1 modify tags:")
|
||||||
|
code, out, err = self.t("_get 1.tags")
|
||||||
|
self.assertEqual("\n", out)
|
||||||
|
|
||||||
|
|
||||||
class TestVirtualTags(TestCase):
|
class TestVirtualTags(TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
27
test/test_certs/api.cert.pem
Normal file
27
test/test_certs/api.cert.pem
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIErDCCAxSgAwIBAgIUImT6dvD09I4yGowOKIyYCFwCU/IwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwdDEVMBMGA1UEAxMMbG9jYWxob3N0IENBMR4wHAYDVQQKDBVHw7Z0ZWJvcmcg
|
||||||
|
Qml0IEZhY3RvcnkxEjAQBgNVBAcMCUfDtnRlYm9yZzEaMBgGA1UECAwRVsOkc3Ry
|
||||||
|
YSBHw7Z0YWxhbmQxCzAJBgNVBAYTAlNFMB4XDTE5MDMwMzE1MDAzOVoXDTIwMDMw
|
||||||
|
MjE1MDAzOVowNDESMBAGA1UEAxMJbG9jYWxob3N0MR4wHAYDVQQKDBVHw7Z0ZWJv
|
||||||
|
cmcgQml0IEZhY3RvcnkwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDm
|
||||||
|
SHBv3zILItaJrG+3y7dWgvsX6X3FzLVXOrhFOBNbsrHSsktt20yHZhofoelkgtlc
|
||||||
|
kBsft5GUYGEBZuJIIFcOvpbXXTz/cYge8WhpcFo64APNc7zxykaTeIHMmEqjxqtO
|
||||||
|
efyOmuEyWya2XbCA4aZ6emyirkem952wBJNYLIWoy53YQ79dFvH2SOWKPlXCa1B/
|
||||||
|
ih7b9qYZE2XHoLQSbLCJqDWoQTxmOe13/1aKnJIuIy/igGsM7ygmphG4kzvyfbiA
|
||||||
|
o+Nd50USVeeeF8P+X2YZfXFKmbzJTg4q6UUB4vqn4XegW065eIyN1Xw3Z/l8CiP0
|
||||||
|
G27iuk/ZfRuz00+d9oTG5OtvyCMcc/kDI4Db1Xu9C5z9UpF5IIM85Ky91YxMMqPu
|
||||||
|
ChQQoDXJ7mdtC7zRvn6t0erMVbpaqyXxKll/ybPFAZYIgV7jtSPPJDAcTsCZQLRc
|
||||||
|
VcggScYSiWkRgW+DKsECczo++6577q++cyRBtENPOXUZ6J74eKBHizYda0chVzcC
|
||||||
|
AwEAAaN2MHQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAjAPBgNV
|
||||||
|
HQ8BAf8EBQMDB6AAMB0GA1UdDgQWBBQuQdj0wAQOzML5AQ13xsb4E8Vh9DAfBgNV
|
||||||
|
HSMEGDAWgBSmHbkkwFmCruE7I7TSI0SlD1hLiDANBgkqhkiG9w0BAQsFAAOCAYEA
|
||||||
|
CD77JzLXB0YbgJrrkotw0I8rmrb8mw0tHkDGH70UyG6VhEt3vaYcYioxs3TIo23U
|
||||||
|
dhOTApGdHzjrZePa0y2Kh7YtnPLAUml6hHTATE1D7Hufdtamd56URgBrEylKkpKl
|
||||||
|
6UnwHWzlkdRWzPRo+RD9RMBzTUMiooZaP9zzzYQNEfCmMTlHqztOLPNfFR6kFLr5
|
||||||
|
Xnc04EV/hvkByoBmo9/i4qAa3AW4y6cUHYpxVS9nBF2h364aDIIA0mn9hEewRJBq
|
||||||
|
3odng4d+e9v9b+G1ExQXM3en39REo/b73P2VATZyMxmq5gxL1OmbOa13A+S2D0Og
|
||||||
|
OkqNq6vzRhTxLbkqc0fjKv2TUFrnYCsCROvrMF+TAN0mG4NcfgeeziBTmHJq0D3I
|
||||||
|
LNTRMU/BoMYG3wGu0BUak83a9JhwViR3o0iFnctuMbD2Mz6QOawgWz8BuTcwIur6
|
||||||
|
USeERs3+G2XX499wWswGgWK5JGZ1skD1PiRsZfuaCMJHBiWolv4/G+My5IYg6B8a
|
||||||
|
-----END CERTIFICATE-----
|
||||||
182
test/test_certs/api.key.pem
Normal file
182
test/test_certs/api.key.pem
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
Public Key Info:
|
||||||
|
Public Key Algorithm: RSA
|
||||||
|
Key Security Level: High (3072 bits)
|
||||||
|
|
||||||
|
modulus:
|
||||||
|
00:e6:48:70:6f:df:32:0b:22:d6:89:ac:6f:b7:cb:b7
|
||||||
|
56:82:fb:17:e9:7d:c5:cc:b5:57:3a:b8:45:38:13:5b
|
||||||
|
b2:b1:d2:b2:4b:6d:db:4c:87:66:1a:1f:a1:e9:64:82
|
||||||
|
d9:5c:90:1b:1f:b7:91:94:60:61:01:66:e2:48:20:57
|
||||||
|
0e:be:96:d7:5d:3c:ff:71:88:1e:f1:68:69:70:5a:3a
|
||||||
|
e0:03:cd:73:bc:f1:ca:46:93:78:81:cc:98:4a:a3:c6
|
||||||
|
ab:4e:79:fc:8e:9a:e1:32:5b:26:b6:5d:b0:80:e1:a6
|
||||||
|
7a:7a:6c:a2:ae:47:a6:f7:9d:b0:04:93:58:2c:85:a8
|
||||||
|
cb:9d:d8:43:bf:5d:16:f1:f6:48:e5:8a:3e:55:c2:6b
|
||||||
|
50:7f:8a:1e:db:f6:a6:19:13:65:c7:a0:b4:12:6c:b0
|
||||||
|
89:a8:35:a8:41:3c:66:39:ed:77:ff:56:8a:9c:92:2e
|
||||||
|
23:2f:e2:80:6b:0c:ef:28:26:a6:11:b8:93:3b:f2:7d
|
||||||
|
b8:80:a3:e3:5d:e7:45:12:55:e7:9e:17:c3:fe:5f:66
|
||||||
|
19:7d:71:4a:99:bc:c9:4e:0e:2a:e9:45:01:e2:fa:a7
|
||||||
|
e1:77:a0:5b:4e:b9:78:8c:8d:d5:7c:37:67:f9:7c:0a
|
||||||
|
23:f4:1b:6e:e2:ba:4f:d9:7d:1b:b3:d3:4f:9d:f6:84
|
||||||
|
c6:e4:eb:6f:c8:23:1c:73:f9:03:23:80:db:d5:7b:bd
|
||||||
|
0b:9c:fd:52:91:79:20:83:3c:e4:ac:bd:d5:8c:4c:32
|
||||||
|
a3:ee:0a:14:10:a0:35:c9:ee:67:6d:0b:bc:d1:be:7e
|
||||||
|
ad:d1:ea:cc:55:ba:5a:ab:25:f1:2a:59:7f:c9:b3:c5
|
||||||
|
01:96:08:81:5e:e3:b5:23:cf:24:30:1c:4e:c0:99:40
|
||||||
|
b4:5c:55:c8:20:49:c6:12:89:69:11:81:6f:83:2a:c1
|
||||||
|
02:73:3a:3e:fb:ae:7b:ee:af:be:73:24:41:b4:43:4f
|
||||||
|
39:75:19:e8:9e:f8:78:a0:47:8b:36:1d:6b:47:21:57
|
||||||
|
37:
|
||||||
|
|
||||||
|
public exponent:
|
||||||
|
01:00:01:
|
||||||
|
|
||||||
|
private exponent:
|
||||||
|
00:8b:e2:40:fa:93:f8:10:2f:af:66:9d:ea:97:19:16
|
||||||
|
5b:64:e1:26:1b:5d:9d:43:c6:7c:20:5d:43:1e:d7:13
|
||||||
|
82:ae:e6:30:0c:05:c5:8a:ed:4c:a6:5d:c4:ba:c3:a5
|
||||||
|
80:67:eb:d9:ae:20:92:3c:31:77:7b:a4:85:9c:0e:99
|
||||||
|
13:89:ce:93:30:3e:17:65:5d:ac:7e:34:50:a8:41:07
|
||||||
|
36:80:d8:d2:8f:59:c8:e7:aa:39:2f:8f:9a:8a:ec:85
|
||||||
|
88:15:f9:9f:e2:f8:4e:07:8a:bb:2f:58:26:19:83:f8
|
||||||
|
de:b9:73:38:36:e9:ab:91:0a:a6:9b:80:ed:b4:cd:d4
|
||||||
|
45:2b:b2:ed:24:57:65:d2:c1:2a:72:d4:d1:1c:c3:26
|
||||||
|
f1:15:28:4f:aa:8a:5f:47:28:33:51:5a:5b:48:3d:e1
|
||||||
|
d7:1c:e8:cb:36:25:7c:6b:7f:c6:be:c2:51:1c:de:e7
|
||||||
|
4b:d4:90:a0:35:66:fb:f7:c5:d2:67:3d:59:a2:b6:a0
|
||||||
|
8a:c6:03:8b:db:3a:39:32:28:21:ba:11:3b:a3:77:90
|
||||||
|
59:0d:72:81:01:ad:5f:66:92:b5:6c:61:d2:8c:60:eb
|
||||||
|
e5:62:8d:5c:71:72:99:7c:12:b8:1c:c6:ba:ee:64:17
|
||||||
|
41:04:b6:d2:59:f0:1a:0f:74:ca:d5:19:1e:98:f4:61
|
||||||
|
75:c3:e1:50:27:af:fa:48:87:3d:c9:11:aa:70:3e:56
|
||||||
|
64:a1:dc:42:d9:16:a6:d2:14:98:48:cd:ad:d2:09:33
|
||||||
|
00:10:5f:3c:69:36:81:96:75:44:f8:56:95:d2:e5:76
|
||||||
|
85:41:85:8d:f1:64:14:05:6b:f7:2a:26:6a:d9:f0:b9
|
||||||
|
d0:de:d6:3d:7e:7c:6c:2c:26:49:22:5e:2a:04:21:4f
|
||||||
|
c9:d7:3c:a1:1b:7b:d1:27:1d:80:38:25:57:7f:a2:99
|
||||||
|
46:d7:60:56:e6:5d:50:71:36:b6:af:a6:5b:d0:92:d8
|
||||||
|
19:0e:86:4b:3b:34:cf:cd:ea:e5:35:e4:4e:65:e3:20
|
||||||
|
91:
|
||||||
|
|
||||||
|
prime1:
|
||||||
|
00:f7:07:aa:be:24:f1:2f:26:ea:0e:45:86:b0:45:05
|
||||||
|
df:b8:59:d6:3b:40:5c:dd:c8:bf:18:3e:76:74:e8:31
|
||||||
|
35:ca:b0:d4:63:df:4d:4c:d5:0c:46:6e:31:71:3c:17
|
||||||
|
5e:45:4e:5c:a1:96:4f:69:5e:92:bd:e8:09:27:85:50
|
||||||
|
e3:29:1d:bb:8e:f9:2c:41:af:0f:c8:e0:d3:70:6a:d4
|
||||||
|
b9:67:43:08:e4:4a:c9:12:f6:d2:7e:7d:bc:69:52:ba
|
||||||
|
48:96:0a:7e:42:e7:6e:82:e8:0c:6c:1b:a5:01:f0:36
|
||||||
|
2c:ae:a4:2f:d8:62:ef:ab:1c:47:3a:98:79:40:68:dd
|
||||||
|
de:6a:b4:8c:53:03:09:78:11:27:36:6f:e4:2b:fb:f4
|
||||||
|
4d:bf:13:30:37:1f:51:fd:2f:84:d9:b9:62:ea:91:a8
|
||||||
|
e8:72:9d:78:14:bd:5a:9e:1f:06:12:70:19:bd:3b:80
|
||||||
|
b2:5c:33:8b:d6:5b:9c:2f:f9:12:46:55:4a:5a:10:bc
|
||||||
|
f5:
|
||||||
|
|
||||||
|
prime2:
|
||||||
|
00:ee:a5:18:9e:e5:9a:59:d4:d9:0c:42:4c:cf:a1:d7
|
||||||
|
ca:8f:b5:45:24:59:f9:83:1f:f4:f2:82:01:50:3c:e0
|
||||||
|
ae:bf:17:39:09:cd:5a:71:dd:ab:9e:b6:d6:1e:47:dc
|
||||||
|
34:eb:28:c0:4c:77:e5:05:5d:e3:0a:57:bf:65:1f:f8
|
||||||
|
29:68:08:45:ee:da:01:bc:57:c6:35:a7:2c:82:62:64
|
||||||
|
2a:cd:46:eb:54:eb:27:e2:eb:d5:d3:d3:04:ef:05:a1
|
||||||
|
4c:63:ec:d4:23:6f:60:02:71:a8:c3:ab:a5:2f:26:4e
|
||||||
|
ea:e1:a3:f5:e4:d2:59:19:c9:26:18:c5:4d:45:1e:61
|
||||||
|
1d:53:7c:6a:83:d2:18:40:dd:10:af:e8:24:09:1a:06
|
||||||
|
9b:f1:51:33:8e:13:e0:ce:18:b3:f0:8b:f3:9d:12:b3
|
||||||
|
e7:88:01:a1:c1:38:0d:c9:c6:0e:49:99:37:2f:be:60
|
||||||
|
e3:37:9d:c6:a8:cb:4b:c5:c0:49:2a:5d:fb:f2:fb:e7
|
||||||
|
fb:
|
||||||
|
|
||||||
|
coefficient:
|
||||||
|
22:52:c4:c2:c3:dd:30:13:54:c7:95:54:b2:cb:24:b5
|
||||||
|
0c:88:f4:22:a7:6a:13:b3:17:5f:0b:b2:8c:4f:2c:e6
|
||||||
|
d3:b6:ca:cd:e6:70:e4:37:5d:65:16:50:ae:b8:cc:bf
|
||||||
|
80:81:3d:e5:5c:0e:6d:ec:f9:4e:f5:49:00:fd:63:28
|
||||||
|
2a:ab:03:92:ae:b6:a6:97:ed:f0:97:25:55:06:3f:15
|
||||||
|
be:cf:70:22:bd:af:f8:2c:3f:ce:d5:e5:e6:5f:94:d7
|
||||||
|
d2:0a:fb:99:d7:ad:ab:d1:3a:b8:fb:6c:b0:47:1c:60
|
||||||
|
81:b1:8c:5e:df:25:8b:93:82:8c:28:52:99:5e:e7:e7
|
||||||
|
f8:fc:30:48:bd:43:82:9e:39:2b:7b:14:6b:fb:1b:8a
|
||||||
|
7d:f1:1e:06:01:ca:7d:59:54:79:00:fa:76:bd:a9:a1
|
||||||
|
02:ad:c4:7e:0b:56:c2:37:6b:7d:20:5f:53:ef:46:88
|
||||||
|
cf:24:e2:4c:25:fc:98:49:40:38:10:19:6f:3c:18:4c
|
||||||
|
|
||||||
|
|
||||||
|
exp1:
|
||||||
|
00:8b:1e:3a:3e:13:37:f0:c2:0d:96:33:f9:82:53:9c
|
||||||
|
d7:3d:4e:fa:a3:2b:c0:20:f6:e9:07:92:45:cb:d8:e7
|
||||||
|
bd:cf:84:7e:58:30:6d:ac:13:5f:72:5a:a4:65:8c:dd
|
||||||
|
ec:2d:43:d0:4f:00:03:80:e7:cd:e4:3d:44:ca:88:fd
|
||||||
|
e0:b0:4b:1a:51:8e:6a:2a:23:98:d4:1c:29:77:69:f2
|
||||||
|
9a:e7:58:8d:2d:64:20:91:19:87:b9:cc:bd:ca:e2:d8
|
||||||
|
1e:00:c1:b0:11:a5:9c:4b:04:bb:da:36:47:5b:2c:18
|
||||||
|
96:59:54:05:cd:eb:09:e6:67:6a:85:c9:50:9f:c1:6f
|
||||||
|
11:cf:2e:16:c8:b9:31:1f:f9:29:08:33:43:60:b1:e8
|
||||||
|
07:d0:cf:d1:9b:79:7c:07:06:37:df:15:d4:6b:1d:d4
|
||||||
|
ed:f3:7e:53:1d:fa:f5:89:8f:17:30:53:09:6b:d4:92
|
||||||
|
c9:df:ba:f7:c9:a4:95:f5:3e:63:d8:50:38:2b:38:b9
|
||||||
|
f1:
|
||||||
|
|
||||||
|
exp2:
|
||||||
|
00:ed:06:45:41:ec:c2:35:5e:d6:84:fa:84:d7:e4:e3
|
||||||
|
33:69:30:9d:8f:d1:5d:a5:02:e4:82:c8:e5:0d:10:aa
|
||||||
|
08:65:fb:66:c7:79:92:cf:6d:5f:bb:af:d5:53:16:04
|
||||||
|
7c:fa:e3:ea:bb:08:8a:0b:9e:88:96:09:39:2b:f3:68
|
||||||
|
c3:97:74:40:21:4f:9e:51:b6:cc:43:15:db:7b:54:c6
|
||||||
|
30:4c:da:97:7a:2c:65:dd:58:67:74:90:2e:62:48:b1
|
||||||
|
3f:f2:2f:93:33:ee:b6:e9:36:82:6c:75:db:06:cd:81
|
||||||
|
ac:80:98:1c:ee:3c:8e:0a:b2:62:88:4f:ce:c3:4b:bd
|
||||||
|
21:27:7e:77:3c:9e:3b:40:91:50:b5:a6:57:c4:42:79
|
||||||
|
36:01:a4:a9:14:00:62:53:d0:ed:47:89:79:59:14:ee
|
||||||
|
62:94:0f:2a:dd:82:13:0f:c9:0a:ff:c6:91:ad:75:e5
|
||||||
|
3d:48:4c:08:b8:35:d2:f8:82:57:29:21:57:d0:aa:aa
|
||||||
|
69:
|
||||||
|
|
||||||
|
|
||||||
|
Public Key PIN:
|
||||||
|
pin-sha256:SSdvX3918szw/veSWFuQJD3lGynf9mFLiTxpZPj/Jz4=
|
||||||
|
Public Key ID:
|
||||||
|
sha256:49276f5f7f75f2ccf0fef792585b90243de51b29dff6614b893c6964f8ff273e
|
||||||
|
sha1:2e41d8f4c0040eccc2f9010d77c6c6f813c561f4
|
||||||
|
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIG5QIBAAKCAYEA5khwb98yCyLWiaxvt8u3VoL7F+l9xcy1Vzq4RTgTW7Kx0rJL
|
||||||
|
bdtMh2YaH6HpZILZXJAbH7eRlGBhAWbiSCBXDr6W1108/3GIHvFoaXBaOuADzXO8
|
||||||
|
8cpGk3iBzJhKo8arTnn8jprhMlsmtl2wgOGmenpsoq5HpvedsASTWCyFqMud2EO/
|
||||||
|
XRbx9kjlij5VwmtQf4oe2/amGRNlx6C0Emywiag1qEE8Zjntd/9WipySLiMv4oBr
|
||||||
|
DO8oJqYRuJM78n24gKPjXedFElXnnhfD/l9mGX1xSpm8yU4OKulFAeL6p+F3oFtO
|
||||||
|
uXiMjdV8N2f5fAoj9Btu4rpP2X0bs9NPnfaExuTrb8gjHHP5AyOA29V7vQuc/VKR
|
||||||
|
eSCDPOSsvdWMTDKj7goUEKA1ye5nbQu80b5+rdHqzFW6Wqsl8SpZf8mzxQGWCIFe
|
||||||
|
47UjzyQwHE7AmUC0XFXIIEnGEolpEYFvgyrBAnM6Pvuue+6vvnMkQbRDTzl1Geie
|
||||||
|
+HigR4s2HWtHIVc3AgMBAAECggGBAIviQPqT+BAvr2ad6pcZFltk4SYbXZ1Dxnwg
|
||||||
|
XUMe1xOCruYwDAXFiu1Mpl3EusOlgGfr2a4gkjwxd3ukhZwOmROJzpMwPhdlXax+
|
||||||
|
NFCoQQc2gNjSj1nI56o5L4+aiuyFiBX5n+L4TgeKuy9YJhmD+N65czg26auRCqab
|
||||||
|
gO20zdRFK7LtJFdl0sEqctTRHMMm8RUoT6qKX0coM1FaW0g94dcc6Ms2JXxrf8a+
|
||||||
|
wlEc3udL1JCgNWb798XSZz1ZoragisYDi9s6OTIoIboRO6N3kFkNcoEBrV9mkrVs
|
||||||
|
YdKMYOvlYo1ccXKZfBK4HMa67mQXQQS20lnwGg90ytUZHpj0YXXD4VAnr/pIhz3J
|
||||||
|
EapwPlZkodxC2Ram0hSYSM2t0gkzABBfPGk2gZZ1RPhWldLldoVBhY3xZBQFa/cq
|
||||||
|
JmrZ8LnQ3tY9fnxsLCZJIl4qBCFPydc8oRt70ScdgDglV3+imUbXYFbmXVBxNrav
|
||||||
|
plvQktgZDoZLOzTPzerlNeROZeMgkQKBwQD3B6q+JPEvJuoORYawRQXfuFnWO0Bc
|
||||||
|
3ci/GD52dOgxNcqw1GPfTUzVDEZuMXE8F15FTlyhlk9pXpK96AknhVDjKR27jvks
|
||||||
|
Qa8PyODTcGrUuWdDCORKyRL20n59vGlSukiWCn5C526C6AxsG6UB8DYsrqQv2GLv
|
||||||
|
qxxHOph5QGjd3mq0jFMDCXgRJzZv5Cv79E2/EzA3H1H9L4TZuWLqkajocp14FL1a
|
||||||
|
nh8GEnAZvTuAslwzi9ZbnC/5EkZVSloQvPUCgcEA7qUYnuWaWdTZDEJMz6HXyo+1
|
||||||
|
RSRZ+YMf9PKCAVA84K6/FzkJzVpx3auettYeR9w06yjATHflBV3jCle/ZR/4KWgI
|
||||||
|
Re7aAbxXxjWnLIJiZCrNRutU6yfi69XT0wTvBaFMY+zUI29gAnGow6ulLyZO6uGj
|
||||||
|
9eTSWRnJJhjFTUUeYR1TfGqD0hhA3RCv6CQJGgab8VEzjhPgzhiz8IvznRKz54gB
|
||||||
|
ocE4DcnGDkmZNy++YOM3ncaoy0vFwEkqXfvy++f7AoHBAIseOj4TN/DCDZYz+YJT
|
||||||
|
nNc9TvqjK8Ag9ukHkkXL2Oe9z4R+WDBtrBNfclqkZYzd7C1D0E8AA4DnzeQ9RMqI
|
||||||
|
/eCwSxpRjmoqI5jUHCl3afKa51iNLWQgkRmHucy9yuLYHgDBsBGlnEsEu9o2R1ss
|
||||||
|
GJZZVAXN6wnmZ2qFyVCfwW8Rzy4WyLkxH/kpCDNDYLHoB9DP0Zt5fAcGN98V1Gsd
|
||||||
|
1O3zflMd+vWJjxcwUwlr1JLJ37r3yaSV9T5j2FA4Kzi58QKBwQDtBkVB7MI1XtaE
|
||||||
|
+oTX5OMzaTCdj9FdpQLkgsjlDRCqCGX7Zsd5ks9tX7uv1VMWBHz64+q7CIoLnoiW
|
||||||
|
CTkr82jDl3RAIU+eUbbMQxXbe1TGMEzal3osZd1YZ3SQLmJIsT/yL5Mz7rbpNoJs
|
||||||
|
ddsGzYGsgJgc7jyOCrJiiE/Ow0u9ISd+dzyeO0CRULWmV8RCeTYBpKkUAGJT0O1H
|
||||||
|
iXlZFO5ilA8q3YITD8kK/8aRrXXlPUhMCLg10viCVykhV9CqqmkCgcAiUsTCw90w
|
||||||
|
E1THlVSyyyS1DIj0IqdqE7MXXwuyjE8s5tO2ys3mcOQ3XWUWUK64zL+AgT3lXA5t
|
||||||
|
7PlO9UkA/WMoKqsDkq62ppft8JclVQY/Fb7PcCK9r/gsP87V5eZflNfSCvuZ162r
|
||||||
|
0Tq4+2ywRxxggbGMXt8li5OCjChSmV7n5/j8MEi9Q4KeOSt7FGv7G4p98R4GAcp9
|
||||||
|
WVR5APp2vamhAq3EfgtWwjdrfSBfU+9GiM8k4kwl/JhJQDgQGW88GEw=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
0
test/tw-1837.t
Executable file → Normal file
0
test/tw-1837.t
Executable file → Normal file
89
test/tw-2689.t.cpp
Normal file
89
test/tw-2689.t.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included
|
||||||
|
// in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
// https://www.opensource.org/licenses/mit-license.php
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <cmake.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <main.h>
|
||||||
|
#include <test.h>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
int main (int, char**)
|
||||||
|
{
|
||||||
|
UnitTest test (12);
|
||||||
|
|
||||||
|
// Ensure environment has no influence.
|
||||||
|
unsetenv ("TASKDATA");
|
||||||
|
unsetenv ("TASKRC");
|
||||||
|
|
||||||
|
// Inform Task about the attributes in the JSON below
|
||||||
|
Task::attributes["depends"] = "string";
|
||||||
|
Task::attributes["uuid"] = "string";
|
||||||
|
|
||||||
|
// depends in [..] string from a taskserver (issue#2689)
|
||||||
|
auto sample = "{"
|
||||||
|
"\"depends\":\"[\\\"92a40a34-37f3-4785-8ca1-ff89cfbfd105\\\",\\\"e08e35fa-e42b-4de0-acc4-518fca8f6365\\\"]\","
|
||||||
|
"\"uuid\":\"00000000-0000-0000-0000-000000000000\""
|
||||||
|
"}";
|
||||||
|
auto json = Task (sample);
|
||||||
|
auto value = json.get ("uuid");
|
||||||
|
test.is (value, "00000000-0000-0000-0000-000000000000", "json [..] uuid");
|
||||||
|
value = json.get ("depends");
|
||||||
|
test.is (value, "92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365", "json [..] depends");
|
||||||
|
test.ok (json.has ("dep_92a40a34-37f3-4785-8ca1-ff89cfbfd105"), "json [..] dep attr");
|
||||||
|
test.ok (json.has ("dep_e08e35fa-e42b-4de0-acc4-518fca8f6365"), "json [..] dep attr");
|
||||||
|
|
||||||
|
// depends in comma-delimited string from a taskserver (deprecated format)
|
||||||
|
sample = "{"
|
||||||
|
"\"depends\":\"92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365\","
|
||||||
|
"\"uuid\":\"00000000-0000-0000-0000-000000000000\""
|
||||||
|
"}";
|
||||||
|
json = Task (sample);
|
||||||
|
value = json.get ("uuid");
|
||||||
|
test.is (value, "00000000-0000-0000-0000-000000000000", "json comma-separated uuid");
|
||||||
|
value = json.get ("depends");
|
||||||
|
test.is (value, "92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365", "json comma-separated depends");
|
||||||
|
test.ok (json.has ("dep_92a40a34-37f3-4785-8ca1-ff89cfbfd105"), "json comma-separated dep attr");
|
||||||
|
test.ok (json.has ("dep_e08e35fa-e42b-4de0-acc4-518fca8f6365"), "json comma-separated dep attr");
|
||||||
|
|
||||||
|
// depends in a JSON array from a taskserver
|
||||||
|
sample = "{"
|
||||||
|
"\"depends\":[\"92a40a34-37f3-4785-8ca1-ff89cfbfd105\",\"e08e35fa-e42b-4de0-acc4-518fca8f6365\"],"
|
||||||
|
"\"uuid\":\"00000000-0000-0000-0000-000000000000\""
|
||||||
|
"}";
|
||||||
|
json = Task (sample);
|
||||||
|
value = json.get ("uuid");
|
||||||
|
test.is (value, "00000000-0000-0000-0000-000000000000", "json array uuid");
|
||||||
|
value = json.get ("depends");
|
||||||
|
test.is (value, "92a40a34-37f3-4785-8ca1-ff89cfbfd105,e08e35fa-e42b-4de0-acc4-518fca8f6365", "json array depends");
|
||||||
|
test.ok (json.has ("dep_92a40a34-37f3-4785-8ca1-ff89cfbfd105"), "json array dep attr");
|
||||||
|
test.ok (json.has ("dep_e08e35fa-e42b-4de0-acc4-518fca8f6365"), "json array dep attr");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -314,7 +314,7 @@ class Test1447(TestCase):
|
|||||||
self.t = Task()
|
self.t = Task()
|
||||||
|
|
||||||
def test_filter_uda(self):
|
def test_filter_uda(self):
|
||||||
"""1447: Verify ability to filter on empty UDA"""
|
"""1447: Verify ability to filter on empty UDA that resembles named date"""
|
||||||
self.t.config('uda.sep.type', 'string')
|
self.t.config('uda.sep.type', 'string')
|
||||||
self.t('add one')
|
self.t('add one')
|
||||||
self.t('add two sep:foo')
|
self.t('add two sep:foo')
|
||||||
|
|||||||
@@ -82,6 +82,24 @@ class TestUDAOrphans(TestCase):
|
|||||||
code, out, err = self.t("_get 1.description")
|
code, out, err = self.t("_get 1.description")
|
||||||
self.assertIn("one extra:foo", out)
|
self.assertIn("one extra:foo", out)
|
||||||
|
|
||||||
|
def test_orphan_identification(self):
|
||||||
|
"""Verify that orphans are identified by +ORPHAN tag and udas command"""
|
||||||
|
|
||||||
|
# Create one task with legitimate orphan attribute and others without
|
||||||
|
self.t("rc.uda.extra.type:string rc.uda.extra.label:Extra add one extra:foo")
|
||||||
|
self.t("add two no attributes")
|
||||||
|
self.t("add three +test ")
|
||||||
|
self.t("add four +test depends:3")
|
||||||
|
self.t("4 annotate annotation content")
|
||||||
|
|
||||||
|
# Only the first task should be identified as orphan
|
||||||
|
code, out, err = self.t("+ORPHAN ids")
|
||||||
|
self.assertEqual("1", out.strip())
|
||||||
|
|
||||||
|
# Only the first task should be identified as orphan
|
||||||
|
code, out, err = self.t("udas")
|
||||||
|
self.assertRegex(out, r'extra\s+1\s+1 Orphan UDA')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from simpletap import TAPTestRunner
|
from simpletap import TAPTestRunner
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ int main (int, char**)
|
|||||||
Task rightAgain (right);
|
Task rightAgain (right);
|
||||||
|
|
||||||
std::string output = left.diff (right);
|
std::string output = left.diff (right);
|
||||||
t.ok (left.data != right.data, "Detected changes");
|
t.ok (!(left == right), "Detected changes");
|
||||||
t.ok (output.find ("Zero will be changed from '0' to '00'") != std::string::npos, "Detected change zero:0 -> zero:00");
|
t.ok (output.find ("Zero will be changed from '0' to '00'") != std::string::npos, "Detected change zero:0 -> zero:00");
|
||||||
t.ok (output.find ("One will be deleted") != std::string::npos, "Detected deletion one:1 ->");
|
t.ok (output.find ("One will be deleted") != std::string::npos, "Detected deletion one:1 ->");
|
||||||
t.ok (output.find ("Two") == std::string::npos, "Detected no change two:2 -> two:2");
|
t.ok (output.find ("Two") == std::string::npos, "Detected no change two:2 -> two:2");
|
||||||
|
|||||||
@@ -77,11 +77,6 @@ class TestVersion(TestCase):
|
|||||||
self.assertIn("MIT license", out)
|
self.assertIn("MIT license", out)
|
||||||
self.assertIn("https://taskwarrior.org", out)
|
self.assertIn("https://taskwarrior.org", out)
|
||||||
|
|
||||||
def slurp_git(self):
|
|
||||||
git_cmd = ("git", "rev-parse", "--short", "--verify", "HEAD")
|
|
||||||
_, hash, _ = run_cmd_wait(git_cmd)
|
|
||||||
return hash.rstrip("\n")
|
|
||||||
|
|
||||||
def test_under_version(self):
|
def test_under_version(self):
|
||||||
"""_version and diagnostics output expected version and syntax"""
|
"""_version and diagnostics output expected version and syntax"""
|
||||||
code, out, err = self.t("_version")
|
code, out, err = self.t("_version")
|
||||||
@@ -94,8 +89,7 @@ class TestVersion(TestCase):
|
|||||||
if os.path.exists("../.git"):
|
if os.path.exists("../.git"):
|
||||||
if 2 >= len(version) > 0:
|
if 2 >= len(version) > 0:
|
||||||
git = version[1]
|
git = version[1]
|
||||||
git_expected = "({0})".format(self.slurp_git())
|
self.assertRegex(git, r'\([a-f0-9]*\)'))
|
||||||
self.assertEqual(git_expected, git)
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unexpected output from _version '{0}'".format(
|
raise ValueError("Unexpected output from _version '{0}'".format(
|
||||||
out))
|
out))
|
||||||
|
|||||||
12
test/wait.t
12
test/wait.t
@@ -87,14 +87,18 @@ class Test1486(TestCase):
|
|||||||
|
|
||||||
def test_waiting(self):
|
def test_waiting(self):
|
||||||
"""1486: Verify waiting report shows waiting tasks"""
|
"""1486: Verify waiting report shows waiting tasks"""
|
||||||
self.t.config('uda.sep.type', 'string')
|
|
||||||
|
|
||||||
self.t('add regular')
|
self.t('add regular')
|
||||||
self.t('add waited wait:later')
|
self.t('add waited and pending wait:later')
|
||||||
|
self.t('add waited but deleted wait:later')
|
||||||
|
self.t('add waited but done wait:later')
|
||||||
|
self.t('rc.confirmation=off 3 delete')
|
||||||
|
self.t('4 done')
|
||||||
|
|
||||||
code, out, err = self.t('waiting')
|
code, out, err = self.t('waiting')
|
||||||
self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code))
|
self.assertEqual(0, code, "Exit code was non-zero ({0})".format(code))
|
||||||
self.assertIn('waited', out)
|
self.assertIn('waited and pending', out)
|
||||||
|
self.assertNotIn('waited but deleted', out)
|
||||||
|
self.assertNotIn('waited but done', out)
|
||||||
self.assertNotIn('regular', out)
|
self.assertNotIn('regular', out)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user