Compare commits
585 commits
v2.0.0-rc.
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1350740c98 | ||
|
|
04640cccbe | ||
|
|
babcdb0405 | ||
|
|
2f4f41a31d | ||
|
|
2f8c477f66 | ||
|
|
064659a70e | ||
|
|
eef651f0d4 | ||
|
|
d67d80eb41 | ||
|
|
a88e4b3ba4 | ||
|
|
dbdd2e9c26 | ||
|
|
05fbd33a3a | ||
|
|
b2e31b6083 | ||
|
|
00a12d11aa | ||
|
|
b517e40e5c | ||
|
|
dea1d300b3 | ||
|
|
4de87bf099 | ||
|
|
aa4731953e | ||
|
|
363b619d87 | ||
|
|
7005ced0b5 | ||
|
|
99b7757a3a | ||
|
|
dd4b774958 | ||
|
|
b04a400329 | ||
|
|
7410d25ee4 | ||
|
|
987d8cb65c | ||
|
|
d425892b61 | ||
|
|
f7aacc4fc0 | ||
|
|
0de1e040b0 | ||
|
|
4b42e4b0de | ||
|
|
863246eed0 | ||
|
|
3865a5f27e | ||
|
|
b26353c04d | ||
|
|
8d9a8a8265 | ||
|
|
1aa822da56 | ||
|
|
786e5cc027 | ||
|
|
9806d677f3 | ||
|
|
efd28ffb3a | ||
|
|
93103e2c49 | ||
|
|
0029890ead | ||
|
|
79268800ff | ||
|
|
9f8d6bd4ca | ||
|
|
669ee2bff8 | ||
|
|
0dccfa26a5 | ||
|
|
c244b0dd51 | ||
|
|
4e6bb49788 | ||
|
|
14989f1ddd | ||
|
|
58c088977e | ||
|
|
c22febdbd6 | ||
|
|
1b0c1a87a2 | ||
|
|
84a323f0de | ||
|
|
0b76161d86 | ||
|
|
6ed6a5da88 | ||
|
|
47000723a9 | ||
|
|
6b420de752 | ||
|
|
bd4a84cd3e | ||
|
|
1aafcb201b | ||
|
|
4d2505dac8 | ||
|
|
ff3a86d393 | ||
|
|
5dae945e95 | ||
|
|
8102d3cf61 | ||
|
|
2ffca85422 | ||
|
|
299f7800c3 | ||
|
|
66912a3a54 | ||
|
|
3732d59249 | ||
|
|
412928fc59 | ||
|
|
61695e2896 | ||
|
|
93d0fbc6c3 | ||
|
|
61d90c1c41 | ||
|
|
f6f05db2f6 | ||
|
|
41a901694d | ||
|
|
3516684c68 | ||
|
|
638a4a286f | ||
|
|
9d805bb021 | ||
|
|
8d34de633a | ||
|
|
409b5e2f6b | ||
|
|
657d48aac0 | ||
|
|
611957e270 | ||
|
|
dfe2eec8df | ||
|
|
40a9c0bdc8 | ||
|
|
a2d34e195c | ||
|
|
e46184baa2 | ||
|
|
7a3a4398d1 | ||
|
|
2c7e03d266 | ||
|
|
c76b24a1fd | ||
|
|
68515376a1 | ||
|
|
551a0b407f | ||
|
|
32239a0b9d | ||
|
|
0d314aecd1 | ||
|
|
09cbe87595 | ||
|
|
a716739a18 | ||
|
|
24ccfefa26 | ||
|
|
ca2397830c | ||
|
|
66c305b854 | ||
|
|
be09a43715 | ||
|
|
d2e3705575 | ||
|
|
d0ead3a14c | ||
|
|
9247496115 | ||
|
|
432901db33 | ||
|
|
838bdf6095 | ||
|
|
7d173d5f4c | ||
|
|
897447d7d7 | ||
|
|
37caf65775 | ||
|
|
be4b1d0624 | ||
|
|
1cc02ff4e1 | ||
|
|
7e37ce307a | ||
|
|
81b0f67107 | ||
|
|
51cd7854f9 | ||
|
|
88b06ef3e3 | ||
|
|
c89192a37f | ||
|
|
df005c3f06 | ||
|
|
7cd6bb9aff | ||
|
|
1167be6add | ||
|
|
7b6ee267af | ||
|
|
3a480e4ab0 | ||
|
|
6db4aae9e5 | ||
|
|
4dae65d5c7 | ||
|
|
35008fba4b | ||
|
|
5ca9d22276 | ||
|
|
7d4a5dce6f | ||
|
|
59ff2956ab | ||
|
|
1a7612c0ff | ||
|
|
86f43ba5ae | ||
|
|
f9d328e9b8 | ||
|
|
35e518fe4f | ||
|
|
5373ae180b | ||
|
|
95e4c40a30 | ||
|
|
a09d50a94f | ||
|
|
26adbdb220 | ||
|
|
a3a0385991 | ||
|
|
a5e2fab2f1 | ||
|
|
0ed9e76a35 | ||
|
|
82a2da0033 | ||
|
|
b164b511d8 | ||
|
|
31a418601b | ||
|
|
93a53cdb4e | ||
|
|
1c59463034 | ||
|
|
41037b20f3 | ||
|
|
e5c813949f | ||
|
|
4b9114afe5 | ||
|
|
7e3c867d74 | ||
|
|
57ff821635 | ||
|
|
50806ba4e7 | ||
|
|
1275853d62 | ||
|
|
95006629ec | ||
|
|
e08596da35 | ||
|
|
1f61965110 | ||
|
|
4067ecdb92 | ||
|
|
e1f641748a | ||
|
|
f5e1437f34 | ||
|
|
c541b98e14 | ||
|
|
bc6ca55e8d | ||
|
|
2466a74fec | ||
|
|
973842064c | ||
|
|
05c2915e59 | ||
|
|
b0c5a70216 | ||
|
|
2f832aa610 | ||
|
|
6b2b9de2c2 | ||
|
|
1d5c2b67f2 | ||
|
|
68395e5562 | ||
|
|
d7fbb14ac6 | ||
|
|
206919b836 | ||
|
|
a77a8def37 | ||
|
|
ac47539d09 | ||
|
|
295d1fe156 | ||
|
|
182250152f | ||
|
|
1b4420cadd | ||
|
|
b141f14762 | ||
|
|
ad2314549a | ||
|
|
c04489d624 | ||
|
|
db07f271d6 | ||
|
|
60b7eb80f5 | ||
|
|
31318145e7 | ||
|
|
7a042fdd6b | ||
|
|
abb9064e11 | ||
|
|
bba6114a86 | ||
|
|
c8901b5796 | ||
|
|
45e6f9f266 | ||
|
|
b49cad2290 | ||
|
|
561a7f9efa | ||
|
|
d3859b8088 | ||
|
|
c6ccf02623 | ||
|
|
7ef1a6f9a5 | ||
|
|
f8ea8a73b7 | ||
|
|
d262ff2176 | ||
|
|
a9707cd9c3 | ||
|
|
1e0921c621 | ||
|
|
72b0610d2c | ||
|
|
dbb27ed3b0 | ||
|
|
679e67b555 | ||
|
|
f788039ba0 | ||
|
|
69987a8a7f | ||
|
|
6ce9292c52 | ||
|
|
e44b4b1363 | ||
|
|
cfe56add8f | ||
|
|
7253dbb900 | ||
|
|
5f4b45c4b9 | ||
|
|
73f7d4304b | ||
|
|
04b598599a | ||
|
|
6572b68bf2 | ||
|
|
b8594e1994 | ||
|
|
6ad7f00be2 | ||
|
|
9987e7c695 | ||
|
|
239d943fad | ||
|
|
6567ad423d | ||
|
|
73ab5ba050 | ||
|
|
be5d5ee1c7 | ||
|
|
110b90bc75 | ||
|
|
79d061f431 | ||
|
|
1882e58796 | ||
|
|
ac9e2ce706 | ||
|
|
8735dfccb6 | ||
|
|
ea14e3d5ef | ||
|
|
4f5db83d30 | ||
|
|
97b245883c | ||
|
|
8c6ed5aa14 | ||
|
|
78b9cb69ae | ||
|
|
9ed73c7a66 | ||
|
|
defff26b9a | ||
|
|
0475545a01 | ||
|
|
fb66c8f983 | ||
|
|
006d82c02b | ||
|
|
8fc491c499 | ||
|
|
794bdb2e1c | ||
|
|
642b000ad4 | ||
|
|
ba257b53dc | ||
|
|
084eb408dd | ||
|
|
bf7b52ad30 | ||
|
|
a4e619efa4 | ||
|
|
51d19ebc2e | ||
|
|
32b7cfc5d1 | ||
|
|
38a9d8f93e | ||
|
|
e55d145036 | ||
|
|
7668889493 | ||
|
|
a65a3b9562 | ||
|
|
08f14e2db1 | ||
|
|
f220ca11e4 | ||
|
|
b057c6f308 | ||
|
|
9aa2ea924e | ||
|
|
5915ecdaa1 | ||
|
|
ea9c1fee48 | ||
|
|
f046d183df | ||
|
|
9e831469a5 | ||
|
|
c3d7df3bc5 | ||
|
|
242fc5ae7b | ||
|
|
49bf898e0e | ||
|
|
8ee76bf44b | ||
|
|
316803df77 | ||
|
|
c178c2e91e | ||
|
|
cc9d262086 | ||
|
|
e56b2b0980 | ||
|
|
73c87c1972 | ||
|
|
640935927e | ||
|
|
59474646cb | ||
|
|
1f261e6bbf | ||
|
|
33076dc13e | ||
|
|
2d4c293a27 | ||
|
|
e48259d990 | ||
|
|
98db156d30 | ||
|
|
908272a00f | ||
|
|
275d09972f | ||
|
|
27bfd3c987 | ||
|
|
739751a021 | ||
|
|
426d9ed2a5 | ||
|
|
ed506ef091 | ||
|
|
235dbb153f | ||
|
|
4162188f80 | ||
|
|
a61c7cfd43 | ||
|
|
c7c56bca03 | ||
|
|
d410c1ac22 | ||
|
|
8462e52b30 | ||
|
|
406be9f02a | ||
|
|
12bf3068bd | ||
|
|
d2a143ef30 | ||
|
|
964387c5b0 | ||
|
|
d7d08f0c36 | ||
|
|
1844fed147 | ||
|
|
ae91dcedd0 | ||
|
|
c37321b6fa | ||
|
|
10687999f3 | ||
|
|
c3a037bc65 | ||
|
|
ee43076fca | ||
|
|
fd78929a3f | ||
|
|
5918662d06 | ||
|
|
7a57dc9667 | ||
|
|
ea5129b888 | ||
|
|
3d5ebc7509 | ||
|
|
953a5d64bf | ||
|
|
fb778f9fa9 | ||
|
|
45632dd0e4 | ||
|
|
559cdb420b | ||
|
|
76fb5227af | ||
|
|
884b95be07 | ||
|
|
05c774139a | ||
|
|
90d37608c8 | ||
|
|
7be6ef0f4d | ||
|
|
4a3ed66135 | ||
|
|
0cc9550fbf | ||
|
|
bdd88f4b90 | ||
|
|
ee58e2d700 | ||
|
|
4e28c5c327 | ||
|
|
8731f97266 | ||
|
|
e53c7f0e21 | ||
|
|
845a631961 | ||
|
|
05695a7f4a | ||
|
|
5b2c3ab3d3 | ||
|
|
076e14400b | ||
|
|
fe8365bcc1 | ||
|
|
e772b26d1c | ||
|
|
1cac91c05d | ||
|
|
fd1a13c1e2 | ||
|
|
df4481fdbf | ||
|
|
e284b58f5e | ||
|
|
3be8155e8a | ||
|
|
499c67fea5 | ||
|
|
1c8a9eefc0 | ||
|
|
78f5c78ced | ||
|
|
eda3e66ce6 | ||
|
|
128204d3c9 | ||
|
|
1c36b2986a | ||
|
|
f89c3481a7 | ||
|
|
de3d404dfe | ||
|
|
42c8e26ff4 | ||
|
|
2498775e2f | ||
|
|
eca5b3cfd8 | ||
|
|
db48e96dca | ||
|
|
594e6a42a8 | ||
|
|
746d3e625e | ||
|
|
8b5111f8de | ||
|
|
f5a6dd3dd2 | ||
|
|
3c3f528815 | ||
|
|
aa6dd18c37 | ||
|
|
013aff2475 | ||
|
|
34808c6b77 | ||
|
|
28ad9f623c | ||
|
|
62f8c16c31 | ||
|
|
abd6196b11 | ||
|
|
8aa2a9eb5c | ||
|
|
3cee31e0ee | ||
|
|
64fcd31b1c | ||
|
|
90c1f5f59e | ||
|
|
00446c5341 | ||
|
|
404be7afd2 | ||
|
|
18be919ab8 | ||
|
|
dc85ddc735 | ||
|
|
6cebab0e7f | ||
|
|
e0eee9e6fb | ||
|
|
060225cc7c | ||
|
|
b3a3fd8a30 | ||
|
|
37fb939e8e | ||
|
|
a04aa652d9 | ||
|
|
88ce8e31ca | ||
|
|
f1ff703e64 | ||
|
|
dc9c866041 | ||
|
|
236347eaec | ||
|
|
3da01dda79 | ||
|
|
a4c43bd6ae | ||
|
|
aa4d674e36 | ||
|
|
00de8c88a2 | ||
|
|
c3c5f60c0d | ||
|
|
e573663789 | ||
|
|
a2d0bad3b8 | ||
|
|
4a159277a2 | ||
|
|
8f1208eb01 | ||
|
|
5c35de28eb | ||
|
|
d8e0a99070 | ||
|
|
a25d06a8d7 | ||
|
|
0f08c85938 | ||
|
|
f8f5c10c57 | ||
|
|
ef1ccb093e | ||
|
|
7b1727dc74 | ||
|
|
4200bd7a3b | ||
|
|
f72ee2ea46 | ||
|
|
4f607633dd | ||
|
|
ef08c53ba9 | ||
|
|
a8b9b2b49a | ||
|
|
5fe55be6f3 | ||
|
|
fa3f7851d2 | ||
|
|
a047423f4e | ||
|
|
67572dbea2 | ||
|
|
f44b3e8bf4 | ||
|
|
89573407e5 | ||
|
|
d4cd37994a | ||
|
|
8fc91c921d | ||
|
|
a5c4b2d219 | ||
|
|
5a764deda2 | ||
|
|
9a89f31b9c | ||
|
|
53958687a6 | ||
|
|
50cb2bf51b | ||
|
|
994e9c7aa2 | ||
|
|
a0ab25214d | ||
|
|
f8d49b4370 | ||
|
|
d952ad0f27 | ||
|
|
39338293db | ||
|
|
5ff6db6315 | ||
|
|
e5d8e985ba | ||
|
|
829a6dc814 | ||
|
|
6d73e467b0 | ||
|
|
f68f930dd2 | ||
|
|
0697ac6d55 | ||
|
|
4e6db583e3 | ||
|
|
201c6a9f5c | ||
|
|
5bf0575b46 | ||
|
|
faeb7b7e80 | ||
|
|
d131860324 | ||
|
|
defd8f399b | ||
|
|
0e1a908207 | ||
|
|
3c06b401b9 | ||
|
|
3ad45ff86e | ||
|
|
9eb33e8ab5 | ||
|
|
86f884412e | ||
|
|
c6624a3fcf | ||
|
|
ce88d603fc | ||
|
|
4c15a9ca93 | ||
|
|
a8a7147f50 | ||
|
|
07f94aeb9e | ||
|
|
7e9930bc7e | ||
|
|
1bb5a0bbdd | ||
|
|
4418f5f00a | ||
|
|
6a02f2dd22 | ||
|
|
cca1b34f0d | ||
|
|
e4e0df62b9 | ||
|
|
35f86fe528 | ||
|
|
06fda7c15a | ||
|
|
8055f309da | ||
|
|
9c30d10b97 | ||
|
|
3199a1ac7b | ||
|
|
b43cc1ae91 | ||
|
|
2512e72f32 | ||
|
|
c3259937f1 | ||
|
|
65f24c5674 | ||
|
|
e60e6efb03 | ||
|
|
37401d7c1c | ||
|
|
cc3083710e | ||
|
|
39f8d9b972 | ||
|
|
c76a421ff8 | ||
|
|
1906abd0e5 | ||
|
|
7d41bd6afd | ||
|
|
7fbc8c238a | ||
|
|
a5835850c6 | ||
|
|
1cdfda0fc8 | ||
|
|
4a22c4cb57 | ||
|
|
cbfa0aa10d | ||
|
|
ed942a8f85 | ||
|
|
bae73b15a2 | ||
|
|
13a4880c59 | ||
|
|
bed84b7853 | ||
|
|
9c2c1fe880 | ||
|
|
030bcd5fe0 | ||
|
|
ffb6fe227e | ||
|
|
84c1bd1d2a | ||
|
|
a222ff624c | ||
|
|
8116017763 | ||
|
|
22c48033ac | ||
|
|
fe6fad4587 | ||
|
|
1d01d73e74 | ||
|
|
0f849d4581 | ||
|
|
97127c535d | ||
|
|
5fdaa490c4 | ||
|
|
98cb8655b3 | ||
|
|
60ee73bf8a | ||
|
|
ffbb530f81 | ||
|
|
25631b3ed7 | ||
|
|
cab77729f6 | ||
|
|
5b8ef31d76 | ||
|
|
b88c775ebe | ||
|
|
16d63a92ab | ||
|
|
1b0af44179 | ||
|
|
5215321466 | ||
|
|
ea43094ed6 | ||
|
|
2059fd422a | ||
|
|
0eaa02c9a2 | ||
|
|
7c3b7a1164 | ||
|
|
564f44f053 | ||
|
|
9e3ba28811 | ||
|
|
ab157d7152 | ||
|
|
94be9010f1 | ||
|
|
1fba984dd3 | ||
|
|
23419f7568 | ||
|
|
165007b636 | ||
|
|
e0f4a33c82 | ||
|
|
af6fd91521 | ||
|
|
ebf085f3e2 | ||
|
|
6073d82124 | ||
|
|
ffb5d524a6 | ||
|
|
f31edcacba | ||
|
|
ad69edd48c | ||
|
|
267748f967 | ||
|
|
01c0f1527c | ||
|
|
21c4e720e1 | ||
|
|
523f2109fc | ||
|
|
ba239b6f15 | ||
|
|
0517e840b8 | ||
|
|
22b9783d07 | ||
|
|
63e4caf59c | ||
|
|
36a1266012 | ||
|
|
817317c5af | ||
|
|
929d9f1312 | ||
|
|
0f2d650436 | ||
|
|
1b9200b201 | ||
|
|
bea194cee0 | ||
|
|
91a6c8a794 | ||
|
|
a481fad7ca | ||
|
|
47b2e059b4 | ||
|
|
37b936eadf | ||
|
|
2000c467a1 | ||
|
|
0f7a45a142 | ||
|
|
477b858f9b | ||
|
|
cee09dbdf0 | ||
|
|
4ed847bf14 | ||
|
|
316ece4fa0 | ||
|
|
a6d119ac81 | ||
|
|
c2c253d3e3 | ||
|
|
f3cadb9667 | ||
|
|
2aaae31cc2 | ||
|
|
d4bab74b1a | ||
|
|
fb931d6189 | ||
|
|
149a76dfb9 | ||
|
|
b3bf2006c5 | ||
|
|
04b9b44e92 | ||
|
|
2e2dac21cd | ||
|
|
4e835fba42 | ||
|
|
40f0548919 | ||
|
|
269b1b6cfb | ||
|
|
c225d665e8 | ||
|
|
b81d9eb17e | ||
|
|
801879fafd | ||
|
|
aab1891829 | ||
|
|
259d3ef972 | ||
|
|
ba13de46a0 | ||
|
|
744025efe2 | ||
|
|
1e22a3ec8f | ||
|
|
7d02e67f3b | ||
|
|
a4f7fa9bcd | ||
|
|
2a18c91e70 | ||
|
|
d3b053cae0 | ||
|
|
5b5feda59c | ||
|
|
7290650a08 | ||
|
|
0b3c3986c5 | ||
|
|
29a9236a6e | ||
|
|
67655d674b | ||
|
|
639a13cf84 | ||
|
|
1f2ffdbe42 | ||
|
|
f43e03e785 | ||
|
|
6a47f8f3a5 | ||
|
|
5303b2ff24 | ||
|
|
8cf734d016 | ||
|
|
ed8d33930d | ||
|
|
5ad783ae3d | ||
|
|
3d0927a857 | ||
|
|
06d506855e | ||
|
|
57a288a564 | ||
|
|
4b77d69d5a | ||
|
|
11c00bbd46 | ||
|
|
7821447330 | ||
|
|
0da16bbb4f | ||
|
|
3d7c48f17c | ||
|
|
e9e3d928ea | ||
|
|
4bea8bb758 | ||
|
|
80d92ba181 | ||
|
|
0f38cdeb79 | ||
|
|
9bbc87ae89 | ||
|
|
2f53bf777e | ||
|
|
e0b8b4a53f | ||
|
|
8fe49abaa4 | ||
|
|
8679bc354d | ||
|
|
f2858f6c45 | ||
|
|
5e0ef645d1 | ||
|
|
f500bb09ea | ||
|
|
7c79cc32db | ||
|
|
730dccf069 | ||
|
|
ae39ce97b6 | ||
|
|
55a10d9f4a | ||
|
|
90c9d08744 | ||
|
|
dbd8766966 | ||
|
|
0e5c6cd892 | ||
|
|
fa9bdebe4c | ||
|
|
f32c6c9f67 | ||
|
|
5cba4efde9 | ||
|
|
7c48e86a99 | ||
|
|
024c37da67 | ||
|
|
15a2794ea3 | ||
|
|
ae347be300 | ||
|
|
ce90977163 | ||
|
|
146d951aca | ||
|
|
285c3fefc5 | ||
|
|
5cac9fd99f |
136 changed files with 31637 additions and 8913 deletions
342
.all-contributorsrc
Normal file
342
.all-contributorsrc
Normal file
|
|
@ -0,0 +1,342 @@
|
||||||
|
{
|
||||||
|
"projectName": "diff2html",
|
||||||
|
"projectOwner": "rtfpessoa",
|
||||||
|
"repoType": "github",
|
||||||
|
"repoHost": "https://github.com",
|
||||||
|
"files": [
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"imageSize": 100,
|
||||||
|
"commit": true,
|
||||||
|
"commitConvention": "none",
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"login": "rtfpessoa",
|
||||||
|
"name": "Rodrigo Fernandes",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/902384?v=4",
|
||||||
|
"profile": "https://rtfpessoa.xyz",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "stockmind",
|
||||||
|
"name": "stockmind",
|
||||||
|
"avatar_url": "https://avatars3.githubusercontent.com/u/5653847?v=4",
|
||||||
|
"profile": "https://github.com/stockmind",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "lantian",
|
||||||
|
"name": "Ivan Vorontsov",
|
||||||
|
"avatar_url": "https://avatars3.githubusercontent.com/u/535545?v=4",
|
||||||
|
"profile": "https://github.com/lantian",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "brewern",
|
||||||
|
"name": "Nick Brewer",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/129300?v=4",
|
||||||
|
"profile": "http://www.nick-brewer.com",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "romellem",
|
||||||
|
"name": "Matt Wade",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/8504000?v=4",
|
||||||
|
"profile": "http://heyitsmattwade.com",
|
||||||
|
"contributions": [
|
||||||
|
"bug",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "mrfyda",
|
||||||
|
"name": "Rafael Cortês",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/593860?v=4",
|
||||||
|
"profile": "http://mrfyda.github.io",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "nmatpt",
|
||||||
|
"name": "Nuno Teixeira",
|
||||||
|
"avatar_url": "https://avatars2.githubusercontent.com/u/5034733?v=4",
|
||||||
|
"profile": "https://github.com/nmatpt",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "kaishuu0123",
|
||||||
|
"name": "Koki Oyatsu",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/1567423?v=4",
|
||||||
|
"profile": "https://saino.me/",
|
||||||
|
"contributions": [
|
||||||
|
"bug",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Jameskmonger",
|
||||||
|
"name": "James Monger",
|
||||||
|
"avatar_url": "https://avatars2.githubusercontent.com/u/2037007?v=4",
|
||||||
|
"profile": "http://www.jamesmonger.com",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "wesssel",
|
||||||
|
"name": "Wessel van der Pal",
|
||||||
|
"avatar_url": "https://avatars2.githubusercontent.com/u/7767299?v=4",
|
||||||
|
"profile": "http://wesssel.github.io/",
|
||||||
|
"contributions": [
|
||||||
|
"security",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "jung-kim",
|
||||||
|
"name": "jk-kim",
|
||||||
|
"avatar_url": "https://avatars2.githubusercontent.com/u/5281068?v=4",
|
||||||
|
"profile": "https://jung-kim.github.io",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "sss0791",
|
||||||
|
"name": "Sergey Semenov",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/1446970?v=4",
|
||||||
|
"profile": "https://github.com/sss0791",
|
||||||
|
"contributions": [
|
||||||
|
"bug",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "starpit",
|
||||||
|
"name": "Nick Mitchell",
|
||||||
|
"avatar_url": "https://avatars3.githubusercontent.com/u/4741620?v=4",
|
||||||
|
"profile": "http://researcher.watson.ibm.com/researcher/view.php?person=us-nickm",
|
||||||
|
"contributions": [
|
||||||
|
"bug",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "samiraguiar",
|
||||||
|
"name": "Samir Aguiar",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/13439135?v=4",
|
||||||
|
"profile": "https://github.com/samiraguiar",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "pubkey",
|
||||||
|
"name": "pubkey",
|
||||||
|
"avatar_url": "https://avatars3.githubusercontent.com/u/8926560?v=4",
|
||||||
|
"profile": "https://twitter.com/pubkeypubkey",
|
||||||
|
"contributions": [
|
||||||
|
"doc",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "iliyaZelenko",
|
||||||
|
"name": "Илья",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/13103045?v=4",
|
||||||
|
"profile": "https://github.com/iliyaZelenko",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "mohd-akram",
|
||||||
|
"name": "Mohamed Akram",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/1823771?v=4",
|
||||||
|
"profile": "https://akr.am",
|
||||||
|
"contributions": [
|
||||||
|
"bug",
|
||||||
|
"doc",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "emarcotte",
|
||||||
|
"name": "Eugene Marcotte",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/249390?v=4",
|
||||||
|
"profile": "https://github.com/emarcotte",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "dsabanin",
|
||||||
|
"name": "Dima Sabanin",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/8316?v=4",
|
||||||
|
"profile": "http://twitter.com/dimasabanin",
|
||||||
|
"contributions": [
|
||||||
|
"maintenance",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "benabbottnz",
|
||||||
|
"name": "Ben Abbott",
|
||||||
|
"avatar_url": "https://avatars2.githubusercontent.com/u/2616473?v=4",
|
||||||
|
"profile": "https://github.com/benabbottnz",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "dickeylth",
|
||||||
|
"name": "弘树@阿里",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/2196373?v=4",
|
||||||
|
"profile": "http://webminer.js.org",
|
||||||
|
"contributions": [
|
||||||
|
"bug",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Rantanen",
|
||||||
|
"name": "Mikko Rantanen",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/385385?v=4",
|
||||||
|
"profile": "https://github.com/Rantanen",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "extend1994",
|
||||||
|
"name": "Ann",
|
||||||
|
"avatar_url": "https://avatars2.githubusercontent.com/u/13430892?v=4",
|
||||||
|
"profile": "https://github.com/extend1994",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "escitalopram",
|
||||||
|
"name": "escitalopram",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/1155220?v=4",
|
||||||
|
"profile": "https://github.com/escitalopram",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "dependabot[bot]",
|
||||||
|
"name": "dependabot[bot]",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/in/29110?v=4",
|
||||||
|
"profile": "https://github.com/apps/dependabot",
|
||||||
|
"contributions": [
|
||||||
|
"security",
|
||||||
|
"maintenance"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "JoshuaKGoldberg",
|
||||||
|
"name": "Josh Goldberg",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/3335181?v=4",
|
||||||
|
"profile": "http://www.joshuakgoldberg.com",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "apeckham",
|
||||||
|
"name": "Aaron",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/14110?v=4",
|
||||||
|
"profile": "https://github.com/apeckham",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "pgrimaud",
|
||||||
|
"name": "Pierre Grimaud",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/1866496?v=4",
|
||||||
|
"profile": "https://github.com/pgrimaud",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "domdomegg",
|
||||||
|
"name": "Adam Jones",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/4953590?v=4",
|
||||||
|
"profile": "https://domdomegg.github.io/",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "charguer",
|
||||||
|
"name": "Arthur Charguéraud",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/1830652?v=4",
|
||||||
|
"profile": "https://github.com/charguer",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Pierrci",
|
||||||
|
"name": "Pierric Cistac",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/5020707?v=4",
|
||||||
|
"profile": "https://twitter.com/pierrci",
|
||||||
|
"contributions": [
|
||||||
|
"doc",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "xlith",
|
||||||
|
"name": "Civan Yavuzşen",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/510560?v=4",
|
||||||
|
"profile": "https://github.com/xlith",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "timgates42",
|
||||||
|
"name": "Tim Gates",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/47873678?v=4",
|
||||||
|
"profile": "https://github.com/timgates42",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "campersau",
|
||||||
|
"name": "campersau",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/4009570?v=4",
|
||||||
|
"profile": "https://github.com/campersau",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "dependabot-preview[bot]",
|
||||||
|
"name": "dependabot-preview[bot]",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/in/2141?v=4",
|
||||||
|
"profile": "https://github.com/apps/dependabot-preview",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contributorsPerLine": 7
|
||||||
|
}
|
||||||
44
.github/ISSUE_TEMPLATE.md
vendored
Normal file
44
.github/ISSUE_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
### Step -1: Before filling an issue check out troubleshooting section
|
||||||
|
|
||||||
|
- Go to [README.md#Troubleshooting](https://github.com/rtfpessoa/diff2html#troubleshooting)
|
||||||
|
|
||||||
|
### Step 0: Describe your environment
|
||||||
|
|
||||||
|
- OS: **\_**
|
||||||
|
- diff2html version: **\_**
|
||||||
|
- Using diff2html directly or using diff2html-ui helper: **\_**
|
||||||
|
- Extra flags: **\_**
|
||||||
|
|
||||||
|
### Step 1: Describe the problem:
|
||||||
|
|
||||||
|
#### Steps to reproduce:
|
||||||
|
|
||||||
|
1. ***
|
||||||
|
2. ***
|
||||||
|
3. ***
|
||||||
|
|
||||||
|
#### diff example:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
diff --git describe.c
|
||||||
|
index fabadb8,cc95eb0..4866510
|
||||||
|
--- a/describe.c
|
||||||
|
+++ b/describe.c
|
||||||
|
@@@ -98,20 -98,12 +98,20 @@@
|
||||||
|
return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Observed Results:
|
||||||
|
|
||||||
|
- What happened? This could be a description, log output, etc.
|
||||||
|
|
||||||
|
#### Expected Results:
|
||||||
|
|
||||||
|
- What did you expect to happen?
|
||||||
|
|
||||||
|
#### Relevant Code:
|
||||||
|
|
||||||
|
```
|
||||||
|
// TODO(you): code here to reproduce the problem
|
||||||
|
```
|
||||||
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: Bug report about: Create a report to help us improve title: "" labels: "" assignees: "" ---**Describe the bug** A
|
||||||
|
clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce** Steps to reproduce the behavior:
|
||||||
|
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior** A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots** If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
|
||||||
|
- OS: [e.g. Windows, Linux, Mac]
|
||||||
|
- Browser [e.g. Firefox, Chrome, Safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Additional context** Add any other context about the problem here.
|
||||||
12
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: Feature request about: Suggest an idea for this project title: "" labels: "" assignees: "" ---**Is your feature
|
||||||
|
request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always
|
||||||
|
frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like** A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered** A clear and concise description of any alternative solutions or features
|
||||||
|
you've considered.
|
||||||
|
|
||||||
|
**Additional context** Add any other context or screenshots about the feature request here.
|
||||||
12
.github/workflows/ci.yml
vendored
Normal file
12
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-and-publish:
|
||||||
|
uses: ./.github/workflows/test-and-publish.yml
|
||||||
|
with:
|
||||||
|
environment: dev
|
||||||
|
secrets: inherit
|
||||||
35
.github/workflows/release.yml
vendored
Normal file
35
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
name: release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-and-publish:
|
||||||
|
uses: ./.github/workflows/test-and-publish.yml
|
||||||
|
with:
|
||||||
|
environment: production
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
publish-website:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: amazon/aws-cli
|
||||||
|
needs: [test-and-publish]
|
||||||
|
environment: 'production'
|
||||||
|
steps:
|
||||||
|
- name: Download docs
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docs
|
||||||
|
path: docs/
|
||||||
|
- name: Publish to S3
|
||||||
|
working-directory: docs
|
||||||
|
env:
|
||||||
|
AWS_CF_DISTRIBUTION_ID: ${{ secrets.AWS_CF_DISTRIBUTION_ID }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
|
||||||
|
run: |
|
||||||
|
aws s3 sync --region eu-west-1 --delete . s3://diff2html.xyz --metadata-directive REPLACE --cache-control max-age=31557600
|
||||||
|
aws cloudfront create-invalidation --region eu-west-1 --distribution-id $AWS_CF_DISTRIBUTION_ID --paths /index.html /demo.html /sitemap.xml /robots.txt
|
||||||
148
.github/workflows/test-and-publish.yml
vendored
Normal file
148
.github/workflows/test-and-publish.yml
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
name: test-and-publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
environment:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: codacy/git-version
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Fix tar access
|
||||||
|
run: apk add --update --no-progress tar
|
||||||
|
- name: Fix git access
|
||||||
|
run: |
|
||||||
|
git config --global --add safe.directory /__w/diff2html/diff2html
|
||||||
|
- name: Get next version
|
||||||
|
run: |
|
||||||
|
export NEXT_VERSION="$(/bin/git-version --folder=$PWD --release-branch=master)"
|
||||||
|
echo "Next version is ${NEXT_VERSION}"
|
||||||
|
echo "${NEXT_VERSION}" > next-version.txt
|
||||||
|
echo "version=${NEXT_VERSION}" >> $GITHUB_ENV
|
||||||
|
- name: Get next npm tag name
|
||||||
|
run: |
|
||||||
|
if [ "${GITHUB_REF_NAME}" = "master" ]; then
|
||||||
|
export PUBLISH_TAG="latest"
|
||||||
|
elif [ "${GITHUB_REF_NAME}" = "next" ]; then
|
||||||
|
export PUBLISH_TAG="next"
|
||||||
|
else
|
||||||
|
export PUBLISH_TAG="pr"
|
||||||
|
fi
|
||||||
|
echo "Next tag is ${PUBLISH_TAG}"
|
||||||
|
echo "${PUBLISH_TAG}" > publish-tag.txt
|
||||||
|
- name: Upload versions
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: versions
|
||||||
|
if-no-files-found: error
|
||||||
|
path: |
|
||||||
|
next-version.txt
|
||||||
|
publish-tag.txt
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [version]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [16.x, 18.x, 20.x, 22.x]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'npm'
|
||||||
|
- name: Log environment setup
|
||||||
|
run: |
|
||||||
|
node -v
|
||||||
|
npm -v
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm install
|
||||||
|
- name: Build templates
|
||||||
|
run: npm run build:templates
|
||||||
|
- name: Build library
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build]
|
||||||
|
environment: ${{ inputs.environment }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Download versions
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: versions
|
||||||
|
- name: Store version
|
||||||
|
run: echo "version=$(cat next-version.txt)" >> $GITHUB_ENV
|
||||||
|
- name: Configure Git
|
||||||
|
run: |
|
||||||
|
git config user.email "gh-actions@users.noreply.github.com"
|
||||||
|
git config user.name "GitHub Actions"
|
||||||
|
- name: Tag commit
|
||||||
|
uses: tvdias/github-tagger@v0.0.1
|
||||||
|
with:
|
||||||
|
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
|
tag: '${{ env.version }}'
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm install
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
node-version: '22.x'
|
||||||
|
- name: Configure NPM version
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
run: |
|
||||||
|
rm -f .npmrc
|
||||||
|
touch .npmrc
|
||||||
|
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> .npmrc
|
||||||
|
echo "registry=https://registry.npmjs.org/" >> .npmrc
|
||||||
|
echo "access=public" >> .npmrc
|
||||||
|
echo "save-exact=true" >> .npmrc
|
||||||
|
- name: Version package
|
||||||
|
run: |
|
||||||
|
# Update version in packages to publish
|
||||||
|
npm version $(cat next-version.txt) -m "Release version %s"
|
||||||
|
- name: Publish to NPM
|
||||||
|
run: npm publish --tag $(cat publish-tag.txt)
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '22.x'
|
||||||
|
registry-url: 'https://npm.pkg.github.com'
|
||||||
|
- name: Configure NPM version
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
rm -f .npmrc
|
||||||
|
touch .npmrc
|
||||||
|
echo "//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}" >> .npmrc
|
||||||
|
echo "@rtfpessoa:registry=https://npm.pkg.github.com/" >> .npmrc
|
||||||
|
echo "access=public" >> .npmrc
|
||||||
|
echo "save-exact=true" >> .npmrc
|
||||||
|
- name: Publish to GPR
|
||||||
|
run: |
|
||||||
|
# HACK: Override npm package name to be able to publish in GitHub
|
||||||
|
sed -i 's/^ "name":.*/ "name": "@rtfpessoa\/diff2html",/g' package.json
|
||||||
|
echo "Going to publish version $(cat next-version.txt) to GitHub"
|
||||||
|
npm publish --tag $(cat publish-tag.txt)
|
||||||
|
# HACK: Restore npm package name
|
||||||
|
sed -i 's/^ "name":.*/ "name": "diff2html",/g' package.json
|
||||||
|
- name: Upload docs
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docs
|
||||||
|
if-no-files-found: error
|
||||||
|
path: docs/
|
||||||
22
.gitignore
vendored
22
.gitignore
vendored
|
|
@ -18,9 +18,29 @@ target/
|
||||||
# Node
|
# Node
|
||||||
node_modules/
|
node_modules/
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
# Istanbul
|
# Coverage
|
||||||
coverage/
|
coverage/
|
||||||
|
|
||||||
# Bower
|
# Bower
|
||||||
bower_components/
|
bower_components/
|
||||||
|
|
||||||
|
# Terraform
|
||||||
|
/terraform/.terraform
|
||||||
|
|
||||||
|
# Compiled templates
|
||||||
|
/src/diff2html-templates.*
|
||||||
|
|
||||||
|
# Compiled website
|
||||||
|
/docs/
|
||||||
|
|
||||||
|
# Bundles temporary typescript files compiled by webpack
|
||||||
|
/bundles-out/
|
||||||
|
|
||||||
|
# Web bundles
|
||||||
|
/bundles/
|
||||||
|
# CommonJS library
|
||||||
|
/lib/
|
||||||
|
# ESNext library
|
||||||
|
/lib-esm/
|
||||||
|
|
|
||||||
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
_
|
||||||
3
.husky/pre-commit
Executable file
3
.husky/pre-commit
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
npm run lint:staged
|
||||||
49
.jscsrc
49
.jscsrc
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"disallowKeywords": [
|
|
||||||
"with"
|
|
||||||
],
|
|
||||||
"disallowKeywordsOnNewLine": [
|
|
||||||
"else"
|
|
||||||
],
|
|
||||||
"disallowMixedSpacesAndTabs": true,
|
|
||||||
"disallowMultipleVarDecl": {
|
|
||||||
"allExcept": [
|
|
||||||
"undefined"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"disallowNewlineBeforeBlockStatements": true,
|
|
||||||
"disallowSpaceAfterObjectKeys": true,
|
|
||||||
"disallowSpaceAfterPrefixUnaryOperators": true,
|
|
||||||
"disallowSpacesInFunction": {
|
|
||||||
"beforeOpeningRoundBrace": true
|
|
||||||
},
|
|
||||||
"disallowSpacesInsideParentheses": true,
|
|
||||||
"disallowTrailingWhitespace": true,
|
|
||||||
"maximumLineLength": 130,
|
|
||||||
"requireCamelCaseOrUpperCaseIdentifiers": true,
|
|
||||||
"requireCapitalizedConstructors": true,
|
|
||||||
"requireSpaceAfterKeywords": [
|
|
||||||
"if",
|
|
||||||
"else",
|
|
||||||
"for",
|
|
||||||
"while",
|
|
||||||
"do",
|
|
||||||
"switch",
|
|
||||||
"case",
|
|
||||||
"return",
|
|
||||||
"try",
|
|
||||||
"catch",
|
|
||||||
"typeof"
|
|
||||||
],
|
|
||||||
"requireSpaceAfterLineComment": true,
|
|
||||||
"requireSpaceAfterBinaryOperators": true,
|
|
||||||
"requireSpaceBeforeBinaryOperators": true,
|
|
||||||
"requireSpaceBeforeBlockStatements": true,
|
|
||||||
"requireSpaceBeforeObjectValues": true,
|
|
||||||
"requireSpacesInFunction": {
|
|
||||||
"beforeOpeningCurlyBrace": true
|
|
||||||
},
|
|
||||||
"requireTrailingComma": null,
|
|
||||||
"validateIndentation": 2,
|
|
||||||
"validateLineBreaks": "LF"
|
|
||||||
}
|
|
||||||
16
.prettierrc.json
Normal file
16
.prettierrc.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"htmlWhitespaceSensitivity": "css",
|
||||||
|
"insertPragma": false,
|
||||||
|
"jsxSingleQuote": false,
|
||||||
|
"printWidth": 120,
|
||||||
|
"proseWrap": "always",
|
||||||
|
"quoteProps": "as-needed",
|
||||||
|
"requirePragma": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"useTabs": false
|
||||||
|
}
|
||||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
|
}
|
||||||
1
CNAME
Normal file
1
CNAME
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
diff2html.xyz
|
||||||
61
CODE_OF_CONDUCT.md
Normal file
61
CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making
|
||||||
|
participation in our project and our community a harassment-free experience for everyone, regardless of age, body size,
|
||||||
|
disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education,
|
||||||
|
socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
|
- Using welcoming and inclusive language
|
||||||
|
- Being respectful of differing viewpoints and experiences
|
||||||
|
- Gracefully accepting constructive criticism
|
||||||
|
- Focusing on what is best for the community
|
||||||
|
- Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
|
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
- Public or private harassment
|
||||||
|
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||||
|
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take
|
||||||
|
appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits,
|
||||||
|
issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any
|
||||||
|
contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the
|
||||||
|
project or its community. Examples of representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed representative at an online or offline
|
||||||
|
event. Representation of a project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at
|
||||||
|
rtfrodrigo [at] gmail [dot] com. All complaints will be reviewed and investigated and will result in a response that is
|
||||||
|
deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with
|
||||||
|
regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent
|
||||||
|
repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at
|
||||||
|
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
|
||||||
|
|
@ -2,31 +2,33 @@
|
||||||
|
|
||||||
### Main rules
|
### Main rules
|
||||||
|
|
||||||
* Before you open a ticket or send a pull request, [search](https://github.com/rtfpessoa/diff2html/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one.
|
- Before you open a ticket or send a pull request, [search](https://github.com/rtfpessoa/diff2html/issues) for previous
|
||||||
|
discussions about the same feature or issue. Add to the earlier ticket if you find one.
|
||||||
|
|
||||||
* If you're proposing a new feature, make sure you create an issue to let other contributors know what you are working on.
|
- If you're proposing a new feature, make sure you create an issue to let other contributors know what you are working
|
||||||
|
on.
|
||||||
|
|
||||||
* Before sending a pull request make sure your code is tested.
|
- Before sending a pull request make sure your code is tested.
|
||||||
|
|
||||||
* Before sending a pull request for a feature, be sure to run tests with `npm test`.
|
- Before sending a pull request for a feature, be sure to run tests with `npm run test`.
|
||||||
|
|
||||||
* Use the same coding style as the rest of the codebase, most of the check can be performed with `npm run style`.
|
- Use the same coding style as the rest of the codebase, most of the check can be performed with `npm run run lint`.
|
||||||
|
|
||||||
* Use `git rebase` (not `git merge`) to sync your work from time to time with the master branch.
|
- Use `git rebase` (not `git merge`) to sync your work from time to time with the master branch.
|
||||||
|
|
||||||
* After creating your pull request make sure the build is passing on [CircleCI](https://circleci.com/gh/rtfpessoa/diff2html)
|
- After creating your pull request make sure the build is passing on
|
||||||
and that [Codacy](https://www.codacy.com/app/Codacy/diff2html) is also confident in the code quality.
|
[CircleCI](https://circleci.com/gh/rtfpessoa/diff2html) and that [Codacy](https://www.codacy.com/app/Codacy/diff2html)
|
||||||
|
is also confident in the code quality.
|
||||||
|
|
||||||
* In your pull request, do not commit the `dist` or `build` folder if you needed to build the release files.
|
- In your pull request, do not commit the `dist` or `build` folder if you needed to build the release files.
|
||||||
|
|
||||||
### Commit Style
|
### Commit Style
|
||||||
|
|
||||||
Writing good commit logs is important. A commit log should describe what changed and why.
|
Writing good commit logs is important. A commit log should describe what changed and why. Follow these guidelines when
|
||||||
Follow these guidelines when writing one:
|
writing one:
|
||||||
|
|
||||||
1. The first line should be 50 characters or less and contain a short
|
1. The first line should be 50 characters or less and contain a short description of the change prefixed with the name
|
||||||
description of the change prefixed with the name of the changed
|
of the changed subsystem (e.g. "net: add localAddress and localPort to Socket").
|
||||||
subsystem (e.g. "net: add localAddress and localPort to Socket").
|
|
||||||
2. Keep the second line blank.
|
2. Keep the second line blank.
|
||||||
3. Wrap all other lines at 72 columns.
|
3. Wrap all other lines at 72 columns.
|
||||||
|
|
||||||
|
|
@ -49,14 +51,11 @@ nicely even when it is indented.
|
||||||
|
|
||||||
By making a contribution to this project, I certify that:
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
* (a) The contribution was created in whole or in part by me and I
|
- (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source
|
||||||
have the right to submit it under the open source license indicated
|
license indicated in the file; or
|
||||||
in the file; or
|
- (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate
|
||||||
* (b) The contribution is based upon previous work that, to the best
|
open source license and I have the right under that license to submit that work with modifications, whether created in
|
||||||
of my knowledge, is covered under an appropriate open source license
|
whole or in part by me, under the same open source license (unless I am permitted to submit under a different
|
||||||
and I have the right under that license to submit that work with
|
license), as indicated in the file; or
|
||||||
modifications, whether created in whole or in part by me, under the
|
- (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not
|
||||||
same open source license (unless I am permitted to submit under a
|
modified it.
|
||||||
different license), as indicated in the file; or
|
|
||||||
* (c) The contribution was provided directly to me by some other
|
|
||||||
person who certified (a), (b) or (c) and I have not modified it.
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
This is the list of all the kind people that have contributed to the diff2html project.
|
This is the list of all the kind people that have contributed to the diff2html project. This list is ordered by first
|
||||||
This list is ordered by first contribution.
|
contribution.
|
||||||
|
|
||||||
Thanks,
|
Thanks, @rtfpessoa
|
||||||
@rtfpessoa
|
|
||||||
|
|
||||||
----------
|
---
|
||||||
|
|
||||||
Rodrigo Fernandes, [@rtfpessoa](https://github.com/rtfpessoa)
|
Rodrigo Fernandes, [@rtfpessoa](https://github.com/rtfpessoa)
|
||||||
|
|
||||||
|
|
|
||||||
20
LICENSE
20
LICENSE
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright 2014-2016 Rodrigo Fernandes https://rtfpessoa.github.io/
|
|
||||||
|
|
||||||
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.
|
|
||||||
14
LICENSE.md
Normal file
14
LICENSE.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
Copyright 2014-2016 Rodrigo Fernandes https://rtfpessoa.github.io/
|
||||||
|
|
||||||
|
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.
|
||||||
627
README.md
627
README.md
|
|
@ -1,180 +1,613 @@
|
||||||
# diff2html
|
# diff2html
|
||||||
|
|
||||||
[](https://circleci.com/gh/rtfpessoa/diff2html)
|
[](https://www.npmjs.com/package/diff2html)
|
||||||
[](https://www.codacy.com/app/Codacy/diff2html)
|
[](https://www.npmjs.com/package/diff2html)
|
||||||
[](https://www.codacy.com/app/Codacy/diff2html)
|
[](https://www.npmjs.com/package/diff2html)
|
||||||
|
[](https://github.com/rtfpessoa/diff2html/actions/workflows/release.yml)
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/diff2html)
|
[](https://www.npmjs.com/package/diff2html)
|
||||||
[](https://david-dm.org/rtfpessoa/diff2html)
|
[](https://www.npmjs.com/package/diff2html)
|
||||||
[](https://david-dm.org/rtfpessoa/diff2html#info=devDependencies)
|
[](https://www.npmjs.com/package/diff2html)
|
||||||
|
[](https://www.npmjs.com/package/diff2html)
|
||||||
|
|
||||||
[]()
|
[](https://www.jsdelivr.com/package/npm/diff2html)
|
||||||
[]()
|
[](#contributors)
|
||||||
[](https://www.npmjs.com/package/diff2html)
|
|
||||||
[](https://gitter.im/rtfpessoa/diff2html?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
|
||||||
|
|
||||||
diff2html generates pretty HTML diffs from git or unified diff output.
|
diff2html generates pretty HTML diffs from git diff or unified diff output.
|
||||||
|
|
||||||
[](https://nodei.co/npm/diff2html/)
|
## Table of Contents
|
||||||
|
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [Features](#features)
|
||||||
|
- [Online Example](#online-example)
|
||||||
|
- [Distributions](#distributions)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [Diff Text Input](#diff-text-input)
|
||||||
|
- [Diff2HtmlUI Usage](#diff2htmlui-usage)
|
||||||
|
- [Diff2HtmlUI API](#diff2htmlui-api)
|
||||||
|
- [Diff2HtmlUI Configuration](#diff2htmlui-configuration)
|
||||||
|
- [Diff2HtmlUI Browser](#diff2htmlui-browser)
|
||||||
|
- [Diff2HtmlUI Examples](#diff2htmlui-examples)
|
||||||
|
- [Diff2Html Usage](#diff2html-usage)
|
||||||
|
- [Diff2Html API](#diff2html-api)
|
||||||
|
- [Diff2Html Configuration](#diff2html-configuration)
|
||||||
|
- [Diff2Html Browser](#diff2html-browser)
|
||||||
|
- [Diff2Html NPM / Node.js Library](#diff2html-npm--nodejs-library)
|
||||||
|
- [Diff2Html Examples](#diff2html-examples)
|
||||||
|
- [Troubleshooting](#troubleshooting)
|
||||||
|
- [1. Out of memory or Slow execution](#1-out-of-memory-or-slow-execution)
|
||||||
|
- [Contribute](#contribute)
|
||||||
|
- [Contributors](#contributors)
|
||||||
|
- [License](#license)
|
||||||
|
- [Thanks](#thanks)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Supports git and unified diffs
|
- Supports git and unified diffs
|
||||||
|
|
||||||
* Line by line and Side by side diff
|
- Line by line and Side by side diff
|
||||||
|
|
||||||
* New and old line numbers
|
- New and old line numbers
|
||||||
|
|
||||||
* Inserted and removed lines
|
- Inserted and removed lines
|
||||||
|
|
||||||
* GitHub like style
|
- GitHub like visual style
|
||||||
|
|
||||||
* Code syntax highlight
|
- Code syntax highlight
|
||||||
|
|
||||||
* Line similarity matching
|
- Line similarity matching
|
||||||
|
|
||||||
* Easy code selection
|
- Easy code selection
|
||||||
|
|
||||||
## Online Example
|
## Online Example
|
||||||
|
|
||||||
> Go to [diff2html](http://rtfpessoa.github.io/diff2html/)
|
> Go to [diff2html](https://diff2html.xyz/demo.html)
|
||||||
|
|
||||||
## Distributions
|
## Distributions
|
||||||
|
|
||||||
* [WebJar](http://www.webjars.org/)
|
- [jsdelivr CDN](https://www.jsdelivr.com/package/npm/diff2html)
|
||||||
|
- [WebJar](http://www.webjars.org/)
|
||||||
|
- [Node Library](https://www.npmjs.org/package/diff2html)
|
||||||
|
- [NPM CLI](https://www.npmjs.org/package/diff2html-cli)
|
||||||
|
- Manually use from jsdelivr or build the project:
|
||||||
|
- Browser / Bundle
|
||||||
|
- Parser and HTML Generator
|
||||||
|
- [bundles/js/diff2html.min.js](https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html.min.js) - includes the
|
||||||
|
diff parser and html generator
|
||||||
|
- Wrapper and helper adding syntax highlight, synchronized scroll, and other nice features
|
||||||
|
- [bundles/js/diff2html-ui.min.js](https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js) -
|
||||||
|
includes the wrapper of diff2html with highlight for all `highlight.js` supported languages
|
||||||
|
- [bundles/js/diff2html-ui-slim.min.js](https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui-slim.min.js) -
|
||||||
|
includes the wrapper of diff2html with "the most common" `highlight.js` supported languages
|
||||||
|
- [bundles/js/diff2html-ui-base.min.js](https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui-base.min.js) -
|
||||||
|
includes the wrapper of diff2html without including a `highlight.js` implementation. You can use it without
|
||||||
|
syntax highlight or by passing your own implementation with the languages you prefer
|
||||||
|
- NPM / Node.js library
|
||||||
|
- ES5
|
||||||
|
- [lib/diff2html.js](https://cdn.jsdelivr.net/npm/diff2html/lib/diff2html.js) - includes the diff parser and html
|
||||||
|
generator
|
||||||
|
- [lib/ui/js/diff2html-ui.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui.js) - includes the
|
||||||
|
wrapper of diff2html with highlight for all `highlight.js` supported languages
|
||||||
|
- [lib/ui/js/diff2html-ui-slim.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-slim.js) -
|
||||||
|
includes the wrapper of diff2html with "the most common" `highlight.js` supported languages
|
||||||
|
- [lib/ui/js/diff2html-ui-base.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-base.js)
|
||||||
|
- ES6
|
||||||
|
- [lib-esm/diff2html.js](https://cdn.jsdelivr.net/npm/diff2html/lib-esm/diff2html.js) - includes the diff parser
|
||||||
|
and html generator
|
||||||
|
- [lib/ui/js/diff2html-ui.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui.js) - includes the
|
||||||
|
wrapper of diff2html with highlight for all `highlight.js` supported languages
|
||||||
|
- [lib/ui/js/diff2html-ui-slim.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-slim.js) -
|
||||||
|
includes the wrapper of diff2html with "the most common" `highlight.js` supported languages
|
||||||
|
- [lib/ui/js/diff2html-ui-base.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-base.js) -
|
||||||
|
includes the wrapper of diff2html without including a `highlight.js` implementation. You can use it without
|
||||||
|
syntax highlight or by passing your own implementation with the languages you prefer
|
||||||
|
|
||||||
* [Node Module](https://www.npmjs.org/package/diff2html)
|
## Usage
|
||||||
|
|
||||||
* [Bower Package](http://bower.io/search/?q=diff2html)
|
Diff2Html can be used in various ways as listed in the [distributions](#distributions) section. The two main ways are:
|
||||||
|
|
||||||
* [Node CLI](https://www.npmjs.org/package/diff2html-cli)
|
- [Diff2HtmlUI](#diff2htmlui-usage): using this wrapper makes it easy to inject the html in the DOM and adds some nice
|
||||||
|
features to the diff, like syntax highlight.
|
||||||
|
- [Diff2Html](#diff2html-usage): using the parser and html generator directly from the library gives you complete
|
||||||
|
control about what you can do with the json or html generated.
|
||||||
|
|
||||||
* Manually download and import `dist/diff2html.min.js` into your page
|
Below you can find more details and examples about each option.
|
||||||
|
|
||||||
## How to use
|
## Diff Text Input
|
||||||
|
|
||||||
> Pretty HTML diff
|
diff2html accepts the text contents of a
|
||||||
|
[unified diff](https://www.gnu.org/software/diffutils/manual/html_node/Unified-Format.html) or the superset format git
|
||||||
|
diff (https://git-scm.com/docs/git-diff) (not combined or word diff). To provide multiples files as input just
|
||||||
|
concatenate the diffs (just like the output of git diff).
|
||||||
|
|
||||||
Diff2Html.getPrettyHtml(exInput, configuration)
|
## Diff2HtmlUI Usage
|
||||||
|
|
||||||
> Intermediate Json From Git Word Diff Output
|
|
||||||
|
|
||||||
Diff2Html.getJsonFromDiff(exInput)
|
|
||||||
|
|
||||||
> Check out the `index.html` for a complete example.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
The HTML output accepts a Javascript object with configuration. Possible options:
|
|
||||||
|
|
||||||
- `inputFormat`: the format of the input data: `'diff'` or `'json'`, default is `'diff'`
|
|
||||||
- `outputFormat`: the format of the output data: `'line-by-line'` or `'side-by-side'`, default is `'line-by-line'`
|
|
||||||
- `showFiles`: show a file list before the diff: `true` or `false`, default is `false`
|
|
||||||
- `matching`: matching level: `'lines'` for matching lines, `'words'` for matching lines and words or `'none'`, default is `none`
|
|
||||||
- `matchWordsThreshold`: similarity threshold for word matching, default is 0.25
|
|
||||||
- `matchingMaxComparisons`: perform at most this much comparisons for line matching a block of changes, default is `2500`
|
|
||||||
|
|
||||||
## Diff2HtmlUI Helper
|
|
||||||
|
|
||||||
> Simple wrapper to ease simple tasks in the browser such as: code highlight and js effects
|
> Simple wrapper to ease simple tasks in the browser such as: code highlight and js effects
|
||||||
|
|
||||||
### How to use
|
- Invoke Diff2html
|
||||||
|
- Inject output in DOM element
|
||||||
|
- Enable collapsible file summary list
|
||||||
|
- Enable syntax highlight of the code in the diffs
|
||||||
|
|
||||||
> HTML resource imports
|
### Diff2HtmlUI API
|
||||||
|
|
||||||
|
> Create a Diff2HtmlUI instance
|
||||||
|
|
||||||
|
```ts
|
||||||
|
constructor(target: HTMLElement, diffInput?: string | DiffFile[]) // diff2html-ui, diff2html-ui-slim
|
||||||
|
constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}, hljs?: HighlightJS) // diff2html-ui-base
|
||||||
|
```
|
||||||
|
|
||||||
|
> Generate and inject in the document the Pretty HTML representation of the diff
|
||||||
|
|
||||||
|
```ts
|
||||||
|
draw(): void
|
||||||
|
```
|
||||||
|
|
||||||
|
> Enable extra features
|
||||||
|
|
||||||
|
```ts
|
||||||
|
synchronisedScroll(): void
|
||||||
|
fileListToggle(startVisible: boolean): void
|
||||||
|
highlightCode(): void
|
||||||
|
stickyFileHeaders(): void
|
||||||
|
```
|
||||||
|
|
||||||
|
### Diff2HtmlUI Configuration
|
||||||
|
|
||||||
|
- `synchronisedScroll`: scroll both panes in side-by-side mode: `true` or `false`, default is `true`
|
||||||
|
- `highlight`: syntax highlight the code on the diff: `true` or `false`, default is `true`
|
||||||
|
- `fileListToggle`: allow the file summary list to be toggled: `true` or `false`, default is `true`
|
||||||
|
- `fileListStartVisible`: choose if the file summary list starts visible: `true` or `false`, default is `false`
|
||||||
|
- `fileContentToggle`: allow each file contents to be toggled: `true` or `false`, default is `true`
|
||||||
|
- `stickyFileHeaders`: make file headers sticky: `true` or `false`, default is `true`
|
||||||
|
- [All the options](#diff2html-configuration) from Diff2Html are also valid configurations in Diff2HtmlUI
|
||||||
|
|
||||||
|
### Diff2HtmlUI Browser
|
||||||
|
|
||||||
|
#### Mandatory HTML resource imports
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- CSS -->
|
<!-- CSS -->
|
||||||
<link rel="stylesheet" type="text/css" href="dist/diff2html.css">
|
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
|
||||||
|
|
||||||
<!-- Javascripts -->
|
<!-- Javascripts -->
|
||||||
<script type="text/javascript" src="dist/diff2html.js"></script>
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
|
||||||
<script type="text/javascript" src="dist/diff2html-ui.js"></script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> Init
|
#### Init
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var diff2htmlUi = new Diff2HtmlUI({diff: diffString});
|
const targetElement = document.getElementById('destination-elem-id');
|
||||||
|
const configuration = { drawFileList: true, matching: 'lines' };
|
||||||
|
|
||||||
|
const diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
|
||||||
// or
|
// or
|
||||||
var diff2htmlUi = new Diff2HtmlUI({json: diffJson});
|
const diff2htmlUi = new Diff2HtmlUI(targetElement, diffJson, configuration);
|
||||||
```
|
```
|
||||||
|
|
||||||
> Draw
|
#### Draw
|
||||||
|
|
||||||
```js
|
```js
|
||||||
diff2htmlUi.draw('html-target-elem', {inputFormat: 'json', showFiles: true, matching: 'lines'});
|
diff2htmlUi.draw();
|
||||||
```
|
```
|
||||||
|
|
||||||
> Highlight Code
|
#### Syntax Highlight
|
||||||
|
|
||||||
```js
|
**NOTE:** The highlight.js css should come before the diff2html css
|
||||||
diff2htmlUi.highlightCode('html-target-elem');
|
|
||||||
```
|
|
||||||
|
|
||||||
> Collapse File Summary List
|
|
||||||
|
|
||||||
```js
|
|
||||||
diff2htmlUi.fileListCloseable('html-target-elem', false);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Syntax Highlight
|
|
||||||
|
|
||||||
> Add the dependencies.
|
|
||||||
Choose one color scheme, and add the main highlight code.
|
|
||||||
If your favourite language is not included in the default package also add its javascript highlight file.
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- Stylesheet -->
|
<!-- Stylesheet -->
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/styles/github.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github.min.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
|
||||||
|
|
||||||
<!-- Javascripts -->
|
<!-- Javascripts -->
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.js"></script>
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/highlight.min.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/languages/scala.min.js"></script>
|
|
||||||
<script type="text/javascript" src="dist/diff2html-ui.js"></script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> Invoke the Diff2HtmlUI helper
|
> Pass the option `highlight` with value true or invoke `diff2htmlUi.highlightCode()` after `diff2htmlUi.draw()`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$(document).ready(function() {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
var diff2htmlUi = new Diff2HtmlUI({diff: lineDiffExample});
|
const diffString = `diff --git a/sample.js b/sample.js
|
||||||
diff2htmlUi.draw('#line-by-line', {inputFormat: 'json', showFiles: true, matching: 'lines'});
|
index 0000001..0ddf2ba
|
||||||
diff2htmlUi.highlightCode('#line-by-line');
|
--- a/sample.js
|
||||||
|
+++ b/sample.js
|
||||||
|
@@ -1 +1 @@
|
||||||
|
-console.log("Hello World!")
|
||||||
|
+console.log("Hello from Diff2Html!")`;
|
||||||
|
const targetElement = document.getElementById('myDiffElement');
|
||||||
|
const configuration = { drawFileList: true, matching: 'lines', highlight: true };
|
||||||
|
const diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
|
||||||
|
diff2htmlUi.draw();
|
||||||
|
diff2htmlUi.highlightCode();
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Collapsable File Summary List
|
When using the `auto` color scheme, you will need to specify both the light and dark themes for highlight.js to use.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github.min.css"
|
||||||
|
media="screen and (prefers-color-scheme: light)"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css"
|
||||||
|
media="screen and (prefers-color-scheme: dark)"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Collapsable File Summary List
|
||||||
|
|
||||||
> Add the dependencies.
|
> Add the dependencies.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- Javascripts -->
|
<!-- Javascripts -->
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.js"></script>
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
|
||||||
<script type="text/javascript" src="dist/diff2html-ui.js"></script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> Invoke the Diff2HtmlUI helper
|
> Invoke the Diff2HtmlUI helper Pass the option `fileListToggle` with value true or invoke
|
||||||
|
> `diff2htmlUi.fileListToggle()` after `diff2htmlUi.draw()`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$(document).ready(function() {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
var diff2htmlUi = new Diff2HtmlUI({diff: lineDiffExample});
|
const targetElement = document.getElementById('myDiffElement');
|
||||||
diff2htmlUi.draw('#line-by-line', {inputFormat: 'json', showFiles: true, matching: 'lines'});
|
var diff2htmlUi = new Diff2HtmlUI(targetElement, lineDiffExample, { drawFileList: true, matching: 'lines' });
|
||||||
diff2htmlUi.fileListCloseable('#line-by-line', false);
|
diff2htmlUi.draw();
|
||||||
|
diff2htmlUi.fileListToggle(false);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributions
|
### Diff2HtmlUI Examples
|
||||||
|
|
||||||
This is a developer friendly project, all the contributions are welcome.
|
#### Example with plain HTML+CSS+JS
|
||||||
To contribute just send a pull request with your changes following the guidelines described in `CONTRIBUTING.md`.
|
|
||||||
I will try to review them as soon as possible.
|
```html
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en-us">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<!-- Make sure to load the highlight.js CSS file before the Diff2Html CSS file -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.1/styles/github.min.css" />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css"
|
||||||
|
/>
|
||||||
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<script>
|
||||||
|
const diffString = `diff --git a/sample.js b/sample.js
|
||||||
|
index 0000001..0ddf2ba
|
||||||
|
--- a/sample.js
|
||||||
|
+++ b/sample.js
|
||||||
|
@@ -1 +1 @@
|
||||||
|
-console.log("Hello World!")
|
||||||
|
+console.log("Hello from Diff2Html!")`;
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var targetElement = document.getElementById('myDiffElement');
|
||||||
|
var configuration = {
|
||||||
|
drawFileList: true,
|
||||||
|
fileListToggle: false,
|
||||||
|
fileListStartVisible: false,
|
||||||
|
fileContentToggle: false,
|
||||||
|
matching: 'lines',
|
||||||
|
outputFormat: 'side-by-side',
|
||||||
|
synchronisedScroll: true,
|
||||||
|
highlight: true,
|
||||||
|
renderNothingWhenEmpty: false,
|
||||||
|
};
|
||||||
|
var diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
|
||||||
|
diff2htmlUi.draw();
|
||||||
|
diff2htmlUi.highlightCode();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
<div id="myDiffElement"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### StimulusJS with TypeScript
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Controller } from '@hotwired/stimulus';
|
||||||
|
|
||||||
|
import { Diff2HtmlUI, Diff2HtmlUIConfig } from 'diff2html/lib/ui/js/diff2html-ui-slim.js';
|
||||||
|
|
||||||
|
// Requires `npm install highlight.js`
|
||||||
|
import 'highlight.js/styles/github.css';
|
||||||
|
import 'diff2html/bundles/css/diff2html.min.css';
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
connect(): void {
|
||||||
|
const diff2htmlUi = new Diff2HtmlUI(this.diffElement, this.unifiedDiff, this.diffConfiguration);
|
||||||
|
|
||||||
|
diff2htmlUi.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
get unifiedDiff(): string {
|
||||||
|
return this.data.get('unifiedDiff') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
get diffElement(): HTMLElement {
|
||||||
|
return this.element as HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
get diffConfiguration(): Diff2HtmlUIConfig {
|
||||||
|
return {
|
||||||
|
drawFileList: true,
|
||||||
|
matching: 'lines',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Diff2Html Usage
|
||||||
|
|
||||||
|
### Diff2Html API
|
||||||
|
|
||||||
|
> JSON representation of the diff
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function parse(diffInput: string, configuration: Diff2HtmlConfig = {}): DiffFile[];
|
||||||
|
```
|
||||||
|
|
||||||
|
> Pretty HTML representation of the diff
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function html(diffInput: string | DiffFile[], configuration: Diff2HtmlConfig = {}): string;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Diff2Html Configuration
|
||||||
|
|
||||||
|
The HTML output accepts a Javascript object with configuration. Possible options:
|
||||||
|
|
||||||
|
- `outputFormat`: the format of the output data: `'line-by-line'` or `'side-by-side'`, default is `'line-by-line'`
|
||||||
|
- `drawFileList`: show a file list before the diff: `true` or `false`, default is `true`
|
||||||
|
- `srcPrefix`: add a prefix to all source (before changes) filepaths, default is `''`. Should match the prefix used when
|
||||||
|
[generating the diff](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---src-prefixltprefixgt).
|
||||||
|
- `dstPrefix`: add a prefix to all destination (after changes) filepaths, default is `''`. Should match the prefix used
|
||||||
|
when [generating the diff](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---dst-prefixltprefixgt)
|
||||||
|
- `diffMaxChanges`: number of changed lines after which a file diff is deemed as too big and not displayed, default is
|
||||||
|
`undefined`
|
||||||
|
- `diffMaxLineLength`: number of characters in a diff line after which a file diff is deemed as too big and not
|
||||||
|
displayed, default is `undefined`
|
||||||
|
- `diffTooBigMessage`: function allowing to customize the message in case of file diff too big (if `diffMaxChanges` or
|
||||||
|
`diffMaxLineLength` is set). Will be given a file index as a number and should return a string.
|
||||||
|
- `matching`: matching level: `'lines'` for matching lines, `'words'` for matching lines and words or `'none'`, default
|
||||||
|
is `none`
|
||||||
|
- `matchWordsThreshold`: similarity threshold for word matching, default is `0.25`
|
||||||
|
- `maxLineLengthHighlight`: only perform diff changes highlight if lines are smaller than this, default is `10000`
|
||||||
|
- `diffStyle`: show differences level in each line: `'word'` or `'char'`, default is `'word'`
|
||||||
|
- `renderNothingWhenEmpty`: render nothing if the diff shows no change in its comparison: `true` or `false`, default is
|
||||||
|
`false`
|
||||||
|
- `matchingMaxComparisons`: perform at most this much comparisons for line matching a block of changes, default is
|
||||||
|
`2500`
|
||||||
|
- `maxLineSizeInBlockForComparison`: maximum number of characters of the bigger line in a block to apply comparison,
|
||||||
|
default is `200`
|
||||||
|
- `compiledTemplates`: object ([Hogan.js](https://github.com/twitter/hogan.js/) template values) with previously
|
||||||
|
compiled templates to replace parts of the html, default is `{}`. For example:
|
||||||
|
`{ "tag-file-changed": Hogan.compile("<span class="d2h-tag d2h-changed d2h-changed-tag">MODIFIED</span>") }`
|
||||||
|
- `rawTemplates`: object (string values) with raw not compiled templates to replace parts of the html, default is `{}`.
|
||||||
|
For example: `{ "tag-file-changed": "<span class="d2h-tag d2h-changed d2h-changed-tag">MODIFIED</span>" }`
|
||||||
|
> For more information regarding the possible templates look into
|
||||||
|
> [src/templates](https://github.com/rtfpessoa/diff2html/tree/master/src/templates)
|
||||||
|
- `highlightLanguages`: Map of extension to language name, used for highlighting. This overrides the default language
|
||||||
|
detection based on file extensions.
|
||||||
|
- `colorScheme`: color scheme to use for the diff, default is `light`. Possible values are `light`, `dark`, and `auto`
|
||||||
|
which will use the browser's preferred color scheme.
|
||||||
|
|
||||||
|
### Diff2Html Browser
|
||||||
|
|
||||||
|
Import the stylesheet and the library code.
|
||||||
|
|
||||||
|
To load correctly in the Browser you need to include the stylesheet in the final HTML.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- CSS -->
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
|
||||||
|
|
||||||
|
<!-- Javascripts -->
|
||||||
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html.min.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
It will now be available as a global variable named `Diff2Html`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
var diffHtml = Diff2Html.html('<Unified Diff String>', {
|
||||||
|
drawFileList: true,
|
||||||
|
matching: 'lines',
|
||||||
|
outputFormat: 'side-by-side',
|
||||||
|
});
|
||||||
|
document.getElementById('destination-elem-id').innerHTML = diffHtml;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Diff2Html NPM / Node.js Library
|
||||||
|
|
||||||
|
```js
|
||||||
|
const Diff2html = require('diff2html');
|
||||||
|
const diffJson = Diff2html.parse('<Unified Diff String>');
|
||||||
|
const diffHtml = Diff2html.html(diffJson, { drawFileList: true });
|
||||||
|
console.log(diffHtml);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Diff2Html Examples
|
||||||
|
|
||||||
|
#### Example with Angular
|
||||||
|
|
||||||
|
- Typescript
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import * as Diff2Html from 'diff2html';
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
export class AppDiffComponent implements OnInit {
|
||||||
|
outputHtml: string;
|
||||||
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
let strInput =
|
||||||
|
'--- a/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n+++ b/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n@@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (\n \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n+func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {\n+\tr0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))\n+\tn = int(r0)\n+\tif e1 != 0 {\n+\t\terr = errnoErr(e1)\n+\t}\n+\treturn\n+}\n+\n+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n+\n func read(fd int, p []byte) (n int, err error) {\n \tvar _p0 unsafe.Pointer\n \tif len(p) > 0 {\n';
|
||||||
|
let outputHtml = Diff2Html.html(strInput, { drawFileList: true, matching: 'lines' });
|
||||||
|
this.outputHtml = outputHtml;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- HTML
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>diff2html</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div [innerHtml]="outputHtml"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
- `.angular-cli.json` - Add styles
|
||||||
|
|
||||||
|
```json
|
||||||
|
"styles": [
|
||||||
|
"diff2html.min.css"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example with Vue.js
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div v-html="prettyHtml" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as Diff2Html from 'diff2html';
|
||||||
|
import 'diff2html/bundles/css/diff2html.min.css';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
diffs:
|
||||||
|
'--- a/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n+++ b/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n@@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (\n \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n+func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {\n+\tr0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))\n+\tn = int(r0)\n+\tif e1 != 0 {\n+\t\terr = errnoErr(e1)\n+\t}\n+\treturn\n+}\n+\n+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n+\n func read(fd int, p []byte) (n int, err error) {\n \tvar _p0 unsafe.Pointer\n \tif len(p) > 0 {\n',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
prettyHtml() {
|
||||||
|
return Diff2Html.html(this.diffs, {
|
||||||
|
drawFileList: true,
|
||||||
|
matching: 'lines',
|
||||||
|
outputFormat: 'side-by-side',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### 1. Out of memory or Slow execution
|
||||||
|
|
||||||
|
#### Causes:
|
||||||
|
|
||||||
|
- Big files
|
||||||
|
- Big lines
|
||||||
|
|
||||||
|
#### Fix:
|
||||||
|
|
||||||
|
- Disable the line matching algorithm, by setting the option `{"matching": "none"}` when invoking diff2html
|
||||||
|
|
||||||
|
## Contribute
|
||||||
|
|
||||||
|
This is a developer friendly project, all the contributions are welcome. To contribute just send a pull request with
|
||||||
|
your changes following the guidelines described in `CONTRIBUTING.md`. I will try to review them as soon as possible.
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
<!-- markdownlint-disable -->
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://rtfpessoa.xyz"><img src="https://avatars0.githubusercontent.com/u/902384?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rodrigo Fernandes</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=rtfpessoa" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/stockmind"><img src="https://avatars3.githubusercontent.com/u/5653847?v=4?s=100" width="100px;" alt=""/><br /><sub><b>stockmind</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=stockmind" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/lantian"><img src="https://avatars3.githubusercontent.com/u/535545?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ivan Vorontsov</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=lantian" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://www.nick-brewer.com"><img src="https://avatars1.githubusercontent.com/u/129300?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nick Brewer</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=brewern" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://heyitsmattwade.com"><img src="https://avatars0.githubusercontent.com/u/8504000?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Wade</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Aromellem" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=romellem" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://mrfyda.github.io"><img src="https://avatars1.githubusercontent.com/u/593860?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rafael Cortês</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=mrfyda" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/nmatpt"><img src="https://avatars2.githubusercontent.com/u/5034733?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nuno Teixeira</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=nmatpt" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://saino.me/"><img src="https://avatars0.githubusercontent.com/u/1567423?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Koki Oyatsu</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Akaishuu0123" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=kaishuu0123" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://www.jamesmonger.com"><img src="https://avatars2.githubusercontent.com/u/2037007?v=4?s=100" width="100px;" alt=""/><br /><sub><b>James Monger</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=Jameskmonger" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="http://wesssel.github.io/"><img src="https://avatars2.githubusercontent.com/u/7767299?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wessel van der Pal</b></sub></a><br /><a href="#security-wesssel" title="Security">🛡️</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=wesssel" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://jung-kim.github.io"><img src="https://avatars2.githubusercontent.com/u/5281068?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jk-kim</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=jung-kim" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/sss0791"><img src="https://avatars1.githubusercontent.com/u/1446970?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sergey Semenov</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Asss0791" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=sss0791" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://researcher.watson.ibm.com/researcher/view.php?person=us-nickm"><img src="https://avatars3.githubusercontent.com/u/4741620?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nick Mitchell</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Astarpit" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=starpit" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/samiraguiar"><img src="https://avatars0.githubusercontent.com/u/13439135?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Samir Aguiar</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=samiraguiar" title="Documentation">📖</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://twitter.com/pubkeypubkey"><img src="https://avatars3.githubusercontent.com/u/8926560?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pubkey</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=pubkey" title="Documentation">📖</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=pubkey" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/iliyaZelenko"><img src="https://avatars1.githubusercontent.com/u/13103045?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Илья</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=iliyaZelenko" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://akr.am"><img src="https://avatars0.githubusercontent.com/u/1823771?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mohamed Akram</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Amohd-akram" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=mohd-akram" title="Documentation">📖</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=mohd-akram" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/emarcotte"><img src="https://avatars0.githubusercontent.com/u/249390?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eugene Marcotte</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=emarcotte" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://twitter.com/dimasabanin"><img src="https://avatars0.githubusercontent.com/u/8316?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dima Sabanin</b></sub></a><br /><a href="#maintenance-dsabanin" title="Maintenance">🚧</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=dsabanin" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/benabbottnz"><img src="https://avatars2.githubusercontent.com/u/2616473?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ben Abbott</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=benabbottnz" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="http://webminer.js.org"><img src="https://avatars1.githubusercontent.com/u/2196373?v=4?s=100" width="100px;" alt=""/><br /><sub><b>弘树@阿里</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Adickeylth" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=dickeylth" title="Documentation">📖</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/Rantanen"><img src="https://avatars0.githubusercontent.com/u/385385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mikko Rantanen</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3ARantanen" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/extend1994"><img src="https://avatars2.githubusercontent.com/u/13430892?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ann</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=extend1994" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/escitalopram"><img src="https://avatars0.githubusercontent.com/u/1155220?v=4?s=100" width="100px;" alt=""/><br /><sub><b>escitalopram</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=escitalopram" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/apps/dependabot"><img src="https://avatars0.githubusercontent.com/in/29110?v=4?s=100" width="100px;" alt=""/><br /><sub><b>dependabot[bot]</b></sub></a><br /><a href="#security-dependabot[bot]" title="Security">🛡️</a> <a href="#maintenance-dependabot[bot]" title="Maintenance">🚧</a></td>
|
||||||
|
<td align="center"><a href="http://www.joshuakgoldberg.com"><img src="https://avatars1.githubusercontent.com/u/3335181?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Josh Goldberg</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=JoshuaKGoldberg" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/apeckham"><img src="https://avatars.githubusercontent.com/u/14110?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aaron</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=apeckham" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/pgrimaud"><img src="https://avatars.githubusercontent.com/u/1866496?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pierre Grimaud</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=pgrimaud" title="Documentation">📖</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://domdomegg.github.io/"><img src="https://avatars.githubusercontent.com/u/4953590?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Adam Jones</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=domdomegg" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/charguer"><img src="https://avatars.githubusercontent.com/u/1830652?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arthur Charguéraud</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=charguer" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://twitter.com/pierrci"><img src="https://avatars.githubusercontent.com/u/5020707?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pierric Cistac</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=Pierrci" title="Documentation">📖</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=Pierrci" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/xlith"><img src="https://avatars.githubusercontent.com/u/510560?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Civan Yavuzşen</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=xlith" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/timgates42"><img src="https://avatars.githubusercontent.com/u/47873678?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Gates</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=timgates42" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/campersau"><img src="https://avatars.githubusercontent.com/u/4009570?v=4?s=100" width="100px;" alt=""/><br /><sub><b>campersau</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=campersau" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/apps/dependabot-preview"><img src="https://avatars.githubusercontent.com/in/2141?v=4?s=100" width="100px;" alt=""/><br /><sub><b>dependabot-preview[bot]</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=dependabot-preview[bot]" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- markdownlint-restore -->
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
|
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification.
|
||||||
|
Contributions of any kind welcome!
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright 2014-2016 Rodrigo Fernandes. Released under the terms of the MIT license.
|
Copyright 2014-present Rodrigo Fernandes. Released under the terms of the MIT license.
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
This project is inspired in [pretty-diff](https://github.com/scottgonzalez/pretty-diff) by [Scott González](https://github.com/scottgonzalez).
|
This project is inspired in [pretty-diff](https://github.com/scottgonzalez/pretty-diff) by
|
||||||
|
[Scott González](https://github.com/scottgonzalez).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
35
SECURITY.md
Normal file
35
SECURITY.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 2.9.x | :white_check_mark: |
|
||||||
|
| < 2.9 | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
We take all security bugs in `diff2html` seriously. Thank you for the help improving the security of `diff2html`. We
|
||||||
|
appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions.
|
||||||
|
|
||||||
|
Report security bugs by emailing the lead maintainer at `rtfrodrigo [at] gmail [dot] com`.
|
||||||
|
|
||||||
|
The lead maintainer will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours
|
||||||
|
indicating the next steps in handling your report. After the initial reply to your report, the security team will
|
||||||
|
endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional
|
||||||
|
information or guidance.
|
||||||
|
|
||||||
|
Report security bugs in third-party modules to the person or team maintaining the module.
|
||||||
|
|
||||||
|
## Disclosure Policy
|
||||||
|
|
||||||
|
When the security team receives a security bug report, they will assign it to a primary handler. This person will
|
||||||
|
coordinate the fix and release process, involving the following steps:
|
||||||
|
|
||||||
|
- Confirm the problem and determine the affected versions.
|
||||||
|
- Audit code to find any potential similar problems.
|
||||||
|
- Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible.
|
||||||
|
|
||||||
|
## Comments on this Policy
|
||||||
|
|
||||||
|
If you have suggestions on how this process could be improved please submit a pull request.
|
||||||
51
bower.json
51
bower.json
|
|
@ -1,51 +0,0 @@
|
||||||
{
|
|
||||||
"name": "diff2html",
|
|
||||||
"version": "2.0.0-rc.3",
|
|
||||||
"homepage": "http://rtfpessoa.github.io/diff2html/",
|
|
||||||
"description": "Fast Diff to colorized HTML",
|
|
||||||
"keywords": [
|
|
||||||
"git",
|
|
||||||
"diff",
|
|
||||||
"pretty",
|
|
||||||
"side",
|
|
||||||
"line",
|
|
||||||
"side-by-side",
|
|
||||||
"line-by-line",
|
|
||||||
"character",
|
|
||||||
"highlight",
|
|
||||||
"pretty",
|
|
||||||
"color",
|
|
||||||
"html",
|
|
||||||
"diff2html",
|
|
||||||
"difftohtml",
|
|
||||||
"colorized"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
"Rodrigo Fernandes <rtfrodrigo@gmail.com>"
|
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git://github.com/rtfpessoa/diff2html.git"
|
|
||||||
},
|
|
||||||
"main": [
|
|
||||||
"./dist/diff2html-templates.js",
|
|
||||||
"./dist/diff2html.js",
|
|
||||||
"./dist/diff2html-ui.js",
|
|
||||||
"./dist/diff2html.css"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"moduleType": [
|
|
||||||
"globals",
|
|
||||||
"node"
|
|
||||||
],
|
|
||||||
"ignore": [
|
|
||||||
"**/.*",
|
|
||||||
"webpack.config.js",
|
|
||||||
"package.json",
|
|
||||||
"release.sh",
|
|
||||||
"circle.yml",
|
|
||||||
"css",
|
|
||||||
"src",
|
|
||||||
"test"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
test:
|
|
||||||
override:
|
|
||||||
- nvm install 0.12 && npm test
|
|
||||||
- nvm install 4 && npm test
|
|
||||||
- nvm install 5 && npm test
|
|
||||||
post:
|
|
||||||
- npm install
|
|
||||||
- npm test
|
|
||||||
- cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage
|
|
||||||
354
dist/diff2html-ui.js
vendored
354
dist/diff2html-ui.js
vendored
|
|
@ -1,354 +0,0 @@
|
||||||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|
||||||
(function (global){
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Diff to HTML (diff2html-ui.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
* Depends on: [ jQuery ]
|
|
||||||
* Optional dependencies on: [ highlight.js ]
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*global $, hljs, Diff2Html*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var highlightJS = require('./highlight.js-internals.js').HighlightJS;
|
|
||||||
|
|
||||||
var diffJson = null;
|
|
||||||
var defaultTarget = "body";
|
|
||||||
var currentSelectionColumnId = -1;
|
|
||||||
|
|
||||||
function Diff2HtmlUI(config) {
|
|
||||||
var cfg = config || {};
|
|
||||||
|
|
||||||
if (cfg.diff) {
|
|
||||||
diffJson = Diff2Html.getJsonFromDiff(cfg.diff);
|
|
||||||
} else if (cfg.json) {
|
|
||||||
diffJson = cfg.json;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._initSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype.draw = function(targetId, config) {
|
|
||||||
var cfg = config || {};
|
|
||||||
var $target = this._getTarget(targetId);
|
|
||||||
$target.html(Diff2Html.getPrettyHtml(diffJson, cfg));
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype.fileListCloseable = function(targetId, startVisible) {
|
|
||||||
var $target = this._getTarget(targetId);
|
|
||||||
|
|
||||||
var hashTag = this._getHashTag();
|
|
||||||
|
|
||||||
var $showBtn = $target.find(".d2h-show");
|
|
||||||
var $hideBtn = $target.find(".d2h-hide");
|
|
||||||
var $fileList = $target.find(".d2h-file-list");
|
|
||||||
|
|
||||||
if (hashTag === 'files-summary-show') show();
|
|
||||||
else if (hashTag === 'files-summary-hide') hide();
|
|
||||||
else if (startVisible) show();
|
|
||||||
else hide();
|
|
||||||
|
|
||||||
$showBtn.click(show);
|
|
||||||
$hideBtn.click(hide);
|
|
||||||
|
|
||||||
function show() {
|
|
||||||
$showBtn.hide();
|
|
||||||
$hideBtn.show();
|
|
||||||
$fileList.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide() {
|
|
||||||
$hideBtn.hide();
|
|
||||||
$showBtn.show();
|
|
||||||
$fileList.hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype.highlightCode = function(targetId) {
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
var $target = that._getTarget(targetId);
|
|
||||||
|
|
||||||
// collect all the diff files and execute the highlight on their lines
|
|
||||||
var $files = $target.find(".d2h-file-wrapper");
|
|
||||||
$files.map(function(_i, file) {
|
|
||||||
var oldLinesState;
|
|
||||||
var newLinesState;
|
|
||||||
var $file = $(file);
|
|
||||||
var language = $file.data("lang");
|
|
||||||
|
|
||||||
// collect all the code lines and execute the highlight on them
|
|
||||||
var $codeLines = $file.find(".d2h-code-line-ctn");
|
|
||||||
$codeLines.map(function(_j, line) {
|
|
||||||
var $line = $(line);
|
|
||||||
var text = line.textContent;
|
|
||||||
var lineParent = line.parentNode;
|
|
||||||
|
|
||||||
var lineState;
|
|
||||||
if (lineParent.className.indexOf("d2h-del") !== -1) {
|
|
||||||
lineState = oldLinesState;
|
|
||||||
} else {
|
|
||||||
lineState = newLinesState;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = hljs.getLanguage(language) ? hljs.highlight(language, text, true, lineState) : hljs.highlightAuto(text);
|
|
||||||
|
|
||||||
if (lineParent.className.indexOf("d2h-del") !== -1) {
|
|
||||||
oldLinesState = result.top;
|
|
||||||
} else if (lineParent.className.indexOf("d2h-ins") !== -1) {
|
|
||||||
newLinesState = result.top;
|
|
||||||
} else {
|
|
||||||
oldLinesState = result.top;
|
|
||||||
newLinesState = result.top;
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalStream = highlightJS.nodeStream(line);
|
|
||||||
if (originalStream.length) {
|
|
||||||
var resultNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
|
||||||
resultNode.innerHTML = result.value;
|
|
||||||
result.value = highlightJS.mergeStreams(originalStream, highlightJS.nodeStream(resultNode), text);
|
|
||||||
}
|
|
||||||
|
|
||||||
$line.addClass("hljs");
|
|
||||||
$line.addClass(result.language);
|
|
||||||
$line.html(result.value);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._getTarget = function(targetId) {
|
|
||||||
var $target;
|
|
||||||
|
|
||||||
if (typeof targetId === 'object' && targetId instanceof jQuery) {
|
|
||||||
$target = targetId;
|
|
||||||
} else if (typeof targetId === 'string') {
|
|
||||||
$target = $(targetId);
|
|
||||||
} else {
|
|
||||||
console.error("Wrong target provided! Falling back to default value 'body'.");
|
|
||||||
console.log("Please provide a jQuery object or a valid DOM query string.");
|
|
||||||
$target = $(defaultTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $target;
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._getHashTag = function() {
|
|
||||||
var docUrl = document.URL;
|
|
||||||
var hashTagIndex = docUrl.indexOf('#');
|
|
||||||
|
|
||||||
var hashTag = null;
|
|
||||||
if (hashTagIndex !== -1) {
|
|
||||||
hashTag = docUrl.substr(hashTagIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashTag;
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._distinct = function(collection) {
|
|
||||||
return collection.filter(function(v, i) {
|
|
||||||
return collection.indexOf(v) === i;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._initSelection = function() {
|
|
||||||
var body = $('body');
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
body.on('mousedown', '.d2h-diff-table', function(event) {
|
|
||||||
var target = $(event.target);
|
|
||||||
var table = target.closest('.d2h-diff-table');
|
|
||||||
|
|
||||||
if (target.closest('.d2h-code-line,.d2h-code-side-line').length) {
|
|
||||||
table.removeClass('selecting-left');
|
|
||||||
table.addClass('selecting-right');
|
|
||||||
currentSelectionColumnId = 1;
|
|
||||||
} else if (target.closest('.d2h-code-linenumber,.d2h-code-side-linenumber').length) {
|
|
||||||
table.removeClass('selecting-right');
|
|
||||||
table.addClass('selecting-left');
|
|
||||||
currentSelectionColumnId = 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
body.on('copy', '.d2h-diff-table', function(event) {
|
|
||||||
var clipboardData = event.originalEvent.clipboardData;
|
|
||||||
var text = that._getSelectedText();
|
|
||||||
clipboardData.setData('text', text);
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._getSelectedText = function() {
|
|
||||||
var sel = window.getSelection();
|
|
||||||
var range = sel.getRangeAt(0);
|
|
||||||
var doc = range.cloneContents();
|
|
||||||
var nodes = doc.querySelectorAll('tr');
|
|
||||||
var text = '';
|
|
||||||
var idx = currentSelectionColumnId;
|
|
||||||
|
|
||||||
if (nodes.length === 0) {
|
|
||||||
text = doc.textContent;
|
|
||||||
} else {
|
|
||||||
[].forEach.call(nodes, function(tr, i) {
|
|
||||||
var td = tr.cells[tr.cells.length === 1 ? 0 : idx];
|
|
||||||
text += (i ? '\n' : '') + td.textContent.replace(/(?:\r\n|\r|\n)/g, '');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.Diff2HtmlUI = Diff2HtmlUI;
|
|
||||||
|
|
||||||
// Expose diff2html in the browser
|
|
||||||
global.Diff2HtmlUI = Diff2HtmlUI;
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
||||||
},{"./highlight.js-internals.js":2}],2:[function(require,module,exports){
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* highlight.js
|
|
||||||
* Author: isagalaev
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
function HighlightJS() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copied from Highlight.js Private API
|
|
||||||
* Will be removed when this part of the API is exposed
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Utility functions */
|
|
||||||
|
|
||||||
function escape(value) {
|
|
||||||
return value.replace(/&/gm, '&').replace(/</gm, '<').replace(/>/gm, '>');
|
|
||||||
}
|
|
||||||
|
|
||||||
function tag(node) {
|
|
||||||
return node.nodeName.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stream merging */
|
|
||||||
|
|
||||||
HighlightJS.prototype.nodeStream = function(node) {
|
|
||||||
var result = [];
|
|
||||||
(function _nodeStream(node, offset) {
|
|
||||||
for (var child = node.firstChild; child; child = child.nextSibling) {
|
|
||||||
if (child.nodeType == 3)
|
|
||||||
offset += child.nodeValue.length;
|
|
||||||
else if (child.nodeType == 1) {
|
|
||||||
result.push({
|
|
||||||
event: 'start',
|
|
||||||
offset: offset,
|
|
||||||
node: child
|
|
||||||
});
|
|
||||||
offset = _nodeStream(child, offset);
|
|
||||||
// Prevent void elements from having an end tag that would actually
|
|
||||||
// double them in the output. There are more void elements in HTML
|
|
||||||
// but we list only those realistically expected in code display.
|
|
||||||
if (!tag(child).match(/br|hr|img|input/)) {
|
|
||||||
result.push({
|
|
||||||
event: 'stop',
|
|
||||||
offset: offset,
|
|
||||||
node: child
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
})(node, 0);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
HighlightJS.prototype.mergeStreams = function(original, highlighted, value) {
|
|
||||||
var processed = 0;
|
|
||||||
var result = '';
|
|
||||||
var nodeStack = [];
|
|
||||||
|
|
||||||
function selectStream() {
|
|
||||||
if (!original.length || !highlighted.length) {
|
|
||||||
return original.length ? original : highlighted;
|
|
||||||
}
|
|
||||||
if (original[0].offset != highlighted[0].offset) {
|
|
||||||
return (original[0].offset < highlighted[0].offset) ? original : highlighted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
To avoid starting the stream just before it should stop the order is
|
|
||||||
ensured that original always starts first and closes last:
|
|
||||||
if (event1 == 'start' && event2 == 'start')
|
|
||||||
return original;
|
|
||||||
if (event1 == 'start' && event2 == 'stop')
|
|
||||||
return highlighted;
|
|
||||||
if (event1 == 'stop' && event2 == 'start')
|
|
||||||
return original;
|
|
||||||
if (event1 == 'stop' && event2 == 'stop')
|
|
||||||
return highlighted;
|
|
||||||
... which is collapsed to:
|
|
||||||
*/
|
|
||||||
return highlighted[0].event == 'start' ? original : highlighted;
|
|
||||||
}
|
|
||||||
|
|
||||||
function open(node) {
|
|
||||||
function attrStr(a) {
|
|
||||||
return ' ' + a.nodeName + '="' + escape(a.value) + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
result += '<' + tag(node) + Array.prototype.map.call(node.attributes, attrStr).join('') + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function close(node) {
|
|
||||||
result += '</' + tag(node) + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function render(event) {
|
|
||||||
(event.event == 'start' ? open : close)(event.node);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (original.length || highlighted.length) {
|
|
||||||
var stream = selectStream();
|
|
||||||
result += escape(value.substr(processed, stream[0].offset - processed));
|
|
||||||
processed = stream[0].offset;
|
|
||||||
if (stream == original) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
On any opening or closing tag of the original markup we first close
|
|
||||||
the entire highlighted node stack, then render the original tag along
|
|
||||||
with all the following original tags at the same offset and then
|
|
||||||
reopen all the tags on the highlighted stack.
|
|
||||||
*/
|
|
||||||
nodeStack.reverse().forEach(close);
|
|
||||||
do {
|
|
||||||
render(stream.splice(0, 1)[0]);
|
|
||||||
stream = selectStream();
|
|
||||||
} while (stream == original && stream.length && stream[0].offset == processed);
|
|
||||||
nodeStack.reverse().forEach(open);
|
|
||||||
} else {
|
|
||||||
if (stream[0].event == 'start') {
|
|
||||||
nodeStack.push(stream[0].node);
|
|
||||||
} else {
|
|
||||||
nodeStack.pop();
|
|
||||||
}
|
|
||||||
render(stream.splice(0, 1)[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result + escape(value.substr(processed));
|
|
||||||
};
|
|
||||||
|
|
||||||
/* **** Highlight.js Private API **** */
|
|
||||||
|
|
||||||
module.exports.HighlightJS = new HighlightJS();
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
},{}]},{},[1]);
|
|
||||||
1
dist/diff2html-ui.min.js
vendored
1
dist/diff2html-ui.min.js
vendored
File diff suppressed because one or more lines are too long
305
dist/diff2html.css
vendored
305
dist/diff2html.css
vendored
|
|
@ -1,305 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Diff to HTML (diff2html.css)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
.d2h-wrapper {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-header {
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-bottom: 1px solid #d8d8d8;
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-stats {
|
|
||||||
display: inline;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-lines-added {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-lines-added > * {
|
|
||||||
background-color: #ceffce;
|
|
||||||
border: 1px solid #b4e2b4;
|
|
||||||
color: #399839;
|
|
||||||
border-radius: 5px 0 0 5px;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-lines-deleted {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-lines-deleted > * {
|
|
||||||
background-color: #f7c8c8;
|
|
||||||
border: 1px solid #e9aeae;
|
|
||||||
color: #c33;
|
|
||||||
border-radius: 0 5px 5px 0;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-name-wrapper {
|
|
||||||
display: inline-flex;
|
|
||||||
width: 90%;
|
|
||||||
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-name {
|
|
||||||
line-height: 33px;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-wrapper {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-diff-table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
font-family: "Menlo", "Consolas", monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-diff-tbody > tr > td > div {
|
|
||||||
height: 16px;
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-files-diff {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-diff {
|
|
||||||
overflow-x: scroll;
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-side-diff {
|
|
||||||
display: inline-block;
|
|
||||||
overflow-x: scroll;
|
|
||||||
overflow-y: hidden;
|
|
||||||
width: 50%;
|
|
||||||
margin-right: -4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-code-line {
|
|
||||||
display: block;
|
|
||||||
white-space: nowrap;
|
|
||||||
padding: 0 10px;
|
|
||||||
margin-left: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-code-side-line {
|
|
||||||
display: block;
|
|
||||||
white-space: pre;
|
|
||||||
padding: 0 10px;
|
|
||||||
height: 18px;
|
|
||||||
line-height: 18px;
|
|
||||||
margin-left: 50px;
|
|
||||||
/* Override HighlightJS */
|
|
||||||
color: inherit;
|
|
||||||
overflow-x: inherit;
|
|
||||||
background: none;
|
|
||||||
/* ******************** */
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-code-line del,
|
|
||||||
.d2h-code-side-line del {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: -1px;
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: #ffb6ba;
|
|
||||||
border-radius: 0.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-code-line ins,
|
|
||||||
.d2h-code-side-line ins {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: -1px;
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: #97f295;
|
|
||||||
border-radius: 0.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-code-line-prefix {
|
|
||||||
float: left;
|
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-code-line-ctn {
|
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-num1 {
|
|
||||||
box-sizing: border-box;
|
|
||||||
float: left;
|
|
||||||
width: 40px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
padding-left: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-num2 {
|
|
||||||
box-sizing: border-box;
|
|
||||||
float: right;
|
|
||||||
width: 40px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
padding-left: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-code-linenumber {
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: absolute;
|
|
||||||
width: 86px;
|
|
||||||
height: 18px;
|
|
||||||
padding-left: 2px;
|
|
||||||
padding-right: 2px;
|
|
||||||
line-height: 18px;
|
|
||||||
background-color: #fff;
|
|
||||||
color: rgba(0, 0, 0, 0.3);
|
|
||||||
text-align: right;
|
|
||||||
border: solid #eeeeee;
|
|
||||||
border-width: 0 1px 0 1px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-code-side-linenumber {
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: absolute;
|
|
||||||
width: 56px;
|
|
||||||
padding-left: 5px;
|
|
||||||
padding-right: 5px;
|
|
||||||
height: 18px;
|
|
||||||
line-height: 18px;
|
|
||||||
background-color: #fff;
|
|
||||||
color: rgba(0, 0, 0, 0.3);
|
|
||||||
text-align: right;
|
|
||||||
border: solid #eeeeee;
|
|
||||||
border-width: 0 1px 0 1px;
|
|
||||||
cursor: pointer;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Changes Highlight
|
|
||||||
*/
|
|
||||||
|
|
||||||
.d2h-del {
|
|
||||||
background-color: #fee8e9;
|
|
||||||
border-color: #e9aeae;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-ins {
|
|
||||||
background-color: #dfd;
|
|
||||||
border-color: #b4e2b4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-info {
|
|
||||||
background-color: #f8fafd;
|
|
||||||
color: rgba(0, 0, 0, 0.3);
|
|
||||||
border-color: #d5e4f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-del.d2h-change, .d2h-ins.d2h-change {
|
|
||||||
background-color: #ffc;
|
|
||||||
}
|
|
||||||
|
|
||||||
ins.d2h-change, del.d2h-change {
|
|
||||||
background-color: #fad771;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-diff .d2h-del.d2h-change {
|
|
||||||
background-color: #fae1af;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-diff .d2h-ins.d2h-change {
|
|
||||||
background-color: #ded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* File Summary List
|
|
||||||
*/
|
|
||||||
|
|
||||||
.d2h-file-list-wrapper {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-list-wrapper a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #3572b0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-list-wrapper a:visited {
|
|
||||||
color: #3572b0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-list-header {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-list-title {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-list-line {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-list-line .d2h-file-name {
|
|
||||||
line-height: 21px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-list {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-switch {
|
|
||||||
display: none;
|
|
||||||
font-size: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Selection util.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.selecting-left .d2h-code-line,
|
|
||||||
.selecting-left .d2h-code-line *,
|
|
||||||
.selecting-right td.d2h-code-linenumber,
|
|
||||||
.selecting-right td.d2h-code-linenumber *,
|
|
||||||
.selecting-left .d2h-code-side-line,
|
|
||||||
.selecting-left .d2h-code-side-line *,
|
|
||||||
.selecting-right td.d2h-code-side-linenumber,
|
|
||||||
.selecting-right td.d2h-code-side-linenumber * {
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selecting-left .d2h-code-line::selection,
|
|
||||||
.selecting-left .d2h-code-line *::selection
|
|
||||||
.selecting-right td.d2h-code-linenumber::selection,
|
|
||||||
.selecting-left .d2h-code-side-line::selection,
|
|
||||||
.selecting-left .d2h-code-side-line *::selection,
|
|
||||||
.selecting-right td.d2h-code-side-linenumber::selection,
|
|
||||||
.selecting-right td.d2h-code-side-linenumber *::selection {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
3748
dist/diff2html.js
vendored
3748
dist/diff2html.js
vendored
File diff suppressed because it is too large
Load diff
1
dist/diff2html.min.css
vendored
1
dist/diff2html.min.css
vendored
|
|
@ -1 +0,0 @@
|
||||||
.d2h-code-line-prefix,.line-num1{float:left}.d2h-wrapper{text-align:left}.d2h-file-header{padding:5px 10px;border-bottom:1px solid #d8d8d8;background-color:#f7f7f7}.d2h-file-stats{display:inline;text-align:center}.d2h-lines-added{text-align:right}.d2h-lines-added>*{background-color:#ceffce;border:1px solid #b4e2b4;color:#399839;border-radius:5px 0 0 5px;padding:2px}.d2h-lines-deleted{text-align:left}.d2h-lines-deleted>*{background-color:#f7c8c8;border:1px solid #e9aeae;color:#c33;border-radius:0 5px 5px 0;padding:2px}.d2h-file-name-wrapper{display:inline-flex;width:90%;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:15px}.d2h-file-name{line-height:33px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.d2h-file-diff,.d2h-file-side-diff{overflow-x:scroll;overflow-y:hidden}.d2h-file-wrapper{border:1px solid #ddd;border-radius:3px;margin-bottom:1em}.d2h-diff-table{width:100%;border-collapse:collapse;font-family:Menlo,Consolas,monospace;font-size:13px}.d2h-diff-tbody>tr>td>div{height:16px;line-height:16px}.d2h-files-diff{width:100%}.d2h-file-side-diff{display:inline-block;width:50%;margin-right:-4px}.d2h-code-line{display:block;white-space:nowrap;padding:0 10px;margin-left:80px}.d2h-code-side-line{display:block;white-space:pre;padding:0 10px;height:18px;line-height:18px;margin-left:50px;color:inherit;overflow-x:inherit;background:0 0}.d2h-code-line del,.d2h-code-side-line del{display:inline-block;margin-top:-1px;text-decoration:none;background-color:#ffb6ba;border-radius:.2em}.d2h-code-line ins,.d2h-code-side-line ins{display:inline-block;margin-top:-1px;text-decoration:none;background-color:#97f295;border-radius:.2em}.d2h-code-line-ctn,.d2h-code-line-prefix{background:0 0;padding:0}.d2h-code-linenumber,.d2h-code-side-linenumber{position:absolute;height:18px;line-height:18px;background-color:#fff;text-align:right;color:rgba(0,0,0,.3);cursor:pointer}.line-num1,.line-num2{width:40px;padding-left:3px;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis}.line-num2{float:right}.d2h-code-linenumber{box-sizing:border-box;width:86px;padding-left:2px;padding-right:2px;border:solid #eee;border-width:0 1px}.d2h-code-side-linenumber{box-sizing:border-box;width:56px;padding-left:5px;padding-right:5px;border:solid #eee;border-width:0 1px;overflow:hidden;text-overflow:ellipsis}.d2h-file-list-header,.d2h-file-list-line{text-align:left}.d2h-del{background-color:#fee8e9;border-color:#e9aeae}.d2h-ins{background-color:#dfd;border-color:#b4e2b4}.d2h-info{background-color:#f8fafd;color:rgba(0,0,0,.3);border-color:#d5e4f2}.d2h-del.d2h-change,.d2h-ins.d2h-change{background-color:#ffc}del.d2h-change,ins.d2h-change{background-color:#fad771}.d2h-file-diff .d2h-del.d2h-change{background-color:#fae1af}.d2h-file-diff .d2h-ins.d2h-change{background-color:#ded}.d2h-file-list-wrapper{margin-bottom:10px}.d2h-file-list-wrapper a{text-decoration:none;color:#3572b0}.d2h-file-list-wrapper a:visited{color:#3572b0}.d2h-file-list-title{font-weight:700}.d2h-file-list-line .d2h-file-name{line-height:21px}.d2h-file-list{display:block}.d2h-file-switch{display:none;font-size:10px;cursor:pointer}.selecting-left .d2h-code-line,.selecting-left .d2h-code-line *,.selecting-left .d2h-code-side-line,.selecting-left .d2h-code-side-line *,.selecting-right td.d2h-code-linenumber,.selecting-right td.d2h-code-linenumber *,.selecting-right td.d2h-code-side-linenumber,.selecting-right td.d2h-code-side-linenumber *{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.selecting-left .d2h-code-line ::selection .selecting-right td.d2h-code-linenumber::selection,.selecting-left .d2h-code-line::selection,.selecting-left .d2h-code-side-line ::selection,.selecting-left .d2h-code-side-line::selection,.selecting-right td.d2h-code-side-linenumber ::selection,.selecting-right td.d2h-code-side-linenumber::selection{background:0 0}
|
|
||||||
5
dist/diff2html.min.js
vendored
5
dist/diff2html.min.js
vendored
File diff suppressed because one or more lines are too long
77
eslint.config.mjs
Normal file
77
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import globals from 'globals';
|
||||||
|
import eslint from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import pluginJest from 'eslint-plugin-jest';
|
||||||
|
import json from '@eslint/json';
|
||||||
|
import pluginPromise from 'eslint-plugin-promise';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{ ...eslint.configs.recommended, files: ['src/**/*.{js,mjs,cjs,ts}'] },
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
// ...tseslint.configs.recommendedTypeChecked,
|
||||||
|
// ...tseslint.configs.strict,
|
||||||
|
// ...tseslint.configs.stylistic,
|
||||||
|
// ...tseslint.configs.strictTypeChecked,
|
||||||
|
// ...tseslint.configs.stylisticTypeChecked,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
args: 'all',
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
caughtErrors: 'all',
|
||||||
|
caughtErrorsIgnorePattern: '^_',
|
||||||
|
destructuredArrayIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
ignoreRestSiblings: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pluginPromise.configs['flat/recommended'],
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
json,
|
||||||
|
},
|
||||||
|
files: ['**/*.json'],
|
||||||
|
language: 'json/json',
|
||||||
|
rules: {
|
||||||
|
'json/no-duplicate-keys': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...pluginJest.configs['flat/recommended'],
|
||||||
|
...pluginJest.configs['flat/style'],
|
||||||
|
files: ['src/__tests__/**/*tests.ts'],
|
||||||
|
plugins: { jest: pluginJest },
|
||||||
|
languageOptions: {
|
||||||
|
globals: pluginJest.environments.globals.globals,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node,
|
||||||
|
...globals.es2025,
|
||||||
|
Atomics: 'readonly',
|
||||||
|
SharedArrayBuffer: 'readonly',
|
||||||
|
document: 'readonly',
|
||||||
|
navigator: 'readonly',
|
||||||
|
window: 'readonly',
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
project: './tsconfig.eslint.json',
|
||||||
|
tsconfigRootDir: './',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['src/diff2html-templates.*', 'coverage/', 'docs/', 'bundles-out/', 'bundles/', 'lib/', 'lib-esm/'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...tseslint.configs.disableTypeChecked,
|
||||||
|
files: ['**/*.{js,mjs,cjs}'],
|
||||||
|
},
|
||||||
|
];
|
||||||
23
jest.config.js
Normal file
23
jest.config.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
module.exports = {
|
||||||
|
verbose: true,
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
coverageDirectory: './coverage',
|
||||||
|
coverageReporters: ['lcov', 'text', 'html', 'json', 'cobertura', 'clover'],
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'src/**/*.ts',
|
||||||
|
'!src/ui/**',
|
||||||
|
'!src/diff2html-templates.ts',
|
||||||
|
'!src/__tests__/**',
|
||||||
|
'!node_modules/**',
|
||||||
|
],
|
||||||
|
coverageThreshold: {
|
||||||
|
global: {
|
||||||
|
statements: 93,
|
||||||
|
branches: 86,
|
||||||
|
functions: 98,
|
||||||
|
lines: 93,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prettierPath: require.resolve('prettier-2'),
|
||||||
|
};
|
||||||
18121
package-lock.json
generated
Normal file
18121
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
150
package.json
150
package.json
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "diff2html",
|
"name": "diff2html",
|
||||||
"version": "2.0.0-rc.3",
|
"version": "3.0.0-beta.1",
|
||||||
"homepage": "http://rtfpessoa.github.io/diff2html/",
|
"homepage": "https://diff2html.xyz",
|
||||||
"description": "Fast Diff to colorized HTML",
|
"description": "Fast Diff to colorized HTML",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"git",
|
"git",
|
||||||
|
|
@ -26,51 +26,135 @@
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://www.github.com/rtfpessoa/diff2html.git"
|
"url": "git://github.com/rtfpessoa/diff2html.git"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://www.github.com/rtfpessoa/diff2html/issues"
|
"url": "https://www.github.com/rtfpessoa/diff2html/issues"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"preferGlobal": true,
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"release": "./scripts/release.sh",
|
"lint:staged": "lint-staged",
|
||||||
"release-bower": "./scripts/update-bower-version.sh",
|
"lint:check": "eslint",
|
||||||
"templates": "./scripts/hulk.js --wrapper node --variable 'browserTemplates' ./src/templates/*.mustache > ./src/templates/diff2html-templates.js",
|
"lint:fix": "eslint --fix",
|
||||||
"style": "jscs src/*.js src/ui/js/*.js",
|
"prettier": "prettier --ignore-path .gitignore '**/*.+(js|jsx|ts|tsx|json|css|html|md|mdx)'",
|
||||||
"coverage": "istanbul cover _mocha -- -u exports -R spec ./test/**/*",
|
"format:check": "npm run prettier --check",
|
||||||
"check-coverage": "istanbul check-coverage --statements 90 --functions 90 --branches 85 --lines 90 ./coverage/coverage.json",
|
"format:fix": "npm run prettier --write",
|
||||||
"test": "npm run style && npm run coverage && npm run check-coverage",
|
"build": "npm run build:css && npm run build:templates && npm run build:commonjs && npm run build:esm && npm run build:bundles && npm run build:website",
|
||||||
"codacy": "npm run coverage && cat ./coverage/lcov.info | codacy-coverage",
|
"build:commonjs": "rm -rf lib; tsc -p tsconfig.json -m CommonJS --outDir lib",
|
||||||
"preversion": "npm run release && npm test",
|
"build:esm": "rm -rf lib-esm; tsc -p tsconfig.json -m ESNext --outDir lib-esm",
|
||||||
"version": "npm run release-bower && git add -A src dist package.json bower.json",
|
"build:bundles": "rm -rf ./bundles/js; webpack --mode production --config webpack.bundles.ts",
|
||||||
"postversion": "git push && git push --tags"
|
"build:css": "rm -rf ./bundles/css; postcss --config ./postcss.config.js --no-map -o ./bundles/css/diff2html.min.css ./src/ui/css/diff2html.css",
|
||||||
|
"build:templates": "ts-node ./scripts/hulk.ts --wrapper ts --variable 'defaultTemplates' ./src/templates/*.mustache > ./src/diff2html-templates.ts",
|
||||||
|
"build:website": "rm -rf docs; webpack --mode production --config webpack.website.ts",
|
||||||
|
"gen": "npm run gen:toc",
|
||||||
|
"gen:toc-base": "markdown-toc --maxdepth 3 --bullets='-' -i",
|
||||||
|
"gen:toc": "npm run gen:toc-base README.md",
|
||||||
|
"test": "is-ci 'test:coverage' 'test:watch'",
|
||||||
|
"test:coverage": "jest --coverage",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --watch",
|
||||||
|
"coverage:open": "npm run test:coverage && open ./coverage/index.html",
|
||||||
|
"coverage:push": "curl -Ls https://coverage.codacy.com/get.sh | bash",
|
||||||
|
"validate": "npm run build:templates && npm run format:check && npm run lint:check && npm run build && npm run test:coverage",
|
||||||
|
"fix": "npm run format:fix && npm run lint:fix",
|
||||||
|
"start": "npm run start:website",
|
||||||
|
"start:website": "webpack serve --mode development --config webpack.website.ts",
|
||||||
|
"preversion": "npm run validate",
|
||||||
|
"version": "git add -A package.json",
|
||||||
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"main": "./src/diff2html.js",
|
"main": "./lib/diff2html.js",
|
||||||
"browser": {
|
"module": "./lib-esm/diff2html.js",
|
||||||
"fs": false
|
"types": "./lib/diff2html.d.ts",
|
||||||
|
"lint-staged": {
|
||||||
|
"**/*.+(js|jsx|ts|tsx|json)": [
|
||||||
|
"prettier --write",
|
||||||
|
"eslint --fix"
|
||||||
|
],
|
||||||
|
"**/*.+(css|html|md|mdx)": [
|
||||||
|
"prettier --write"
|
||||||
|
],
|
||||||
|
"README.md": [
|
||||||
|
"npm run gen:toc-base"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff": "^2.2.2",
|
"diff": "^7.0.0",
|
||||||
"hogan.js": "^3.0.2"
|
"hogan.js": "3.0.2"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"highlight.js": "11.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browserify": "^13.0.0",
|
"prettier-2": "npm:prettier@^2",
|
||||||
"clean-css": "^3.4.10",
|
"@eslint/js": "^9.17.0",
|
||||||
"codacy-coverage": "^1.1.3",
|
"@eslint/json": "^0.9.0",
|
||||||
"fast-html-parser": "^1.0.1",
|
"@types/diff": "^6.0.0",
|
||||||
"istanbul": "^0.4.2",
|
"@types/hogan.js": "3.0.5",
|
||||||
"jscs": "^3.0.3",
|
"@types/jest": "^29.5.14",
|
||||||
"mkdirp": "^0.5.1",
|
"@types/node": "^22.10.2",
|
||||||
"mocha": "^2.4.5",
|
"@types/nopt": "3.0.32",
|
||||||
"nopt": "^3.0.6",
|
"all-contributors-cli": "^6.24.0",
|
||||||
"uglifyjs": "^2.4.10"
|
"autoprefixer": "^10.4.20",
|
||||||
|
"bulma": "^1.0.2",
|
||||||
|
"clipboard": "2.0.11",
|
||||||
|
"copy-webpack-plugin": "^12.0.2",
|
||||||
|
"css-loader": "^7.1.2",
|
||||||
|
"cssnano": "^7.0.6",
|
||||||
|
"eslint": "^9.17.0",
|
||||||
|
"eslint-plugin-jest": "28.10.0",
|
||||||
|
"eslint-plugin-promise": "^7.2.1",
|
||||||
|
"file-loader": "6.2.0",
|
||||||
|
"globals": "^15.14.0",
|
||||||
|
"handlebars": "4.7.8",
|
||||||
|
"handlebars-loader": "1.7.3",
|
||||||
|
"html-webpack-plugin": "^5.6.3",
|
||||||
|
"husky": "^9.1.7",
|
||||||
|
"image-webpack-loader": "8.1.0",
|
||||||
|
"is-ci-cli": "2.2.0",
|
||||||
|
"jest": "29.7.0",
|
||||||
|
"lint-staged": "^15.2.11",
|
||||||
|
"markdown-toc": "^1.2.0",
|
||||||
|
"mini-css-extract-plugin": "^2.9.2",
|
||||||
|
"mkdirp": "3.0.1",
|
||||||
|
"nopt": "^8.0.0",
|
||||||
|
"postcss": "^8.4.49",
|
||||||
|
"postcss-cli": "11.0.0",
|
||||||
|
"postcss-import": "^16.1.0",
|
||||||
|
"postcss-loader": "^8.1.1",
|
||||||
|
"postcss-preset-env": "^10.1.2",
|
||||||
|
"prettier": "^3.4.2",
|
||||||
|
"ts-jest": "^29.2.5",
|
||||||
|
"ts-loader": "9.5.1",
|
||||||
|
"ts-node": "10.9.2",
|
||||||
|
"typescript": "^5.7.2",
|
||||||
|
"typescript-eslint": "^8.18.2",
|
||||||
|
"url-loader": "4.1.1",
|
||||||
|
"webpack": "^5.97.1",
|
||||||
|
"webpack-cli": "^6.0.1",
|
||||||
|
"webpack-dev-server": "^5.2.0",
|
||||||
|
"whatwg-fetch": "3.6.20"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"lodash": ">=4.17.20",
|
||||||
|
"minimist": ">=1.2.5",
|
||||||
|
"acorn": ">=7.4.0",
|
||||||
|
"autolinker": ">=3.14.1",
|
||||||
|
"bl": ">=2.2.1",
|
||||||
|
"decompress": ">=4.2.1",
|
||||||
|
"node-forge": ">=0.10.0",
|
||||||
|
"trim-newlines": ">=3.0.1",
|
||||||
|
"async": ">=2.6.4",
|
||||||
|
"terser": ">=5.14.2",
|
||||||
|
"semver-regex": ">=4.0.5",
|
||||||
|
"http-cache-semantics": ">=4.1.1"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"bundles",
|
||||||
"dist"
|
"lib",
|
||||||
|
"lib-esm"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
postcss.config.js
Normal file
10
postcss.config.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
module.exports = {
|
||||||
|
sourceMap: false,
|
||||||
|
plugins: {
|
||||||
|
'postcss-import': {},
|
||||||
|
'postcss-preset-env': {
|
||||||
|
browsers: 'last 2 versions',
|
||||||
|
},
|
||||||
|
cssnano: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -1,285 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Diff to HTML by rtfpessoa</title>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Diff to HTML (index.html)
|
|
||||||
Author: rtfpessoa
|
|
||||||
-->
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/styles/github.min.css">
|
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.js"></script>
|
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/highlight.min.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/languages/scala.min.js"></script>
|
|
||||||
|
|
||||||
<!-- diff2html -->
|
|
||||||
<link rel="stylesheet" type="text/css" href="../dist/diff2html.css">
|
|
||||||
<script type="text/javascript" src="../dist/diff2html.js"></script>
|
|
||||||
<script type="text/javascript" src="../dist/diff2html-ui.js"></script>
|
|
||||||
<!-- -->
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var lineDiffExample =
|
|
||||||
'diff --git a/src/core/init.java b/src/core/init.java\n' +
|
|
||||||
'index e49196a..50f310c 100644\n' +
|
|
||||||
'--- a/src/core/init.java\n' +
|
|
||||||
'+++ b/src/core/init.java\n' +
|
|
||||||
'@@ -101,7 +101,7 @@\n' +
|
|
||||||
' /**\n' +
|
|
||||||
' * Setter for property filesize.\n' +
|
|
||||||
' *\n' +
|
|
||||||
" * @param filesize value of property 'filesize'.\n" +
|
|
||||||
' */\n' +
|
|
||||||
' public void setFilesize(int filesize) {\n' +
|
|
||||||
'- this.filesizeOld = filesizeOld;\n' +
|
|
||||||
'+ this.filesizeNew = filesizeNew;\n' +
|
|
||||||
' }\n' +
|
|
||||||
'diff --git a/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/coverage.init b/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/coverage.init\n' +
|
|
||||||
'index fc56817..e8e7e49 100644\n' +
|
|
||||||
'--- a/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/coverage.init\n' +
|
|
||||||
'+++ b/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/coverage.init\n' +
|
|
||||||
'@@ -19,7 +19,7 @@\n' +
|
|
||||||
' -opt "\-nostart"\n' +
|
|
||||||
' \n' +
|
|
||||||
' # skip stopenv\n' +
|
|
||||||
'--do "runbvt,stopenv,getlogs,pullcoveragedata,stopenv,getlogs,pullcoveragedata,stopenv,getlogs,pullcoveragedata,stopenv,getlogs,pullcoveragedata,stopenv,getlogs,pullcoveragedata,stopenv,getlogs,pullcoveragedata,stopenv,getlogs,pullcoveragedata,stopenv,getlogs,pullcoveragedata,stopenv,getlogs,pullcoveragedata,stopenv,getlogs,pullcoveragedata"\n' +
|
|
||||||
'+-do "runbvt,getlogs,pullcoveragedata"\n' +
|
|
||||||
' \n' +
|
|
||||||
' ##########################################\n' +
|
|
||||||
' # logs files to bring back to base\n' +
|
|
||||||
'diff --git a/src/attributes/attr.js b/src/attributes/attr.js\n' +
|
|
||||||
'index facdd41..b627fe8 100644\n' +
|
|
||||||
'--- a/src/attributes/attr.js\n' +
|
|
||||||
'+++ b/src/attributes/attr.js\n' +
|
|
||||||
'@@ -1,11 +1,10 @@\n' +
|
|
||||||
' define([\n' +
|
|
||||||
' "../core",\n' +
|
|
||||||
' "../var/rnotwhite",\n' +
|
|
||||||
'- "../var/strundefined",\n' +
|
|
||||||
' "../core/access",\n' +
|
|
||||||
' "./support",\n' +
|
|
||||||
' "../selector"\n' +
|
|
||||||
'-], function( jQuery, rnotwhite, strundefined, access, support ) {\n' +
|
|
||||||
'+], function( jQuery, rnotwhite, access, support ) {\n' +
|
|
||||||
' \n' +
|
|
||||||
' var nodeHook, boolHook,\n' +
|
|
||||||
' attrHandle = jQuery.expr.attrHandle;\n' +
|
|
||||||
'@@ -33,7 +32,7 @@ jQuery.extend({\n' +
|
|
||||||
' }\n' +
|
|
||||||
' \n' +
|
|
||||||
' // Fallback to prop when attributes are not supported\n' +
|
|
||||||
'- if ( typeof elem.getAttribute === strundefined ) {\n' +
|
|
||||||
'+ if ( !elem.getAttribute ) {\n' +
|
|
||||||
' return jQuery.prop( elem, name, value );\n' +
|
|
||||||
' }\n' +
|
|
||||||
' \n' +
|
|
||||||
'diff --git a/src/attributes/classes.js b/src/attributes/classes.js\n' +
|
|
||||||
'index c617824..c8d1393 100644\n' +
|
|
||||||
'--- a/src/attributes/classes.js\n' +
|
|
||||||
'+++ b/src/attributes/classes.js\n' +
|
|
||||||
'@@ -1,10 +1,9 @@\n' +
|
|
||||||
' define([\n' +
|
|
||||||
' "../core",\n' +
|
|
||||||
' "../var/rnotwhite",\n' +
|
|
||||||
'- "../var/strundefined",\n' +
|
|
||||||
' "../data/var/dataPriv",\n' +
|
|
||||||
' "../core/init"\n' +
|
|
||||||
'-], function( jQuery, rnotwhite, strundefined, dataPriv ) {\n' +
|
|
||||||
'+], function( jQuery, rnotwhite, dataPriv ) {\n' +
|
|
||||||
' \n' +
|
|
||||||
' var rclass = /[\\t\\r\\n\\f]/g;\n' +
|
|
||||||
' \n' +
|
|
||||||
'@@ -128,7 +127,7 @@ jQuery.fn.extend({\n' +
|
|
||||||
' }\n' +
|
|
||||||
' \n' +
|
|
||||||
' // Toggle whole class name\n' +
|
|
||||||
'- } else if ( type === strundefined || type === "boolean" ) {\n' +
|
|
||||||
'+ } else if ( value === undefined || type === "boolean" ) {\n' +
|
|
||||||
' if ( this.className ) {\n' +
|
|
||||||
' // store className if set\n' +
|
|
||||||
' dataPriv.set( this, "__className__", this.className );\n' +
|
|
||||||
'diff --git a/src/core/init.js b/src/core/init.js\n' +
|
|
||||||
'index e49196a..50f310c 100644\n' +
|
|
||||||
'--- a/src/core/init.js\n' +
|
|
||||||
'+++ b/src/core/init.js\n' +
|
|
||||||
'@@ -101,7 +101,7 @@ var rootjQuery,\n' +
|
|
||||||
' // HANDLE: $(function)\n' +
|
|
||||||
' // Shortcut for document ready\n' +
|
|
||||||
' } else if ( jQuery.isFunction( selector ) ) {\n' +
|
|
||||||
'- return typeof rootjQuery.ready !== "undefined" ?\n' +
|
|
||||||
'+ return rootjQuery.ready !== undefined ?\n' +
|
|
||||||
' rootjQuery.ready( selector ) :\n' +
|
|
||||||
' // Execute immediately if ready is not present\n' +
|
|
||||||
' selector( jQuery );\n' +
|
|
||||||
'diff --git a/src/event.js b/src/event.js\n' +
|
|
||||||
'index 7336f4d..6183f70 100644\n' +
|
|
||||||
'--- a/src/event.js\n' +
|
|
||||||
'+++ b/src/event.js\n' +
|
|
||||||
'@@ -1,6 +1,5 @@\n' +
|
|
||||||
' define([\n' +
|
|
||||||
' "./core",\n' +
|
|
||||||
'- "./var/strundefined",\n' +
|
|
||||||
' "./var/rnotwhite",\n' +
|
|
||||||
' "./var/hasOwn",\n' +
|
|
||||||
' "./var/slice",\n' +
|
|
||||||
'@@ -10,7 +9,7 @@ define([\n' +
|
|
||||||
' "./core/init",\n' +
|
|
||||||
' "./data/accepts",\n' +
|
|
||||||
' "./selector"\n' +
|
|
||||||
'-], function( jQuery, strundefined, rnotwhite, hasOwn, slice, support, dataPriv ) {\n' +
|
|
||||||
'+], function( jQuery, rnotwhite, hasOwn, slice, support, dataPriv ) {\n' +
|
|
||||||
' \n' +
|
|
||||||
' var\n' +
|
|
||||||
' rkeyEvent = /^key/,\n' +
|
|
||||||
'@@ -72,7 +71,7 @@ jQuery.event = {\n' +
|
|
||||||
' eventHandle = elemData.handle = function( e ) {\n' +
|
|
||||||
' // Discard the second event of a jQuery.event.trigger() and\n' +
|
|
||||||
' // when an event is called after a page has unloaded\n' +
|
|
||||||
'- return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?\n' +
|
|
||||||
'+ return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?\n' +
|
|
||||||
' jQuery.event.dispatch.apply( elem, arguments ) : undefined;\n' +
|
|
||||||
' };\n' +
|
|
||||||
' }\n' +
|
|
||||||
'diff --git a/src/exports/global.js b/src/exports/global.js\n' +
|
|
||||||
'index 6513287..1db4144 100644\n' +
|
|
||||||
'--- a/src/exports/global.js\n' +
|
|
||||||
'+++ b/src/exports/global.js\n' +
|
|
||||||
'@@ -1,7 +1,9 @@\n' +
|
|
||||||
' define([\n' +
|
|
||||||
'- "../core",\n' +
|
|
||||||
'- "../var/strundefined"\n' +
|
|
||||||
'-], function( jQuery, strundefined ) {\n' +
|
|
||||||
'+ "../core"\n' +
|
|
||||||
'+], function( jQuery ) {\n' +
|
|
||||||
'+\n' +
|
|
||||||
'+/* exported noGlobal */\n' +
|
|
||||||
'+/* global noGlobal: false */\n' +
|
|
||||||
' \n' +
|
|
||||||
' var\n' +
|
|
||||||
' // Map over jQuery in case of overwrite\n' +
|
|
||||||
'@@ -25,7 +27,7 @@ jQuery.noConflict = function( deep ) {\n' +
|
|
||||||
' // Expose jQuery and $ identifiers, even in AMD\n' +
|
|
||||||
' // (#7102#comment:10, https://github.com/jquery/jquery/pull/557)\n' +
|
|
||||||
' // and CommonJS for browser emulators (#13566)\n' +
|
|
||||||
'-if ( typeof noGlobal === strundefined ) {\n' +
|
|
||||||
'+if ( !noGlobal ) {\n' +
|
|
||||||
' window.jQuery = window.$ = jQuery;\n' +
|
|
||||||
' }\n' +
|
|
||||||
' \n' +
|
|
||||||
'diff --git a/src/offset.js b/src/offset.js\n' +
|
|
||||||
'index cc6ffb4..fa51f18 100644\n' +
|
|
||||||
'--- a/src/offset.js\n' +
|
|
||||||
'+++ b/src/offset.js\n' +
|
|
||||||
'@@ -1,6 +1,5 @@\n' +
|
|
||||||
' define([\n' +
|
|
||||||
' "./core",\n' +
|
|
||||||
'- "./var/strundefined",\n' +
|
|
||||||
' "./core/access",\n' +
|
|
||||||
' "./css/var/rnumnonpx",\n' +
|
|
||||||
' "./css/curCSS",\n' +
|
|
||||||
'@@ -10,7 +9,7 @@ define([\n' +
|
|
||||||
' "./core/init",\n' +
|
|
||||||
' "./css",\n' +
|
|
||||||
' "./selector" // contains\n' +
|
|
||||||
'-], function( jQuery, strundefined, access, rnumnonpx, curCSS, addGetHookIf, support ) {\n' +
|
|
||||||
'+], function( jQuery, access, rnumnonpx, curCSS, addGetHookIf, support ) {\n' +
|
|
||||||
' \n' +
|
|
||||||
' var docElem = window.document.documentElement;\n' +
|
|
||||||
' \n' +
|
|
||||||
'@@ -99,7 +98,7 @@ jQuery.fn.extend({\n' +
|
|
||||||
' \n' +
|
|
||||||
' // Support: BlackBerry 5, iOS 3 (original iPhone)\n' +
|
|
||||||
' // If we dont have gBCR, just use 0,0 rather than error\n' +
|
|
||||||
'- if ( typeof elem.getBoundingClientRect !== strundefined ) {\n' +
|
|
||||||
'+ if ( elem.getBoundingClientRect !== undefined ) {\n' +
|
|
||||||
' box = elem.getBoundingClientRect();\n' +
|
|
||||||
' }\n' +
|
|
||||||
' win = getWindow( doc );\n' +
|
|
||||||
'diff --git a/src/var/strundefined.js b/src/var/strundefined.js\n' +
|
|
||||||
'deleted file mode 100644\n' +
|
|
||||||
'index 04e16b0..0000000\n' +
|
|
||||||
'--- a/src/var/strundefined.js\n' +
|
|
||||||
'+++ /dev/null\n' +
|
|
||||||
'@@ -1,3 +0,0 @@\n' +
|
|
||||||
'-define(function() {\n' +
|
|
||||||
'- return typeof undefined;\n' +
|
|
||||||
'-});\n' +
|
|
||||||
'diff --git a/components/textdiff/textdiff.html b/components/textdiff/textdiff.html\n' +
|
|
||||||
'index a3484bf..82209af 100644\n' +
|
|
||||||
'--- a/components/textdiff/textdiff.html\n' +
|
|
||||||
'+++ b/components/textdiff/textdiff.html\n' +
|
|
||||||
'@@ -1,6 +1,8 @@\n' +
|
|
||||||
' <!-- ko if: isShowingDiffs -->\n' +
|
|
||||||
' <div>\n' +
|
|
||||||
'- <div data-bind="event: { load: setDom($element) }"></div>\n' +
|
|
||||||
'+ <!-- ko if: isParsed -->\n' +
|
|
||||||
'+ <div data-bind="template: {nodes: ko.utils.parseHtmlFragment(htmlSrc())}"></div>\n' +
|
|
||||||
'+ <!-- /ko -->\n' +
|
|
||||||
' <div class="btn-load-more" data-bind="visible: loadMoreCount() > 0">\n' +
|
|
||||||
' </div>\n' +
|
|
||||||
'diff --git a/test.js b/test.js\n' +
|
|
||||||
'new file mode 100644\n' +
|
|
||||||
'index 0000000..e1e22ec\n' +
|
|
||||||
'--- /dev/null\n' +
|
|
||||||
'+++ b/test.js\n' +
|
|
||||||
'@@ -0,0 +1,6 @@\n' +
|
|
||||||
"+var parser = require('./source/git-parser');\n" +
|
|
||||||
'+\n' +
|
|
||||||
"+var text = 'diff --git a/components/app/app.html b/components/app/app.html\\nindex ecb7a95..027bd9b 100644\\n--- a/components/app/app.html\\n+++ b/components/app/app.html\\n@@ -52,0 +53,3 @@\\n+\\n+\\n+\\n@@ -56,0 +60,3 @@\\n+\\n+\\n+\\n'\n" +
|
|
||||||
'+var patchLineList = [ false, false, false, false ];\n' +
|
|
||||||
'+\n' +
|
|
||||||
'+console.log(parser.parsePatchDiffResult(text, patchLineList));\n' +
|
|
||||||
"diff --git a/a.xml b/b.xml\n" +
|
|
||||||
"index e54317e..82a9a56 100644\n" +
|
|
||||||
"--- a/a.xml\n" +
|
|
||||||
"+++ b/b.xml\n" +
|
|
||||||
"@@ -242,4 +242,6 @@ need to create a new job for native server java api and move these tests to a ne\n" +
|
|
||||||
" </packages>\n" +
|
|
||||||
" </test>\n" +
|
|
||||||
" -->\n" +
|
|
||||||
"+\n" +
|
|
||||||
"+\n" +
|
|
||||||
"--- a/sample.js\n" +
|
|
||||||
"+++ b/sample.js\n" +
|
|
||||||
"@@ -1 +1,2 @@\n" +
|
|
||||||
"-test\n" +
|
|
||||||
"+test1r\n" +
|
|
||||||
"+test2r\n";
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
var diff2htmlUi = new Diff2HtmlUI({diff: lineDiffExample});
|
|
||||||
|
|
||||||
diff2htmlUi.draw('#line-by-line', {inputFormat: 'json', showFiles: true, matching: 'lines'});
|
|
||||||
diff2htmlUi.fileListCloseable('#line-by-line', false);
|
|
||||||
diff2htmlUi.highlightCode('#line-by-line');
|
|
||||||
|
|
||||||
diff2htmlUi.draw('#side-by-side', {
|
|
||||||
inputFormat: 'json',
|
|
||||||
showFiles: true,
|
|
||||||
matching: 'lines',
|
|
||||||
outputFormat: 'side-by-side'
|
|
||||||
});
|
|
||||||
diff2htmlUi.fileListCloseable('#side-by-side', false);
|
|
||||||
diff2htmlUi.highlightCode('#side-by-side');
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body style="text-align: center; font-family: 'Source Sans Pro',sans-serif;">
|
|
||||||
<h1>Diff to HTML by <a href="https://github.com/rtfpessoa">rtfpessoa</a></h1>
|
|
||||||
|
|
||||||
<h2>Line by Line</h2>
|
|
||||||
|
|
||||||
<div id="line-by-line" style="margin: 0 auto; max-width: 900px;">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Side by Side</h2>
|
|
||||||
|
|
||||||
<div id="side-by-side" style="margin: 0 auto;">
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
207
scripts/hulk.js
207
scripts/hulk.js
|
|
@ -1,207 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright 2011 Twitter, Inc.
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// dependencies
|
|
||||||
var hogan = require('hogan.js');
|
|
||||||
var path = require('path');
|
|
||||||
var nopt = require('nopt');
|
|
||||||
var mkderp = require('mkdirp');
|
|
||||||
var fs = require('fs');
|
|
||||||
|
|
||||||
|
|
||||||
// locals
|
|
||||||
var specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
|
|
||||||
var specialsRegExp = new RegExp('(\\' + specials.join('|\\') + ')', 'g');
|
|
||||||
var options = {
|
|
||||||
'namespace': String,
|
|
||||||
'outputdir': path,
|
|
||||||
'variable': String,
|
|
||||||
'wrapper': String,
|
|
||||||
'version': true,
|
|
||||||
'help': true
|
|
||||||
};
|
|
||||||
var shortHand = {
|
|
||||||
'n': ['--namespace'],
|
|
||||||
'o': ['--outputdir'],
|
|
||||||
'vn': ['--variable'],
|
|
||||||
'w': ['--wrapper'],
|
|
||||||
'h': ['--help'],
|
|
||||||
'v': ['--version']
|
|
||||||
};
|
|
||||||
var templates;
|
|
||||||
|
|
||||||
|
|
||||||
// options
|
|
||||||
options = nopt(options, shortHand);
|
|
||||||
|
|
||||||
|
|
||||||
// escape special regexp characters
|
|
||||||
function esc(text) {
|
|
||||||
return text.replace(specialsRegExp, '\\$1');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// cyan function for rob
|
|
||||||
function cyan(text) {
|
|
||||||
return '\033[36m' + text + '\033[39m';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// check for dirs and correct ext (<3 for windows)
|
|
||||||
function extractFiles(args) {
|
|
||||||
var usage = '\n' +
|
|
||||||
cyan('USAGE:') + ' hulk [--wrapper wrapper] [--outputdir outputdir] ' +
|
|
||||||
'[--namespace namespace] [--variable variable] FILES\n\n' +
|
|
||||||
cyan('OPTIONS:') + ' [-w, --wrapper] :: wraps the template (i.e. amd)\n' +
|
|
||||||
' [-o, --outputdir] :: outputs the templates as individual files to a directory\n\n' +
|
|
||||||
' [-n, --namespace] :: prepend string to template names\n\n' +
|
|
||||||
' [-vn, --variable] :: variable name for non-amd wrapper\n\n' +
|
|
||||||
cyan('EXAMPLE:') + ' hulk --wrapper amd ./templates/*.mustache\n\n' +
|
|
||||||
cyan('NOTE:') + ' hulk supports the "*" wildcard and allows you to target specific extensions too\n';
|
|
||||||
var files = [];
|
|
||||||
|
|
||||||
if (options.version) {
|
|
||||||
console.log(require('../package.json').version);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!args.length || options.help) {
|
|
||||||
console.log(usage);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
args.forEach(function(arg) {
|
|
||||||
|
|
||||||
if (/\*/.test(arg)) {
|
|
||||||
arg = arg.split('*');
|
|
||||||
return files = files.concat(
|
|
||||||
fs.readdirSync(arg[0] || '.')
|
|
||||||
.map(function(f) {
|
|
||||||
var file = path.join(arg[0], f);
|
|
||||||
return new RegExp(esc(arg[1]) + '$').test(f) && fs.statSync(file).isFile() && file;
|
|
||||||
})
|
|
||||||
.filter(function(f) {
|
|
||||||
return f;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fs.statSync(arg).isFile()) files.push(arg);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// remove utf-8 byte order mark, http://en.wikipedia.org/wiki/Byte_order_mark
|
|
||||||
function removeByteOrderMark(text) {
|
|
||||||
if (text.charCodeAt(0) === 0xfeff) {
|
|
||||||
return text.substring(1);
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// wrap templates
|
|
||||||
function wrap(file, name, openedFile) {
|
|
||||||
switch (options.wrapper) {
|
|
||||||
case "amd":
|
|
||||||
return 'define(' + (!options.outputdir ? '"' + path.join(path.dirname(file), name) + '", ' : '') +
|
|
||||||
'[ "hogan.js" ], function(Hogan){ return new Hogan.Template(' +
|
|
||||||
hogan.compile(openedFile, {asString: 1}) +
|
|
||||||
');});';
|
|
||||||
case "node":
|
|
||||||
var globalObj = 'global.' + (options.variable || 'templates') + '["' + name + '"]';
|
|
||||||
var globalStmt = globalObj + ' = new Hogan.Template(' + hogan.compile(openedFile, {asString: 1}) + ');';
|
|
||||||
var nodeOutput = globalStmt;
|
|
||||||
|
|
||||||
// if we have a template per file the export will expose the template directly
|
|
||||||
if (options.outputdir) {
|
|
||||||
nodeOutput = nodeOutput + '\n' + 'module.exports = ' + globalObj + ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeOutput;
|
|
||||||
default:
|
|
||||||
return (options.variable || 'templates') +
|
|
||||||
'["' + name + '"] = new Hogan.Template(' +
|
|
||||||
hogan.compile(openedFile, {asString: 1}) +
|
|
||||||
');';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function prepareOutput(content) {
|
|
||||||
var variableName = options.variable || 'templates';
|
|
||||||
switch (options.wrapper) {
|
|
||||||
case "amd":
|
|
||||||
return content;
|
|
||||||
case "node":
|
|
||||||
var nodeExport = '';
|
|
||||||
|
|
||||||
// if we have aggregated templates the export will expose the template map
|
|
||||||
if (!options.outputdir) {
|
|
||||||
nodeExport = 'module.exports = global.' + variableName + ';\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '(function() {\n' +
|
|
||||||
'if (!!!global.' + variableName + ') global.' + variableName + ' = {};\n' +
|
|
||||||
'var Hogan = require("hogan.js");' +
|
|
||||||
content + '\n' +
|
|
||||||
nodeExport +
|
|
||||||
'})();';
|
|
||||||
default:
|
|
||||||
return 'if (!!!' + variableName + ') var ' + variableName + ' = {};\n' + content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// write the directory
|
|
||||||
if (options.outputdir) {
|
|
||||||
mkderp.sync(options.outputdir);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Prepend namespace to template name
|
|
||||||
function namespace(name) {
|
|
||||||
return (options.namespace || '') + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// write a template foreach file that matches template extension
|
|
||||||
templates = extractFiles(options.argv.remain)
|
|
||||||
.map(function(file) {
|
|
||||||
var openedFile = fs.readFileSync(file, 'utf-8');
|
|
||||||
var name;
|
|
||||||
if (!openedFile) return;
|
|
||||||
name = namespace(path.basename(file).replace(/\..*$/, ''));
|
|
||||||
openedFile = removeByteOrderMark(openedFile.trim());
|
|
||||||
openedFile = wrap(file, name, openedFile);
|
|
||||||
if (!options.outputdir) return openedFile;
|
|
||||||
fs.writeFileSync(path.join(options.outputdir, name + '.js')
|
|
||||||
, prepareOutput(openedFile));
|
|
||||||
})
|
|
||||||
.filter(function(t) {
|
|
||||||
return t;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// output templates
|
|
||||||
if (!templates.length || options.outputdir) process.exit(0);
|
|
||||||
|
|
||||||
console.log(prepareOutput(templates.join('\n')));
|
|
||||||
188
scripts/hulk.ts
Executable file
188
scripts/hulk.ts
Executable file
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Twitter, Inc.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
import * as hogan from 'hogan.js';
|
||||||
|
import nopt from 'nopt';
|
||||||
|
import * as mkderp from 'mkdirp';
|
||||||
|
|
||||||
|
const options = nopt(
|
||||||
|
{
|
||||||
|
namespace: String,
|
||||||
|
outputdir: path,
|
||||||
|
variable: String,
|
||||||
|
wrapper: String,
|
||||||
|
version: true,
|
||||||
|
help: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: ['--namespace'],
|
||||||
|
o: ['--outputdir'],
|
||||||
|
vn: ['--variable'],
|
||||||
|
w: ['--wrapper'],
|
||||||
|
h: ['--help'],
|
||||||
|
v: ['--version'],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
|
||||||
|
const specialsRegExp = new RegExp('(\\' + specials.join('|\\') + ')', 'g');
|
||||||
|
function escape(text: string): string {
|
||||||
|
return text.replace(specialsRegExp, '\\$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
function cyan(text: string): string {
|
||||||
|
return '\x1B[36m' + text + '\x1B[39m';
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractFiles(files: string[]): string[] {
|
||||||
|
const usage = `${cyan(
|
||||||
|
'USAGE:',
|
||||||
|
)} hulk [--wrapper wrapper] [--outputdir outputdir] [--namespace namespace] [--variable variable] FILES
|
||||||
|
|
||||||
|
${cyan('OPTIONS:')} [-w, --wrapper] :: wraps the template (i.e. amd)
|
||||||
|
[-o, --outputdir] :: outputs the templates as individual files to a directory
|
||||||
|
|
||||||
|
[-n, --namespace] :: prepend string to template names
|
||||||
|
|
||||||
|
[-vn, --variable] :: variable name for non-amd wrapper
|
||||||
|
|
||||||
|
${cyan('EXAMPLE:')} hulk --wrapper amd ./templates/*.mustache
|
||||||
|
|
||||||
|
${cyan('NOTE:')} hulk supports the "*" wildcard and allows you to target specific extensions too
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (options.version) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
|
console.log(require('../package.json').version);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!files.length || options.help) {
|
||||||
|
console.log(usage);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files
|
||||||
|
.map((fileGlob: string) => {
|
||||||
|
if (/\*/.test(fileGlob)) {
|
||||||
|
const [fileGlobPrefix, fileGlobSuffix] = fileGlob.split('*');
|
||||||
|
|
||||||
|
return fs.readdirSync(fileGlobPrefix || '.').reduce<string[]>((previousFiles, relativeFilePath) => {
|
||||||
|
const file = path.join(fileGlobPrefix, relativeFilePath);
|
||||||
|
if (new RegExp(`${escape(fileGlobSuffix)}$`).test(relativeFilePath) && fs.statSync(file).isFile()) {
|
||||||
|
previousFiles.push(file);
|
||||||
|
}
|
||||||
|
return previousFiles;
|
||||||
|
}, []);
|
||||||
|
} else if (fs.statSync(fileGlob).isFile()) {
|
||||||
|
return [fileGlob];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.reduce((previous, current) => previous.concat(current), []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove utf-8 byte order mark, http://en.wikipedia.org/wiki/Byte_order_mark
|
||||||
|
function removeByteOrderMark(text: string): string {
|
||||||
|
if (text.charCodeAt(0) === 0xfeff) {
|
||||||
|
return text.substring(1);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap templates
|
||||||
|
function wrap(file: string, name: string, openedFile: string): string {
|
||||||
|
const hoganTemplateString = `new Hogan.Template(${hogan.compile(openedFile, { asString: true })})`;
|
||||||
|
|
||||||
|
const objectName = options.variable || 'templates';
|
||||||
|
const objectAccessor = `${objectName}["${name}"]`;
|
||||||
|
const objectStmt = `${objectAccessor} = ${hoganTemplateString};`;
|
||||||
|
|
||||||
|
switch (options.wrapper) {
|
||||||
|
case 'amd':
|
||||||
|
return `define(${
|
||||||
|
!options.outputdir ? `"${path.join(path.dirname(file), name)}", ` : ''
|
||||||
|
}["hogan.js"], function(Hogan) { return ${hoganTemplateString}; });`;
|
||||||
|
|
||||||
|
case 'node':
|
||||||
|
// If we have a template per file the export will expose the template directly
|
||||||
|
return options.outputdir ? `global.${objectStmt};\nmodule.exports = ${objectAccessor};` : `global.${objectStmt}`;
|
||||||
|
|
||||||
|
case 'ts':
|
||||||
|
return `// @ts-ignore\n${objectStmt}`;
|
||||||
|
default:
|
||||||
|
return objectStmt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareOutput(content: string): string {
|
||||||
|
const variableName = options.variable || 'templates';
|
||||||
|
switch (options.wrapper) {
|
||||||
|
case 'amd':
|
||||||
|
return content;
|
||||||
|
case 'node':
|
||||||
|
return `(function() {
|
||||||
|
if (!!!global.${variableName}) global.${variableName} = {};
|
||||||
|
var Hogan = require("hogan.js");
|
||||||
|
${content}
|
||||||
|
${!options.outputdir ? `module.exports = global.${variableName};\n` : ''})();`;
|
||||||
|
|
||||||
|
case 'ts':
|
||||||
|
return `import * as Hogan from "hogan.js";
|
||||||
|
type CompiledTemplates = { [name: string]: Hogan.Template };
|
||||||
|
export const ${variableName}: CompiledTemplates = {};
|
||||||
|
${content}`;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 'if (!!!' + variableName + ') var ' + variableName + ' = {};\n' + content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the directory
|
||||||
|
if (options.outputdir) {
|
||||||
|
mkderp.sync(options.outputdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend namespace to template name
|
||||||
|
function namespace(name: string): string {
|
||||||
|
return (options.namespace || '') + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a template foreach file that matches template extension
|
||||||
|
const templates = extractFiles(options.argv.remain)
|
||||||
|
.map(file => {
|
||||||
|
const timmedFileContents = fs.readFileSync(file, 'utf8').trim();
|
||||||
|
|
||||||
|
if (!timmedFileContents) return;
|
||||||
|
|
||||||
|
const name = namespace(path.basename(file).replace(/\..*$/, ''));
|
||||||
|
const cleanFileContents = wrap(file, name, removeByteOrderMark(timmedFileContents));
|
||||||
|
|
||||||
|
if (!options.outputdir) return cleanFileContents;
|
||||||
|
|
||||||
|
const fileExtension = options.wrapper === 'ts' ? 'ts' : 'js';
|
||||||
|
|
||||||
|
return fs.writeFileSync(path.join(options.outputdir, `${name}.${fileExtension}`), prepareOutput(cleanFileContents));
|
||||||
|
})
|
||||||
|
.filter(templateContents => typeof templateContents !== 'undefined');
|
||||||
|
|
||||||
|
// Output templates
|
||||||
|
if (!templates.length || options.outputdir) process.exit(0);
|
||||||
|
|
||||||
|
console.log(prepareOutput(templates.join('\n')));
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
#
|
|
||||||
# diff2html release script
|
|
||||||
# by rtfpessoa
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
INPUT_DIR=src
|
|
||||||
INTPUT_TEMPLATES_DIR=${INPUT_DIR}/templates
|
|
||||||
INPUT_UI_DIR=${INPUT_DIR}/ui
|
|
||||||
INPUT_JS_FILE=${INPUT_DIR}/diff2html.js
|
|
||||||
INPUT_JS_UI_FILE=${INPUT_UI_DIR}/js/diff2html-ui.js
|
|
||||||
INPUT_CSS_FILE=${INPUT_UI_DIR}/css/diff2html.css
|
|
||||||
|
|
||||||
GENERATED_TEMPLATES_FILE=${INTPUT_TEMPLATES_DIR}/diff2html-templates.js
|
|
||||||
|
|
||||||
OUTPUT_DIR=dist
|
|
||||||
OUTPUT_JS_FILE=${OUTPUT_DIR}/diff2html.js
|
|
||||||
OUTPUT_MIN_JS_FILE=${OUTPUT_DIR}/diff2html.min.js
|
|
||||||
OUTPUT_JS_UI_FILE=${OUTPUT_DIR}/diff2html-ui.js
|
|
||||||
OUTPUT_MIN_JS_UI_FILE=${OUTPUT_DIR}/diff2html-ui.min.js
|
|
||||||
OUTPUT_CSS_FILE=${OUTPUT_DIR}/diff2html.css
|
|
||||||
OUTPUT_MIN_CSS_FILE=${OUTPUT_DIR}/diff2html.min.css
|
|
||||||
|
|
||||||
echo "Creating diff2html release ..."
|
|
||||||
|
|
||||||
echo "Cleaning previous versions ..."
|
|
||||||
rm -rf ${OUTPUT_DIR}
|
|
||||||
mkdir -p ${OUTPUT_DIR}
|
|
||||||
|
|
||||||
echo "Minifying ${OUTPUT_CSS_FILE} to ${OUTPUT_MIN_CSS_FILE}"
|
|
||||||
cp -f ${INPUT_CSS_FILE} ${OUTPUT_CSS_FILE}
|
|
||||||
cleancss --advanced --compatibility=ie8 -o ${OUTPUT_MIN_CSS_FILE} ${OUTPUT_CSS_FILE}
|
|
||||||
|
|
||||||
echo "Pre-compile hogan.js templates"
|
|
||||||
npm run templates
|
|
||||||
|
|
||||||
echo "Generating js aggregation file in ${OUTPUT_JS_FILE}"
|
|
||||||
browserify -e ${INPUT_JS_FILE} -o ${OUTPUT_JS_FILE}
|
|
||||||
|
|
||||||
echo "Minifying ${OUTPUT_JS_FILE} to ${OUTPUT_MIN_JS_FILE}"
|
|
||||||
uglifyjs ${OUTPUT_JS_FILE} -c -o ${OUTPUT_MIN_JS_FILE}
|
|
||||||
|
|
||||||
echo "Generating js ui aggregation file in ${OUTPUT_JS_UI_FILE}"
|
|
||||||
browserify -e ${INPUT_JS_UI_FILE} -o ${OUTPUT_JS_UI_FILE}
|
|
||||||
|
|
||||||
echo "Minifying ${OUTPUT_JS_UI_FILE} to ${OUTPUT_MIN_JS_UI_FILE}"
|
|
||||||
uglifyjs ${OUTPUT_JS_UI_FILE} -c -o ${OUTPUT_MIN_JS_UI_FILE}
|
|
||||||
|
|
||||||
echo "diff2html release created successfully!"
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
#
|
|
||||||
# diff2html update bower version
|
|
||||||
# by rtfpessoa
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "diff2html updating bower version..."
|
|
||||||
|
|
||||||
RELEASE_VERSION=$(cat package.json | grep "version" | head -1 | gsed -e 's/ "version": "\(.*\)",/\1/')
|
|
||||||
|
|
||||||
gsed -i 's/.*"version".*/ "version": "'${RELEASE_VERSION}'",/' bower.json
|
|
||||||
|
|
||||||
echo "diff2html updated bower version successfully!"
|
|
||||||
2518
src/__tests__/diff-parser-tests.ts
Normal file
2518
src/__tests__/diff-parser-tests.ts
Normal file
File diff suppressed because it is too large
Load diff
1546
src/__tests__/diff2html-tests.ts
Normal file
1546
src/__tests__/diff2html-tests.ts
Normal file
File diff suppressed because one or more lines are too long
26
src/__tests__/diffs/bad-escaping.diff
Normal file
26
src/__tests__/diffs/bad-escaping.diff
Normal file
File diff suppressed because one or more lines are too long
272
src/__tests__/file-list-renderer-tests.ts
Normal file
272
src/__tests__/file-list-renderer-tests.ts
Normal file
|
|
@ -0,0 +1,272 @@
|
||||||
|
import { FileListRenderer } from '../file-list-renderer';
|
||||||
|
import HoganJsUtils from '../hoganjs-utils';
|
||||||
|
import { ColorSchemeType } from '../types';
|
||||||
|
|
||||||
|
describe('FileListRenderer', () => {
|
||||||
|
describe('render', () => {
|
||||||
|
it('should expose old and new files to templates', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({
|
||||||
|
rawTemplates: {
|
||||||
|
'file-summary-wrapper': '{{{files}}}',
|
||||||
|
'file-summary-line': '{{oldName}}, {{newName}}, {{fileName}}',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const fileListRenderer = new FileListRenderer(hoganUtils);
|
||||||
|
const files = [
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name1.js',
|
||||||
|
newName: 'my/file/name2.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 0,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'dev/null',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
isNew: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 0,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'dev/null',
|
||||||
|
isDeleted: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const fileHtml = fileListRenderer.render(files);
|
||||||
|
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"my/file/name.js, my/file/name.js, my/file/name.js
|
||||||
|
my/file/name1.js, my/file/name2.js, my/file/{name1.js → name2.js}
|
||||||
|
dev/null, my/file/name.js, my/file/name.js
|
||||||
|
my/file/name.js, dev/null, my/file/name.js"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for all kinds of files', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const fileListRenderer = new FileListRenderer(hoganUtils);
|
||||||
|
|
||||||
|
const files = [
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name1.js',
|
||||||
|
newName: 'my/file/name2.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 0,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'dev/null',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
isNew: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 0,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'dev/null',
|
||||||
|
isDeleted: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const fileHtml = fileListRenderer.render(files);
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<div class="d2h-file-list-wrapper d2h-light-color-scheme">
|
||||||
|
<div class="d2h-file-list-header">
|
||||||
|
<span class="d2h-file-list-title">Files changed (4)</span>
|
||||||
|
<a class="d2h-file-switch d2h-hide">hide</a>
|
||||||
|
<a class="d2h-file-switch d2h-show">show</a>
|
||||||
|
</div>
|
||||||
|
<ol class="d2h-file-list">
|
||||||
|
<li class="d2h-file-list-line">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-changed" height="16" title="modified" version="1.1"
|
||||||
|
viewBox="0 0 14 16" width="14">
|
||||||
|
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z"></path>
|
||||||
|
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
|
||||||
|
<span class="d2h-file-stats">
|
||||||
|
<span class="d2h-lines-added">+12</span>
|
||||||
|
<span class="d2h-lines-deleted">-41</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="d2h-file-list-line">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-moved" height="16" title="renamed" version="1.1"
|
||||||
|
viewBox="0 0 14 16" width="14">
|
||||||
|
<path d="M6 9H3V7h3V4l5 4-5 4V9z m8-7v12c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h12c0.55 0 1 0.45 1 1z m-1 0H1v12h12V2z"></path>
|
||||||
|
</svg> <a href="#d2h-662683" class="d2h-file-name">my/file/{name1.js → name2.js}</a>
|
||||||
|
<span class="d2h-file-stats">
|
||||||
|
<span class="d2h-lines-added">+12</span>
|
||||||
|
<span class="d2h-lines-deleted">-41</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="d2h-file-list-line">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-added" height="16" title="added" version="1.1" viewBox="0 0 14 16"
|
||||||
|
width="14">
|
||||||
|
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM6 9H3V7h3V4h2v3h3v2H8v3H6V9z"></path>
|
||||||
|
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
|
||||||
|
<span class="d2h-file-stats">
|
||||||
|
<span class="d2h-lines-added">+12</span>
|
||||||
|
<span class="d2h-lines-deleted">-0</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="d2h-file-list-line">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-deleted" height="16" title="removed" version="1.1"
|
||||||
|
viewBox="0 0 14 16" width="14">
|
||||||
|
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM11 9H3V7h8v2z"></path>
|
||||||
|
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
|
||||||
|
<span class="d2h-file-stats">
|
||||||
|
<span class="d2h-lines-added">+0</span>
|
||||||
|
<span class="d2h-lines-deleted">-41</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with dark colorScheme', () => {
|
||||||
|
it('should include dark colorScheme', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const fileListRenderer = new FileListRenderer(hoganUtils, {
|
||||||
|
colorScheme: ColorSchemeType.DARK,
|
||||||
|
});
|
||||||
|
|
||||||
|
const files = [
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const fileHtml = fileListRenderer.render(files);
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<div class="d2h-file-list-wrapper d2h-dark-color-scheme">
|
||||||
|
<div class="d2h-file-list-header">
|
||||||
|
<span class="d2h-file-list-title">Files changed (1)</span>
|
||||||
|
<a class="d2h-file-switch d2h-hide">hide</a>
|
||||||
|
<a class="d2h-file-switch d2h-show">show</a>
|
||||||
|
</div>
|
||||||
|
<ol class="d2h-file-list">
|
||||||
|
<li class="d2h-file-list-line">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-changed" height="16" title="modified" version="1.1"
|
||||||
|
viewBox="0 0 14 16" width="14">
|
||||||
|
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z"></path>
|
||||||
|
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
|
||||||
|
<span class="d2h-file-stats">
|
||||||
|
<span class="d2h-lines-added">+12</span>
|
||||||
|
<span class="d2h-lines-deleted">-41</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with auto colorScheme', () => {
|
||||||
|
it('should include auto colorScheme', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const fileListRenderer = new FileListRenderer(hoganUtils, {
|
||||||
|
colorScheme: ColorSchemeType.AUTO,
|
||||||
|
});
|
||||||
|
|
||||||
|
const files = [
|
||||||
|
{
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const fileHtml = fileListRenderer.render(files);
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<div class="d2h-file-list-wrapper d2h-auto-color-scheme">
|
||||||
|
<div class="d2h-file-list-header">
|
||||||
|
<span class="d2h-file-list-title">Files changed (1)</span>
|
||||||
|
<a class="d2h-file-switch d2h-hide">hide</a>
|
||||||
|
<a class="d2h-file-switch d2h-show">show</a>
|
||||||
|
</div>
|
||||||
|
<ol class="d2h-file-list">
|
||||||
|
<li class="d2h-file-list-line">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-changed" height="16" title="modified" version="1.1"
|
||||||
|
viewBox="0 0 14 16" width="14">
|
||||||
|
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z"></path>
|
||||||
|
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
|
||||||
|
<span class="d2h-file-stats">
|
||||||
|
<span class="d2h-lines-added">+12</span>
|
||||||
|
<span class="d2h-lines-deleted">-41</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
56
src/__tests__/hogan-cache-tests.ts
Normal file
56
src/__tests__/hogan-cache-tests.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import HoganJsUtils from '../hoganjs-utils';
|
||||||
|
import { CSSLineClass } from '../render-utils';
|
||||||
|
|
||||||
|
describe('HoganJsUtils', () => {
|
||||||
|
describe('render', () => {
|
||||||
|
it('should render view', () => {
|
||||||
|
const hoganJsUtils = new HoganJsUtils({});
|
||||||
|
const result = hoganJsUtils.render('generic', 'empty-diff', {
|
||||||
|
contentClass: 'd2h-code-line',
|
||||||
|
CSSLineClass: CSSLineClass,
|
||||||
|
});
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
"<tr>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
File without changes
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw exception if template is missing', () => {
|
||||||
|
const hoganJsUtils = new HoganJsUtils({});
|
||||||
|
expect(() => hoganJsUtils.render('generic', 'missing-template', {})).toThrow(Error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow templates to be overridden with compiled templates', () => {
|
||||||
|
const emptyDiffTemplate = HoganJsUtils.compile('<p>{{myName}}</p>');
|
||||||
|
const hoganJsUtils = new HoganJsUtils({ compiledTemplates: { 'generic-empty-diff': emptyDiffTemplate } });
|
||||||
|
|
||||||
|
const result = hoganJsUtils.render('generic', 'empty-diff', { myName: 'Rodrigo Fernandes' });
|
||||||
|
expect(result).toMatchInlineSnapshot(`"<p>Rodrigo Fernandes</p>"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow templates to be overridden with uncompiled templates', () => {
|
||||||
|
const emptyDiffTemplate = '<p>{{myName}}</p>';
|
||||||
|
const hoganJsUtils = new HoganJsUtils({ rawTemplates: { 'generic-empty-diff': emptyDiffTemplate } });
|
||||||
|
|
||||||
|
const result = hoganJsUtils.render('generic', 'empty-diff', { myName: 'Rodrigo Fernandes' });
|
||||||
|
expect(result).toMatchInlineSnapshot(`"<p>Rodrigo Fernandes</p>"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow templates to be overridden giving priority to raw templates', () => {
|
||||||
|
const emptyDiffTemplate = HoganJsUtils.compile('<p>Not used!</p>');
|
||||||
|
const emptyDiffTemplateUncompiled = '<p>{{myName}}</p>';
|
||||||
|
const hoganJsUtils = new HoganJsUtils({
|
||||||
|
compiledTemplates: { 'generic-empty-diff': emptyDiffTemplate },
|
||||||
|
rawTemplates: { 'generic-empty-diff': emptyDiffTemplateUncompiled },
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = hoganJsUtils.render('generic', 'empty-diff', { myName: 'Rodrigo Fernandes' });
|
||||||
|
expect(result).toMatchInlineSnapshot(`"<p>Rodrigo Fernandes</p>"`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
725
src/__tests__/line-by-line-tests.ts
Normal file
725
src/__tests__/line-by-line-tests.ts
Normal file
|
|
@ -0,0 +1,725 @@
|
||||||
|
import LineByLineRenderer from '../line-by-line-renderer';
|
||||||
|
import HoganJsUtils from '../hoganjs-utils';
|
||||||
|
import { LineType, DiffFile, LineMatchingType } from '../types';
|
||||||
|
import { CSSLineClass } from '../render-utils';
|
||||||
|
|
||||||
|
describe('LineByLineRenderer', () => {
|
||||||
|
describe('_generateEmptyDiff', () => {
|
||||||
|
it('should return an empty diff', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
const fileHtml = lineByLineRenderer.generateEmptyDiff();
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<tr>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
File without changes
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('makeLineHtml', () => {
|
||||||
|
it('should work for insertions', () => {
|
||||||
|
const file = {
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
|
||||||
|
type: CSSLineClass.INSERTS,
|
||||||
|
prefix: '+',
|
||||||
|
content: 'test',
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 30,
|
||||||
|
});
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-ins">
|
||||||
|
<div class="line-num1"></div>
|
||||||
|
<div class="line-num2">30</div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn">test</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for deletions', () => {
|
||||||
|
const file = {
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
|
||||||
|
type: CSSLineClass.DELETES,
|
||||||
|
prefix: '-',
|
||||||
|
content: 'test',
|
||||||
|
oldNumber: 30,
|
||||||
|
newNumber: undefined,
|
||||||
|
});
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-del">
|
||||||
|
<div class="line-num1">30</div>
|
||||||
|
<div class="line-num2"></div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-del">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">-</span>
|
||||||
|
<span class="d2h-code-line-ctn">test</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert indents into non breakin spaces (2 white spaces)', () => {
|
||||||
|
const file = {
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
|
||||||
|
type: CSSLineClass.INSERTS,
|
||||||
|
prefix: '+',
|
||||||
|
content: ' test',
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 30,
|
||||||
|
});
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-ins">
|
||||||
|
<div class="line-num1"></div>
|
||||||
|
<div class="line-num2">30</div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn"> test</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert indents into non breakin spaces (4 white spaces)', () => {
|
||||||
|
const file = {
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
|
||||||
|
type: CSSLineClass.INSERTS,
|
||||||
|
prefix: '+',
|
||||||
|
content: ' test',
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 30,
|
||||||
|
});
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-ins">
|
||||||
|
<div class="line-num1"></div>
|
||||||
|
<div class="line-num2">30</div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn"> test</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should preserve tabs', () => {
|
||||||
|
const file = {
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
|
||||||
|
type: CSSLineClass.INSERTS,
|
||||||
|
prefix: '+',
|
||||||
|
content: '\ttest',
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 30,
|
||||||
|
});
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-ins">
|
||||||
|
<div class="line-num1"></div>
|
||||||
|
<div class="line-num2">30</div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn"> test</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('makeFileDiffHtml', () => {
|
||||||
|
it('should work for simple file', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
|
||||||
|
const file = {
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
const diffs = '<span>Random Html</span>';
|
||||||
|
|
||||||
|
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
|
||||||
|
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<div id="d2h-781444" class="d2h-file-wrapper" data-lang="js">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">my/file/name.js</span>
|
||||||
|
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<span>Random Html</span>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
it('should work for simple added file', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
|
||||||
|
const file = {
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 0,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'dev/null',
|
||||||
|
newName: 'my/file/name.js',
|
||||||
|
isNew: true,
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
const diffs = '<span>Random Html</span>';
|
||||||
|
|
||||||
|
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
|
||||||
|
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<div id="d2h-781444" class="d2h-file-wrapper" data-lang="js">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">my/file/name.js</span>
|
||||||
|
<span class="d2h-tag d2h-added d2h-added-tag">ADDED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<span>Random Html</span>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
it('should work for simple deleted file', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
|
||||||
|
const file = {
|
||||||
|
addedLines: 0,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name.js',
|
||||||
|
newName: 'dev/null',
|
||||||
|
isDeleted: true,
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
const diffs = '<span>Random Html</span>';
|
||||||
|
|
||||||
|
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
|
||||||
|
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<div id="d2h-781444" class="d2h-file-wrapper" data-lang="js">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">my/file/name.js</span>
|
||||||
|
<span class="d2h-tag d2h-deleted d2h-deleted-tag">DELETED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<span>Random Html</span>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
it('should work for simple renamed file', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
|
||||||
|
const file = {
|
||||||
|
addedLines: 12,
|
||||||
|
deletedLines: 41,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name1.js',
|
||||||
|
newName: 'my/file/name2.js',
|
||||||
|
isRename: true,
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
const diffs = '<span>Random Html</span>';
|
||||||
|
|
||||||
|
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
|
||||||
|
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
"<div id="d2h-662683" class="d2h-file-wrapper" data-lang="js">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">my/file/{name1.js → name2.js}</span>
|
||||||
|
<span class="d2h-tag d2h-moved d2h-moved-tag">RENAMED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<span>Random Html</span>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
it('should return empty when option renderNothingWhenEmpty is true and file blocks not present', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {
|
||||||
|
renderNothingWhenEmpty: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const file = {
|
||||||
|
addedLines: 0,
|
||||||
|
deletedLines: 0,
|
||||||
|
language: 'js',
|
||||||
|
oldName: 'my/file/name1.js',
|
||||||
|
newName: 'my/file/name2.js',
|
||||||
|
isRename: true,
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
blocks: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const diffs = '<span>Random Html</span>';
|
||||||
|
|
||||||
|
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
|
||||||
|
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`""`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateLineByLineJsonHtml', () => {
|
||||||
|
it('should work for list of files', () => {
|
||||||
|
const exampleJson: DiffFile[] = [
|
||||||
|
{
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
content: '-test',
|
||||||
|
type: LineType.DELETE,
|
||||||
|
oldNumber: 1,
|
||||||
|
newNumber: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '+test1r',
|
||||||
|
type: LineType.INSERT,
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
oldStartLine: 1,
|
||||||
|
oldStartLine2: undefined,
|
||||||
|
newStartLine: 1,
|
||||||
|
header: '@@ -1 +1 @@',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deletedLines: 1,
|
||||||
|
addedLines: 1,
|
||||||
|
checksumBefore: '0000001',
|
||||||
|
checksumAfter: '0ddf2ba',
|
||||||
|
oldName: 'sample',
|
||||||
|
newName: 'sample',
|
||||||
|
language: 'txt',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {
|
||||||
|
matching: LineMatchingType.LINES,
|
||||||
|
});
|
||||||
|
const html = lineByLineRenderer.render(exampleJson);
|
||||||
|
expect(html).toMatchInlineSnapshot(`
|
||||||
|
"<div class="d2h-wrapper d2h-light-color-scheme">
|
||||||
|
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="txt">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">sample</span>
|
||||||
|
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-info"></td>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-line">@@ -1 +1 @@</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-del d2h-change">
|
||||||
|
<div class="line-num1">1</div>
|
||||||
|
<div class="line-num2"></div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-del d2h-change">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">-</span>
|
||||||
|
<span class="d2h-code-line-ctn"><del>test</del></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-ins d2h-change">
|
||||||
|
<div class="line-num1"></div>
|
||||||
|
<div class="line-num2">1</div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins d2h-change">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn"><ins>test1r</ins></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for empty blocks', () => {
|
||||||
|
const exampleJson = [
|
||||||
|
{
|
||||||
|
blocks: [],
|
||||||
|
deletedLines: 0,
|
||||||
|
addedLines: 0,
|
||||||
|
oldName: 'sample',
|
||||||
|
language: 'js',
|
||||||
|
newName: 'sample',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {
|
||||||
|
renderNothingWhenEmpty: false,
|
||||||
|
});
|
||||||
|
const html = lineByLineRenderer.render(exampleJson);
|
||||||
|
expect(html).toMatchInlineSnapshot(`
|
||||||
|
"<div class="d2h-wrapper d2h-light-color-scheme">
|
||||||
|
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">sample</span>
|
||||||
|
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<tr>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
File without changes
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for too big file diff', () => {
|
||||||
|
const exampleJson = [
|
||||||
|
{
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
header: '<a href="http://example.com">Custom link to render</a>',
|
||||||
|
lines: [],
|
||||||
|
newStartLine: 0,
|
||||||
|
oldStartLine: 0,
|
||||||
|
oldStartLine2: undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deletedLines: 0,
|
||||||
|
addedLines: 0,
|
||||||
|
oldName: 'sample',
|
||||||
|
language: 'js',
|
||||||
|
newName: 'sample',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
isTooBig: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils);
|
||||||
|
const html = lineByLineRenderer.render(exampleJson);
|
||||||
|
expect(html).toMatchInlineSnapshot(`
|
||||||
|
"<div class="d2h-wrapper d2h-light-color-scheme">
|
||||||
|
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">sample</span>
|
||||||
|
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-info"></td>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-line"><a href="http://example.com">Custom link to render</a></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_generateFileHtml', () => {
|
||||||
|
it('should work for simple file', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
|
||||||
|
const file: DiffFile = {
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
content: ' one context line',
|
||||||
|
type: LineType.CONTEXT,
|
||||||
|
oldNumber: 1,
|
||||||
|
newNumber: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '-test',
|
||||||
|
type: LineType.DELETE,
|
||||||
|
oldNumber: 2,
|
||||||
|
newNumber: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '+test1r',
|
||||||
|
type: LineType.INSERT,
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '+test2r',
|
||||||
|
type: LineType.INSERT,
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
oldStartLine: 1,
|
||||||
|
oldStartLine2: undefined,
|
||||||
|
newStartLine: 1,
|
||||||
|
header: '@@ -1 +1 @@',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deletedLines: 1,
|
||||||
|
addedLines: 1,
|
||||||
|
checksumBefore: '0000001',
|
||||||
|
checksumAfter: '0ddf2ba',
|
||||||
|
oldName: 'sample',
|
||||||
|
language: 'txt',
|
||||||
|
newName: 'sample',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const html = lineByLineRenderer.generateFileHtml(file);
|
||||||
|
|
||||||
|
expect(html).toMatchInlineSnapshot(`
|
||||||
|
"<tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-info"></td>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-line">@@ -1 +1 @@</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-cntx">
|
||||||
|
<div class="line-num1">1</div>
|
||||||
|
<div class="line-num2">1</div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-cntx">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix"> </span>
|
||||||
|
<span class="d2h-code-line-ctn">one context line</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-del d2h-change">
|
||||||
|
<div class="line-num1">2</div>
|
||||||
|
<div class="line-num2"></div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-del d2h-change">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">-</span>
|
||||||
|
<span class="d2h-code-line-ctn"><del>test</del></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-ins d2h-change">
|
||||||
|
<div class="line-num1"></div>
|
||||||
|
<div class="line-num2">2</div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins d2h-change">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn"><ins>test1r</ins></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-linenumber d2h-ins">
|
||||||
|
<div class="line-num1"></div>
|
||||||
|
<div class="line-num2">3</div>
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins">
|
||||||
|
<div class="d2h-code-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn">test2r</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
152
src/__tests__/printer-utils-tests.ts
Normal file
152
src/__tests__/printer-utils-tests.ts
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
import { escapeForHtml, getHtmlId, filenameDiff, diffHighlight } from '../render-utils';
|
||||||
|
import { DiffStyleType, LineMatchingType } from '../types';
|
||||||
|
|
||||||
|
describe('Utils', () => {
|
||||||
|
describe('escapeForHtml', () => {
|
||||||
|
it('should escape & with &', () => {
|
||||||
|
const result = escapeForHtml('&');
|
||||||
|
expect(result).toBe('&');
|
||||||
|
});
|
||||||
|
it('should escape < with <', () => {
|
||||||
|
const result = escapeForHtml('<');
|
||||||
|
expect(result).toBe('<');
|
||||||
|
});
|
||||||
|
it('should escape > with >', () => {
|
||||||
|
const result = escapeForHtml('>');
|
||||||
|
expect(result).toBe('>');
|
||||||
|
});
|
||||||
|
it('should escape " with "', () => {
|
||||||
|
const result = escapeForHtml('"');
|
||||||
|
expect(result).toBe('"');
|
||||||
|
});
|
||||||
|
it("should escape ' with '", () => {
|
||||||
|
const result = escapeForHtml("'");
|
||||||
|
expect(result).toBe(''');
|
||||||
|
});
|
||||||
|
it('should escape / with /', () => {
|
||||||
|
const result = escapeForHtml('/');
|
||||||
|
expect(result).toBe('/');
|
||||||
|
});
|
||||||
|
it('should escape a string containing HTML code', () => {
|
||||||
|
const result = escapeForHtml(`<a href="/search?q=diff2html">Search 'Diff2Html'</a>`);
|
||||||
|
expect(result).toBe(
|
||||||
|
'<a href="/search?q=diff2html">Search 'Diff2Html'</a>',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getHtmlId', () => {
|
||||||
|
it('should generate file unique id', () => {
|
||||||
|
const result = getHtmlId({
|
||||||
|
oldName: 'sample.js',
|
||||||
|
newName: 'sample.js',
|
||||||
|
});
|
||||||
|
expect(result).toBe('d2h-960013');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getDiffName', () => {
|
||||||
|
it('should generate the file name for a changed file', () => {
|
||||||
|
const result = filenameDiff({
|
||||||
|
oldName: 'sample.js',
|
||||||
|
newName: 'sample.js',
|
||||||
|
});
|
||||||
|
expect(result).toBe('sample.js');
|
||||||
|
});
|
||||||
|
it('should generate the file name for a changed file and full rename', () => {
|
||||||
|
const result = filenameDiff({
|
||||||
|
oldName: 'sample1.js',
|
||||||
|
newName: 'sample2.js',
|
||||||
|
});
|
||||||
|
expect(result).toBe('sample1.js → sample2.js');
|
||||||
|
});
|
||||||
|
it('should generate the file name for a changed file and prefix rename', () => {
|
||||||
|
const result = filenameDiff({
|
||||||
|
oldName: 'src/path/sample.js',
|
||||||
|
newName: 'source/path/sample.js',
|
||||||
|
});
|
||||||
|
expect(result).toBe('{src → source}/path/sample.js');
|
||||||
|
});
|
||||||
|
it('should generate the file name for a changed file and suffix rename', () => {
|
||||||
|
const result = filenameDiff({
|
||||||
|
oldName: 'src/path/sample1.js',
|
||||||
|
newName: 'src/path/sample2.js',
|
||||||
|
});
|
||||||
|
expect(result).toBe('src/path/{sample1.js → sample2.js}');
|
||||||
|
});
|
||||||
|
it('should generate the file name for a changed file and middle rename', () => {
|
||||||
|
const result = filenameDiff({
|
||||||
|
oldName: 'src/really/big/path/sample.js',
|
||||||
|
newName: 'src/small/path/sample.js',
|
||||||
|
});
|
||||||
|
expect(result).toBe('src/{really/big → small}/path/sample.js');
|
||||||
|
});
|
||||||
|
it('should generate the file name for a deleted file', () => {
|
||||||
|
const result = filenameDiff({
|
||||||
|
oldName: 'src/my/file.js',
|
||||||
|
newName: '/dev/null',
|
||||||
|
});
|
||||||
|
expect(result).toBe('src/my/file.js');
|
||||||
|
});
|
||||||
|
it('should generate the file name for a new file', () => {
|
||||||
|
const result = filenameDiff({
|
||||||
|
oldName: '/dev/null',
|
||||||
|
newName: 'src/my/file.js',
|
||||||
|
});
|
||||||
|
expect(result).toBe('src/my/file.js');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('diffHighlight', () => {
|
||||||
|
it('should highlight two lines', () => {
|
||||||
|
const result = diffHighlight('-var myVar = 2;', '+var myVariable = 3;', false, {
|
||||||
|
matching: LineMatchingType.WORDS,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
oldLine: {
|
||||||
|
prefix: '-',
|
||||||
|
content: 'var <del>myVar</del> = <del>2</del>;',
|
||||||
|
},
|
||||||
|
newLine: {
|
||||||
|
prefix: '+',
|
||||||
|
content: 'var <ins>myVariable</ins> = <ins>3</ins>;',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should highlight two lines char by char', () => {
|
||||||
|
const result = diffHighlight('-var myVar = 2;', '+var myVariable = 3;', false, {
|
||||||
|
diffStyle: DiffStyleType.CHAR,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
oldLine: {
|
||||||
|
prefix: '-',
|
||||||
|
content: 'var myVar = <del>2</del>;',
|
||||||
|
},
|
||||||
|
newLine: {
|
||||||
|
prefix: '+',
|
||||||
|
content: 'var myVar<ins>iable</ins> = <ins>3</ins>;',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should highlight combined diff lines', () => {
|
||||||
|
const result = diffHighlight(' -var myVar = 2;', ' +var myVariable = 3;', true, {
|
||||||
|
diffStyle: DiffStyleType.WORD,
|
||||||
|
matching: LineMatchingType.WORDS,
|
||||||
|
matchWordsThreshold: 1.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
oldLine: {
|
||||||
|
prefix: ' -',
|
||||||
|
content: 'var <del class="d2h-change">myVar</del> = <del class="d2h-change">2</del>;',
|
||||||
|
},
|
||||||
|
newLine: {
|
||||||
|
prefix: ' +',
|
||||||
|
content: 'var <ins class="d2h-change">myVariable</ins> = <ins class="d2h-change">3</ins>;',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
538
src/__tests__/side-by-side-printer-tests.ts
Normal file
538
src/__tests__/side-by-side-printer-tests.ts
Normal file
|
|
@ -0,0 +1,538 @@
|
||||||
|
import SideBySideRenderer from '../side-by-side-renderer';
|
||||||
|
import HoganJsUtils from '../hoganjs-utils';
|
||||||
|
import { LineType, DiffLine, DiffFile, LineMatchingType } from '../types';
|
||||||
|
import { CSSLineClass } from '../render-utils';
|
||||||
|
|
||||||
|
describe('SideBySideRenderer', () => {
|
||||||
|
describe('generateEmptyDiff', () => {
|
||||||
|
it('should return an empty diff', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
|
||||||
|
const fileHtml = sideBySideRenderer.generateEmptyDiff();
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"left": "<tr>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
File without changes
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>",
|
||||||
|
"right": "",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateSideBySideFileHtml', () => {
|
||||||
|
it('should generate lines with the right prefixes', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
|
||||||
|
|
||||||
|
const file: DiffFile = {
|
||||||
|
isGitDiff: true,
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
content: ' context',
|
||||||
|
type: LineType.CONTEXT,
|
||||||
|
oldNumber: 19,
|
||||||
|
newNumber: 19,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '-removed',
|
||||||
|
type: LineType.DELETE,
|
||||||
|
oldNumber: 20,
|
||||||
|
newNumber: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '+added',
|
||||||
|
type: LineType.INSERT,
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '+another added',
|
||||||
|
type: LineType.INSERT,
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 21,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
oldStartLine: 19,
|
||||||
|
newStartLine: 19,
|
||||||
|
header: '@@ -19,7 +19,7 @@',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deletedLines: 1,
|
||||||
|
addedLines: 2,
|
||||||
|
checksumBefore: 'fc56817',
|
||||||
|
checksumAfter: 'e8e7e49',
|
||||||
|
mode: '100644',
|
||||||
|
oldName: 'coverage.init',
|
||||||
|
language: 'init',
|
||||||
|
newName: 'coverage.init',
|
||||||
|
isCombined: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileHtml = sideBySideRenderer.generateFileHtml(file);
|
||||||
|
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"left": "<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-info"></td>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-side-line">@@ -19,7 +19,7 @@</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-cntx">
|
||||||
|
19
|
||||||
|
</td>
|
||||||
|
<td class="d2h-cntx">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix"> </span>
|
||||||
|
<span class="d2h-code-line-ctn">context</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-del d2h-change">
|
||||||
|
20
|
||||||
|
</td>
|
||||||
|
<td class="d2h-del d2h-change">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix">-</span>
|
||||||
|
<span class="d2h-code-line-ctn"><del>removed</del></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="d2h-cntx d2h-emptyplaceholder">
|
||||||
|
<div class="d2h-code-side-line d2h-code-side-emptyplaceholder">
|
||||||
|
<span class="d2h-code-line-prefix"> </span>
|
||||||
|
<span class="d2h-code-line-ctn"><br></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>",
|
||||||
|
"right": "<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-info"></td>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-side-line"> </div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-cntx">
|
||||||
|
19
|
||||||
|
</td>
|
||||||
|
<td class="d2h-cntx">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix"> </span>
|
||||||
|
<span class="d2h-code-line-ctn">context</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-ins d2h-change">
|
||||||
|
20
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins d2h-change">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn"><ins>added</ins></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-ins">
|
||||||
|
21
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn">another added</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateSingleLineHtml', () => {
|
||||||
|
it('should work for insertions', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
|
||||||
|
const fileHtml = sideBySideRenderer.generateLineHtml(undefined, {
|
||||||
|
type: CSSLineClass.INSERTS,
|
||||||
|
prefix: '+',
|
||||||
|
content: 'test',
|
||||||
|
number: 30,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"left": "<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="d2h-cntx d2h-emptyplaceholder">
|
||||||
|
<div class="d2h-code-side-line d2h-code-side-emptyplaceholder">
|
||||||
|
<span class="d2h-code-line-prefix"> </span>
|
||||||
|
<span class="d2h-code-line-ctn"><br></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>",
|
||||||
|
"right": "<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-ins">
|
||||||
|
30
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn">test</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
it('should work for deletions', () => {
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
|
||||||
|
const fileHtml = sideBySideRenderer.generateLineHtml(
|
||||||
|
{
|
||||||
|
type: CSSLineClass.DELETES,
|
||||||
|
prefix: '-',
|
||||||
|
content: 'test',
|
||||||
|
number: 30,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fileHtml).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"left": "<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-del">
|
||||||
|
30
|
||||||
|
</td>
|
||||||
|
<td class="d2h-del">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix">-</span>
|
||||||
|
<span class="d2h-code-line-ctn">test</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>",
|
||||||
|
"right": "<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="d2h-cntx d2h-emptyplaceholder">
|
||||||
|
<div class="d2h-code-side-line d2h-code-side-emptyplaceholder">
|
||||||
|
<span class="d2h-code-line-prefix"> </span>
|
||||||
|
<span class="d2h-code-line-ctn"><br></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateSideBySideJsonHtml', () => {
|
||||||
|
it('should work for list of files', () => {
|
||||||
|
const exampleJson: DiffFile[] = [
|
||||||
|
{
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
lines: [
|
||||||
|
{
|
||||||
|
content: '-test',
|
||||||
|
type: LineType.DELETE,
|
||||||
|
oldNumber: 1,
|
||||||
|
newNumber: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '+test1r',
|
||||||
|
type: LineType.INSERT,
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
oldStartLine: 1,
|
||||||
|
oldStartLine2: undefined,
|
||||||
|
newStartLine: 1,
|
||||||
|
header: '@@ -1 +1 @@',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deletedLines: 1,
|
||||||
|
addedLines: 1,
|
||||||
|
checksumBefore: '0000001',
|
||||||
|
checksumAfter: '0ddf2ba',
|
||||||
|
oldName: 'sample',
|
||||||
|
language: 'txt',
|
||||||
|
newName: 'sample',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, { matching: LineMatchingType.LINES });
|
||||||
|
const html = sideBySideRenderer.render(exampleJson);
|
||||||
|
expect(html).toMatchInlineSnapshot(`
|
||||||
|
"<div class="d2h-wrapper d2h-light-color-scheme">
|
||||||
|
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="txt">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">sample</span>
|
||||||
|
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-files-diff">
|
||||||
|
<div class="d2h-file-side-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-info"></td>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-side-line">@@ -1 +1 @@</div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-del d2h-change">
|
||||||
|
1
|
||||||
|
</td>
|
||||||
|
<td class="d2h-del d2h-change">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix">-</span>
|
||||||
|
<span class="d2h-code-line-ctn"><del>test</del></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-side-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-info"></td>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-side-line"> </div>
|
||||||
|
</td>
|
||||||
|
</tr><tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-ins d2h-change">
|
||||||
|
1
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins d2h-change">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn"><ins>test1r</ins></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
it('should work for files without blocks', () => {
|
||||||
|
const exampleJson: DiffFile[] = [
|
||||||
|
{
|
||||||
|
blocks: [],
|
||||||
|
oldName: 'sample',
|
||||||
|
language: 'js',
|
||||||
|
newName: 'sample',
|
||||||
|
isCombined: false,
|
||||||
|
addedLines: 0,
|
||||||
|
deletedLines: 0,
|
||||||
|
isGitDiff: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
|
||||||
|
const html = sideBySideRenderer.render(exampleJson);
|
||||||
|
expect(html).toMatchInlineSnapshot(`
|
||||||
|
"<div class="d2h-wrapper d2h-light-color-scheme">
|
||||||
|
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">sample</span>
|
||||||
|
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-files-diff">
|
||||||
|
<div class="d2h-file-side-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<tr>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
File without changes
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-side-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for too big file diff', () => {
|
||||||
|
const exampleJson = [
|
||||||
|
{
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
header: '<a href="http://example.com">Custom link to render</a>',
|
||||||
|
lines: [],
|
||||||
|
newStartLine: 0,
|
||||||
|
oldStartLine: 0,
|
||||||
|
oldStartLine2: undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deletedLines: 0,
|
||||||
|
addedLines: 0,
|
||||||
|
oldName: 'sample',
|
||||||
|
language: 'js',
|
||||||
|
newName: 'sample',
|
||||||
|
isCombined: false,
|
||||||
|
isGitDiff: false,
|
||||||
|
isTooBig: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const sideBySideRenderer = new SideBySideRenderer(hoganUtils);
|
||||||
|
const html = sideBySideRenderer.render(exampleJson);
|
||||||
|
expect(html).toMatchInlineSnapshot(`
|
||||||
|
"<div class="d2h-wrapper d2h-light-color-scheme">
|
||||||
|
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg> <span class="d2h-file-name">sample</span>
|
||||||
|
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-files-diff">
|
||||||
|
<div class="d2h-file-side-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-info"></td>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-side-line"><a href="http://example.com">Custom link to render</a></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-side-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-info"></td>
|
||||||
|
<td class="d2h-info">
|
||||||
|
<div class="d2h-code-side-line"> </div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('processLines', () => {
|
||||||
|
it('should process file lines', () => {
|
||||||
|
const oldLines: DiffLine[] = [
|
||||||
|
{
|
||||||
|
content: '-test',
|
||||||
|
type: LineType.DELETE,
|
||||||
|
oldNumber: 1,
|
||||||
|
newNumber: undefined,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const newLines: DiffLine[] = [
|
||||||
|
{
|
||||||
|
content: '+test1r',
|
||||||
|
type: LineType.INSERT,
|
||||||
|
oldNumber: undefined,
|
||||||
|
newNumber: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const hoganUtils = new HoganJsUtils({});
|
||||||
|
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, { matching: LineMatchingType.LINES });
|
||||||
|
const html = sideBySideRenderer.processChangedLines(false, oldLines, newLines);
|
||||||
|
|
||||||
|
expect(html).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"left": "<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-del d2h-change">
|
||||||
|
1
|
||||||
|
</td>
|
||||||
|
<td class="d2h-del d2h-change">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix">-</span>
|
||||||
|
<span class="d2h-code-line-ctn"><del>test</del></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>",
|
||||||
|
"right": "<tr>
|
||||||
|
<td class="d2h-code-side-linenumber d2h-ins d2h-change">
|
||||||
|
1
|
||||||
|
</td>
|
||||||
|
<td class="d2h-ins d2h-change">
|
||||||
|
<div class="d2h-code-side-line">
|
||||||
|
<span class="d2h-code-line-prefix">+</span>
|
||||||
|
<span class="d2h-code-line-ctn"><ins>test1r</ins></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
31
src/__tests__/utils-tests.ts
Normal file
31
src/__tests__/utils-tests.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { escapeForRegExp, unifyPath, hashCode } from '../utils';
|
||||||
|
|
||||||
|
describe('Utils', () => {
|
||||||
|
describe('escapeForRegExp', () => {
|
||||||
|
it('should escape markdown link text', () => {
|
||||||
|
const result = escapeForRegExp('[Link](https://diff2html.xyz)');
|
||||||
|
expect(result).toBe('\\[Link\\]\\(https:\\/\\/diff2html\\.xyz\\)');
|
||||||
|
});
|
||||||
|
it('should escape all dangerous characters', () => {
|
||||||
|
const result = escapeForRegExp('-[]/{}()*+?.\\^$|');
|
||||||
|
expect(result).toBe('\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('unifyPath', () => {
|
||||||
|
it('should unify windows style path', () => {
|
||||||
|
const result = unifyPath('\\Users\\Downloads\\diff.html');
|
||||||
|
expect(result).toBe('/Users/Downloads/diff.html');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hashCode', () => {
|
||||||
|
it('should create consistent hash for a text piece', () => {
|
||||||
|
const string = '/home/diff2html/diff.html';
|
||||||
|
expect(hashCode(string)).toEqual(hashCode(string));
|
||||||
|
});
|
||||||
|
it('should create different hash for different text pieces', () => {
|
||||||
|
expect(hashCode('/home/diff2html/diff1.html')).not.toEqual(hashCode('/home/diff2html/diff2.html'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,347 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Diff Parser (diff-parser.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var utils = require('./utils.js').Utils;
|
|
||||||
|
|
||||||
var LINE_TYPE = {
|
|
||||||
INSERTS: 'd2h-ins',
|
|
||||||
DELETES: 'd2h-del',
|
|
||||||
INSERT_CHANGES: 'd2h-ins d2h-change',
|
|
||||||
DELETE_CHANGES: 'd2h-del d2h-change',
|
|
||||||
CONTEXT: 'd2h-cntx',
|
|
||||||
INFO: 'd2h-info'
|
|
||||||
};
|
|
||||||
|
|
||||||
function DiffParser() {
|
|
||||||
}
|
|
||||||
|
|
||||||
DiffParser.prototype.LINE_TYPE = LINE_TYPE;
|
|
||||||
|
|
||||||
DiffParser.prototype.generateDiffJson = function(diffInput, configuration) {
|
|
||||||
var config = configuration || {};
|
|
||||||
|
|
||||||
var files = [];
|
|
||||||
var currentFile = null;
|
|
||||||
var currentBlock = null;
|
|
||||||
var oldLine = null;
|
|
||||||
var oldLine2 = null; // Used for combined diff
|
|
||||||
var newLine = null;
|
|
||||||
|
|
||||||
/* Add previous block(if exists) before start a new file */
|
|
||||||
var saveBlock = function() {
|
|
||||||
if (currentBlock) {
|
|
||||||
currentFile.blocks.push(currentBlock);
|
|
||||||
currentBlock = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add previous file(if exists) before start a new one
|
|
||||||
* if it has name (to avoid binary files errors)
|
|
||||||
*/
|
|
||||||
var saveFile = function() {
|
|
||||||
if (currentFile && currentFile.newName) {
|
|
||||||
files.push(currentFile);
|
|
||||||
currentFile = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Create file structure */
|
|
||||||
var startFile = function() {
|
|
||||||
saveBlock();
|
|
||||||
saveFile();
|
|
||||||
|
|
||||||
currentFile = {};
|
|
||||||
currentFile.blocks = [];
|
|
||||||
currentFile.deletedLines = 0;
|
|
||||||
currentFile.addedLines = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
var startBlock = function(line) {
|
|
||||||
saveBlock();
|
|
||||||
|
|
||||||
var values;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* From Range:
|
|
||||||
* -<start line>[,<number of lines>]
|
|
||||||
*
|
|
||||||
* To Range:
|
|
||||||
* +<start line>[,<number of lines>]
|
|
||||||
*
|
|
||||||
* @@ from-file-range to-file-range @@
|
|
||||||
*
|
|
||||||
* @@@ from-file-range from-file-range to-file-range @@@
|
|
||||||
*
|
|
||||||
* number of lines is optional, if omited consider 0
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (values = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@.*/.exec(line)) {
|
|
||||||
currentFile.isCombined = false;
|
|
||||||
oldLine = values[1];
|
|
||||||
newLine = values[2];
|
|
||||||
} else if (values = /^@@@ -(\d+)(?:,\d+)? -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@@.*/.exec(line)) {
|
|
||||||
currentFile.isCombined = true;
|
|
||||||
oldLine = values[1];
|
|
||||||
oldLine2 = values[2];
|
|
||||||
newLine = values[3];
|
|
||||||
} else {
|
|
||||||
console.error("Failed to parse lines, starting in 0!");
|
|
||||||
oldLine = 0;
|
|
||||||
newLine = 0;
|
|
||||||
currentFile.isCombined = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create block metadata */
|
|
||||||
currentBlock = {};
|
|
||||||
currentBlock.lines = [];
|
|
||||||
currentBlock.oldStartLine = oldLine;
|
|
||||||
currentBlock.oldStartLine2 = oldLine2;
|
|
||||||
currentBlock.newStartLine = newLine;
|
|
||||||
currentBlock.header = line;
|
|
||||||
};
|
|
||||||
|
|
||||||
var createLine = function(line) {
|
|
||||||
var currentLine = {};
|
|
||||||
currentLine.content = line;
|
|
||||||
|
|
||||||
var newLinePrefixes = !currentFile.isCombined ? ['+'] : ['+', ' +'];
|
|
||||||
var delLinePrefixes = !currentFile.isCombined ? ['-'] : ['-', ' -'];
|
|
||||||
|
|
||||||
/* Fill the line data */
|
|
||||||
if (utils.startsWith(line, newLinePrefixes)) {
|
|
||||||
currentFile.addedLines++;
|
|
||||||
|
|
||||||
currentLine.type = LINE_TYPE.INSERTS;
|
|
||||||
currentLine.oldNumber = null;
|
|
||||||
currentLine.newNumber = newLine++;
|
|
||||||
|
|
||||||
currentBlock.lines.push(currentLine);
|
|
||||||
|
|
||||||
} else if (utils.startsWith(line, delLinePrefixes)) {
|
|
||||||
currentFile.deletedLines++;
|
|
||||||
|
|
||||||
currentLine.type = LINE_TYPE.DELETES;
|
|
||||||
currentLine.oldNumber = oldLine++;
|
|
||||||
currentLine.newNumber = null;
|
|
||||||
|
|
||||||
currentBlock.lines.push(currentLine);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
currentLine.type = LINE_TYPE.CONTEXT;
|
|
||||||
currentLine.oldNumber = oldLine++;
|
|
||||||
currentLine.newNumber = newLine++;
|
|
||||||
|
|
||||||
currentBlock.lines.push(currentLine);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var diffLines =
|
|
||||||
diffInput.replace(/\\ No newline at end of file/g, '')
|
|
||||||
.replace(/\r\n?/g, '\n')
|
|
||||||
.split('\n');
|
|
||||||
|
|
||||||
/* Diff */
|
|
||||||
var oldMode = /^old mode (\d{6})/;
|
|
||||||
var newMode = /^new mode (\d{6})/;
|
|
||||||
var deletedFileMode = /^deleted file mode (\d{6})/;
|
|
||||||
var newFileMode = /^new file mode (\d{6})/;
|
|
||||||
|
|
||||||
var copyFrom = /^copy from "?(.+)"?/;
|
|
||||||
var copyTo = /^copy to "?(.+)"?/;
|
|
||||||
|
|
||||||
var renameFrom = /^rename from "?(.+)"?/;
|
|
||||||
var renameTo = /^rename to "?(.+)"?/;
|
|
||||||
|
|
||||||
var similarityIndex = /^similarity index (\d+)%/;
|
|
||||||
var dissimilarityIndex = /^dissimilarity index (\d+)%/;
|
|
||||||
var index = /^index ([0-9a-z]+)\.\.([0-9a-z]+)\s*(\d{6})?/;
|
|
||||||
|
|
||||||
/* Combined Diff */
|
|
||||||
var combinedIndex = /^index ([0-9a-z]+),([0-9a-z]+)\.\.([0-9a-z]+)/;
|
|
||||||
var combinedMode = /^mode (\d{6}),(\d{6})\.\.(\d{6})/;
|
|
||||||
var combinedNewFile = /^new file mode (\d{6})/;
|
|
||||||
var combinedDeletedFile = /^deleted file mode (\d{6}),(\d{6})/;
|
|
||||||
|
|
||||||
diffLines.forEach(function(line) {
|
|
||||||
// Unmerged paths, and possibly other non-diffable files
|
|
||||||
// https://github.com/scottgonzalez/pretty-diff/issues/11
|
|
||||||
// Also, remove some useless lines
|
|
||||||
if (!line || utils.startsWith(line, '*')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
utils.startsWith(line, 'diff') || // Git diffs always start with diff
|
|
||||||
!currentFile || // If we do not have a file yet, we should crete one
|
|
||||||
(
|
|
||||||
currentFile && // If we already have some file in progress and
|
|
||||||
(
|
|
||||||
currentFile.oldName && utils.startsWith(line, '---') || // Either we reached a old file identification line
|
|
||||||
currentFile.newName && utils.startsWith(line, '+++') // Or we reached a new file identification line
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
startFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
var values;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* --- Date Timestamp[FractionalSeconds] TimeZone
|
|
||||||
* --- 2002-02-21 23:30:39.942229878 -0800
|
|
||||||
*/
|
|
||||||
if (currentFile && !currentFile.oldName &&
|
|
||||||
utils.startsWith(line, '---') && (values = getSrcFilename(line, config))) {
|
|
||||||
currentFile.oldName = values;
|
|
||||||
currentFile.language = getExtension(currentFile.oldName, currentFile.language);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* +++ Date Timestamp[FractionalSeconds] TimeZone
|
|
||||||
* +++ 2002-02-21 23:30:39.942229878 -0800
|
|
||||||
*/
|
|
||||||
if (currentFile && !currentFile.newName &&
|
|
||||||
utils.startsWith(line, '+++') && (values = getDstFilename(line, config))) {
|
|
||||||
currentFile.newName = values;
|
|
||||||
currentFile.language = getExtension(currentFile.newName, currentFile.language);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentFile && utils.startsWith(line, '@')) {
|
|
||||||
startBlock(line);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There are three types of diff lines. These lines are defined by the way they start.
|
|
||||||
* 1. New line starts with: +
|
|
||||||
* 2. Old line starts with: -
|
|
||||||
* 3. Context line starts with: <SPACE>
|
|
||||||
*/
|
|
||||||
if (currentBlock && (utils.startsWith(line, '+') || utils.startsWith(line, '-') || utils.startsWith(line, ' '))) {
|
|
||||||
createLine(line);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
(currentFile && currentFile.blocks.length) ||
|
|
||||||
(currentBlock && currentBlock.lines.length)
|
|
||||||
) {
|
|
||||||
startFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Git diffs provide more information regarding files modes, renames, copies,
|
|
||||||
* commits between changes and similarity indexes
|
|
||||||
*/
|
|
||||||
if ((values = oldMode.exec(line))) {
|
|
||||||
currentFile.oldMode = values[1];
|
|
||||||
} else if ((values = newMode.exec(line))) {
|
|
||||||
currentFile.newMode = values[1];
|
|
||||||
} else if ((values = deletedFileMode.exec(line))) {
|
|
||||||
currentFile.deletedFileMode = values[1];
|
|
||||||
currentFile.isDeleted = true;
|
|
||||||
} else if ((values = newFileMode.exec(line))) {
|
|
||||||
currentFile.newFileMode = values[1];
|
|
||||||
currentFile.isNew = true;
|
|
||||||
} else if ((values = copyFrom.exec(line))) {
|
|
||||||
currentFile.oldName = values[1];
|
|
||||||
currentFile.isCopy = true;
|
|
||||||
} else if ((values = copyTo.exec(line))) {
|
|
||||||
currentFile.newName = values[1];
|
|
||||||
currentFile.isCopy = true;
|
|
||||||
} else if ((values = renameFrom.exec(line))) {
|
|
||||||
currentFile.oldName = values[1];
|
|
||||||
currentFile.isRename = true;
|
|
||||||
} else if ((values = renameTo.exec(line))) {
|
|
||||||
currentFile.newName = values[1];
|
|
||||||
currentFile.isRename = true;
|
|
||||||
} else if ((values = similarityIndex.exec(line))) {
|
|
||||||
currentFile.unchangedPercentage = values[1];
|
|
||||||
} else if ((values = dissimilarityIndex.exec(line))) {
|
|
||||||
currentFile.changedPercentage = values[1];
|
|
||||||
} else if ((values = index.exec(line))) {
|
|
||||||
currentFile.checksumBefore = values[1];
|
|
||||||
currentFile.checksumAfter = values[2];
|
|
||||||
values[3] && (currentFile.mode = values[3]);
|
|
||||||
} else if ((values = combinedIndex.exec(line))) {
|
|
||||||
currentFile.checksumBefore = [values[2], values[3]];
|
|
||||||
currentFile.checksumAfter = values[1];
|
|
||||||
} else if ((values = combinedMode.exec(line))) {
|
|
||||||
currentFile.oldMode = [values[2], values[3]];
|
|
||||||
currentFile.newMode = values[1];
|
|
||||||
} else if ((values = combinedNewFile.exec(line))) {
|
|
||||||
currentFile.newFileMode = values[1];
|
|
||||||
currentFile.isNew = true;
|
|
||||||
} else if ((values = combinedDeletedFile.exec(line))) {
|
|
||||||
currentFile.deletedFileMode = values[1];
|
|
||||||
currentFile.isDeleted = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
saveBlock();
|
|
||||||
saveFile();
|
|
||||||
|
|
||||||
return files;
|
|
||||||
};
|
|
||||||
|
|
||||||
function getExtension(filename, language) {
|
|
||||||
var nameSplit = filename.split('.');
|
|
||||||
if (nameSplit.length > 1) {
|
|
||||||
return nameSplit[nameSplit.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return language;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSrcFilename(line, cfg) {
|
|
||||||
var prefixes = ["a/", "i/", "w/", "c/", "o/"];
|
|
||||||
|
|
||||||
if (cfg.srcPrefix) {
|
|
||||||
prefixes.push(cfg.srcPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _getFilename('---', line, prefixes);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDstFilename(line, cfg) {
|
|
||||||
var prefixes = ["b/", "i/", "w/", "c/", "o/"];
|
|
||||||
|
|
||||||
if (cfg.dstPrefix) {
|
|
||||||
prefixes.push(cfg.dstPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _getFilename('\\+\\+\\+', line, prefixes);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getFilename(linePrefix, line, prefixes) {
|
|
||||||
var FilenameRegExp = new RegExp('^' + linePrefix + ' "?(.+?)"?$');
|
|
||||||
|
|
||||||
var filename;
|
|
||||||
var values = FilenameRegExp.exec(line);
|
|
||||||
if (values && values[1]) {
|
|
||||||
filename = values[1];
|
|
||||||
var matchingPrefixes = prefixes.filter(function(p) {
|
|
||||||
return filename.indexOf(p) === 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matchingPrefixes[0]) {
|
|
||||||
// Remove prefix if exists
|
|
||||||
filename = filename.slice(matchingPrefixes[0].length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.DiffParser = new DiffParser();
|
|
||||||
|
|
||||||
})();
|
|
||||||
481
src/diff-parser.ts
Normal file
481
src/diff-parser.ts
Normal file
|
|
@ -0,0 +1,481 @@
|
||||||
|
import { DiffFile, DiffBlock, DiffLine, LineType } from './types';
|
||||||
|
import { escapeForRegExp } from './utils';
|
||||||
|
|
||||||
|
export interface DiffParserConfig {
|
||||||
|
srcPrefix?: string;
|
||||||
|
dstPrefix?: string;
|
||||||
|
diffMaxChanges?: number;
|
||||||
|
diffMaxLineLength?: number;
|
||||||
|
diffTooBigMessage?: (fileIndex: number) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExtension(filename: string, language: string): string {
|
||||||
|
const filenameParts = filename.split('.');
|
||||||
|
return filenameParts.length > 1 ? filenameParts[filenameParts.length - 1] : language;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startsWithAny(str: string, prefixes: string[]): boolean {
|
||||||
|
return prefixes.reduce<boolean>((startsWith, prefix) => startsWith || str.startsWith(prefix), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseDiffFilenamePrefixes = ['a/', 'b/', 'i/', 'w/', 'c/', 'o/'];
|
||||||
|
function getFilename(line: string, linePrefix?: string, extraPrefix?: string): string {
|
||||||
|
const prefixes = extraPrefix !== undefined ? [...baseDiffFilenamePrefixes, extraPrefix] : baseDiffFilenamePrefixes;
|
||||||
|
|
||||||
|
const FilenameRegExp = linePrefix
|
||||||
|
? new RegExp(`^${escapeForRegExp(linePrefix)} "?(.+?)"?$`)
|
||||||
|
: new RegExp('^"?(.+?)"?$');
|
||||||
|
|
||||||
|
const [, filename = ''] = FilenameRegExp.exec(line) || [];
|
||||||
|
const matchingPrefix = prefixes.find(p => filename.indexOf(p) === 0);
|
||||||
|
const fnameWithoutPrefix = matchingPrefix ? filename.slice(matchingPrefix.length) : filename;
|
||||||
|
|
||||||
|
// Cleanup timestamps generated by the unified diff (diff command) as specified in
|
||||||
|
// https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html
|
||||||
|
// Ie: 2016-10-25 11:37:14.000000000 +0200
|
||||||
|
return fnameWithoutPrefix.replace(/\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)? [+-]\d{4}.*$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSrcFilename(line: string, srcPrefix?: string): string | undefined {
|
||||||
|
return getFilename(line, '---', srcPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDstFilename(line: string, dstPrefix?: string): string | undefined {
|
||||||
|
return getFilename(line, '+++', dstPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Docs:
|
||||||
|
* - Unified: https://www.gnu.org/software/diffutils/manual/html_node/Unified-Format.html
|
||||||
|
* - Git Diff: https://git-scm.com/docs/git-diff-tree#_raw_output_format
|
||||||
|
* - Git Combined Diff: https://git-scm.com/docs/git-diff-tree#_combined_diff_format
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFile[] {
|
||||||
|
const files: DiffFile[] = [];
|
||||||
|
let currentFile: DiffFile | null = null;
|
||||||
|
let currentBlock: DiffBlock | null = null;
|
||||||
|
let oldLine: number | null = null;
|
||||||
|
let oldLine2: number | null = null; // Used for combined diff
|
||||||
|
let newLine: number | null = null;
|
||||||
|
|
||||||
|
let possibleOldName: string | null = null;
|
||||||
|
let possibleNewName: string | null = null;
|
||||||
|
|
||||||
|
/* Diff Header */
|
||||||
|
const oldFileNameHeader = '--- ';
|
||||||
|
const newFileNameHeader = '+++ ';
|
||||||
|
const hunkHeaderPrefix = '@@';
|
||||||
|
|
||||||
|
/* Diff */
|
||||||
|
const oldMode = /^old mode (\d{6})/;
|
||||||
|
const newMode = /^new mode (\d{6})/;
|
||||||
|
const deletedFileMode = /^deleted file mode (\d{6})/;
|
||||||
|
const newFileMode = /^new file mode (\d{6})/;
|
||||||
|
|
||||||
|
const copyFrom = /^copy from "?(.+)"?/;
|
||||||
|
const copyTo = /^copy to "?(.+)"?/;
|
||||||
|
|
||||||
|
const renameFrom = /^rename from "?(.+)"?/;
|
||||||
|
const renameTo = /^rename to "?(.+)"?/;
|
||||||
|
|
||||||
|
const similarityIndex = /^similarity index (\d+)%/;
|
||||||
|
const dissimilarityIndex = /^dissimilarity index (\d+)%/;
|
||||||
|
const index = /^index ([\da-z]+)\.\.([\da-z]+)\s*(\d{6})?/;
|
||||||
|
|
||||||
|
const binaryFiles = /^Binary files (.*) and (.*) differ/;
|
||||||
|
const binaryDiff = /^GIT binary patch/;
|
||||||
|
|
||||||
|
/* Combined Diff */
|
||||||
|
const combinedIndex = /^index ([\da-z]+),([\da-z]+)\.\.([\da-z]+)/;
|
||||||
|
const combinedMode = /^mode (\d{6}),(\d{6})\.\.(\d{6})/;
|
||||||
|
const combinedNewFile = /^new file mode (\d{6})/;
|
||||||
|
const combinedDeletedFile = /^deleted file mode (\d{6}),(\d{6})/;
|
||||||
|
|
||||||
|
const diffLines = diffInput
|
||||||
|
.replace(/\\ No newline at end of file/g, '')
|
||||||
|
.replace(/\r\n?/g, '\n')
|
||||||
|
.split('\n');
|
||||||
|
|
||||||
|
/* Add previous block(if exists) before start a new file */
|
||||||
|
function saveBlock(): void {
|
||||||
|
if (currentBlock !== null && currentFile !== null) {
|
||||||
|
currentFile.blocks.push(currentBlock);
|
||||||
|
currentBlock = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add previous file(if exists) before start a new one
|
||||||
|
* if it has name (to avoid binary files errors)
|
||||||
|
*/
|
||||||
|
function saveFile(): void {
|
||||||
|
if (currentFile !== null) {
|
||||||
|
if (!currentFile.oldName && possibleOldName !== null) {
|
||||||
|
currentFile.oldName = possibleOldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentFile.newName && possibleNewName !== null) {
|
||||||
|
currentFile.newName = possibleNewName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentFile.newName) {
|
||||||
|
files.push(currentFile);
|
||||||
|
currentFile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleOldName = null;
|
||||||
|
possibleNewName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create file structure */
|
||||||
|
function startFile(): void {
|
||||||
|
saveBlock();
|
||||||
|
saveFile();
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
currentFile = {
|
||||||
|
blocks: [],
|
||||||
|
deletedLines: 0,
|
||||||
|
addedLines: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function startBlock(line: string): void {
|
||||||
|
saveBlock();
|
||||||
|
|
||||||
|
let values;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From Range:
|
||||||
|
* -<start line>[,<number of lines>]
|
||||||
|
*
|
||||||
|
* To Range:
|
||||||
|
* +<start line>[,<number of lines>]
|
||||||
|
*
|
||||||
|
* @@ from-file-range to-file-range @@
|
||||||
|
*
|
||||||
|
* @@@ from-file-range from-file-range to-file-range @@@
|
||||||
|
*
|
||||||
|
* number of lines is optional, if omited consider 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (currentFile !== null) {
|
||||||
|
if ((values = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@.*/.exec(line))) {
|
||||||
|
currentFile.isCombined = false;
|
||||||
|
oldLine = parseInt(values[1], 10);
|
||||||
|
newLine = parseInt(values[2], 10);
|
||||||
|
} else if ((values = /^@@@ -(\d+)(?:,\d+)? -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@@.*/.exec(line))) {
|
||||||
|
currentFile.isCombined = true;
|
||||||
|
oldLine = parseInt(values[1], 10);
|
||||||
|
oldLine2 = parseInt(values[2], 10);
|
||||||
|
newLine = parseInt(values[3], 10);
|
||||||
|
} else {
|
||||||
|
if (line.startsWith(hunkHeaderPrefix)) {
|
||||||
|
console.error('Failed to parse lines, starting in 0!');
|
||||||
|
}
|
||||||
|
|
||||||
|
oldLine = 0;
|
||||||
|
newLine = 0;
|
||||||
|
currentFile.isCombined = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create block metadata */
|
||||||
|
currentBlock = {
|
||||||
|
lines: [],
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
oldStartLine: oldLine,
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
oldStartLine2: oldLine2,
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
newStartLine: newLine,
|
||||||
|
header: line,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLine(line: string): void {
|
||||||
|
if (currentFile === null || currentBlock === null || oldLine === null || newLine === null) return;
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
const currentLine: DiffLine = {
|
||||||
|
content: line,
|
||||||
|
};
|
||||||
|
|
||||||
|
const addedPrefixes = currentFile.isCombined ? ['+ ', ' +', '++'] : ['+'];
|
||||||
|
const deletedPrefixes = currentFile.isCombined ? ['- ', ' -', '--'] : ['-'];
|
||||||
|
|
||||||
|
if (startsWithAny(line, addedPrefixes)) {
|
||||||
|
currentFile.addedLines++;
|
||||||
|
currentLine.type = LineType.INSERT;
|
||||||
|
currentLine.oldNumber = undefined;
|
||||||
|
currentLine.newNumber = newLine++;
|
||||||
|
} else if (startsWithAny(line, deletedPrefixes)) {
|
||||||
|
currentFile.deletedLines++;
|
||||||
|
currentLine.type = LineType.DELETE;
|
||||||
|
currentLine.oldNumber = oldLine++;
|
||||||
|
currentLine.newNumber = undefined;
|
||||||
|
} else {
|
||||||
|
currentLine.type = LineType.CONTEXT;
|
||||||
|
currentLine.oldNumber = oldLine++;
|
||||||
|
currentLine.newNumber = newLine++;
|
||||||
|
}
|
||||||
|
currentBlock.lines.push(currentLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks if there is a hunk header coming before a new file starts
|
||||||
|
*
|
||||||
|
* Hunk header is a group of three lines started by ( `--- ` , `+++ ` , `@@` )
|
||||||
|
*/
|
||||||
|
function existHunkHeader(line: string, lineIdx: number): boolean {
|
||||||
|
let idx = lineIdx;
|
||||||
|
|
||||||
|
while (idx < diffLines.length - 3) {
|
||||||
|
if (line.startsWith('diff')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
diffLines[idx].startsWith(oldFileNameHeader) &&
|
||||||
|
diffLines[idx + 1].startsWith(newFileNameHeader) &&
|
||||||
|
diffLines[idx + 2].startsWith(hunkHeaderPrefix)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
diffLines.forEach((line, lineIndex) => {
|
||||||
|
// Unmerged paths, and possibly other non-diffable files
|
||||||
|
// https://github.com/scottgonzalez/pretty-diff/issues/11
|
||||||
|
// Also, remove some useless lines
|
||||||
|
if (!line || line.startsWith('*')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to store regex capture groups
|
||||||
|
let values;
|
||||||
|
|
||||||
|
const prevLine = diffLines[lineIndex - 1];
|
||||||
|
const nxtLine = diffLines[lineIndex + 1];
|
||||||
|
const afterNxtLine = diffLines[lineIndex + 2];
|
||||||
|
|
||||||
|
if (line.startsWith('diff --git') || line.startsWith('diff --combined')) {
|
||||||
|
startFile();
|
||||||
|
|
||||||
|
// diff --git a/blocked_delta_results.png b/blocked_delta_results.png
|
||||||
|
const gitDiffStart = /^diff --git "?([a-ciow]\/.+)"? "?([a-ciow]\/.+)"?/;
|
||||||
|
if ((values = gitDiffStart.exec(line))) {
|
||||||
|
possibleOldName = getFilename(values[1], undefined, config.dstPrefix);
|
||||||
|
possibleNewName = getFilename(values[2], undefined, config.srcPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentFile === null) {
|
||||||
|
throw new Error('Where is my file !!!');
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFile.isGitDiff = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.startsWith('Binary files') && !currentFile?.isGitDiff) {
|
||||||
|
startFile();
|
||||||
|
const unixDiffBinaryStart = /^Binary files "?([a-ciow]\/.+)"? and "?([a-ciow]\/.+)"? differ/;
|
||||||
|
if ((values = unixDiffBinaryStart.exec(line))) {
|
||||||
|
possibleOldName = getFilename(values[1], undefined, config.dstPrefix);
|
||||||
|
possibleNewName = getFilename(values[2], undefined, config.srcPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentFile === null) {
|
||||||
|
throw new Error('Where is my file !!!');
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFile.isBinary = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!currentFile || // If we do not have a file yet, we should crete one
|
||||||
|
(!currentFile.isGitDiff &&
|
||||||
|
currentFile && // If we already have some file in progress and
|
||||||
|
line.startsWith(oldFileNameHeader) && // If we get to an old file path header line
|
||||||
|
// And is followed by the new file path header line and the hunk header line
|
||||||
|
nxtLine.startsWith(newFileNameHeader) &&
|
||||||
|
afterNxtLine.startsWith(hunkHeaderPrefix))
|
||||||
|
) {
|
||||||
|
startFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore remaining diff for current file if marked as too big
|
||||||
|
if (currentFile?.isTooBig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentFile &&
|
||||||
|
((typeof config.diffMaxChanges === 'number' &&
|
||||||
|
currentFile.addedLines + currentFile.deletedLines > config.diffMaxChanges) ||
|
||||||
|
(typeof config.diffMaxLineLength === 'number' && line.length > config.diffMaxLineLength))
|
||||||
|
) {
|
||||||
|
currentFile.isTooBig = true;
|
||||||
|
currentFile.addedLines = 0;
|
||||||
|
currentFile.deletedLines = 0;
|
||||||
|
currentFile.blocks = [];
|
||||||
|
currentBlock = null;
|
||||||
|
|
||||||
|
const message =
|
||||||
|
typeof config.diffTooBigMessage === 'function'
|
||||||
|
? config.diffTooBigMessage(files.length)
|
||||||
|
: 'Diff too big to be displayed';
|
||||||
|
startBlock(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to make sure that we have the three lines of the header.
|
||||||
|
* This avoids cases like the ones described in:
|
||||||
|
* - https://github.com/rtfpessoa/diff2html/issues/87
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
(line.startsWith(oldFileNameHeader) && nxtLine.startsWith(newFileNameHeader)) ||
|
||||||
|
(line.startsWith(newFileNameHeader) && prevLine.startsWith(oldFileNameHeader))
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
* --- Date Timestamp[FractionalSeconds] TimeZone
|
||||||
|
* --- 2002-02-21 23:30:39.942229878 -0800
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
currentFile &&
|
||||||
|
!currentFile.oldName &&
|
||||||
|
line.startsWith('--- ') &&
|
||||||
|
(values = getSrcFilename(line, config.srcPrefix))
|
||||||
|
) {
|
||||||
|
currentFile.oldName = values;
|
||||||
|
currentFile.language = getExtension(currentFile.oldName, currentFile.language);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* +++ Date Timestamp[FractionalSeconds] TimeZone
|
||||||
|
* +++ 2002-02-21 23:30:39.942229878 -0800
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
currentFile &&
|
||||||
|
!currentFile.newName &&
|
||||||
|
line.startsWith('+++ ') &&
|
||||||
|
(values = getDstFilename(line, config.dstPrefix))
|
||||||
|
) {
|
||||||
|
currentFile.newName = values;
|
||||||
|
currentFile.language = getExtension(currentFile.newName, currentFile.language);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentFile &&
|
||||||
|
(line.startsWith(hunkHeaderPrefix) ||
|
||||||
|
(currentFile.isGitDiff && currentFile.oldName && currentFile.newName && !currentBlock))
|
||||||
|
) {
|
||||||
|
startBlock(line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are three types of diff lines. These lines are defined by the way they start.
|
||||||
|
* 1. New line starts with: +
|
||||||
|
* 2. Old line starts with: -
|
||||||
|
* 3. Context line starts with: <SPACE>
|
||||||
|
*/
|
||||||
|
if (currentBlock && (line.startsWith('+') || line.startsWith('-') || line.startsWith(' '))) {
|
||||||
|
createLine(line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doesNotExistHunkHeader = !existHunkHeader(line, lineIndex);
|
||||||
|
|
||||||
|
if (currentFile === null) {
|
||||||
|
throw new Error('Where is my file !!!');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Git diffs provide more information regarding files modes, renames, copies,
|
||||||
|
* commits between changes and similarity indexes
|
||||||
|
*/
|
||||||
|
if ((values = oldMode.exec(line))) {
|
||||||
|
currentFile.oldMode = values[1];
|
||||||
|
} else if ((values = newMode.exec(line))) {
|
||||||
|
currentFile.newMode = values[1];
|
||||||
|
} else if ((values = deletedFileMode.exec(line))) {
|
||||||
|
currentFile.deletedFileMode = values[1];
|
||||||
|
currentFile.isDeleted = true;
|
||||||
|
} else if ((values = newFileMode.exec(line))) {
|
||||||
|
currentFile.newFileMode = values[1];
|
||||||
|
currentFile.isNew = true;
|
||||||
|
} else if ((values = copyFrom.exec(line))) {
|
||||||
|
if (doesNotExistHunkHeader) {
|
||||||
|
currentFile.oldName = values[1];
|
||||||
|
}
|
||||||
|
currentFile.isCopy = true;
|
||||||
|
} else if ((values = copyTo.exec(line))) {
|
||||||
|
if (doesNotExistHunkHeader) {
|
||||||
|
currentFile.newName = values[1];
|
||||||
|
}
|
||||||
|
currentFile.isCopy = true;
|
||||||
|
} else if ((values = renameFrom.exec(line))) {
|
||||||
|
if (doesNotExistHunkHeader) {
|
||||||
|
currentFile.oldName = values[1];
|
||||||
|
}
|
||||||
|
currentFile.isRename = true;
|
||||||
|
} else if ((values = renameTo.exec(line))) {
|
||||||
|
if (doesNotExistHunkHeader) {
|
||||||
|
currentFile.newName = values[1];
|
||||||
|
}
|
||||||
|
currentFile.isRename = true;
|
||||||
|
} else if ((values = binaryFiles.exec(line))) {
|
||||||
|
currentFile.isBinary = true;
|
||||||
|
currentFile.oldName = getFilename(values[1], undefined, config.srcPrefix);
|
||||||
|
currentFile.newName = getFilename(values[2], undefined, config.dstPrefix);
|
||||||
|
startBlock('Binary file');
|
||||||
|
} else if (binaryDiff.test(line)) {
|
||||||
|
currentFile.isBinary = true;
|
||||||
|
startBlock(line);
|
||||||
|
} else if ((values = similarityIndex.exec(line))) {
|
||||||
|
currentFile.unchangedPercentage = parseInt(values[1], 10);
|
||||||
|
} else if ((values = dissimilarityIndex.exec(line))) {
|
||||||
|
currentFile.changedPercentage = parseInt(values[1], 10);
|
||||||
|
} else if ((values = index.exec(line))) {
|
||||||
|
currentFile.checksumBefore = values[1];
|
||||||
|
currentFile.checksumAfter = values[2];
|
||||||
|
if (values[3]) currentFile.mode = values[3];
|
||||||
|
} else if ((values = combinedIndex.exec(line))) {
|
||||||
|
currentFile.checksumBefore = [values[2], values[3]];
|
||||||
|
currentFile.checksumAfter = values[1];
|
||||||
|
} else if ((values = combinedMode.exec(line))) {
|
||||||
|
currentFile.oldMode = [values[2], values[3]];
|
||||||
|
currentFile.newMode = values[1];
|
||||||
|
} else if ((values = combinedNewFile.exec(line))) {
|
||||||
|
currentFile.newFileMode = values[1];
|
||||||
|
currentFile.isNew = true;
|
||||||
|
} else if ((values = combinedDeletedFile.exec(line))) {
|
||||||
|
currentFile.deletedFileMode = values[1];
|
||||||
|
currentFile.isDeleted = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
saveBlock();
|
||||||
|
saveFile();
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
111
src/diff2html.js
111
src/diff2html.js
|
|
@ -1,111 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Diff to HTML (diff2html.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var diffParser = require('./diff-parser.js').DiffParser;
|
|
||||||
var fileLister = require('./file-list-printer.js').FileListPrinter;
|
|
||||||
var htmlPrinter = require('./html-printer.js').HtmlPrinter;
|
|
||||||
|
|
||||||
function Diff2Html() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Line diff type configuration
|
|
||||||
var config = {
|
|
||||||
'wordByWord': true, // (default)
|
|
||||||
// OR
|
|
||||||
'charByChar': true
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates json object from string diff input
|
|
||||||
*/
|
|
||||||
Diff2Html.prototype.getJsonFromDiff = function(diffInput, config) {
|
|
||||||
var configOrEmpty = config || {};
|
|
||||||
return diffParser.generateDiffJson(diffInput, configOrEmpty);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates the html diff. The config parameter configures the output/input formats and other options
|
|
||||||
*/
|
|
||||||
Diff2Html.prototype.getPrettyHtml = function(diffInput, config) {
|
|
||||||
var configOrEmpty = config || {};
|
|
||||||
|
|
||||||
var diffJson = diffInput;
|
|
||||||
if (!configOrEmpty.inputFormat || configOrEmpty.inputFormat === 'diff') {
|
|
||||||
diffJson = diffParser.generateDiffJson(diffInput, configOrEmpty);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileList = '';
|
|
||||||
if (configOrEmpty.showFiles === true) {
|
|
||||||
fileList = fileLister.generateFileList(diffJson, configOrEmpty);
|
|
||||||
}
|
|
||||||
|
|
||||||
var diffOutput = '';
|
|
||||||
if (configOrEmpty.outputFormat === 'side-by-side') {
|
|
||||||
diffOutput = htmlPrinter.generateSideBySideJsonHtml(diffJson, configOrEmpty);
|
|
||||||
} else {
|
|
||||||
diffOutput = htmlPrinter.generateLineByLineJsonHtml(diffJson, configOrEmpty);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileList + diffOutput;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Deprecated methods - The following methods exist only to maintain compatibility with previous versions
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates pretty html from string diff input
|
|
||||||
*/
|
|
||||||
Diff2Html.prototype.getPrettyHtmlFromDiff = function(diffInput, config) {
|
|
||||||
var configOrEmpty = config || {};
|
|
||||||
configOrEmpty.inputFormat = 'diff';
|
|
||||||
configOrEmpty.outputFormat = 'line-by-line';
|
|
||||||
return this.getPrettyHtml(diffInput, configOrEmpty);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates pretty html from a json object
|
|
||||||
*/
|
|
||||||
Diff2Html.prototype.getPrettyHtmlFromJson = function(diffJson, config) {
|
|
||||||
var configOrEmpty = config || {};
|
|
||||||
configOrEmpty.inputFormat = 'json';
|
|
||||||
configOrEmpty.outputFormat = 'line-by-line';
|
|
||||||
return this.getPrettyHtml(diffJson, configOrEmpty);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates pretty side by side html from string diff input
|
|
||||||
*/
|
|
||||||
Diff2Html.prototype.getPrettySideBySideHtmlFromDiff = function(diffInput, config) {
|
|
||||||
var configOrEmpty = config || {};
|
|
||||||
configOrEmpty.inputFormat = 'diff';
|
|
||||||
configOrEmpty.outputFormat = 'side-by-side';
|
|
||||||
return this.getPrettyHtml(diffInput, configOrEmpty);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generates pretty side by side html from a json object
|
|
||||||
*/
|
|
||||||
Diff2Html.prototype.getPrettySideBySideHtmlFromJson = function(diffJson, config) {
|
|
||||||
var configOrEmpty = config || {};
|
|
||||||
configOrEmpty.inputFormat = 'json';
|
|
||||||
configOrEmpty.outputFormat = 'side-by-side';
|
|
||||||
return this.getPrettyHtml(diffJson, configOrEmpty);
|
|
||||||
};
|
|
||||||
|
|
||||||
var diffObject = new Diff2Html();
|
|
||||||
module.exports.Diff2Html = diffObject;
|
|
||||||
|
|
||||||
// Expose diff2html in the browser
|
|
||||||
global.Diff2Html = diffObject;
|
|
||||||
|
|
||||||
})();
|
|
||||||
46
src/diff2html.ts
Normal file
46
src/diff2html.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import * as DiffParser from './diff-parser';
|
||||||
|
import { FileListRenderer } from './file-list-renderer';
|
||||||
|
import LineByLineRenderer, { LineByLineRendererConfig, defaultLineByLineRendererConfig } from './line-by-line-renderer';
|
||||||
|
import SideBySideRenderer, { SideBySideRendererConfig, defaultSideBySideRendererConfig } from './side-by-side-renderer';
|
||||||
|
import { DiffFile, OutputFormatType } from './types';
|
||||||
|
import HoganJsUtils, { HoganJsUtilsConfig } from './hoganjs-utils';
|
||||||
|
|
||||||
|
export interface Diff2HtmlConfig
|
||||||
|
extends DiffParser.DiffParserConfig,
|
||||||
|
LineByLineRendererConfig,
|
||||||
|
SideBySideRendererConfig,
|
||||||
|
HoganJsUtilsConfig {
|
||||||
|
outputFormat?: OutputFormatType;
|
||||||
|
drawFileList?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultDiff2HtmlConfig = {
|
||||||
|
...defaultLineByLineRendererConfig,
|
||||||
|
...defaultSideBySideRendererConfig,
|
||||||
|
outputFormat: OutputFormatType.LINE_BY_LINE,
|
||||||
|
drawFileList: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parse(diffInput: string, configuration: Diff2HtmlConfig = {}): DiffFile[] {
|
||||||
|
return DiffParser.parse(diffInput, { ...defaultDiff2HtmlConfig, ...configuration });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function html(diffInput: string | DiffFile[], configuration: Diff2HtmlConfig = {}): string {
|
||||||
|
const config = { ...defaultDiff2HtmlConfig, ...configuration };
|
||||||
|
|
||||||
|
const diffJson = typeof diffInput === 'string' ? DiffParser.parse(diffInput, config) : diffInput;
|
||||||
|
|
||||||
|
const hoganUtils = new HoganJsUtils(config);
|
||||||
|
|
||||||
|
const { colorScheme } = config;
|
||||||
|
const fileListRendererConfig = { colorScheme };
|
||||||
|
|
||||||
|
const fileList = config.drawFileList ? new FileListRenderer(hoganUtils, fileListRendererConfig).render(diffJson) : '';
|
||||||
|
|
||||||
|
const diffOutput =
|
||||||
|
config.outputFormat === 'side-by-side'
|
||||||
|
? new SideBySideRenderer(hoganUtils, config).render(diffJson)
|
||||||
|
: new LineByLineRenderer(hoganUtils, config).render(diffJson);
|
||||||
|
|
||||||
|
return fileList + diffOutput;
|
||||||
|
}
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* FileListPrinter (file-list-printer.js)
|
|
||||||
* Author: nmatpt
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var printerUtils = require('./printer-utils.js').PrinterUtils;
|
|
||||||
|
|
||||||
function FileListPrinter() {
|
|
||||||
}
|
|
||||||
|
|
||||||
FileListPrinter.prototype.generateFileList = function(diffFiles) {
|
|
||||||
return '<div class="d2h-file-list-wrapper">\n' +
|
|
||||||
' <div class="d2h-file-list-header">\n' +
|
|
||||||
' <span class="d2h-file-list-title">Files changed (' + diffFiles.length + ')  </span>\n' +
|
|
||||||
' <a class="d2h-file-switch d2h-hide">hide</a>\n' +
|
|
||||||
' <a class="d2h-file-switch d2h-show">show</a>\n' +
|
|
||||||
' </div>\n' +
|
|
||||||
' <table class="d2h-file-list">\n' +
|
|
||||||
|
|
||||||
diffFiles.map(function(file) {
|
|
||||||
return ' <tr class="d2h-file-list-line">\n' +
|
|
||||||
' <td class="d2h-lines-added">\n' +
|
|
||||||
' <span>+' + file.addedLines + '</span>\n' +
|
|
||||||
' </td>\n' +
|
|
||||||
' <td class="d2h-lines-deleted">\n' +
|
|
||||||
' <span>-' + file.deletedLines + '</span>\n' +
|
|
||||||
' </td>\n' +
|
|
||||||
' <td class="d2h-file-name-wrapper">\n' +
|
|
||||||
' <a href="#' + printerUtils.getHtmlId(file) + '" class="d2h-file-name">' +
|
|
||||||
' ' + printerUtils.getDiffName(file) +
|
|
||||||
' </a>\n' +
|
|
||||||
' </td>\n' +
|
|
||||||
' </tr>\n';
|
|
||||||
}).join('\n') +
|
|
||||||
'</table></div>\n';
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.FileListPrinter = new FileListPrinter();
|
|
||||||
|
|
||||||
})();
|
|
||||||
52
src/file-list-renderer.ts
Normal file
52
src/file-list-renderer.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import * as renderUtils from './render-utils';
|
||||||
|
import HoganJsUtils from './hoganjs-utils';
|
||||||
|
import { ColorSchemeType, DiffFile } from './types';
|
||||||
|
|
||||||
|
const baseTemplatesPath = 'file-summary';
|
||||||
|
const iconsBaseTemplatesPath = 'icon';
|
||||||
|
|
||||||
|
export interface FileListRendererConfig {
|
||||||
|
colorScheme?: ColorSchemeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultFileListRendererConfig = {
|
||||||
|
colorScheme: renderUtils.defaultRenderConfig.colorScheme,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class FileListRenderer {
|
||||||
|
private readonly hoganUtils: HoganJsUtils;
|
||||||
|
private readonly config: typeof defaultFileListRendererConfig;
|
||||||
|
|
||||||
|
constructor(hoganUtils: HoganJsUtils, config: FileListRendererConfig = {}) {
|
||||||
|
this.hoganUtils = hoganUtils;
|
||||||
|
this.config = { ...defaultFileListRendererConfig, ...config };
|
||||||
|
}
|
||||||
|
|
||||||
|
render(diffFiles: DiffFile[]): string {
|
||||||
|
const files = diffFiles
|
||||||
|
.map(file =>
|
||||||
|
this.hoganUtils.render(
|
||||||
|
baseTemplatesPath,
|
||||||
|
'line',
|
||||||
|
{
|
||||||
|
fileHtmlId: renderUtils.getHtmlId(file),
|
||||||
|
oldName: file.oldName,
|
||||||
|
newName: file.newName,
|
||||||
|
fileName: renderUtils.filenameDiff(file),
|
||||||
|
deletedLines: '-' + file.deletedLines,
|
||||||
|
addedLines: '+' + file.addedLines,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileIcon: this.hoganUtils.template(iconsBaseTemplatesPath, renderUtils.getFileIcon(file)),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
return this.hoganUtils.render(baseTemplatesPath, 'wrapper', {
|
||||||
|
colorScheme: renderUtils.colorSchemeToCss(this.config.colorScheme),
|
||||||
|
filesNumber: diffFiles.length,
|
||||||
|
files: files,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Utils (hoganjs-utils.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
var hogan = require('hogan.js');
|
|
||||||
|
|
||||||
var hoganTemplates = require('./templates/diff2html-templates.js');
|
|
||||||
|
|
||||||
var templatesPath = path.resolve(__dirname, 'templates');
|
|
||||||
|
|
||||||
function HoganJsUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
HoganJsUtils.prototype.render = function(namespace, view, params, configuration) {
|
|
||||||
var config = configuration || {};
|
|
||||||
var templateKey = this._templateKey(namespace, view);
|
|
||||||
|
|
||||||
var template = this._getTemplate(templateKey, config);
|
|
||||||
if (template) {
|
|
||||||
return template.render(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
HoganJsUtils.prototype._getTemplate = function(templateKey, config) {
|
|
||||||
var template;
|
|
||||||
|
|
||||||
if (!config.noCache) {
|
|
||||||
template = this._readFromCache(templateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!template) {
|
|
||||||
template = this._loadTemplate(templateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
return template;
|
|
||||||
};
|
|
||||||
|
|
||||||
HoganJsUtils.prototype._loadTemplate = function(templateKey) {
|
|
||||||
var template;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (fs.readFileSync) {
|
|
||||||
var templatePath = path.join(templatesPath, templateKey);
|
|
||||||
var templateContent = fs.readFileSync(templatePath + '.mustache', 'utf8');
|
|
||||||
template = hogan.compile(templateContent);
|
|
||||||
hoganTemplates[templateKey] = template;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to read (template: ' + templateKey + ') from fs: ' + e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return template;
|
|
||||||
};
|
|
||||||
|
|
||||||
HoganJsUtils.prototype._readFromCache = function(templateKey) {
|
|
||||||
return hoganTemplates[templateKey];
|
|
||||||
};
|
|
||||||
|
|
||||||
HoganJsUtils.prototype._templateKey = function(namespace, view) {
|
|
||||||
return namespace + '-' + view;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.HoganJsUtils = new HoganJsUtils();
|
|
||||||
|
|
||||||
})();
|
|
||||||
54
src/hoganjs-utils.ts
Normal file
54
src/hoganjs-utils.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import * as Hogan from 'hogan.js';
|
||||||
|
|
||||||
|
import { defaultTemplates } from './diff2html-templates';
|
||||||
|
|
||||||
|
export interface RawTemplates {
|
||||||
|
[name: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CompiledTemplates {
|
||||||
|
[name: string]: Hogan.Template;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HoganJsUtilsConfig {
|
||||||
|
compiledTemplates?: CompiledTemplates;
|
||||||
|
rawTemplates?: RawTemplates;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class HoganJsUtils {
|
||||||
|
private preCompiledTemplates: CompiledTemplates;
|
||||||
|
|
||||||
|
constructor({ compiledTemplates = {}, rawTemplates = {} }: HoganJsUtilsConfig) {
|
||||||
|
const compiledRawTemplates = Object.entries(rawTemplates).reduce<CompiledTemplates>(
|
||||||
|
(previousTemplates, [name, templateString]) => {
|
||||||
|
const compiledTemplate: Hogan.Template = Hogan.compile(templateString, { asString: false });
|
||||||
|
return { ...previousTemplates, [name]: compiledTemplate };
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
this.preCompiledTemplates = { ...defaultTemplates, ...compiledTemplates, ...compiledRawTemplates };
|
||||||
|
}
|
||||||
|
|
||||||
|
static compile(templateString: string): Hogan.Template {
|
||||||
|
return Hogan.compile(templateString, { asString: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
render(namespace: string, view: string, params: Hogan.Context, partials?: Hogan.Partials, indent?: string): string {
|
||||||
|
const templateKey = this.templateKey(namespace, view);
|
||||||
|
try {
|
||||||
|
const template = this.preCompiledTemplates[templateKey];
|
||||||
|
return template.render(params, partials, indent);
|
||||||
|
} catch (_e) {
|
||||||
|
throw new Error(`Could not find template to render '${templateKey}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template(namespace: string, view: string): Hogan.Template {
|
||||||
|
return this.preCompiledTemplates[this.templateKey(namespace, view)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private templateKey(namespace: string, view: string): string {
|
||||||
|
return `${namespace}-${view}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* HtmlPrinter (html-printer.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var LineByLinePrinter = require('./line-by-line-printer.js').LineByLinePrinter;
|
|
||||||
var SideBySidePrinter = require('./side-by-side-printer.js').SideBySidePrinter;
|
|
||||||
|
|
||||||
function HtmlPrinter() {
|
|
||||||
}
|
|
||||||
|
|
||||||
HtmlPrinter.prototype.generateLineByLineJsonHtml = function(diffFiles, config) {
|
|
||||||
var lineByLinePrinter = new LineByLinePrinter(config);
|
|
||||||
return lineByLinePrinter.generateLineByLineJsonHtml(diffFiles);
|
|
||||||
};
|
|
||||||
|
|
||||||
HtmlPrinter.prototype.generateSideBySideJsonHtml = function(diffFiles, config) {
|
|
||||||
var sideBySidePrinter = new SideBySidePrinter(config);
|
|
||||||
return sideBySidePrinter.generateSideBySideJsonHtml(diffFiles);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.HtmlPrinter = new HtmlPrinter();
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -1,191 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* LineByLinePrinter (line-by-line-printer.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var diffParser = require('./diff-parser.js').DiffParser;
|
|
||||||
var printerUtils = require('./printer-utils.js').PrinterUtils;
|
|
||||||
var utils = require('./utils.js').Utils;
|
|
||||||
var Rematch = require('./rematch.js').Rematch;
|
|
||||||
|
|
||||||
var hoganUtils = require('./hoganjs-utils.js').HoganJsUtils;
|
|
||||||
var baseTemplatesPath = 'line-by-line';
|
|
||||||
|
|
||||||
function LineByLinePrinter(config) {
|
|
||||||
this.config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
LineByLinePrinter.prototype.makeFileDiffHtml = function(file, diffs) {
|
|
||||||
return hoganUtils.render(baseTemplatesPath, 'file-diff', {
|
|
||||||
file: file,
|
|
||||||
fileDiffName: printerUtils.getDiffName(file),
|
|
||||||
fileHtmlId: printerUtils.getHtmlId(file),
|
|
||||||
diffs: diffs
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
LineByLinePrinter.prototype.makeLineByLineHtmlWrapper = function(content) {
|
|
||||||
return hoganUtils.render(baseTemplatesPath, 'wrapper', {'content': content});
|
|
||||||
};
|
|
||||||
|
|
||||||
LineByLinePrinter.prototype.generateLineByLineJsonHtml = function(diffFiles) {
|
|
||||||
var that = this;
|
|
||||||
var htmlDiffs = diffFiles.map(function(file) {
|
|
||||||
var diffs;
|
|
||||||
if (file.blocks.length) {
|
|
||||||
diffs = that._generateFileHtml(file);
|
|
||||||
} else {
|
|
||||||
diffs = that._generateEmptyDiff();
|
|
||||||
}
|
|
||||||
return that.makeFileDiffHtml(file, diffs);
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.makeLineByLineHtmlWrapper(htmlDiffs.join('\n'));
|
|
||||||
};
|
|
||||||
|
|
||||||
var matcher = Rematch.rematch(function(a, b) {
|
|
||||||
var amod = a.content.substr(1);
|
|
||||||
var bmod = b.content.substr(1);
|
|
||||||
|
|
||||||
return Rematch.distance(amod, bmod);
|
|
||||||
});
|
|
||||||
|
|
||||||
LineByLinePrinter.prototype.makeColumnLineNumberHtml = function(block) {
|
|
||||||
return hoganUtils.render(baseTemplatesPath, 'column-line-number', {
|
|
||||||
diffParser: diffParser,
|
|
||||||
block: utils.escape(block.header)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
LineByLinePrinter.prototype._generateFileHtml = function(file) {
|
|
||||||
var that = this;
|
|
||||||
return file.blocks.map(function(block) {
|
|
||||||
|
|
||||||
var lines = that.makeColumnLineNumberHtml(block);
|
|
||||||
var oldLines = [];
|
|
||||||
var newLines = [];
|
|
||||||
|
|
||||||
function processChangeBlock() {
|
|
||||||
var matches;
|
|
||||||
var insertType;
|
|
||||||
var deleteType;
|
|
||||||
|
|
||||||
var comparisons = oldLines.length * newLines.length;
|
|
||||||
var maxComparisons = that.config.matchingMaxComparisons || 2500;
|
|
||||||
var doMatching = comparisons < maxComparisons && (that.config.matching === 'lines' ||
|
|
||||||
that.config.matching === 'words');
|
|
||||||
|
|
||||||
if (doMatching) {
|
|
||||||
matches = matcher(oldLines, newLines);
|
|
||||||
insertType = diffParser.LINE_TYPE.INSERT_CHANGES;
|
|
||||||
deleteType = diffParser.LINE_TYPE.DELETE_CHANGES;
|
|
||||||
} else {
|
|
||||||
matches = [[oldLines, newLines]];
|
|
||||||
insertType = diffParser.LINE_TYPE.INSERTS;
|
|
||||||
deleteType = diffParser.LINE_TYPE.DELETES;
|
|
||||||
}
|
|
||||||
|
|
||||||
matches.forEach(function(match) {
|
|
||||||
oldLines = match[0];
|
|
||||||
newLines = match[1];
|
|
||||||
|
|
||||||
var processedOldLines = [];
|
|
||||||
var processedNewLines = [];
|
|
||||||
|
|
||||||
var common = Math.min(oldLines.length, newLines.length);
|
|
||||||
|
|
||||||
var oldLine, newLine;
|
|
||||||
for (var j = 0; j < common; j++) {
|
|
||||||
oldLine = oldLines[j];
|
|
||||||
newLine = newLines[j];
|
|
||||||
|
|
||||||
that.config.isCombined = file.isCombined;
|
|
||||||
var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, that.config);
|
|
||||||
|
|
||||||
processedOldLines +=
|
|
||||||
that.makeLineHtml(deleteType, oldLine.oldNumber, oldLine.newNumber,
|
|
||||||
diff.first.line, diff.first.prefix);
|
|
||||||
processedNewLines +=
|
|
||||||
that.makeLineHtml(insertType, newLine.oldNumber, newLine.newNumber,
|
|
||||||
diff.second.line, diff.second.prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
lines += processedOldLines + processedNewLines;
|
|
||||||
lines += that._processLines(oldLines.slice(common), newLines.slice(common));
|
|
||||||
});
|
|
||||||
|
|
||||||
oldLines = [];
|
|
||||||
newLines = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < block.lines.length; i++) {
|
|
||||||
var line = block.lines[i];
|
|
||||||
var escapedLine = utils.escape(line.content);
|
|
||||||
|
|
||||||
if (line.type !== diffParser.LINE_TYPE.INSERTS &&
|
|
||||||
(newLines.length > 0 || (line.type !== diffParser.LINE_TYPE.DELETES && oldLines.length > 0))) {
|
|
||||||
processChangeBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.type === diffParser.LINE_TYPE.CONTEXT) {
|
|
||||||
lines += that.makeLineHtml(line.type, line.oldNumber, line.newNumber, escapedLine);
|
|
||||||
} else if (line.type === diffParser.LINE_TYPE.INSERTS && !oldLines.length) {
|
|
||||||
lines += that.makeLineHtml(line.type, line.oldNumber, line.newNumber, escapedLine);
|
|
||||||
} else if (line.type === diffParser.LINE_TYPE.DELETES) {
|
|
||||||
oldLines.push(line);
|
|
||||||
} else if (line.type === diffParser.LINE_TYPE.INSERTS && Boolean(oldLines.length)) {
|
|
||||||
newLines.push(line);
|
|
||||||
} else {
|
|
||||||
console.error('Unknown state in html line-by-line generator');
|
|
||||||
processChangeBlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processChangeBlock();
|
|
||||||
|
|
||||||
return lines;
|
|
||||||
}).join('\n');
|
|
||||||
};
|
|
||||||
|
|
||||||
LineByLinePrinter.prototype._processLines = function(oldLines, newLines) {
|
|
||||||
var lines = '';
|
|
||||||
|
|
||||||
for (var i = 0; i < oldLines.length; i++) {
|
|
||||||
var oldLine = oldLines[i];
|
|
||||||
var oldEscapedLine = utils.escape(oldLine.content);
|
|
||||||
lines += this.makeLineHtml(oldLine.type, oldLine.oldNumber, oldLine.newNumber, oldEscapedLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var j = 0; j < newLines.length; j++) {
|
|
||||||
var newLine = newLines[j];
|
|
||||||
var newEscapedLine = utils.escape(newLine.content);
|
|
||||||
lines += this.makeLineHtml(newLine.type, newLine.oldNumber, newLine.newNumber, newEscapedLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines;
|
|
||||||
};
|
|
||||||
|
|
||||||
LineByLinePrinter.prototype.makeLineHtml = function(type, oldNumber, newNumber, content, prefix) {
|
|
||||||
return hoganUtils.render(baseTemplatesPath, 'line',
|
|
||||||
{
|
|
||||||
type: type,
|
|
||||||
oldNumber: utils.valueOrEmpty(oldNumber),
|
|
||||||
newNumber: utils.valueOrEmpty(newNumber),
|
|
||||||
prefix: prefix && utils.convertWhiteSpaceToNonBreakingSpace(prefix),
|
|
||||||
content: content && utils.convertWhiteSpaceToNonBreakingSpace(content)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
LineByLinePrinter.prototype._generateEmptyDiff = function() {
|
|
||||||
return hoganUtils.render(baseTemplatesPath, 'empty-diff', {
|
|
||||||
diffParser: diffParser
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.LineByLinePrinter = LineByLinePrinter;
|
|
||||||
|
|
||||||
})();
|
|
||||||
299
src/line-by-line-renderer.ts
Normal file
299
src/line-by-line-renderer.ts
Normal file
|
|
@ -0,0 +1,299 @@
|
||||||
|
import HoganJsUtils from './hoganjs-utils';
|
||||||
|
import * as Rematch from './rematch';
|
||||||
|
import * as renderUtils from './render-utils';
|
||||||
|
import {
|
||||||
|
DiffFile,
|
||||||
|
DiffLine,
|
||||||
|
LineType,
|
||||||
|
DiffBlock,
|
||||||
|
DiffLineDeleted,
|
||||||
|
DiffLineContent,
|
||||||
|
DiffLineContext,
|
||||||
|
DiffLineInserted,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export interface LineByLineRendererConfig extends renderUtils.RenderConfig {
|
||||||
|
renderNothingWhenEmpty?: boolean;
|
||||||
|
matchingMaxComparisons?: number;
|
||||||
|
maxLineSizeInBlockForComparison?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultLineByLineRendererConfig = {
|
||||||
|
...renderUtils.defaultRenderConfig,
|
||||||
|
renderNothingWhenEmpty: false,
|
||||||
|
matchingMaxComparisons: 2500,
|
||||||
|
maxLineSizeInBlockForComparison: 200,
|
||||||
|
};
|
||||||
|
|
||||||
|
const genericTemplatesPath = 'generic';
|
||||||
|
const baseTemplatesPath = 'line-by-line';
|
||||||
|
const iconsBaseTemplatesPath = 'icon';
|
||||||
|
const tagsBaseTemplatesPath = 'tag';
|
||||||
|
|
||||||
|
export default class LineByLineRenderer {
|
||||||
|
private readonly hoganUtils: HoganJsUtils;
|
||||||
|
private readonly config: typeof defaultLineByLineRendererConfig;
|
||||||
|
|
||||||
|
constructor(hoganUtils: HoganJsUtils, config: LineByLineRendererConfig = {}) {
|
||||||
|
this.hoganUtils = hoganUtils;
|
||||||
|
this.config = { ...defaultLineByLineRendererConfig, ...config };
|
||||||
|
}
|
||||||
|
|
||||||
|
render(diffFiles: DiffFile[]): string {
|
||||||
|
const diffsHtml = diffFiles
|
||||||
|
.map(file => {
|
||||||
|
let diffs;
|
||||||
|
if (file.blocks.length) {
|
||||||
|
diffs = this.generateFileHtml(file);
|
||||||
|
} else {
|
||||||
|
diffs = this.generateEmptyDiff();
|
||||||
|
}
|
||||||
|
return this.makeFileDiffHtml(file, diffs);
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
return this.hoganUtils.render(genericTemplatesPath, 'wrapper', {
|
||||||
|
colorScheme: renderUtils.colorSchemeToCss(this.config.colorScheme),
|
||||||
|
content: diffsHtml,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
makeFileDiffHtml(file: DiffFile, diffs: string): string {
|
||||||
|
if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0) return '';
|
||||||
|
|
||||||
|
const fileDiffTemplate = this.hoganUtils.template(baseTemplatesPath, 'file-diff');
|
||||||
|
const filePathTemplate = this.hoganUtils.template(genericTemplatesPath, 'file-path');
|
||||||
|
const fileIconTemplate = this.hoganUtils.template(iconsBaseTemplatesPath, 'file');
|
||||||
|
const fileTagTemplate = this.hoganUtils.template(tagsBaseTemplatesPath, renderUtils.getFileIcon(file));
|
||||||
|
|
||||||
|
return fileDiffTemplate.render({
|
||||||
|
file: file,
|
||||||
|
fileHtmlId: renderUtils.getHtmlId(file),
|
||||||
|
diffs: diffs,
|
||||||
|
filePath: filePathTemplate.render(
|
||||||
|
{
|
||||||
|
fileDiffName: renderUtils.filenameDiff(file),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileIcon: fileIconTemplate,
|
||||||
|
fileTag: fileTagTemplate,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
generateEmptyDiff(): string {
|
||||||
|
return this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
|
||||||
|
contentClass: 'd2h-code-line',
|
||||||
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
generateFileHtml(file: DiffFile): string {
|
||||||
|
const matcher = Rematch.newMatcherFn(
|
||||||
|
Rematch.newDistanceFn((e: DiffLine) => renderUtils.deconstructLine(e.content, file.isCombined).content),
|
||||||
|
);
|
||||||
|
|
||||||
|
return file.blocks
|
||||||
|
.map(block => {
|
||||||
|
let lines = this.hoganUtils.render(genericTemplatesPath, 'block-header', {
|
||||||
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
|
blockHeader: file.isTooBig ? block.header : renderUtils.escapeForHtml(block.header),
|
||||||
|
lineClass: 'd2h-code-linenumber',
|
||||||
|
contentClass: 'd2h-code-line',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.applyLineGroupping(block).forEach(([contextLines, oldLines, newLines]) => {
|
||||||
|
if (oldLines.length && newLines.length && !contextLines.length) {
|
||||||
|
this.applyRematchMatching(oldLines, newLines, matcher).map(([oldLines, newLines]) => {
|
||||||
|
const { left, right } = this.processChangedLines(file, file.isCombined, oldLines, newLines);
|
||||||
|
lines += left;
|
||||||
|
lines += right;
|
||||||
|
});
|
||||||
|
} else if (contextLines.length) {
|
||||||
|
contextLines.forEach(line => {
|
||||||
|
const { prefix, content } = renderUtils.deconstructLine(line.content, file.isCombined);
|
||||||
|
lines += this.generateSingleLineHtml(file, {
|
||||||
|
type: renderUtils.CSSLineClass.CONTEXT,
|
||||||
|
prefix: prefix,
|
||||||
|
content: content,
|
||||||
|
oldNumber: line.oldNumber,
|
||||||
|
newNumber: line.newNumber,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (oldLines.length || newLines.length) {
|
||||||
|
const { left, right } = this.processChangedLines(file, file.isCombined, oldLines, newLines);
|
||||||
|
lines += left;
|
||||||
|
lines += right;
|
||||||
|
} else {
|
||||||
|
console.error('Unknown state reached while processing groups of lines', contextLines, oldLines, newLines);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
applyLineGroupping(block: DiffBlock): DiffLineGroups {
|
||||||
|
const blockLinesGroups: DiffLineGroups = [];
|
||||||
|
|
||||||
|
let oldLines: (DiffLineDeleted & DiffLineContent)[] = [];
|
||||||
|
let newLines: (DiffLineInserted & DiffLineContent)[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < block.lines.length; i++) {
|
||||||
|
const diffLine = block.lines[i];
|
||||||
|
|
||||||
|
if (
|
||||||
|
(diffLine.type !== LineType.INSERT && newLines.length) ||
|
||||||
|
(diffLine.type === LineType.CONTEXT && oldLines.length > 0)
|
||||||
|
) {
|
||||||
|
blockLinesGroups.push([[], oldLines, newLines]);
|
||||||
|
oldLines = [];
|
||||||
|
newLines = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diffLine.type === LineType.CONTEXT) {
|
||||||
|
blockLinesGroups.push([[diffLine], [], []]);
|
||||||
|
} else if (diffLine.type === LineType.INSERT && oldLines.length === 0) {
|
||||||
|
blockLinesGroups.push([[], [], [diffLine]]);
|
||||||
|
} else if (diffLine.type === LineType.INSERT && oldLines.length > 0) {
|
||||||
|
newLines.push(diffLine);
|
||||||
|
} else if (diffLine.type === LineType.DELETE) {
|
||||||
|
oldLines.push(diffLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldLines.length || newLines.length) {
|
||||||
|
blockLinesGroups.push([[], oldLines, newLines]);
|
||||||
|
oldLines = [];
|
||||||
|
newLines = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockLinesGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyRematchMatching(
|
||||||
|
oldLines: DiffLine[],
|
||||||
|
newLines: DiffLine[],
|
||||||
|
matcher: Rematch.MatcherFn<DiffLine>,
|
||||||
|
): DiffLine[][][] {
|
||||||
|
const comparisons = oldLines.length * newLines.length;
|
||||||
|
const maxLineSizeInBlock = Math.max.apply(
|
||||||
|
null,
|
||||||
|
[0].concat(oldLines.concat(newLines).map(elem => elem.content.length)),
|
||||||
|
);
|
||||||
|
const doMatching =
|
||||||
|
comparisons < this.config.matchingMaxComparisons &&
|
||||||
|
maxLineSizeInBlock < this.config.maxLineSizeInBlockForComparison &&
|
||||||
|
(this.config.matching === 'lines' || this.config.matching === 'words');
|
||||||
|
|
||||||
|
return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
|
||||||
|
}
|
||||||
|
|
||||||
|
processChangedLines(file: DiffFile, isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml {
|
||||||
|
const fileHtml = {
|
||||||
|
right: '',
|
||||||
|
left: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const maxLinesNumber = Math.max(oldLines.length, newLines.length);
|
||||||
|
for (let i = 0; i < maxLinesNumber; i++) {
|
||||||
|
const oldLine = oldLines[i];
|
||||||
|
const newLine = newLines[i];
|
||||||
|
|
||||||
|
const diff =
|
||||||
|
oldLine !== undefined && newLine !== undefined
|
||||||
|
? renderUtils.diffHighlight(oldLine.content, newLine.content, isCombined, this.config)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const preparedOldLine =
|
||||||
|
oldLine !== undefined && oldLine.oldNumber !== undefined
|
||||||
|
? {
|
||||||
|
...(diff !== undefined
|
||||||
|
? {
|
||||||
|
prefix: diff.oldLine.prefix,
|
||||||
|
content: diff.oldLine.content,
|
||||||
|
type: renderUtils.CSSLineClass.DELETE_CHANGES,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...renderUtils.deconstructLine(oldLine.content, isCombined),
|
||||||
|
type: renderUtils.toCSSClass(oldLine.type),
|
||||||
|
}),
|
||||||
|
oldNumber: oldLine.oldNumber,
|
||||||
|
newNumber: oldLine.newNumber,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const preparedNewLine =
|
||||||
|
newLine !== undefined && newLine.newNumber !== undefined
|
||||||
|
? {
|
||||||
|
...(diff !== undefined
|
||||||
|
? {
|
||||||
|
prefix: diff.newLine.prefix,
|
||||||
|
content: diff.newLine.content,
|
||||||
|
type: renderUtils.CSSLineClass.INSERT_CHANGES,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...renderUtils.deconstructLine(newLine.content, isCombined),
|
||||||
|
type: renderUtils.toCSSClass(newLine.type),
|
||||||
|
}),
|
||||||
|
oldNumber: newLine.oldNumber,
|
||||||
|
newNumber: newLine.newNumber,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const { left, right } = this.generateLineHtml(file, preparedOldLine, preparedNewLine);
|
||||||
|
fileHtml.left += left;
|
||||||
|
fileHtml.right += right;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateLineHtml(file: DiffFile, oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml {
|
||||||
|
return {
|
||||||
|
left: this.generateSingleLineHtml(file, oldLine),
|
||||||
|
right: this.generateSingleLineHtml(file, newLine),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSingleLineHtml(file: DiffFile, line?: DiffPreparedLine): string {
|
||||||
|
if (line === undefined) return '';
|
||||||
|
|
||||||
|
const lineNumberHtml = this.hoganUtils.render(baseTemplatesPath, 'numbers', {
|
||||||
|
oldNumber: line.oldNumber || '',
|
||||||
|
newNumber: line.newNumber || '',
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.hoganUtils.render(genericTemplatesPath, 'line', {
|
||||||
|
type: line.type,
|
||||||
|
lineClass: 'd2h-code-linenumber',
|
||||||
|
contentClass: 'd2h-code-line',
|
||||||
|
prefix: line.prefix === ' ' ? ' ' : line.prefix,
|
||||||
|
content: line.content,
|
||||||
|
lineNumber: lineNumberHtml,
|
||||||
|
line,
|
||||||
|
file,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiffLineGroups = [
|
||||||
|
(DiffLineContext & DiffLineContent)[],
|
||||||
|
(DiffLineDeleted & DiffLineContent)[],
|
||||||
|
(DiffLineInserted & DiffLineContent)[],
|
||||||
|
][];
|
||||||
|
|
||||||
|
type DiffPreparedLine = {
|
||||||
|
type: renderUtils.CSSLineClass;
|
||||||
|
prefix: string;
|
||||||
|
content: string;
|
||||||
|
oldNumber?: number;
|
||||||
|
newNumber?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FileHtml = {
|
||||||
|
left: string;
|
||||||
|
right: string;
|
||||||
|
};
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* PrinterUtils (printer-utils.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var jsDiff = require('diff');
|
|
||||||
var utils = require('./utils.js').Utils;
|
|
||||||
var Rematch = require('./rematch.js').Rematch;
|
|
||||||
|
|
||||||
function PrinterUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
PrinterUtils.prototype.getHtmlId = function(file) {
|
|
||||||
var hashCode = function(text) {
|
|
||||||
var i, chr, len;
|
|
||||||
var hash = 0;
|
|
||||||
|
|
||||||
for (i = 0, len = text.length; i < len; i++) {
|
|
||||||
chr = text.charCodeAt(i);
|
|
||||||
hash = ((hash << 5) - hash) + chr;
|
|
||||||
hash |= 0; // Convert to 32bit integer
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
return 'd2h-' + hashCode(this.getDiffName(file)).toString().slice(-6);
|
|
||||||
};
|
|
||||||
|
|
||||||
PrinterUtils.prototype.getDiffName = function(file) {
|
|
||||||
var oldFilename = file.oldName;
|
|
||||||
var newFilename = file.newName;
|
|
||||||
|
|
||||||
if (oldFilename && newFilename && oldFilename !== newFilename && !isDevNullName(oldFilename) && !isDevNullName(newFilename)) {
|
|
||||||
return oldFilename + ' -> ' + newFilename;
|
|
||||||
} else if (newFilename && !isDevNullName(newFilename)) {
|
|
||||||
return newFilename;
|
|
||||||
} else if (oldFilename) {
|
|
||||||
return oldFilename;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Unknown filename';
|
|
||||||
};
|
|
||||||
|
|
||||||
PrinterUtils.prototype.diffHighlight = function(diffLine1, diffLine2, config) {
|
|
||||||
var linePrefix1, linePrefix2, unprefixedLine1, unprefixedLine2;
|
|
||||||
|
|
||||||
var prefixSize = 1;
|
|
||||||
|
|
||||||
if (config.isCombined) {
|
|
||||||
prefixSize = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
linePrefix1 = diffLine1.substr(0, prefixSize);
|
|
||||||
linePrefix2 = diffLine2.substr(0, prefixSize);
|
|
||||||
unprefixedLine1 = diffLine1.substr(prefixSize);
|
|
||||||
unprefixedLine2 = diffLine2.substr(prefixSize);
|
|
||||||
|
|
||||||
var diff;
|
|
||||||
if (config.charByChar) {
|
|
||||||
diff = jsDiff.diffChars(unprefixedLine1, unprefixedLine2);
|
|
||||||
} else {
|
|
||||||
diff = jsDiff.diffWordsWithSpace(unprefixedLine1, unprefixedLine2);
|
|
||||||
}
|
|
||||||
|
|
||||||
var highlightedLine = '';
|
|
||||||
|
|
||||||
var changedWords = [];
|
|
||||||
if (!config.charByChar && config.matching === 'words') {
|
|
||||||
var treshold = 0.25;
|
|
||||||
|
|
||||||
if (typeof (config.matchWordsThreshold) !== 'undefined') {
|
|
||||||
treshold = config.matchWordsThreshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
var matcher = Rematch.rematch(function(a, b) {
|
|
||||||
var amod = a.value;
|
|
||||||
var bmod = b.value;
|
|
||||||
|
|
||||||
return Rematch.distance(amod, bmod);
|
|
||||||
});
|
|
||||||
|
|
||||||
var removed = diff.filter(function isRemoved(element) {
|
|
||||||
return element.removed;
|
|
||||||
});
|
|
||||||
|
|
||||||
var added = diff.filter(function isAdded(element) {
|
|
||||||
return element.added;
|
|
||||||
});
|
|
||||||
|
|
||||||
var chunks = matcher(added, removed);
|
|
||||||
chunks.forEach(function(chunk) {
|
|
||||||
if (chunk[0].length === 1 && chunk[1].length === 1) {
|
|
||||||
var dist = Rematch.distance(chunk[0][0].value, chunk[1][0].value);
|
|
||||||
if (dist < treshold) {
|
|
||||||
changedWords.push(chunk[0][0]);
|
|
||||||
changedWords.push(chunk[1][0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
diff.forEach(function(part) {
|
|
||||||
var addClass = changedWords.indexOf(part) > -1 ? ' class="d2h-change"' : '';
|
|
||||||
var elemType = part.added ? 'ins' : part.removed ? 'del' : null;
|
|
||||||
var escapedValue = utils.escape(part.value);
|
|
||||||
|
|
||||||
if (elemType !== null) {
|
|
||||||
highlightedLine += '<' + elemType + addClass + '>' + escapedValue + '</' + elemType + '>';
|
|
||||||
} else {
|
|
||||||
highlightedLine += escapedValue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
first: {
|
|
||||||
prefix: linePrefix1,
|
|
||||||
line: removeIns(highlightedLine)
|
|
||||||
},
|
|
||||||
second: {
|
|
||||||
prefix: linePrefix2,
|
|
||||||
line: removeDel(highlightedLine)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function isDevNullName(name) {
|
|
||||||
return name.indexOf('dev/null') !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeIns(line) {
|
|
||||||
return line.replace(/(<ins[^>]*>((.|\n)*?)<\/ins>)/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDel(line) {
|
|
||||||
return line.replace(/(<del[^>]*>((.|\n)*?)<\/del>)/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.PrinterUtils = new PrinterUtils();
|
|
||||||
|
|
||||||
})();
|
|
||||||
149
src/rematch.js
149
src/rematch.js
|
|
@ -1,149 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Rematch (rematch.js)
|
|
||||||
* Matching two sequences of objects by similarity
|
|
||||||
* Author: W. Illmeyer, Nexxar GmbH
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var Rematch = {};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2011 Andrei Mackenzie
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
function levenshtein(a, b) {
|
|
||||||
if (a.length == 0) {
|
|
||||||
return b.length;
|
|
||||||
}
|
|
||||||
if (b.length == 0) {
|
|
||||||
return a.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
var matrix = [];
|
|
||||||
|
|
||||||
// Increment along the first column of each row
|
|
||||||
var i;
|
|
||||||
for (i = 0; i <= b.length; i++) {
|
|
||||||
matrix[i] = [i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment each column in the first row
|
|
||||||
var j;
|
|
||||||
for (j = 0; j <= a.length; j++) {
|
|
||||||
matrix[0][j] = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill in the rest of the matrix
|
|
||||||
for (i = 1; i <= b.length; i++) {
|
|
||||||
for (j = 1; j <= a.length; j++) {
|
|
||||||
if (b.charAt(i - 1) == a.charAt(j - 1)) {
|
|
||||||
matrix[i][j] = matrix[i - 1][j - 1];
|
|
||||||
} else {
|
|
||||||
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // Substitution
|
|
||||||
Math.min(matrix[i][j - 1] + 1, // Insertion
|
|
||||||
matrix[i - 1][j] + 1)); // Deletion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matrix[b.length][a.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
Rematch.levenshtein = levenshtein;
|
|
||||||
|
|
||||||
Rematch.distance = function distance(x, y) {
|
|
||||||
x = x.trim();
|
|
||||||
y = y.trim();
|
|
||||||
var lev = levenshtein(x, y);
|
|
||||||
var score = lev / (x.length + y.length);
|
|
||||||
|
|
||||||
return score;
|
|
||||||
};
|
|
||||||
|
|
||||||
Rematch.rematch = function rematch(distanceFunction) {
|
|
||||||
function findBestMatch(a, b, cache) {
|
|
||||||
var cachecount = 0;
|
|
||||||
|
|
||||||
for (var key in cache) {
|
|
||||||
cachecount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bestMatchDist = Infinity;
|
|
||||||
var bestMatch;
|
|
||||||
for (var i = 0; i < a.length; ++i) {
|
|
||||||
for (var j = 0; j < b.length; ++j) {
|
|
||||||
var cacheKey = JSON.stringify([a[i], b[j]]);
|
|
||||||
var md;
|
|
||||||
if (cache.hasOwnProperty(cacheKey)) {
|
|
||||||
md = cache[cacheKey];
|
|
||||||
} else {
|
|
||||||
md = distanceFunction(a[i], b[j]);
|
|
||||||
cache[cacheKey] = md;
|
|
||||||
}
|
|
||||||
if (md < bestMatchDist) {
|
|
||||||
bestMatchDist = md;
|
|
||||||
bestMatch = {indexA: i, indexB: j, score: bestMatchDist};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestMatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
function group(a, b, level, cache) {
|
|
||||||
if (typeof (cache) === "undefined") {
|
|
||||||
cache = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var bm = findBestMatch(a, b, cache);
|
|
||||||
|
|
||||||
if (!level) {
|
|
||||||
level = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bm || (a.length + b.length < 3)) {
|
|
||||||
return [[a, b]];
|
|
||||||
}
|
|
||||||
|
|
||||||
var a1 = a.slice(0, bm.indexA);
|
|
||||||
var b1 = b.slice(0, bm.indexB);
|
|
||||||
var aMatch = [a[bm.indexA]];
|
|
||||||
var bMatch = [b[bm.indexB]];
|
|
||||||
var tailA = bm.indexA + 1;
|
|
||||||
var tailB = bm.indexB + 1;
|
|
||||||
var a2 = a.slice(tailA);
|
|
||||||
var b2 = b.slice(tailB);
|
|
||||||
|
|
||||||
var group1 = group(a1, b1, level + 1, cache);
|
|
||||||
var groupMatch = group(aMatch, bMatch, level + 1, cache);
|
|
||||||
var group2 = group(a2, b2, level + 1, cache);
|
|
||||||
var result = groupMatch;
|
|
||||||
|
|
||||||
if (bm.indexA > 0 || bm.indexB > 0) {
|
|
||||||
result = group1.concat(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.length > tailA || b.length > tailB) {
|
|
||||||
result = result.concat(group2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return group;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.Rematch = Rematch;
|
|
||||||
|
|
||||||
})();
|
|
||||||
135
src/rematch.ts
Normal file
135
src/rematch.ts
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Matching two sequences of objects by similarity
|
||||||
|
* Author: W. Illmeyer, Nexxar GmbH
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type BestMatch = {
|
||||||
|
indexA: number;
|
||||||
|
indexB: number;
|
||||||
|
score: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2011 Andrei Mackenzie
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
export function levenshtein(a: string, b: string): number {
|
||||||
|
if (a.length === 0) {
|
||||||
|
return b.length;
|
||||||
|
}
|
||||||
|
if (b.length === 0) {
|
||||||
|
return a.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const matrix = [];
|
||||||
|
|
||||||
|
// Increment along the first column of each row
|
||||||
|
let i;
|
||||||
|
for (i = 0; i <= b.length; i++) {
|
||||||
|
matrix[i] = [i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment each column in the first row
|
||||||
|
let j;
|
||||||
|
for (j = 0; j <= a.length; j++) {
|
||||||
|
matrix[0][j] = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the rest of the matrix
|
||||||
|
for (i = 1; i <= b.length; i++) {
|
||||||
|
for (j = 1; j <= a.length; j++) {
|
||||||
|
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
||||||
|
matrix[i][j] = matrix[i - 1][j - 1];
|
||||||
|
} else {
|
||||||
|
matrix[i][j] = Math.min(
|
||||||
|
matrix[i - 1][j - 1] + 1, // Substitution
|
||||||
|
Math.min(
|
||||||
|
matrix[i][j - 1] + 1, // Insertion
|
||||||
|
matrix[i - 1][j] + 1,
|
||||||
|
),
|
||||||
|
); // Deletion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matrix[b.length][a.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DistanceFn<T> = (x: T, y: T) => number;
|
||||||
|
|
||||||
|
export function newDistanceFn<T>(str: (value: T) => string): DistanceFn<T> {
|
||||||
|
return (x: T, y: T): number => {
|
||||||
|
const xValue = str(x).trim();
|
||||||
|
const yValue = str(y).trim();
|
||||||
|
const lev = levenshtein(xValue, yValue);
|
||||||
|
return lev / (xValue.length + yValue.length);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MatcherFn<T> = (a: T[], b: T[], level?: number, cache?: Map<string, number>) => T[][][];
|
||||||
|
|
||||||
|
export function newMatcherFn<T>(distance: (x: T, y: T) => number): MatcherFn<T> {
|
||||||
|
function findBestMatch(a: T[], b: T[], cache: Map<string, number> = new Map()): BestMatch | undefined {
|
||||||
|
let bestMatchDist = Infinity;
|
||||||
|
let bestMatch;
|
||||||
|
|
||||||
|
for (let i = 0; i < a.length; ++i) {
|
||||||
|
for (let j = 0; j < b.length; ++j) {
|
||||||
|
const cacheKey = JSON.stringify([a[i], b[j]]);
|
||||||
|
let md;
|
||||||
|
if (!(cache.has(cacheKey) && (md = cache.get(cacheKey)))) {
|
||||||
|
md = distance(a[i], b[j]);
|
||||||
|
cache.set(cacheKey, md);
|
||||||
|
}
|
||||||
|
if (md < bestMatchDist) {
|
||||||
|
bestMatchDist = md;
|
||||||
|
bestMatch = { indexA: i, indexB: j, score: bestMatchDist };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
function group(a: T[], b: T[], level = 0, cache: Map<string, number> = new Map()): T[][][] {
|
||||||
|
const bm = findBestMatch(a, b, cache);
|
||||||
|
|
||||||
|
if (!bm || a.length + b.length < 3) {
|
||||||
|
return [[a, b]];
|
||||||
|
}
|
||||||
|
|
||||||
|
const a1 = a.slice(0, bm.indexA);
|
||||||
|
const b1 = b.slice(0, bm.indexB);
|
||||||
|
const aMatch = [a[bm.indexA]];
|
||||||
|
const bMatch = [b[bm.indexB]];
|
||||||
|
const tailA = bm.indexA + 1;
|
||||||
|
const tailB = bm.indexB + 1;
|
||||||
|
const a2 = a.slice(tailA);
|
||||||
|
const b2 = b.slice(tailB);
|
||||||
|
|
||||||
|
const group1 = group(a1, b1, level + 1, cache);
|
||||||
|
const groupMatch = group(aMatch, bMatch, level + 1, cache);
|
||||||
|
const group2 = group(a2, b2, level + 1, cache);
|
||||||
|
let result = groupMatch;
|
||||||
|
|
||||||
|
if (bm.indexA > 0 || bm.indexB > 0) {
|
||||||
|
result = group1.concat(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.length > tailA || b.length > tailB) {
|
||||||
|
result = result.concat(group2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
304
src/render-utils.ts
Normal file
304
src/render-utils.ts
Normal file
|
|
@ -0,0 +1,304 @@
|
||||||
|
import * as jsDiff from 'diff';
|
||||||
|
|
||||||
|
import { unifyPath, hashCode } from './utils';
|
||||||
|
import * as rematch from './rematch';
|
||||||
|
import {
|
||||||
|
ColorSchemeType,
|
||||||
|
DiffFile,
|
||||||
|
DiffFileName,
|
||||||
|
DiffLineParts,
|
||||||
|
DiffStyleType,
|
||||||
|
LineMatchingType,
|
||||||
|
LineType,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export type CSSLineClass =
|
||||||
|
| 'd2h-ins'
|
||||||
|
| 'd2h-del'
|
||||||
|
| 'd2h-cntx'
|
||||||
|
| 'd2h-info'
|
||||||
|
| 'd2h-ins d2h-change'
|
||||||
|
| 'd2h-del d2h-change';
|
||||||
|
|
||||||
|
export const CSSLineClass: { [_: string]: CSSLineClass } = {
|
||||||
|
INSERTS: 'd2h-ins',
|
||||||
|
DELETES: 'd2h-del',
|
||||||
|
CONTEXT: 'd2h-cntx',
|
||||||
|
INFO: 'd2h-info',
|
||||||
|
INSERT_CHANGES: 'd2h-ins d2h-change',
|
||||||
|
DELETE_CHANGES: 'd2h-del d2h-change',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HighlightedLines = {
|
||||||
|
oldLine: {
|
||||||
|
prefix: string;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
newLine: {
|
||||||
|
prefix: string;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface RenderConfig {
|
||||||
|
matching?: LineMatchingType;
|
||||||
|
matchWordsThreshold?: number;
|
||||||
|
maxLineLengthHighlight?: number;
|
||||||
|
diffStyle?: DiffStyleType;
|
||||||
|
colorScheme?: ColorSchemeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultRenderConfig = {
|
||||||
|
matching: LineMatchingType.NONE,
|
||||||
|
matchWordsThreshold: 0.25,
|
||||||
|
maxLineLengthHighlight: 10000,
|
||||||
|
diffStyle: DiffStyleType.WORD,
|
||||||
|
colorScheme: ColorSchemeType.LIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const separator = '/';
|
||||||
|
const distance = rematch.newDistanceFn((change: jsDiff.Change) => change.value);
|
||||||
|
const matcher = rematch.newMatcherFn(distance);
|
||||||
|
|
||||||
|
function isDevNullName(name: string): boolean {
|
||||||
|
return name.indexOf('dev/null') !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeInsElements(line: string): string {
|
||||||
|
return line.replace(/(<ins[^>]*>((.|\n)*?)<\/ins>)/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeDelElements(line: string): string {
|
||||||
|
return line.replace(/(<del[^>]*>((.|\n)*?)<\/del>)/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert from LineType to CSSLineClass
|
||||||
|
*/
|
||||||
|
export function toCSSClass(lineType: LineType): CSSLineClass {
|
||||||
|
switch (lineType) {
|
||||||
|
case LineType.CONTEXT:
|
||||||
|
return CSSLineClass.CONTEXT;
|
||||||
|
case LineType.INSERT:
|
||||||
|
return CSSLineClass.INSERTS;
|
||||||
|
case LineType.DELETE:
|
||||||
|
return CSSLineClass.DELETES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function colorSchemeToCss(colorScheme: ColorSchemeType): string {
|
||||||
|
switch (colorScheme) {
|
||||||
|
case ColorSchemeType.DARK:
|
||||||
|
return 'd2h-dark-color-scheme';
|
||||||
|
case ColorSchemeType.AUTO:
|
||||||
|
return 'd2h-auto-color-scheme';
|
||||||
|
case ColorSchemeType.LIGHT:
|
||||||
|
default:
|
||||||
|
return 'd2h-light-color-scheme';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefix length of the hunk lines in the diff
|
||||||
|
*/
|
||||||
|
function prefixLength(isCombined: boolean): number {
|
||||||
|
return isCombined ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes all required characters for safe HTML rendering
|
||||||
|
*/
|
||||||
|
// TODO: Test this method inside deconstructLine since it should not be used anywhere else
|
||||||
|
export function escapeForHtml(str: string): string {
|
||||||
|
return str
|
||||||
|
.slice(0)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''')
|
||||||
|
.replace(/\//g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deconstructs diff @line by separating the content from the prefix type
|
||||||
|
*/
|
||||||
|
export function deconstructLine(line: string, isCombined: boolean, escape = true): DiffLineParts {
|
||||||
|
const indexToSplit = prefixLength(isCombined);
|
||||||
|
return {
|
||||||
|
prefix: line.substring(0, indexToSplit),
|
||||||
|
content: escape ? escapeForHtml(line.substring(indexToSplit)) : line.substring(indexToSplit),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates pretty filename diffs
|
||||||
|
*
|
||||||
|
* e.g.:
|
||||||
|
* 1. file = { oldName: "my/path/to/file.js", newName: "my/path/to/new-file.js" }
|
||||||
|
* returns "my/path/to/{file.js → new-file.js}"
|
||||||
|
* 2. file = { oldName: "my/path/to/file.js", newName: "very/new/path/to/new-file.js" }
|
||||||
|
* returns "my/path/to/file.js → very/new/path/to/new-file.js"
|
||||||
|
* 3. file = { oldName: "my/path/to/file.js", newName: "my/path/for/file.js" }
|
||||||
|
* returns "my/path/{to → for}/file.js"
|
||||||
|
*/
|
||||||
|
export function filenameDiff(file: DiffFileName): string {
|
||||||
|
// TODO: Move unify path to parsing
|
||||||
|
const oldFilename = unifyPath(file.oldName);
|
||||||
|
const newFilename = unifyPath(file.newName);
|
||||||
|
|
||||||
|
if (oldFilename !== newFilename && !isDevNullName(oldFilename) && !isDevNullName(newFilename)) {
|
||||||
|
const prefixPaths = [];
|
||||||
|
const suffixPaths = [];
|
||||||
|
|
||||||
|
const oldFilenameParts = oldFilename.split(separator);
|
||||||
|
const newFilenameParts = newFilename.split(separator);
|
||||||
|
|
||||||
|
const oldFilenamePartsSize = oldFilenameParts.length;
|
||||||
|
const newFilenamePartsSize = newFilenameParts.length;
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
let j = oldFilenamePartsSize - 1;
|
||||||
|
let k = newFilenamePartsSize - 1;
|
||||||
|
|
||||||
|
while (i < j && i < k) {
|
||||||
|
if (oldFilenameParts[i] === newFilenameParts[i]) {
|
||||||
|
prefixPaths.push(newFilenameParts[i]);
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (j > i && k > i) {
|
||||||
|
if (oldFilenameParts[j] === newFilenameParts[k]) {
|
||||||
|
suffixPaths.unshift(newFilenameParts[k]);
|
||||||
|
j -= 1;
|
||||||
|
k -= 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalPrefix = prefixPaths.join(separator);
|
||||||
|
const finalSuffix = suffixPaths.join(separator);
|
||||||
|
|
||||||
|
const oldRemainingPath = oldFilenameParts.slice(i, j + 1).join(separator);
|
||||||
|
const newRemainingPath = newFilenameParts.slice(i, k + 1).join(separator);
|
||||||
|
|
||||||
|
if (finalPrefix.length && finalSuffix.length) {
|
||||||
|
return (
|
||||||
|
finalPrefix + separator + '{' + oldRemainingPath + ' → ' + newRemainingPath + '}' + separator + finalSuffix
|
||||||
|
);
|
||||||
|
} else if (finalPrefix.length) {
|
||||||
|
return finalPrefix + separator + '{' + oldRemainingPath + ' → ' + newRemainingPath + '}';
|
||||||
|
} else if (finalSuffix.length) {
|
||||||
|
return '{' + oldRemainingPath + ' → ' + newRemainingPath + '}' + separator + finalSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldFilename + ' → ' + newFilename;
|
||||||
|
} else if (!isDevNullName(newFilename)) {
|
||||||
|
return newFilename;
|
||||||
|
} else {
|
||||||
|
return oldFilename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a unique string numerical identifier based on the names of the file diff
|
||||||
|
*/
|
||||||
|
export function getHtmlId(file: DiffFileName): string {
|
||||||
|
return `d2h-${hashCode(filenameDiff(file)).toString().slice(-6)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the correct icon name for the file
|
||||||
|
*/
|
||||||
|
export function getFileIcon(file: DiffFile): string {
|
||||||
|
let templateName = 'file-changed';
|
||||||
|
|
||||||
|
if (file.isRename) {
|
||||||
|
templateName = 'file-renamed';
|
||||||
|
} else if (file.isCopy) {
|
||||||
|
templateName = 'file-renamed';
|
||||||
|
} else if (file.isNew) {
|
||||||
|
templateName = 'file-added';
|
||||||
|
} else if (file.isDeleted) {
|
||||||
|
templateName = 'file-deleted';
|
||||||
|
} else if (file.newName !== file.oldName) {
|
||||||
|
// If file is not Added, not Deleted and the names changed it must be a rename :)
|
||||||
|
templateName = 'file-renamed';
|
||||||
|
}
|
||||||
|
|
||||||
|
return templateName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight differences between @diffLine1 and @diffLine2 using <ins> and <del> tags
|
||||||
|
*/
|
||||||
|
export function diffHighlight(
|
||||||
|
diffLine1: string,
|
||||||
|
diffLine2: string,
|
||||||
|
isCombined: boolean,
|
||||||
|
config: RenderConfig = {},
|
||||||
|
): HighlightedLines {
|
||||||
|
const { matching, maxLineLengthHighlight, matchWordsThreshold, diffStyle } = { ...defaultRenderConfig, ...config };
|
||||||
|
|
||||||
|
const line1 = deconstructLine(diffLine1, isCombined, false);
|
||||||
|
const line2 = deconstructLine(diffLine2, isCombined, false);
|
||||||
|
|
||||||
|
if (line1.content.length > maxLineLengthHighlight || line2.content.length > maxLineLengthHighlight) {
|
||||||
|
return {
|
||||||
|
oldLine: {
|
||||||
|
prefix: line1.prefix,
|
||||||
|
content: escapeForHtml(line1.content),
|
||||||
|
},
|
||||||
|
newLine: {
|
||||||
|
prefix: line2.prefix,
|
||||||
|
content: escapeForHtml(line2.content),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const diff =
|
||||||
|
diffStyle === 'char'
|
||||||
|
? jsDiff.diffChars(line1.content, line2.content)
|
||||||
|
: jsDiff.diffWordsWithSpace(line1.content, line2.content);
|
||||||
|
|
||||||
|
const changedWords: jsDiff.Change[] = [];
|
||||||
|
if (diffStyle === 'word' && matching === 'words') {
|
||||||
|
const removed = diff.filter(element => element.removed);
|
||||||
|
const added = diff.filter(element => element.added);
|
||||||
|
const chunks = matcher(added, removed);
|
||||||
|
chunks.forEach(chunk => {
|
||||||
|
if (chunk[0].length === 1 && chunk[1].length === 1) {
|
||||||
|
const dist = distance(chunk[0][0], chunk[1][0]);
|
||||||
|
if (dist < matchWordsThreshold) {
|
||||||
|
changedWords.push(chunk[0][0]);
|
||||||
|
changedWords.push(chunk[1][0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const highlightedLine = diff.reduce((highlightedLine, part) => {
|
||||||
|
const elemType = part.added ? 'ins' : part.removed ? 'del' : null;
|
||||||
|
const addClass = changedWords.indexOf(part) > -1 ? ' class="d2h-change"' : '';
|
||||||
|
const escapedValue = escapeForHtml(part.value);
|
||||||
|
|
||||||
|
return elemType !== null
|
||||||
|
? `${highlightedLine}<${elemType}${addClass}>${escapedValue}</${elemType}>`
|
||||||
|
: `${highlightedLine}${escapedValue}`;
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
return {
|
||||||
|
oldLine: {
|
||||||
|
prefix: line1.prefix,
|
||||||
|
content: removeInsElements(highlightedLine),
|
||||||
|
},
|
||||||
|
newLine: {
|
||||||
|
prefix: line2.prefix,
|
||||||
|
content: removeDelElements(highlightedLine),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,275 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* HtmlPrinter (html-printer.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var diffParser = require('./diff-parser.js').DiffParser;
|
|
||||||
var printerUtils = require('./printer-utils.js').PrinterUtils;
|
|
||||||
var utils = require('./utils.js').Utils;
|
|
||||||
var Rematch = require('./rematch.js').Rematch;
|
|
||||||
|
|
||||||
var matcher = Rematch.rematch(function(a, b) {
|
|
||||||
var amod = a.content.substr(1);
|
|
||||||
var bmod = b.content.substr(1);
|
|
||||||
|
|
||||||
return Rematch.distance(amod, bmod);
|
|
||||||
});
|
|
||||||
|
|
||||||
function SideBySidePrinter(config) {
|
|
||||||
this.config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
SideBySidePrinter.prototype.makeDiffHtml = function(file, diffs) {
|
|
||||||
return '<div id="' + printerUtils.getHtmlId(file) + '" class="d2h-file-wrapper" data-lang="' + file.language + '">\n' +
|
|
||||||
' <div class="d2h-file-header">\n' +
|
|
||||||
' <span class="d2h-file-stats">\n' +
|
|
||||||
' <span class="d2h-lines-added">\n' +
|
|
||||||
' <span>+' + file.addedLines + '</span>\n' +
|
|
||||||
' </span>\n' +
|
|
||||||
' <span class="d2h-lines-deleted">\n' +
|
|
||||||
' <span>-' + file.deletedLines + '</span>\n' +
|
|
||||||
' </span>\n' +
|
|
||||||
' </span>\n' +
|
|
||||||
' <span class="d2h-file-name-wrapper">\n' +
|
|
||||||
' <span class="d2h-file-name">' + printerUtils.getDiffName(file) + '</span>\n' +
|
|
||||||
' </span>\n' +
|
|
||||||
' </div>\n' +
|
|
||||||
' <div class="d2h-files-diff">\n' +
|
|
||||||
' <div class="d2h-file-side-diff">\n' +
|
|
||||||
' <div class="d2h-code-wrapper">\n' +
|
|
||||||
' <table class="d2h-diff-table">\n' +
|
|
||||||
' <tbody class="d2h-diff-tbody">\n' +
|
|
||||||
' ' + diffs.left +
|
|
||||||
' </tbody>\n' +
|
|
||||||
' </table>\n' +
|
|
||||||
' </div>\n' +
|
|
||||||
' </div>\n' +
|
|
||||||
' <div class="d2h-file-side-diff">\n' +
|
|
||||||
' <div class="d2h-code-wrapper">\n' +
|
|
||||||
' <table class="d2h-diff-table">\n' +
|
|
||||||
' <tbody class="d2h-diff-tbody">\n' +
|
|
||||||
' ' + diffs.right +
|
|
||||||
' </tbody>\n' +
|
|
||||||
' </table>\n' +
|
|
||||||
' </div>\n' +
|
|
||||||
' </div>\n' +
|
|
||||||
' </div>\n' +
|
|
||||||
' </div>\n';
|
|
||||||
};
|
|
||||||
|
|
||||||
SideBySidePrinter.prototype.generateSideBySideJsonHtml = function(diffFiles) {
|
|
||||||
var that = this;
|
|
||||||
return '<div class="d2h-wrapper">\n' +
|
|
||||||
diffFiles.map(function(file) {
|
|
||||||
|
|
||||||
var diffs;
|
|
||||||
if (file.blocks.length) {
|
|
||||||
diffs = that.generateSideBySideFileHtml(file);
|
|
||||||
} else {
|
|
||||||
diffs = that.generateEmptyDiff();
|
|
||||||
}
|
|
||||||
|
|
||||||
return that.makeDiffHtml(file, diffs);
|
|
||||||
}).join('\n') +
|
|
||||||
'</div>\n';
|
|
||||||
};
|
|
||||||
|
|
||||||
SideBySidePrinter.prototype.makeSideHtml = function(blockHeader) {
|
|
||||||
return '<tr>\n' +
|
|
||||||
' <td class="d2h-code-side-linenumber ' + diffParser.LINE_TYPE.INFO + '"></td>\n' +
|
|
||||||
' <td class="' + diffParser.LINE_TYPE.INFO + '">\n' +
|
|
||||||
' <div class="d2h-code-side-line ' + diffParser.LINE_TYPE.INFO + '">' + blockHeader + '</div>\n' +
|
|
||||||
' </td>\n' +
|
|
||||||
'</tr>\n';
|
|
||||||
};
|
|
||||||
|
|
||||||
SideBySidePrinter.prototype.generateSideBySideFileHtml = function(file) {
|
|
||||||
var that = this;
|
|
||||||
var fileHtml = {};
|
|
||||||
fileHtml.left = '';
|
|
||||||
fileHtml.right = '';
|
|
||||||
|
|
||||||
file.blocks.forEach(function(block) {
|
|
||||||
|
|
||||||
fileHtml.left += that.makeSideHtml(utils.escape(block.header));
|
|
||||||
fileHtml.right += that.makeSideHtml('');
|
|
||||||
|
|
||||||
var oldLines = [];
|
|
||||||
var newLines = [];
|
|
||||||
|
|
||||||
function processChangeBlock() {
|
|
||||||
var matches;
|
|
||||||
var insertType;
|
|
||||||
var deleteType;
|
|
||||||
|
|
||||||
var comparisons = oldLines.length * newLines.length;
|
|
||||||
var maxComparisons = that.config.matchingMaxComparisons || 2500;
|
|
||||||
var doMatching = comparisons < maxComparisons && (that.config.matching === 'lines' ||
|
|
||||||
that.config.matching === 'words');
|
|
||||||
|
|
||||||
if (doMatching) {
|
|
||||||
matches = matcher(oldLines, newLines);
|
|
||||||
insertType = diffParser.LINE_TYPE.INSERT_CHANGES;
|
|
||||||
deleteType = diffParser.LINE_TYPE.DELETE_CHANGES;
|
|
||||||
} else {
|
|
||||||
matches = [[oldLines, newLines]];
|
|
||||||
insertType = diffParser.LINE_TYPE.INSERTS;
|
|
||||||
deleteType = diffParser.LINE_TYPE.DELETES;
|
|
||||||
}
|
|
||||||
|
|
||||||
matches.forEach(function(match) {
|
|
||||||
oldLines = match[0];
|
|
||||||
newLines = match[1];
|
|
||||||
|
|
||||||
var common = Math.min(oldLines.length, newLines.length);
|
|
||||||
var max = Math.max(oldLines.length, newLines.length);
|
|
||||||
|
|
||||||
for (var j = 0; j < common; j++) {
|
|
||||||
var oldLine = oldLines[j];
|
|
||||||
var newLine = newLines[j];
|
|
||||||
|
|
||||||
that.config.isCombined = file.isCombined;
|
|
||||||
|
|
||||||
var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, that.config);
|
|
||||||
|
|
||||||
fileHtml.left +=
|
|
||||||
that.generateSingleLineHtml(deleteType, oldLine.oldNumber,
|
|
||||||
diff.first.line, diff.first.prefix);
|
|
||||||
fileHtml.right +=
|
|
||||||
that.generateSingleLineHtml(insertType, newLine.newNumber,
|
|
||||||
diff.second.line, diff.second.prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max > common) {
|
|
||||||
var oldSlice = oldLines.slice(common);
|
|
||||||
var newSlice = newLines.slice(common);
|
|
||||||
|
|
||||||
var tmpHtml = that.processLines(oldSlice, newSlice);
|
|
||||||
fileHtml.left += tmpHtml.left;
|
|
||||||
fileHtml.right += tmpHtml.right;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
oldLines = [];
|
|
||||||
newLines = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < block.lines.length; i++) {
|
|
||||||
var line = block.lines[i];
|
|
||||||
var prefix = line.content[0];
|
|
||||||
var escapedLine = utils.escape(line.content.substr(1));
|
|
||||||
|
|
||||||
if (line.type !== diffParser.LINE_TYPE.INSERTS &&
|
|
||||||
(newLines.length > 0 || (line.type !== diffParser.LINE_TYPE.DELETES && oldLines.length > 0))) {
|
|
||||||
processChangeBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.type === diffParser.LINE_TYPE.CONTEXT) {
|
|
||||||
fileHtml.left += that.generateSingleLineHtml(line.type, line.oldNumber, escapedLine, prefix);
|
|
||||||
fileHtml.right += that.generateSingleLineHtml(line.type, line.newNumber, escapedLine, prefix);
|
|
||||||
} else if (line.type === diffParser.LINE_TYPE.INSERTS && !oldLines.length) {
|
|
||||||
fileHtml.left += that.generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
|
|
||||||
fileHtml.right += that.generateSingleLineHtml(line.type, line.newNumber, escapedLine, prefix);
|
|
||||||
} else if (line.type === diffParser.LINE_TYPE.DELETES) {
|
|
||||||
oldLines.push(line);
|
|
||||||
} else if (line.type === diffParser.LINE_TYPE.INSERTS && Boolean(oldLines.length)) {
|
|
||||||
newLines.push(line);
|
|
||||||
} else {
|
|
||||||
console.error('unknown state in html side-by-side generator');
|
|
||||||
processChangeBlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processChangeBlock();
|
|
||||||
});
|
|
||||||
|
|
||||||
return fileHtml;
|
|
||||||
};
|
|
||||||
|
|
||||||
SideBySidePrinter.prototype.processLines = function(oldLines, newLines) {
|
|
||||||
var that = this;
|
|
||||||
var fileHtml = {};
|
|
||||||
fileHtml.left = '';
|
|
||||||
fileHtml.right = '';
|
|
||||||
|
|
||||||
var maxLinesNumber = Math.max(oldLines.length, newLines.length);
|
|
||||||
for (var i = 0; i < maxLinesNumber; i++) {
|
|
||||||
var oldLine = oldLines[i];
|
|
||||||
var newLine = newLines[i];
|
|
||||||
var oldContent;
|
|
||||||
var newContent;
|
|
||||||
var oldPrefix;
|
|
||||||
var newPrefix;
|
|
||||||
|
|
||||||
if (oldLine) {
|
|
||||||
oldContent = utils.escape(oldLine.content.substr(1));
|
|
||||||
oldPrefix = oldLine.content[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newLine) {
|
|
||||||
newContent = utils.escape(newLine.content.substr(1));
|
|
||||||
newPrefix = newLine.content[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldLine && newLine) {
|
|
||||||
fileHtml.left += that.generateSingleLineHtml(oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
|
|
||||||
fileHtml.right += that.generateSingleLineHtml(newLine.type, newLine.newNumber, newContent, newPrefix);
|
|
||||||
} else if (oldLine) {
|
|
||||||
fileHtml.left += that.generateSingleLineHtml(oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
|
|
||||||
fileHtml.right += that.generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
|
|
||||||
} else if (newLine) {
|
|
||||||
fileHtml.left += that.generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
|
|
||||||
fileHtml.right += that.generateSingleLineHtml(newLine.type, newLine.newNumber, newContent, newPrefix);
|
|
||||||
} else {
|
|
||||||
console.error('How did it get here?');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileHtml;
|
|
||||||
};
|
|
||||||
|
|
||||||
SideBySidePrinter.prototype.makeSingleLineHtml = function(type, number, htmlContent, htmlPrefix) {
|
|
||||||
return '<tr>\n' +
|
|
||||||
' <td class="d2h-code-side-linenumber ' + type + '">' + number + '</td>\n' +
|
|
||||||
' <td class="' + type + '">' +
|
|
||||||
' <div class="d2h-code-side-line ' + type + '">' + htmlPrefix + htmlContent + '</div>' +
|
|
||||||
' </td>\n' +
|
|
||||||
' </tr>\n';
|
|
||||||
};
|
|
||||||
|
|
||||||
SideBySidePrinter.prototype.generateSingleLineHtml = function(type, number, content, prefix) {
|
|
||||||
var htmlPrefix = '';
|
|
||||||
if (prefix) {
|
|
||||||
htmlPrefix = '<span class="d2h-code-line-prefix">' + prefix + '</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
var htmlContent = '';
|
|
||||||
if (content) {
|
|
||||||
htmlContent = '<span class="d2h-code-line-ctn">' + content + '</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.makeSingleLineHtml(type, number, htmlContent, htmlPrefix);
|
|
||||||
};
|
|
||||||
|
|
||||||
SideBySidePrinter.prototype.generateEmptyDiff = function() {
|
|
||||||
var fileHtml = {};
|
|
||||||
fileHtml.right = '';
|
|
||||||
|
|
||||||
fileHtml.left = '<tr>\n' +
|
|
||||||
' <td class="' + diffParser.LINE_TYPE.INFO + '">' +
|
|
||||||
' <div class="d2h-code-side-line ' + diffParser.LINE_TYPE.INFO + '">' +
|
|
||||||
'File without changes' +
|
|
||||||
' </div>' +
|
|
||||||
' </td>\n' +
|
|
||||||
'</tr>\n';
|
|
||||||
|
|
||||||
return fileHtml;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.SideBySidePrinter = SideBySidePrinter;
|
|
||||||
|
|
||||||
})();
|
|
||||||
314
src/side-by-side-renderer.ts
Normal file
314
src/side-by-side-renderer.ts
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
import HoganJsUtils from './hoganjs-utils';
|
||||||
|
import * as Rematch from './rematch';
|
||||||
|
import * as renderUtils from './render-utils';
|
||||||
|
import {
|
||||||
|
DiffLine,
|
||||||
|
LineType,
|
||||||
|
DiffFile,
|
||||||
|
DiffBlock,
|
||||||
|
DiffLineContext,
|
||||||
|
DiffLineDeleted,
|
||||||
|
DiffLineInserted,
|
||||||
|
DiffLineContent,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export interface SideBySideRendererConfig extends renderUtils.RenderConfig {
|
||||||
|
renderNothingWhenEmpty?: boolean;
|
||||||
|
matchingMaxComparisons?: number;
|
||||||
|
maxLineSizeInBlockForComparison?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultSideBySideRendererConfig = {
|
||||||
|
...renderUtils.defaultRenderConfig,
|
||||||
|
renderNothingWhenEmpty: false,
|
||||||
|
matchingMaxComparisons: 2500,
|
||||||
|
maxLineSizeInBlockForComparison: 200,
|
||||||
|
};
|
||||||
|
|
||||||
|
const genericTemplatesPath = 'generic';
|
||||||
|
const baseTemplatesPath = 'side-by-side';
|
||||||
|
const iconsBaseTemplatesPath = 'icon';
|
||||||
|
const tagsBaseTemplatesPath = 'tag';
|
||||||
|
|
||||||
|
export default class SideBySideRenderer {
|
||||||
|
private readonly hoganUtils: HoganJsUtils;
|
||||||
|
private readonly config: typeof defaultSideBySideRendererConfig;
|
||||||
|
|
||||||
|
constructor(hoganUtils: HoganJsUtils, config: SideBySideRendererConfig = {}) {
|
||||||
|
this.hoganUtils = hoganUtils;
|
||||||
|
this.config = { ...defaultSideBySideRendererConfig, ...config };
|
||||||
|
}
|
||||||
|
|
||||||
|
render(diffFiles: DiffFile[]): string {
|
||||||
|
const diffsHtml = diffFiles
|
||||||
|
.map(file => {
|
||||||
|
let diffs;
|
||||||
|
if (file.blocks.length) {
|
||||||
|
diffs = this.generateFileHtml(file);
|
||||||
|
} else {
|
||||||
|
diffs = this.generateEmptyDiff();
|
||||||
|
}
|
||||||
|
return this.makeFileDiffHtml(file, diffs);
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
return this.hoganUtils.render(genericTemplatesPath, 'wrapper', {
|
||||||
|
colorScheme: renderUtils.colorSchemeToCss(this.config.colorScheme),
|
||||||
|
content: diffsHtml,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
makeFileDiffHtml(file: DiffFile, diffs: FileHtml): string {
|
||||||
|
if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0) return '';
|
||||||
|
|
||||||
|
const fileDiffTemplate = this.hoganUtils.template(baseTemplatesPath, 'file-diff');
|
||||||
|
const filePathTemplate = this.hoganUtils.template(genericTemplatesPath, 'file-path');
|
||||||
|
const fileIconTemplate = this.hoganUtils.template(iconsBaseTemplatesPath, 'file');
|
||||||
|
const fileTagTemplate = this.hoganUtils.template(tagsBaseTemplatesPath, renderUtils.getFileIcon(file));
|
||||||
|
|
||||||
|
return fileDiffTemplate.render({
|
||||||
|
file: file,
|
||||||
|
fileHtmlId: renderUtils.getHtmlId(file),
|
||||||
|
diffs: diffs,
|
||||||
|
filePath: filePathTemplate.render(
|
||||||
|
{
|
||||||
|
fileDiffName: renderUtils.filenameDiff(file),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileIcon: fileIconTemplate,
|
||||||
|
fileTag: fileTagTemplate,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
generateEmptyDiff(): FileHtml {
|
||||||
|
return {
|
||||||
|
right: '',
|
||||||
|
left: this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
|
||||||
|
contentClass: 'd2h-code-side-line',
|
||||||
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
generateFileHtml(file: DiffFile): FileHtml {
|
||||||
|
const matcher = Rematch.newMatcherFn(
|
||||||
|
Rematch.newDistanceFn((e: DiffLine) => renderUtils.deconstructLine(e.content, file.isCombined).content),
|
||||||
|
);
|
||||||
|
|
||||||
|
return file.blocks
|
||||||
|
.map(block => {
|
||||||
|
const fileHtml = {
|
||||||
|
left: this.makeHeaderHtml(block.header, file),
|
||||||
|
right: this.makeHeaderHtml(''),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.applyLineGroupping(block).forEach(([contextLines, oldLines, newLines]) => {
|
||||||
|
if (oldLines.length && newLines.length && !contextLines.length) {
|
||||||
|
this.applyRematchMatching(oldLines, newLines, matcher).map(([oldLines, newLines]) => {
|
||||||
|
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
|
||||||
|
fileHtml.left += left;
|
||||||
|
fileHtml.right += right;
|
||||||
|
});
|
||||||
|
} else if (contextLines.length) {
|
||||||
|
contextLines.forEach(line => {
|
||||||
|
const { prefix, content } = renderUtils.deconstructLine(line.content, file.isCombined);
|
||||||
|
const { left, right } = this.generateLineHtml(
|
||||||
|
{
|
||||||
|
type: renderUtils.CSSLineClass.CONTEXT,
|
||||||
|
prefix: prefix,
|
||||||
|
content: content,
|
||||||
|
number: line.oldNumber,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: renderUtils.CSSLineClass.CONTEXT,
|
||||||
|
prefix: prefix,
|
||||||
|
content: content,
|
||||||
|
number: line.newNumber,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
fileHtml.left += left;
|
||||||
|
fileHtml.right += right;
|
||||||
|
});
|
||||||
|
} else if (oldLines.length || newLines.length) {
|
||||||
|
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
|
||||||
|
fileHtml.left += left;
|
||||||
|
fileHtml.right += right;
|
||||||
|
} else {
|
||||||
|
console.error('Unknown state reached while processing groups of lines', contextLines, oldLines, newLines);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return fileHtml;
|
||||||
|
})
|
||||||
|
.reduce(
|
||||||
|
(accomulated, html) => {
|
||||||
|
return { left: accomulated.left + html.left, right: accomulated.right + html.right };
|
||||||
|
},
|
||||||
|
{ left: '', right: '' },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyLineGroupping(block: DiffBlock): DiffLineGroups {
|
||||||
|
const blockLinesGroups: DiffLineGroups = [];
|
||||||
|
|
||||||
|
let oldLines: (DiffLineDeleted & DiffLineContent)[] = [];
|
||||||
|
let newLines: (DiffLineInserted & DiffLineContent)[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < block.lines.length; i++) {
|
||||||
|
const diffLine = block.lines[i];
|
||||||
|
|
||||||
|
if (
|
||||||
|
(diffLine.type !== LineType.INSERT && newLines.length) ||
|
||||||
|
(diffLine.type === LineType.CONTEXT && oldLines.length > 0)
|
||||||
|
) {
|
||||||
|
blockLinesGroups.push([[], oldLines, newLines]);
|
||||||
|
oldLines = [];
|
||||||
|
newLines = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diffLine.type === LineType.CONTEXT) {
|
||||||
|
blockLinesGroups.push([[diffLine], [], []]);
|
||||||
|
} else if (diffLine.type === LineType.INSERT && oldLines.length === 0) {
|
||||||
|
blockLinesGroups.push([[], [], [diffLine]]);
|
||||||
|
} else if (diffLine.type === LineType.INSERT && oldLines.length > 0) {
|
||||||
|
newLines.push(diffLine);
|
||||||
|
} else if (diffLine.type === LineType.DELETE) {
|
||||||
|
oldLines.push(diffLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldLines.length || newLines.length) {
|
||||||
|
blockLinesGroups.push([[], oldLines, newLines]);
|
||||||
|
oldLines = [];
|
||||||
|
newLines = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockLinesGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyRematchMatching(
|
||||||
|
oldLines: DiffLine[],
|
||||||
|
newLines: DiffLine[],
|
||||||
|
matcher: Rematch.MatcherFn<DiffLine>,
|
||||||
|
): DiffLine[][][] {
|
||||||
|
const comparisons = oldLines.length * newLines.length;
|
||||||
|
const maxLineSizeInBlock = Math.max.apply(
|
||||||
|
null,
|
||||||
|
[0].concat(oldLines.concat(newLines).map(elem => elem.content.length)),
|
||||||
|
);
|
||||||
|
const doMatching =
|
||||||
|
comparisons < this.config.matchingMaxComparisons &&
|
||||||
|
maxLineSizeInBlock < this.config.maxLineSizeInBlockForComparison &&
|
||||||
|
(this.config.matching === 'lines' || this.config.matching === 'words');
|
||||||
|
|
||||||
|
return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
|
||||||
|
}
|
||||||
|
|
||||||
|
makeHeaderHtml(blockHeader: string, file?: DiffFile): string {
|
||||||
|
return this.hoganUtils.render(genericTemplatesPath, 'block-header', {
|
||||||
|
CSSLineClass: renderUtils.CSSLineClass,
|
||||||
|
blockHeader: file?.isTooBig ? blockHeader : renderUtils.escapeForHtml(blockHeader),
|
||||||
|
lineClass: 'd2h-code-side-linenumber',
|
||||||
|
contentClass: 'd2h-code-side-line',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
processChangedLines(isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml {
|
||||||
|
const fileHtml = {
|
||||||
|
right: '',
|
||||||
|
left: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const maxLinesNumber = Math.max(oldLines.length, newLines.length);
|
||||||
|
for (let i = 0; i < maxLinesNumber; i++) {
|
||||||
|
const oldLine = oldLines[i];
|
||||||
|
const newLine = newLines[i];
|
||||||
|
|
||||||
|
const diff =
|
||||||
|
oldLine !== undefined && newLine !== undefined
|
||||||
|
? renderUtils.diffHighlight(oldLine.content, newLine.content, isCombined, this.config)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const preparedOldLine =
|
||||||
|
oldLine !== undefined && oldLine.oldNumber !== undefined
|
||||||
|
? {
|
||||||
|
...(diff !== undefined
|
||||||
|
? {
|
||||||
|
prefix: diff.oldLine.prefix,
|
||||||
|
content: diff.oldLine.content,
|
||||||
|
type: renderUtils.CSSLineClass.DELETE_CHANGES,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...renderUtils.deconstructLine(oldLine.content, isCombined),
|
||||||
|
type: renderUtils.toCSSClass(oldLine.type),
|
||||||
|
}),
|
||||||
|
number: oldLine.oldNumber,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const preparedNewLine =
|
||||||
|
newLine !== undefined && newLine.newNumber !== undefined
|
||||||
|
? {
|
||||||
|
...(diff !== undefined
|
||||||
|
? {
|
||||||
|
prefix: diff.newLine.prefix,
|
||||||
|
content: diff.newLine.content,
|
||||||
|
type: renderUtils.CSSLineClass.INSERT_CHANGES,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...renderUtils.deconstructLine(newLine.content, isCombined),
|
||||||
|
type: renderUtils.toCSSClass(newLine.type),
|
||||||
|
}),
|
||||||
|
number: newLine.newNumber,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const { left, right } = this.generateLineHtml(preparedOldLine, preparedNewLine);
|
||||||
|
fileHtml.left += left;
|
||||||
|
fileHtml.right += right;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateLineHtml(oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml {
|
||||||
|
return {
|
||||||
|
left: this.generateSingleHtml(oldLine),
|
||||||
|
right: this.generateSingleHtml(newLine),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSingleHtml(line?: DiffPreparedLine): string {
|
||||||
|
const lineClass = 'd2h-code-side-linenumber';
|
||||||
|
const contentClass = 'd2h-code-side-line';
|
||||||
|
|
||||||
|
return this.hoganUtils.render(genericTemplatesPath, 'line', {
|
||||||
|
type: line?.type || `${renderUtils.CSSLineClass.CONTEXT} d2h-emptyplaceholder`,
|
||||||
|
lineClass: line !== undefined ? lineClass : `${lineClass} d2h-code-side-emptyplaceholder`,
|
||||||
|
contentClass: line !== undefined ? contentClass : `${contentClass} d2h-code-side-emptyplaceholder`,
|
||||||
|
prefix: line?.prefix === ' ' ? ' ' : line?.prefix,
|
||||||
|
content: line?.content,
|
||||||
|
lineNumber: line?.number,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiffLineGroups = [
|
||||||
|
(DiffLineContext & DiffLineContent)[],
|
||||||
|
(DiffLineDeleted & DiffLineContent)[],
|
||||||
|
(DiffLineInserted & DiffLineContent)[],
|
||||||
|
][];
|
||||||
|
|
||||||
|
type DiffPreparedLine = {
|
||||||
|
type: renderUtils.CSSLineClass;
|
||||||
|
prefix: string;
|
||||||
|
content: string;
|
||||||
|
number: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FileHtml = {
|
||||||
|
left: string;
|
||||||
|
right: string;
|
||||||
|
};
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
(function() {
|
|
||||||
if (!!!global.browserTemplates) global.browserTemplates = {};
|
|
||||||
var Hogan = require("hogan.js");global.browserTemplates["line-by-line-column-line-number"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<tr>");t.b("\n" + i);t.b(" <td class=\"d2h-code-linenumber ");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\"></td>");t.b("\n" + i);t.b(" <td class=\"");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"d2h-code-line ");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\">");t.b(t.t(t.f("blockHeader",c,p,0)));t.b("</div>");t.b("\n" + i);t.b(" </td>");t.b("\n" + i);t.b("</tr>");return t.fl(); },partials: {}, subs: { }});
|
|
||||||
global.browserTemplates["line-by-line-empty-diff"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<tr>");t.b("\n" + i);t.b(" <td class=\"");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"d2h-code-line ");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" File without changes");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" </td>");t.b("\n" + i);t.b("</tr>");return t.fl(); },partials: {}, subs: { }});
|
|
||||||
global.browserTemplates["line-by-line-file-diff"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<div id=\"");t.b(t.v(t.f("fileHtmlId",c,p,0)));t.b("\" class=\"d2h-file-wrapper\" data-lang=\"");t.b(t.v(t.d("file.language",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"d2h-file-header\">");t.b("\n" + i);t.b(" <span class=\"d2h-file-stats\">");t.b("\n" + i);t.b(" <span class=\"d2h-lines-added\">");t.b("\n" + i);t.b(" <span>+");t.b(t.v(t.d("file.addedLines",c,p,0)));t.b("</span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" <span class=\"d2h-lines-deleted\">");t.b("\n" + i);t.b(" <span>-");t.b(t.v(t.d("file.deletedLines",c,p,0)));t.b("</span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" <span class=\"d2h-file-name-wrapper\">");t.b("\n" + i);t.b(" <span class=\"d2h-file-name\"> ");t.b(t.v(t.f("fileDiffName",c,p,0)));t.b("</span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" <div class=\"d2h-file-diff\">");t.b("\n" + i);t.b(" <div class=\"d2h-code-wrapper\">");t.b("\n" + i);t.b(" <table class=\"d2h-diff-table\">");t.b("\n" + i);t.b(" <tbody class=\"d2h-diff-tbody\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("diffs",c,p,0)));t.b("\n" + i);t.b(" </tbody>");t.b("\n" + i);t.b(" </table>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b("</div>");return t.fl(); },partials: {}, subs: { }});
|
|
||||||
global.browserTemplates["line-by-line-line"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<tr>");t.b("\n" + i);t.b(" <td class=\"d2h-code-linenumber ");t.b(t.v(t.f("type",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"line-num1\">");t.b(t.v(t.f("oldNumber",c,p,0)));t.b("</div>");t.b("\n" + i);t.b(" <div class=\"line-num2\">");t.b(t.v(t.f("newNumber",c,p,0)));t.b("</div>");t.b("\n" + i);t.b(" </td>");t.b("\n" + i);t.b(" <td class=\"");t.b(t.v(t.f("type",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"d2h-code-line ");t.b(t.v(t.f("type",c,p,0)));t.b("\">");t.b("\n" + i);if(t.s(t.f("prefix",c,p,1),c,p,0,253,329,"{{ }}")){t.rs(c,p,function(c,p,t){t.b(" <span class=\"d2h-code-line-prefix\">");t.b(t.t(t.f("prefix",c,p,0)));t.b("</span>");t.b("\n" + i);});c.pop();}if(t.s(t.f("content",c,p,1),c,p,0,361,435,"{{ }}")){t.rs(c,p,function(c,p,t){t.b(" <span class=\"d2h-code-line-ctn\">");t.b(t.t(t.f("content",c,p,0)));t.b("</span>");t.b("\n" + i);});c.pop();}t.b(" </div>");t.b("\n" + i);t.b(" </td>");t.b("\n" + i);t.b("</tr>");return t.fl(); },partials: {}, subs: { }});
|
|
||||||
global.browserTemplates["line-by-line-wrapper"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<div class=\"d2h-wrapper\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("content",c,p,0)));t.b("\n" + i);t.b("</div>");return t.fl(); },partials: {}, subs: { }});
|
|
||||||
module.exports = global.browserTemplates;
|
|
||||||
})();
|
|
||||||
10
src/templates/file-summary-line.mustache
Normal file
10
src/templates/file-summary-line.mustache
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<li class="d2h-file-list-line">
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
{{>fileIcon}}
|
||||||
|
<a href="#{{fileHtmlId}}" class="d2h-file-name">{{fileName}}</a>
|
||||||
|
<span class="d2h-file-stats">
|
||||||
|
<span class="d2h-lines-added">{{addedLines}}</span>
|
||||||
|
<span class="d2h-lines-deleted">{{deletedLines}}</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
10
src/templates/file-summary-wrapper.mustache
Normal file
10
src/templates/file-summary-wrapper.mustache
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="d2h-file-list-wrapper {{colorScheme}}">
|
||||||
|
<div class="d2h-file-list-header">
|
||||||
|
<span class="d2h-file-list-title">Files changed ({{filesNumber}})</span>
|
||||||
|
<a class="d2h-file-switch d2h-hide">hide</a>
|
||||||
|
<a class="d2h-file-switch d2h-show">show</a>
|
||||||
|
</div>
|
||||||
|
<ol class="d2h-file-list">
|
||||||
|
{{{files}}}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
6
src/templates/generic-block-header.mustache
Normal file
6
src/templates/generic-block-header.mustache
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<tr>
|
||||||
|
<td class="{{lineClass}} {{CSSLineClass.INFO}}"></td>
|
||||||
|
<td class="{{CSSLineClass.INFO}}">
|
||||||
|
<div class="{{contentClass}}">{{#blockHeader}}{{{blockHeader}}}{{/blockHeader}}{{^blockHeader}} {{/blockHeader}}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
7
src/templates/generic-empty-diff.mustache
Normal file
7
src/templates/generic-empty-diff.mustache
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<tr>
|
||||||
|
<td class="{{CSSLineClass.INFO}}">
|
||||||
|
<div class="{{contentClass}}">
|
||||||
|
File without changes
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
9
src/templates/generic-file-path.mustache
Normal file
9
src/templates/generic-file-path.mustache
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<span class="d2h-file-name-wrapper">
|
||||||
|
{{>fileIcon}}
|
||||||
|
<span class="d2h-file-name">{{fileDiffName}}</span>
|
||||||
|
{{>fileTag}}
|
||||||
|
</span>
|
||||||
|
<label class="d2h-file-collapse">
|
||||||
|
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
|
||||||
|
Viewed
|
||||||
|
</label>
|
||||||
21
src/templates/generic-line.mustache
Normal file
21
src/templates/generic-line.mustache
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<tr>
|
||||||
|
<td class="{{lineClass}} {{type}}">
|
||||||
|
{{{lineNumber}}}
|
||||||
|
</td>
|
||||||
|
<td class="{{type}}">
|
||||||
|
<div class="{{contentClass}}">
|
||||||
|
{{#prefix}}
|
||||||
|
<span class="d2h-code-line-prefix">{{{prefix}}}</span>
|
||||||
|
{{/prefix}}
|
||||||
|
{{^prefix}}
|
||||||
|
<span class="d2h-code-line-prefix"> </span>
|
||||||
|
{{/prefix}}
|
||||||
|
{{#content}}
|
||||||
|
<span class="d2h-code-line-ctn">{{{content}}}</span>
|
||||||
|
{{/content}}
|
||||||
|
{{^content}}
|
||||||
|
<span class="d2h-code-line-ctn"><br></span>
|
||||||
|
{{/content}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
3
src/templates/generic-wrapper.mustache
Normal file
3
src/templates/generic-wrapper.mustache
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="d2h-wrapper {{colorScheme}}">
|
||||||
|
{{{content}}}
|
||||||
|
</div>
|
||||||
4
src/templates/icon-file-added.mustache
Normal file
4
src/templates/icon-file-added.mustache
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-added" height="16" title="added" version="1.1" viewBox="0 0 14 16"
|
||||||
|
width="14">
|
||||||
|
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM6 9H3V7h3V4h2v3h3v2H8v3H6V9z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 292 B |
4
src/templates/icon-file-changed.mustache
Normal file
4
src/templates/icon-file-changed.mustache
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-changed" height="16" title="modified" version="1.1"
|
||||||
|
viewBox="0 0 14 16" width="14">
|
||||||
|
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 324 B |
4
src/templates/icon-file-deleted.mustache
Normal file
4
src/templates/icon-file-deleted.mustache
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-deleted" height="16" title="removed" version="1.1"
|
||||||
|
viewBox="0 0 14 16" width="14">
|
||||||
|
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM11 9H3V7h8v2z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 281 B |
4
src/templates/icon-file-renamed.mustache
Normal file
4
src/templates/icon-file-renamed.mustache
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg aria-hidden="true" class="d2h-icon d2h-moved" height="16" title="renamed" version="1.1"
|
||||||
|
viewBox="0 0 14 16" width="14">
|
||||||
|
<path d="M6 9H3V7h3V4l5 4-5 4V9z m8-7v12c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h12c0.55 0 1 0.45 1 1z m-1 0H1v12h12V2z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 289 B |
3
src/templates/icon-file.mustache
Normal file
3
src/templates/icon-file.mustache
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
|
||||||
|
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 294 B |
|
|
@ -1,6 +0,0 @@
|
||||||
<tr>
|
|
||||||
<td class="d2h-code-linenumber {{diffParser.LINE_TYPE.INFO}}"></td>
|
|
||||||
<td class="{{diffParser.LINE_TYPE.INFO}}">
|
|
||||||
<div class="d2h-code-line {{diffParser.LINE_TYPE.INFO}}">{{{blockHeader}}}</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
<tr>
|
|
||||||
<td class="{{diffParser.LINE_TYPE.INFO}}">
|
|
||||||
<div class="d2h-code-line {{diffParser.LINE_TYPE.INFO}}">
|
|
||||||
File without changes
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
<div id="{{fileHtmlId}}" class="d2h-file-wrapper" data-lang="{{file.language}}">
|
<div id="{{fileHtmlId}}" class="d2h-file-wrapper" data-lang="{{file.language}}">
|
||||||
<div class="d2h-file-header">
|
<div class="d2h-file-header">
|
||||||
<span class="d2h-file-stats">
|
{{{filePath}}}
|
||||||
<span class="d2h-lines-added">
|
|
||||||
<span>+{{file.addedLines}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="d2h-lines-deleted">
|
|
||||||
<span>-{{file.deletedLines}}</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span class="d2h-file-name-wrapper">
|
|
||||||
<span class="d2h-file-name"> {{fileDiffName}}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="d2h-file-diff">
|
<div class="d2h-file-diff">
|
||||||
<div class="d2h-code-wrapper">
|
<div class="d2h-code-wrapper">
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<tr>
|
|
||||||
<td class="d2h-code-linenumber {{type}}">
|
|
||||||
<div class="line-num1">{{oldNumber}}</div>
|
|
||||||
<div class="line-num2">{{newNumber}}</div>
|
|
||||||
</td>
|
|
||||||
<td class="{{type}}">
|
|
||||||
<div class="d2h-code-line {{type}}">
|
|
||||||
{{#prefix}}
|
|
||||||
<span class="d2h-code-line-prefix">{{{prefix}}}</span>
|
|
||||||
{{/prefix}}
|
|
||||||
{{#content}}
|
|
||||||
<span class="d2h-code-line-ctn">{{{content}}}</span>
|
|
||||||
{{/content}}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
2
src/templates/line-by-line-numbers.mustache
Normal file
2
src/templates/line-by-line-numbers.mustache
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<div class="line-num1">{{oldNumber}}</div>
|
||||||
|
<div class="line-num2">{{newNumber}}</div>
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<div class="d2h-wrapper">
|
|
||||||
{{{content}}}
|
|
||||||
</div>
|
|
||||||
25
src/templates/side-by-side-file-diff.mustache
Normal file
25
src/templates/side-by-side-file-diff.mustache
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<div id="{{fileHtmlId}}" class="d2h-file-wrapper" data-lang="{{file.language}}">
|
||||||
|
<div class="d2h-file-header">
|
||||||
|
{{{filePath}}}
|
||||||
|
</div>
|
||||||
|
<div class="d2h-files-diff">
|
||||||
|
<div class="d2h-file-side-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
{{{diffs.left}}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d2h-file-side-diff">
|
||||||
|
<div class="d2h-code-wrapper">
|
||||||
|
<table class="d2h-diff-table">
|
||||||
|
<tbody class="d2h-diff-tbody">
|
||||||
|
{{{diffs.right}}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
1
src/templates/tag-file-added.mustache
Normal file
1
src/templates/tag-file-added.mustache
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<span class="d2h-tag d2h-added d2h-added-tag">ADDED</span>
|
||||||
1
src/templates/tag-file-changed.mustache
Normal file
1
src/templates/tag-file-changed.mustache
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span>
|
||||||
1
src/templates/tag-file-deleted.mustache
Normal file
1
src/templates/tag-file-deleted.mustache
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<span class="d2h-tag d2h-deleted d2h-deleted-tag">DELETED</span>
|
||||||
1
src/templates/tag-file-renamed.mustache
Normal file
1
src/templates/tag-file-renamed.mustache
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<span class="d2h-tag d2h-moved d2h-moved-tag">RENAMED</span>
|
||||||
99
src/types.ts
Normal file
99
src/types.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
export type DiffLineParts = {
|
||||||
|
prefix: string;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum LineType {
|
||||||
|
INSERT = 'insert',
|
||||||
|
DELETE = 'delete',
|
||||||
|
CONTEXT = 'context',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DiffLineDeleted {
|
||||||
|
type: LineType.DELETE;
|
||||||
|
oldNumber: number;
|
||||||
|
newNumber: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DiffLineInserted {
|
||||||
|
type: LineType.INSERT;
|
||||||
|
oldNumber: undefined;
|
||||||
|
newNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DiffLineContext {
|
||||||
|
type: LineType.CONTEXT;
|
||||||
|
oldNumber: number;
|
||||||
|
newNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DiffLineContent = {
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DiffLine = (DiffLineDeleted | DiffLineInserted | DiffLineContext) & DiffLineContent;
|
||||||
|
|
||||||
|
export interface DiffBlock {
|
||||||
|
oldStartLine: number;
|
||||||
|
oldStartLine2?: number;
|
||||||
|
newStartLine: number;
|
||||||
|
header: string;
|
||||||
|
lines: DiffLine[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DiffFileName {
|
||||||
|
oldName: string;
|
||||||
|
newName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DiffFile extends DiffFileName {
|
||||||
|
addedLines: number;
|
||||||
|
deletedLines: number;
|
||||||
|
isCombined: boolean;
|
||||||
|
isGitDiff: boolean;
|
||||||
|
language: string;
|
||||||
|
blocks: DiffBlock[];
|
||||||
|
oldMode?: string | string[];
|
||||||
|
newMode?: string;
|
||||||
|
deletedFileMode?: string;
|
||||||
|
newFileMode?: string;
|
||||||
|
isDeleted?: boolean;
|
||||||
|
isNew?: boolean;
|
||||||
|
isCopy?: boolean;
|
||||||
|
isRename?: boolean;
|
||||||
|
isBinary?: boolean;
|
||||||
|
isTooBig?: boolean;
|
||||||
|
unchangedPercentage?: number;
|
||||||
|
changedPercentage?: number;
|
||||||
|
checksumBefore?: string | string[];
|
||||||
|
checksumAfter?: string;
|
||||||
|
mode?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OutputFormatType = 'line-by-line' | 'side-by-side';
|
||||||
|
|
||||||
|
export const OutputFormatType: { [_: string]: OutputFormatType } = {
|
||||||
|
LINE_BY_LINE: 'line-by-line',
|
||||||
|
SIDE_BY_SIDE: 'side-by-side',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LineMatchingType = 'lines' | 'words' | 'none';
|
||||||
|
|
||||||
|
export const LineMatchingType: { [_: string]: LineMatchingType } = {
|
||||||
|
LINES: 'lines',
|
||||||
|
WORDS: 'words',
|
||||||
|
NONE: 'none',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DiffStyleType = 'word' | 'char';
|
||||||
|
|
||||||
|
export const DiffStyleType: { [_: string]: DiffStyleType } = {
|
||||||
|
WORD: 'word',
|
||||||
|
CHAR: 'char',
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum ColorSchemeType {
|
||||||
|
AUTO = 'auto',
|
||||||
|
DARK = 'dark',
|
||||||
|
LIGHT = 'light',
|
||||||
|
}
|
||||||
|
|
@ -5,113 +5,225 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
:root,
|
||||||
|
:host {
|
||||||
|
--d2h-bg-color: #fff;
|
||||||
|
--d2h-border-color: #ddd;
|
||||||
|
|
||||||
|
--d2h-dim-color: rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
--d2h-line-border-color: #eeeeee;
|
||||||
|
|
||||||
|
--d2h-file-header-bg-color: #f7f7f7;
|
||||||
|
--d2h-file-header-border-color: #d8d8d8;
|
||||||
|
|
||||||
|
--d2h-empty-placeholder-bg-color: #f1f1f1;
|
||||||
|
--d2h-empty-placeholder-border-color: #e1e1e1;
|
||||||
|
|
||||||
|
--d2h-selected-color: #c8e1ff;
|
||||||
|
|
||||||
|
--d2h-ins-bg-color: #dfd;
|
||||||
|
--d2h-ins-border-color: #b4e2b4;
|
||||||
|
--d2h-ins-highlight-bg-color: #97f295;
|
||||||
|
--d2h-ins-label-color: #399839;
|
||||||
|
|
||||||
|
--d2h-del-bg-color: #fee8e9;
|
||||||
|
--d2h-del-border-color: #e9aeae;
|
||||||
|
--d2h-del-highlight-bg-color: #ffb6ba;
|
||||||
|
--d2h-del-label-color: #c33;
|
||||||
|
|
||||||
|
--d2h-change-del-color: #fdf2d0;
|
||||||
|
--d2h-change-ins-color: #ded;
|
||||||
|
|
||||||
|
--d2h-info-bg-color: #f8fafd;
|
||||||
|
--d2h-info-border-color: #d5e4f2;
|
||||||
|
|
||||||
|
--d2h-change-label-color: #d0b44c;
|
||||||
|
--d2h-moved-label-color: #3572b0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Color Scheme
|
||||||
|
*/
|
||||||
|
|
||||||
|
--d2h-dark-color: rgb(230, 237, 243);
|
||||||
|
--d2h-dark-bg-color: rgb(13, 17, 23);
|
||||||
|
--d2h-dark-border-color: rgb(48, 54, 61);
|
||||||
|
|
||||||
|
--d2h-dark-dim-color: rgb(110, 118, 129);
|
||||||
|
|
||||||
|
--d2h-dark-line-border-color: rgb(33, 38, 45);
|
||||||
|
|
||||||
|
--d2h-dark-file-header-bg-color: rgb(22, 27, 34);
|
||||||
|
--d2h-dark-file-header-border-color: rgb(48, 54, 61);
|
||||||
|
|
||||||
|
--d2h-dark-empty-placeholder-bg-color: rgba(110, 118, 129, 0.1);
|
||||||
|
--d2h-dark-empty-placeholder-border-color: rgb(48, 54, 61);
|
||||||
|
|
||||||
|
--d2h-dark-selected-color: rgba(56, 139, 253, 0.1);
|
||||||
|
|
||||||
|
--d2h-dark-ins-bg-color: rgba(46, 160, 67, 0.15);
|
||||||
|
--d2h-dark-ins-border-color: rgba(46, 160, 67, 0.4);
|
||||||
|
--d2h-dark-ins-highlight-bg-color: rgba(46, 160, 67, 0.4);
|
||||||
|
--d2h-dark-ins-label-color: rgb(63, 185, 80);
|
||||||
|
|
||||||
|
--d2h-dark-del-bg-color: rgba(248, 81, 73, 0.1);
|
||||||
|
--d2h-dark-del-border-color: rgba(248, 81, 73, 0.4);
|
||||||
|
--d2h-dark-del-highlight-bg-color: rgba(248, 81, 73, 0.4);
|
||||||
|
--d2h-dark-del-label-color: rgb(248, 81, 73);
|
||||||
|
|
||||||
|
--d2h-dark-change-del-color: rgba(210, 153, 34, 0.2);
|
||||||
|
--d2h-dark-change-ins-color: rgba(46, 160, 67, 0.25);
|
||||||
|
|
||||||
|
--d2h-dark-info-bg-color: rgba(56, 139, 253, 0.1);
|
||||||
|
--d2h-dark-info-border-color: rgba(56, 139, 253, 0.4);
|
||||||
|
|
||||||
|
--d2h-dark-change-label-color: rgb(210, 153, 34);
|
||||||
|
--d2h-dark-moved-label-color: #3572b0;
|
||||||
|
}
|
||||||
|
|
||||||
.d2h-wrapper {
|
.d2h-wrapper {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-header {
|
.d2h-file-header {
|
||||||
|
display: flex;
|
||||||
|
height: 35px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-bottom: 1px solid #d8d8d8;
|
border-bottom: 1px solid var(--d2h-file-header-border-color);
|
||||||
background-color: #f7f7f7;
|
background-color: var(--d2h-file-header-bg-color);
|
||||||
|
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-file-header.d2h-sticky-header {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-stats {
|
.d2h-file-stats {
|
||||||
display: inline;
|
display: -webkit-box;
|
||||||
text-align: center;
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-lines-added {
|
.d2h-lines-added {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
border: 1px solid var(--d2h-ins-border-color);
|
||||||
|
|
||||||
.d2h-lines-added > * {
|
|
||||||
background-color: #ceffce;
|
|
||||||
border: 1px solid #b4e2b4;
|
|
||||||
color: #399839;
|
|
||||||
border-radius: 5px 0 0 5px;
|
border-radius: 5px 0 0 5px;
|
||||||
|
color: var(--d2h-ins-label-color);
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-lines-deleted {
|
.d2h-lines-deleted {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
border: 1px solid var(--d2h-del-border-color);
|
||||||
|
|
||||||
.d2h-lines-deleted > * {
|
|
||||||
background-color: #f7c8c8;
|
|
||||||
border: 1px solid #e9aeae;
|
|
||||||
color: #c33;
|
|
||||||
border-radius: 0 5px 5px 0;
|
border-radius: 0 5px 5px 0;
|
||||||
|
color: var(--d2h-del-label-color);
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-name-wrapper {
|
.d2h-file-name-wrapper {
|
||||||
display: inline-flex;
|
display: -webkit-box;
|
||||||
width: 90%;
|
display: -ms-flexbox;
|
||||||
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
display: flex;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-name {
|
.d2h-file-name {
|
||||||
line-height: 33px;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-wrapper {
|
.d2h-file-wrapper {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid var(--d2h-border-color);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.d2h-file-collapse {
|
||||||
|
justify-content: flex-end;
|
||||||
|
display: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid var(--d2h-border-color);
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-file-collapse.d2h-selected {
|
||||||
|
background-color: var(--d2h-selected-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-file-collapse-input {
|
||||||
|
margin: 0 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.d2h-diff-table {
|
.d2h-diff-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-family: "Menlo", "Consolas", monospace;
|
font-family: 'Menlo', 'Consolas', monospace;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-diff-tbody > tr > td > div {
|
|
||||||
height: 16px;
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-files-diff {
|
.d2h-files-diff {
|
||||||
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-diff {
|
.d2h-file-diff {
|
||||||
overflow-x: scroll;
|
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.d2h-files-diff.d2h-d-none,
|
||||||
|
.d2h-file-diff.d2h-d-none {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.d2h-file-side-diff {
|
.d2h-file-side-diff {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
margin-right: -4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-code-line {
|
.d2h-code-line {
|
||||||
display: block;
|
display: inline-block;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
padding: 0 10px;
|
user-select: none;
|
||||||
margin-left: 80px;
|
width: calc(100% - 16em);
|
||||||
|
/* Compensate for the absolute positioning of the line numbers */
|
||||||
|
padding: 0 8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-code-side-line {
|
.d2h-code-side-line {
|
||||||
display: block;
|
display: inline-block;
|
||||||
white-space: pre;
|
white-space: nowrap;
|
||||||
padding: 0 10px;
|
user-select: none;
|
||||||
height: 18px;
|
width: calc(100% - 9em);
|
||||||
line-height: 18px;
|
/* Compensate for the absolute positioning of the line numbers */
|
||||||
margin-left: 50px;
|
padding: 0 4.5em;
|
||||||
/* Override HighlightJS */
|
}
|
||||||
color: inherit;
|
|
||||||
overflow-x: inherit;
|
.d2h-code-line-ctn {
|
||||||
|
display: inline-block;
|
||||||
background: none;
|
background: none;
|
||||||
/* ******************** */
|
padding: 0;
|
||||||
|
word-wrap: normal;
|
||||||
|
white-space: pre;
|
||||||
|
user-select: text;
|
||||||
|
width: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-code-line del,
|
.d2h-code-line del,
|
||||||
|
|
@ -119,7 +231,7 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: #ffb6ba;
|
background-color: var(--d2h-del-highlight-bg-color);
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,71 +240,92 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: #97f295;
|
background-color: var(--d2h-ins-highlight-bg-color);
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-code-line-prefix {
|
.d2h-code-line-prefix {
|
||||||
float: left;
|
display: inline;
|
||||||
background: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-code-line-ctn {
|
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
word-wrap: normal;
|
||||||
|
white-space: pre;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-num1 {
|
.line-num1 {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
float: left;
|
float: left;
|
||||||
width: 40px;
|
width: 3.5em;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding-left: 3px;
|
padding: 0 0.5em 0 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-num2 {
|
.line-num2 {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
float: right;
|
float: right;
|
||||||
width: 40px;
|
width: 3.5em;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding-left: 3px;
|
padding: 0 0.5em 0 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-code-linenumber {
|
.d2h-code-linenumber {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
width: 7.5em;
|
||||||
|
/* Keep the numbers fixed on line contents scroll */
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 86px;
|
display: inline-block;
|
||||||
height: 18px;
|
background-color: var(--d2h-bg-color);
|
||||||
padding-left: 2px;
|
color: var(--d2h-dim-color);
|
||||||
padding-right: 2px;
|
|
||||||
line-height: 18px;
|
|
||||||
background-color: #fff;
|
|
||||||
color: rgba(0, 0, 0, 0.3);
|
|
||||||
text-align: right;
|
text-align: right;
|
||||||
border: solid #eeeeee;
|
border: solid var(--d2h-line-border-color);
|
||||||
border-width: 0 1px 0 1px;
|
border-width: 0 1px 0 1px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.d2h-code-linenumber:after {
|
||||||
|
content: '\200b';
|
||||||
|
}
|
||||||
|
|
||||||
.d2h-code-side-linenumber {
|
.d2h-code-side-linenumber {
|
||||||
box-sizing: border-box;
|
/* Keep the numbers fixed on line contents scroll */
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 56px;
|
display: inline-block;
|
||||||
padding-left: 5px;
|
box-sizing: border-box;
|
||||||
padding-right: 5px;
|
width: 4em;
|
||||||
height: 18px;
|
background-color: var(--d2h-bg-color);
|
||||||
line-height: 18px;
|
color: var(--d2h-dim-color);
|
||||||
background-color: #fff;
|
|
||||||
color: rgba(0, 0, 0, 0.3);
|
|
||||||
text-align: right;
|
text-align: right;
|
||||||
border: solid #eeeeee;
|
border: solid var(--d2h-line-border-color);
|
||||||
border-width: 0 1px 0 1px;
|
border-width: 0 1px 0 1px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
padding: 0 0.5em 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-code-side-linenumber:after {
|
||||||
|
content: '\200b';
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-code-side-emptyplaceholder,
|
||||||
|
.d2h-emptyplaceholder {
|
||||||
|
background-color: var(--d2h-empty-placeholder-bg-color);
|
||||||
|
border-color: var(--d2h-empty-placeholder-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-code-linenumber,
|
||||||
|
.d2h-code-side-linenumber,
|
||||||
|
.d2h-code-line-prefix,
|
||||||
|
.d2h-emptyplaceholder {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-code-linenumber,
|
||||||
|
.d2h-code-side-linenumber {
|
||||||
|
direction: rtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -200,35 +333,27 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.d2h-del {
|
.d2h-del {
|
||||||
background-color: #fee8e9;
|
background-color: var(--d2h-del-bg-color);
|
||||||
border-color: #e9aeae;
|
border-color: var(--d2h-del-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-ins {
|
.d2h-ins {
|
||||||
background-color: #dfd;
|
background-color: var(--d2h-ins-bg-color);
|
||||||
border-color: #b4e2b4;
|
border-color: var(--d2h-ins-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-info {
|
.d2h-info {
|
||||||
background-color: #f8fafd;
|
background-color: var(--d2h-info-bg-color);
|
||||||
color: rgba(0, 0, 0, 0.3);
|
color: var(--d2h-dim-color);
|
||||||
border-color: #d5e4f2;
|
border-color: var(--d2h-info-border-color);
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-del.d2h-change, .d2h-ins.d2h-change {
|
|
||||||
background-color: #ffc;
|
|
||||||
}
|
|
||||||
|
|
||||||
ins.d2h-change, del.d2h-change {
|
|
||||||
background-color: #fad771;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-diff .d2h-del.d2h-change {
|
.d2h-file-diff .d2h-del.d2h-change {
|
||||||
background-color: #fae1af;
|
background-color: var(--d2h-change-del-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-diff .d2h-ins.d2h-change {
|
.d2h-file-diff .d2h-ins.d2h-change {
|
||||||
background-color: #ded;
|
background-color: var(--d2h-change-ins-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -241,11 +366,11 @@ ins.d2h-change, del.d2h-change {
|
||||||
|
|
||||||
.d2h-file-list-wrapper a {
|
.d2h-file-list-wrapper a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #3572b0;
|
color: var(--d2h-moved-label-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-list-wrapper a:visited {
|
.d2h-file-list-wrapper a:visited {
|
||||||
color: #3572b0;
|
color: var(--d2h-moved-label-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-list-header {
|
.d2h-file-list-header {
|
||||||
|
|
@ -257,15 +382,27 @@ ins.d2h-change, del.d2h-change {
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-list-line {
|
.d2h-file-list-line {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-list-line .d2h-file-name {
|
|
||||||
line-height: 21px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d2h-file-list {
|
.d2h-file-list {
|
||||||
display: block;
|
display: block;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-file-list > li {
|
||||||
|
border-bottom: var(--d2h-border-color) solid 1px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-file-list > li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2h-file-switch {
|
.d2h-file-switch {
|
||||||
|
|
@ -274,32 +411,331 @@ ins.d2h-change, del.d2h-change {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
.d2h-icon {
|
||||||
* Selection util.
|
vertical-align: middle;
|
||||||
|
margin-right: 10px;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-deleted {
|
||||||
|
color: var(--d2h-del-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-added {
|
||||||
|
color: var(--d2h-ins-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-changed {
|
||||||
|
color: var(--d2h-change-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-moved {
|
||||||
|
color: var(--d2h-moved-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-tag {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-left: 5px;
|
||||||
|
padding: 0 2px;
|
||||||
|
background-color: var(--d2h-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-deleted-tag {
|
||||||
|
border: var(--d2h-del-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-added-tag {
|
||||||
|
border: var(--d2h-ins-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-changed-tag {
|
||||||
|
border: var(--d2h-change-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-moved-tag {
|
||||||
|
border: var(--d2h-moved-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dark Mode Colors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.selecting-left .d2h-code-line,
|
.d2h-dark-color-scheme {
|
||||||
.selecting-left .d2h-code-line *,
|
color: var(--d2h-dark-color);
|
||||||
.selecting-right td.d2h-code-linenumber,
|
background-color: var(--d2h-dark-bg-color);
|
||||||
.selecting-right td.d2h-code-linenumber *,
|
|
||||||
.selecting-left .d2h-code-side-line,
|
|
||||||
.selecting-left .d2h-code-side-line *,
|
|
||||||
.selecting-right td.d2h-code-side-linenumber,
|
|
||||||
.selecting-right td.d2h-code-side-linenumber * {
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.selecting-left .d2h-code-line::selection,
|
.d2h-dark-color-scheme .d2h-file-header {
|
||||||
.selecting-left .d2h-code-line *::selection
|
background-color: var(--d2h-dark-file-header-bg-color);
|
||||||
.selecting-right td.d2h-code-linenumber::selection,
|
border-bottom: var(--d2h-dark-file-header-border-color);
|
||||||
.selecting-left .d2h-code-side-line::selection,
|
}
|
||||||
.selecting-left .d2h-code-side-line *::selection,
|
|
||||||
.selecting-right td.d2h-code-side-linenumber::selection,
|
.d2h-dark-color-scheme .d2h-lines-added {
|
||||||
.selecting-right td.d2h-code-side-linenumber *::selection {
|
border: 1px solid var(--d2h-dark-ins-border-color);
|
||||||
background: transparent;
|
color: var(--d2h-dark-ins-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-lines-deleted {
|
||||||
|
border: 1px solid var(--d2h-dark-del-border-color);
|
||||||
|
color: var(--d2h-dark-del-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-code-line del,
|
||||||
|
.d2h-dark-color-scheme .d2h-code-side-line del {
|
||||||
|
background-color: var(--d2h-dark-del-highlight-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-code-line ins,
|
||||||
|
.d2h-dark-color-scheme .d2h-code-side-line ins {
|
||||||
|
background-color: var(--d2h-dark-ins-highlight-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-diff-tbody {
|
||||||
|
border-color: var(--d2h-dark-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-code-side-linenumber {
|
||||||
|
background-color: var(--d2h-dark-bg-color);
|
||||||
|
color: var(--d2h-dark-dim-color);
|
||||||
|
border-color: var(--d2h-dark-line-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-files-diff .d2h-code-side-emptyplaceholder,
|
||||||
|
.d2h-dark-color-scheme .d2h-files-diff .d2h-emptyplaceholder {
|
||||||
|
background-color: var(--d2h-dark-empty-placeholder-bg-color);
|
||||||
|
border-color: var(--d2h-dark-empty-placeholder-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-code-linenumber {
|
||||||
|
background-color: var(--d2h-dark-bg-color);
|
||||||
|
color: var(--d2h-dark-dim-color);
|
||||||
|
border-color: var(--d2h-dark-line-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-del {
|
||||||
|
background-color: var(--d2h-dark-del-bg-color);
|
||||||
|
border-color: var(--d2h-dark-del-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-ins {
|
||||||
|
background-color: var(--d2h-dark-ins-bg-color);
|
||||||
|
border-color: var(--d2h-dark-ins-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-info {
|
||||||
|
background-color: var(--d2h-dark-info-bg-color);
|
||||||
|
color: var(--d2h-dark-dim-color);
|
||||||
|
border-color: var(--d2h-dark-info-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-file-diff .d2h-del.d2h-change {
|
||||||
|
background-color: var(--d2h-dark-change-del-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-file-diff .d2h-ins.d2h-change {
|
||||||
|
background-color: var(--d2h-dark-change-ins-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-file-wrapper {
|
||||||
|
border: 1px solid var(--d2h-dark-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-file-collapse {
|
||||||
|
border: 1px solid var(--d2h-dark-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-file-collapse.d2h-selected {
|
||||||
|
background-color: var(--d2h-dark-selected-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-file-list-wrapper a {
|
||||||
|
color: var(--d2h-dark-moved-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-file-list-wrapper a:visited {
|
||||||
|
color: var(--d2h-dark-moved-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-file-list > li {
|
||||||
|
border-bottom: var(--d2h-dark-bg-color) solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-deleted {
|
||||||
|
color: var(--d2h-dark-del-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-added {
|
||||||
|
color: var(--d2h-dark-ins-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-changed {
|
||||||
|
color: var(--d2h-dark-change-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-moved {
|
||||||
|
color: var(--d2h-dark-moved-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-tag {
|
||||||
|
background-color: var(--d2h-dark-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-deleted-tag {
|
||||||
|
border: var(--d2h-dark-del-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-added-tag {
|
||||||
|
border: var(--d2h-dark-ins-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-changed-tag {
|
||||||
|
border: var(--d2h-dark-change-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-moved-tag {
|
||||||
|
border: var(--d2h-dark-moved-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto Mode Colors
|
||||||
|
*/
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.d2h-auto-color-scheme {
|
||||||
|
background-color: var(--d2h-dark-bg-color);
|
||||||
|
color: var(--d2h-dark-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-file-header {
|
||||||
|
background-color: var(--d2h-dark-file-header-bg-color);
|
||||||
|
border-bottom: var(--d2h-dark-file-header-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-lines-added {
|
||||||
|
border: 1px solid var(--d2h-dark-ins-border-color);
|
||||||
|
color: var(--d2h-dark-ins-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-lines-deleted {
|
||||||
|
border: 1px solid var(--d2h-dark-del-border-color);
|
||||||
|
color: var(--d2h-dark-del-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-code-line del,
|
||||||
|
.d2h-auto-color-scheme .d2h-code-side-line del {
|
||||||
|
background-color: var(--d2h-dark-del-highlight-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-code-line ins,
|
||||||
|
.d2h-auto-color-scheme .d2h-code-side-line ins {
|
||||||
|
background-color: var(--d2h-dark-ins-highlight-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-diff-tbody {
|
||||||
|
border-color: var(--d2h-dark-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-code-side-linenumber {
|
||||||
|
background-color: var(--d2h-dark-bg-color);
|
||||||
|
color: var(--d2h-dark-dim-color);
|
||||||
|
border-color: var(--d2h-dark-line-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-files-diff .d2h-code-side-emptyplaceholder,
|
||||||
|
.d2h-auto-color-scheme .d2h-files-diff .d2h-emptyplaceholder {
|
||||||
|
background-color: var(--d2h-dark-empty-placeholder-bg-color);
|
||||||
|
border-color: var(--d2h-dark-empty-placeholder-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-code-linenumber {
|
||||||
|
background-color: var(--d2h-dark-bg-color);
|
||||||
|
color: var(--d2h-dark-dim-color);
|
||||||
|
border-color: var(--d2h-dark-line-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-del {
|
||||||
|
background-color: var(--d2h-dark-del-bg-color);
|
||||||
|
border-color: var(--d2h-dark-del-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-ins {
|
||||||
|
background-color: var(--d2h-dark-ins-bg-color);
|
||||||
|
border-color: var(--d2h-dark-ins-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-info {
|
||||||
|
background-color: var(--d2h-dark-info-bg-color);
|
||||||
|
color: var(--d2h-dark-dim-color);
|
||||||
|
border-color: var(--d2h-dark-info-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-file-diff .d2h-del.d2h-change {
|
||||||
|
background-color: var(--d2h-dark-change-del-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-file-diff .d2h-ins.d2h-change {
|
||||||
|
background-color: var(--d2h-dark-change-ins-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-file-wrapper {
|
||||||
|
border: 1px solid var(--d2h-dark-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-file-collapse {
|
||||||
|
border: 1px solid var(--d2h-dark-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-file-collapse.d2h-selected {
|
||||||
|
background-color: var(--d2h-dark-selected-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-file-list-wrapper a {
|
||||||
|
color: var(--d2h-dark-moved-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-file-list-wrapper a:visited {
|
||||||
|
color: var(--d2h-dark-moved-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-file-list > li {
|
||||||
|
border-bottom: var(--d2h-dark-bg-color) solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-dark-color-scheme .d2h-deleted {
|
||||||
|
color: var(--d2h-dark-del-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-added {
|
||||||
|
color: var(--d2h-dark-ins-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-changed {
|
||||||
|
color: var(--d2h-dark-change-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-moved {
|
||||||
|
color: var(--d2h-dark-moved-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-tag {
|
||||||
|
background-color: var(--d2h-dark-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-deleted-tag {
|
||||||
|
border: var(--d2h-dark-del-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-added-tag {
|
||||||
|
border: var(--d2h-dark-ins-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-changed-tag {
|
||||||
|
border: var(--d2h-dark-change-label-color) 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d2h-auto-color-scheme .d2h-moved-tag {
|
||||||
|
border: var(--d2h-dark-moved-label-color) 1px solid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
225
src/ui/js/diff2html-ui-base.ts
Normal file
225
src/ui/js/diff2html-ui-base.ts
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
import { closeTags, nodeStream, mergeStreams, getLanguage } from './highlight.js-helpers';
|
||||||
|
|
||||||
|
import { html, Diff2HtmlConfig, defaultDiff2HtmlConfig } from '../../diff2html';
|
||||||
|
import { DiffFile } from '../../types';
|
||||||
|
import { HighlightResult, HLJSApi } from 'highlight.js';
|
||||||
|
|
||||||
|
export interface Diff2HtmlUIConfig extends Diff2HtmlConfig {
|
||||||
|
synchronisedScroll?: boolean;
|
||||||
|
highlight?: boolean;
|
||||||
|
fileListToggle?: boolean;
|
||||||
|
fileListStartVisible?: boolean;
|
||||||
|
highlightLanguages?: Map<string, string>;
|
||||||
|
/**
|
||||||
|
* @deprecated since version 3.1.0
|
||||||
|
* Smart selection is now enabled by default with vanilla CSS
|
||||||
|
*/
|
||||||
|
smartSelection?: boolean;
|
||||||
|
fileContentToggle?: boolean;
|
||||||
|
stickyFileHeaders?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultDiff2HtmlUIConfig = {
|
||||||
|
...defaultDiff2HtmlConfig,
|
||||||
|
synchronisedScroll: true,
|
||||||
|
highlight: true,
|
||||||
|
fileListToggle: true,
|
||||||
|
fileListStartVisible: false,
|
||||||
|
highlightLanguages: new Map<string, string>(),
|
||||||
|
/**
|
||||||
|
* @deprecated since version 3.1.0
|
||||||
|
* Smart selection is now enabled by default with vanilla CSS
|
||||||
|
*/
|
||||||
|
smartSelection: true,
|
||||||
|
fileContentToggle: true,
|
||||||
|
stickyFileHeaders: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Diff2HtmlUI {
|
||||||
|
readonly config: typeof defaultDiff2HtmlUIConfig;
|
||||||
|
readonly diffHtml: string;
|
||||||
|
readonly targetElement: HTMLElement;
|
||||||
|
readonly hljs: HLJSApi | null = null;
|
||||||
|
|
||||||
|
currentSelectionColumnId = -1;
|
||||||
|
|
||||||
|
constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}, hljs?: HLJSApi) {
|
||||||
|
this.config = { ...defaultDiff2HtmlUIConfig, ...config };
|
||||||
|
this.diffHtml = diffInput !== undefined ? html(diffInput, this.config) : target.innerHTML;
|
||||||
|
this.targetElement = target;
|
||||||
|
if (hljs !== undefined) this.hljs = hljs;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(): void {
|
||||||
|
this.targetElement.innerHTML = this.diffHtml;
|
||||||
|
if (this.config.synchronisedScroll) this.synchronisedScroll();
|
||||||
|
if (this.config.highlight) this.highlightCode();
|
||||||
|
if (this.config.fileListToggle) this.fileListToggle(this.config.fileListStartVisible);
|
||||||
|
if (this.config.fileContentToggle) this.fileContentToggle();
|
||||||
|
if (this.config.stickyFileHeaders) this.stickyFileHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronisedScroll(): void {
|
||||||
|
this.targetElement.querySelectorAll('.d2h-file-wrapper').forEach(wrapper => {
|
||||||
|
const [left, right] = Array<Element>().slice.call(wrapper.querySelectorAll('.d2h-file-side-diff'));
|
||||||
|
|
||||||
|
if (left === undefined || right === undefined) return;
|
||||||
|
|
||||||
|
const onScroll = (event: Event): void => {
|
||||||
|
if (event === null || event.target === null) return;
|
||||||
|
|
||||||
|
if (event.target === left) {
|
||||||
|
right.scrollTop = left.scrollTop;
|
||||||
|
right.scrollLeft = left.scrollLeft;
|
||||||
|
} else {
|
||||||
|
left.scrollTop = right.scrollTop;
|
||||||
|
left.scrollLeft = right.scrollLeft;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
left.addEventListener('scroll', onScroll);
|
||||||
|
right.addEventListener('scroll', onScroll);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fileListToggle(startVisible: boolean): void {
|
||||||
|
const showBtn: HTMLElement | null = this.targetElement.querySelector('.d2h-show');
|
||||||
|
const hideBtn: HTMLElement | null = this.targetElement.querySelector('.d2h-hide');
|
||||||
|
const fileList: HTMLElement | null = this.targetElement.querySelector('.d2h-file-list');
|
||||||
|
|
||||||
|
if (showBtn === null || hideBtn === null || fileList === null) return;
|
||||||
|
|
||||||
|
const show: () => void = () => {
|
||||||
|
showBtn.style.display = 'none';
|
||||||
|
hideBtn.style.display = 'inline';
|
||||||
|
fileList.style.display = 'block';
|
||||||
|
};
|
||||||
|
|
||||||
|
const hide: () => void = () => {
|
||||||
|
showBtn.style.display = 'inline';
|
||||||
|
hideBtn.style.display = 'none';
|
||||||
|
fileList.style.display = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
showBtn.addEventListener('click', () => show());
|
||||||
|
hideBtn.addEventListener('click', () => hide());
|
||||||
|
|
||||||
|
const hashTag = this.getHashTag();
|
||||||
|
if (hashTag === 'files-summary-show') show();
|
||||||
|
else if (hashTag === 'files-summary-hide') hide();
|
||||||
|
else if (startVisible) show();
|
||||||
|
else hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
fileContentToggle(): void {
|
||||||
|
this.targetElement.querySelectorAll<HTMLElement>('.d2h-file-collapse').forEach(fileContentToggleBtn => {
|
||||||
|
fileContentToggleBtn.style.display = 'flex';
|
||||||
|
|
||||||
|
const toggleFileContents: (selector: string) => void = selector => {
|
||||||
|
const fileContents: HTMLElement | null | undefined = fileContentToggleBtn
|
||||||
|
.closest('.d2h-file-wrapper')
|
||||||
|
?.querySelector(selector);
|
||||||
|
|
||||||
|
if (fileContents !== null && fileContents !== undefined) {
|
||||||
|
fileContentToggleBtn.classList.toggle('d2h-selected');
|
||||||
|
fileContents.classList.toggle('d2h-d-none');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleHandler: (e: Event) => void = e => {
|
||||||
|
if (fileContentToggleBtn === e.target) return;
|
||||||
|
|
||||||
|
toggleFileContents('.d2h-file-diff');
|
||||||
|
toggleFileContents('.d2h-files-diff');
|
||||||
|
};
|
||||||
|
|
||||||
|
fileContentToggleBtn.addEventListener('click', e => toggleHandler(e));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightCode(): void {
|
||||||
|
const hljs = this.hljs;
|
||||||
|
if (hljs === null) {
|
||||||
|
throw new Error('Missing a `highlight.js` implementation. Please provide one when instantiating Diff2HtmlUI.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all the diff files and execute the highlight on their lines
|
||||||
|
const files = this.targetElement.querySelectorAll('.d2h-file-wrapper');
|
||||||
|
files.forEach(file => {
|
||||||
|
const language = file.getAttribute('data-lang');
|
||||||
|
|
||||||
|
if (!(this.config.highlightLanguages instanceof Map)) {
|
||||||
|
this.config.highlightLanguages = new Map(Object.entries(this.config.highlightLanguages));
|
||||||
|
}
|
||||||
|
|
||||||
|
let hljsLanguage =
|
||||||
|
language && this.config.highlightLanguages.has(language)
|
||||||
|
? this.config.highlightLanguages.get(language)!
|
||||||
|
: language
|
||||||
|
? getLanguage(language)
|
||||||
|
: 'plaintext';
|
||||||
|
|
||||||
|
// Fallback to plaintext in case language is not loaded
|
||||||
|
if (hljs.getLanguage(hljsLanguage) === undefined) {
|
||||||
|
hljsLanguage = 'plaintext';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all the code lines and execute the highlight on them
|
||||||
|
const codeLines = file.querySelectorAll('.d2h-code-line-ctn');
|
||||||
|
codeLines.forEach(line => {
|
||||||
|
const text = line.textContent;
|
||||||
|
const lineParent = line.parentNode;
|
||||||
|
|
||||||
|
if (text === null || lineParent === null || !this.isElement(lineParent)) return;
|
||||||
|
|
||||||
|
const result: HighlightResult = closeTags(
|
||||||
|
hljs.highlight(text, {
|
||||||
|
language: hljsLanguage,
|
||||||
|
ignoreIllegals: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const originalStream = nodeStream(line);
|
||||||
|
if (originalStream.length) {
|
||||||
|
const resultNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||||
|
resultNode.innerHTML = result.value;
|
||||||
|
result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
|
||||||
|
}
|
||||||
|
|
||||||
|
line.classList.add('hljs');
|
||||||
|
if (result.language) {
|
||||||
|
line.classList.add(result.language);
|
||||||
|
}
|
||||||
|
line.innerHTML = result.value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stickyFileHeaders(): void {
|
||||||
|
this.targetElement.querySelectorAll('.d2h-file-header').forEach(header => {
|
||||||
|
header.classList.add('d2h-sticky-header');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated since version 3.1.0
|
||||||
|
*/
|
||||||
|
smartSelection(): void {
|
||||||
|
console.warn('Smart selection is now enabled by default with CSS. No need to call this method anymore.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private getHashTag(): string | null {
|
||||||
|
const docUrl = document.URL;
|
||||||
|
const hashTagIndex = docUrl.indexOf('#');
|
||||||
|
|
||||||
|
let hashTag = null;
|
||||||
|
if (hashTagIndex !== -1) {
|
||||||
|
hashTag = docUrl.substr(hashTagIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isElement(arg?: unknown): arg is Element {
|
||||||
|
return arg !== null && (arg as Element)?.classList !== undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/ui/js/diff2html-ui-slim.ts
Normal file
13
src/ui/js/diff2html-ui-slim.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { hljs } from './highlight.js-slim';
|
||||||
|
|
||||||
|
import { DiffFile } from '../../types';
|
||||||
|
import { Diff2HtmlUI as Diff2HtmlUIBase, Diff2HtmlUIConfig, defaultDiff2HtmlUIConfig } from './diff2html-ui-base';
|
||||||
|
|
||||||
|
export class Diff2HtmlUI extends Diff2HtmlUIBase {
|
||||||
|
constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}) {
|
||||||
|
super(target, diffInput, config, hljs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { defaultDiff2HtmlUIConfig };
|
||||||
|
export type { Diff2HtmlUIConfig };
|
||||||
|
|
@ -1,208 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Diff to HTML (diff2html-ui.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
* Depends on: [ jQuery ]
|
|
||||||
* Optional dependencies on: [ highlight.js ]
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*global $, hljs, Diff2Html*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var highlightJS = require('./highlight.js-internals.js').HighlightJS;
|
|
||||||
|
|
||||||
var diffJson = null;
|
|
||||||
var defaultTarget = "body";
|
|
||||||
var currentSelectionColumnId = -1;
|
|
||||||
|
|
||||||
function Diff2HtmlUI(config) {
|
|
||||||
var cfg = config || {};
|
|
||||||
|
|
||||||
if (cfg.diff) {
|
|
||||||
diffJson = Diff2Html.getJsonFromDiff(cfg.diff);
|
|
||||||
} else if (cfg.json) {
|
|
||||||
diffJson = cfg.json;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._initSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype.draw = function(targetId, config) {
|
|
||||||
var cfg = config || {};
|
|
||||||
var $target = this._getTarget(targetId);
|
|
||||||
$target.html(Diff2Html.getPrettyHtml(diffJson, cfg));
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype.fileListCloseable = function(targetId, startVisible) {
|
|
||||||
var $target = this._getTarget(targetId);
|
|
||||||
|
|
||||||
var hashTag = this._getHashTag();
|
|
||||||
|
|
||||||
var $showBtn = $target.find(".d2h-show");
|
|
||||||
var $hideBtn = $target.find(".d2h-hide");
|
|
||||||
var $fileList = $target.find(".d2h-file-list");
|
|
||||||
|
|
||||||
if (hashTag === 'files-summary-show') show();
|
|
||||||
else if (hashTag === 'files-summary-hide') hide();
|
|
||||||
else if (startVisible) show();
|
|
||||||
else hide();
|
|
||||||
|
|
||||||
$showBtn.click(show);
|
|
||||||
$hideBtn.click(hide);
|
|
||||||
|
|
||||||
function show() {
|
|
||||||
$showBtn.hide();
|
|
||||||
$hideBtn.show();
|
|
||||||
$fileList.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide() {
|
|
||||||
$hideBtn.hide();
|
|
||||||
$showBtn.show();
|
|
||||||
$fileList.hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype.highlightCode = function(targetId) {
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
var $target = that._getTarget(targetId);
|
|
||||||
|
|
||||||
// collect all the diff files and execute the highlight on their lines
|
|
||||||
var $files = $target.find(".d2h-file-wrapper");
|
|
||||||
$files.map(function(_i, file) {
|
|
||||||
var oldLinesState;
|
|
||||||
var newLinesState;
|
|
||||||
var $file = $(file);
|
|
||||||
var language = $file.data("lang");
|
|
||||||
|
|
||||||
// collect all the code lines and execute the highlight on them
|
|
||||||
var $codeLines = $file.find(".d2h-code-line-ctn");
|
|
||||||
$codeLines.map(function(_j, line) {
|
|
||||||
var $line = $(line);
|
|
||||||
var text = line.textContent;
|
|
||||||
var lineParent = line.parentNode;
|
|
||||||
|
|
||||||
var lineState;
|
|
||||||
if (lineParent.className.indexOf("d2h-del") !== -1) {
|
|
||||||
lineState = oldLinesState;
|
|
||||||
} else {
|
|
||||||
lineState = newLinesState;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = hljs.getLanguage(language) ? hljs.highlight(language, text, true, lineState) : hljs.highlightAuto(text);
|
|
||||||
|
|
||||||
if (lineParent.className.indexOf("d2h-del") !== -1) {
|
|
||||||
oldLinesState = result.top;
|
|
||||||
} else if (lineParent.className.indexOf("d2h-ins") !== -1) {
|
|
||||||
newLinesState = result.top;
|
|
||||||
} else {
|
|
||||||
oldLinesState = result.top;
|
|
||||||
newLinesState = result.top;
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalStream = highlightJS.nodeStream(line);
|
|
||||||
if (originalStream.length) {
|
|
||||||
var resultNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
|
||||||
resultNode.innerHTML = result.value;
|
|
||||||
result.value = highlightJS.mergeStreams(originalStream, highlightJS.nodeStream(resultNode), text);
|
|
||||||
}
|
|
||||||
|
|
||||||
$line.addClass("hljs");
|
|
||||||
$line.addClass(result.language);
|
|
||||||
$line.html(result.value);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._getTarget = function(targetId) {
|
|
||||||
var $target;
|
|
||||||
|
|
||||||
if (typeof targetId === 'object' && targetId instanceof jQuery) {
|
|
||||||
$target = targetId;
|
|
||||||
} else if (typeof targetId === 'string') {
|
|
||||||
$target = $(targetId);
|
|
||||||
} else {
|
|
||||||
console.error("Wrong target provided! Falling back to default value 'body'.");
|
|
||||||
console.log("Please provide a jQuery object or a valid DOM query string.");
|
|
||||||
$target = $(defaultTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $target;
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._getHashTag = function() {
|
|
||||||
var docUrl = document.URL;
|
|
||||||
var hashTagIndex = docUrl.indexOf('#');
|
|
||||||
|
|
||||||
var hashTag = null;
|
|
||||||
if (hashTagIndex !== -1) {
|
|
||||||
hashTag = docUrl.substr(hashTagIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashTag;
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._distinct = function(collection) {
|
|
||||||
return collection.filter(function(v, i) {
|
|
||||||
return collection.indexOf(v) === i;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._initSelection = function() {
|
|
||||||
var body = $('body');
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
body.on('mousedown', '.d2h-diff-table', function(event) {
|
|
||||||
var target = $(event.target);
|
|
||||||
var table = target.closest('.d2h-diff-table');
|
|
||||||
|
|
||||||
if (target.closest('.d2h-code-line,.d2h-code-side-line').length) {
|
|
||||||
table.removeClass('selecting-left');
|
|
||||||
table.addClass('selecting-right');
|
|
||||||
currentSelectionColumnId = 1;
|
|
||||||
} else if (target.closest('.d2h-code-linenumber,.d2h-code-side-linenumber').length) {
|
|
||||||
table.removeClass('selecting-right');
|
|
||||||
table.addClass('selecting-left');
|
|
||||||
currentSelectionColumnId = 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
body.on('copy', '.d2h-diff-table', function(event) {
|
|
||||||
var clipboardData = event.originalEvent.clipboardData;
|
|
||||||
var text = that._getSelectedText();
|
|
||||||
clipboardData.setData('text', text);
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Diff2HtmlUI.prototype._getSelectedText = function() {
|
|
||||||
var sel = window.getSelection();
|
|
||||||
var range = sel.getRangeAt(0);
|
|
||||||
var doc = range.cloneContents();
|
|
||||||
var nodes = doc.querySelectorAll('tr');
|
|
||||||
var text = '';
|
|
||||||
var idx = currentSelectionColumnId;
|
|
||||||
|
|
||||||
if (nodes.length === 0) {
|
|
||||||
text = doc.textContent;
|
|
||||||
} else {
|
|
||||||
[].forEach.call(nodes, function(tr, i) {
|
|
||||||
var td = tr.cells[tr.cells.length === 1 ? 0 : idx];
|
|
||||||
text += (i ? '\n' : '') + td.textContent.replace(/(?:\r\n|\r|\n)/g, '');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.Diff2HtmlUI = Diff2HtmlUI;
|
|
||||||
|
|
||||||
// Expose diff2html in the browser
|
|
||||||
global.Diff2HtmlUI = Diff2HtmlUI;
|
|
||||||
|
|
||||||
})();
|
|
||||||
13
src/ui/js/diff2html-ui.ts
Normal file
13
src/ui/js/diff2html-ui.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import hljs from 'highlight.js';
|
||||||
|
|
||||||
|
import { DiffFile } from '../../types';
|
||||||
|
import { Diff2HtmlUI as Diff2HtmlUIBase, Diff2HtmlUIConfig, defaultDiff2HtmlUIConfig } from './diff2html-ui-base';
|
||||||
|
|
||||||
|
export class Diff2HtmlUI extends Diff2HtmlUIBase {
|
||||||
|
constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}) {
|
||||||
|
super(target, diffInput, config, hljs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { defaultDiff2HtmlUIConfig };
|
||||||
|
export type { Diff2HtmlUIConfig };
|
||||||
655
src/ui/js/highlight.js-helpers.ts
Normal file
655
src/ui/js/highlight.js-helpers.ts
Normal file
|
|
@ -0,0 +1,655 @@
|
||||||
|
/*
|
||||||
|
* Adapted Highlight.js Internal APIs
|
||||||
|
* Used to highlight selected html elements using context
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { HighlightResult } from 'highlight.js';
|
||||||
|
|
||||||
|
/* Utility functions */
|
||||||
|
|
||||||
|
function escapeHTML(value: string): string {
|
||||||
|
return value.replace(/&/gm, '&').replace(/</gm, '<').replace(/>/gm, '>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function tag(node: Node): string {
|
||||||
|
return node.nodeName.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stream merging */
|
||||||
|
|
||||||
|
type NodeEvent = {
|
||||||
|
event: 'start' | 'stop';
|
||||||
|
offset: number;
|
||||||
|
node: Node;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function nodeStream(node: Node): NodeEvent[] {
|
||||||
|
const result: NodeEvent[] = [];
|
||||||
|
|
||||||
|
const nodeStream = (node: Node, offset: number): number => {
|
||||||
|
for (let child = node.firstChild; child; child = child.nextSibling) {
|
||||||
|
if (child.nodeType === 3 && child.nodeValue !== null) {
|
||||||
|
offset += child.nodeValue.length;
|
||||||
|
} else if (child.nodeType === 1) {
|
||||||
|
result.push({
|
||||||
|
event: 'start',
|
||||||
|
offset: offset,
|
||||||
|
node: child,
|
||||||
|
});
|
||||||
|
offset = nodeStream(child, offset);
|
||||||
|
// Prevent void elements from having an end tag that would actually
|
||||||
|
// double them in the output. There are more void elements in HTML
|
||||||
|
// but we list only those realistically expected in code display.
|
||||||
|
if (!tag(child).match(/br|hr|img|input/)) {
|
||||||
|
result.push({
|
||||||
|
event: 'stop',
|
||||||
|
offset: offset,
|
||||||
|
node: child,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
nodeStream(node, 0);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mergeStreams(original: NodeEvent[], highlighted: NodeEvent[], value: string): string {
|
||||||
|
let processed = 0;
|
||||||
|
let result = '';
|
||||||
|
const nodeStack = [];
|
||||||
|
|
||||||
|
function isElement(arg?: unknown): arg is Element {
|
||||||
|
return arg !== null && (arg as Element)?.attributes !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectStream(): NodeEvent[] {
|
||||||
|
if (!original.length || !highlighted.length) {
|
||||||
|
return original.length ? original : highlighted;
|
||||||
|
}
|
||||||
|
if (original[0].offset !== highlighted[0].offset) {
|
||||||
|
return original[0].offset < highlighted[0].offset ? original : highlighted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
To avoid starting the stream just before it should stop the order is
|
||||||
|
ensured that original always starts first and closes last:
|
||||||
|
if (event1 == 'start' && event2 == 'start')
|
||||||
|
return original;
|
||||||
|
if (event1 == 'start' && event2 == 'stop')
|
||||||
|
return highlighted;
|
||||||
|
if (event1 == 'stop' && event2 == 'start')
|
||||||
|
return original;
|
||||||
|
if (event1 == 'stop' && event2 == 'stop')
|
||||||
|
return highlighted;
|
||||||
|
... which is collapsed to:
|
||||||
|
*/
|
||||||
|
return highlighted[0].event === 'start' ? original : highlighted;
|
||||||
|
}
|
||||||
|
|
||||||
|
function open(node: Node): void {
|
||||||
|
if (!isElement(node)) {
|
||||||
|
throw new Error('Node is not an Element');
|
||||||
|
}
|
||||||
|
|
||||||
|
result += `<${tag(node)} ${Array<Attr>()
|
||||||
|
.map.call(node.attributes, attr => `${attr.nodeName}="${escapeHTML(attr.value).replace(/"/g, '"')}"`)
|
||||||
|
.join(' ')}>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(node: Node): void {
|
||||||
|
result += '</' + tag(node) + '>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function render(event: NodeEvent): void {
|
||||||
|
(event.event === 'start' ? open : close)(event.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (original.length || highlighted.length) {
|
||||||
|
let stream = selectStream();
|
||||||
|
result += escapeHTML(value.substring(processed, stream[0].offset));
|
||||||
|
processed = stream[0].offset;
|
||||||
|
if (stream === original) {
|
||||||
|
/*
|
||||||
|
On any opening or closing tag of the original markup we first close
|
||||||
|
the entire highlighted node stack, then render the original tag along
|
||||||
|
with all the following original tags at the same offset and then
|
||||||
|
reopen all the tags on the highlighted stack.
|
||||||
|
*/
|
||||||
|
nodeStack.reverse().forEach(close);
|
||||||
|
do {
|
||||||
|
render(stream.splice(0, 1)[0]);
|
||||||
|
stream = selectStream();
|
||||||
|
} while (stream === original && stream.length && stream[0].offset === processed);
|
||||||
|
nodeStack.reverse().forEach(open);
|
||||||
|
} else {
|
||||||
|
if (stream[0].event === 'start') {
|
||||||
|
nodeStack.push(stream[0].node);
|
||||||
|
} else {
|
||||||
|
nodeStack.pop();
|
||||||
|
}
|
||||||
|
render(stream.splice(0, 1)[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result + escapeHTML(value.substr(processed));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/hexojs/hexo-util/blob/979873b63a725377c2bd6ad834d790023496130d/lib/highlight.js#L123
|
||||||
|
export function closeTags(res: HighlightResult): HighlightResult {
|
||||||
|
const tokenStack = new Array<string>();
|
||||||
|
|
||||||
|
res.value = res.value
|
||||||
|
.split('\n')
|
||||||
|
.map(line => {
|
||||||
|
const prepend = tokenStack.map(token => `<span class="${token}">`).join('');
|
||||||
|
const matches = line.matchAll(/(<span class="(.*?)">|<\/span>)/g);
|
||||||
|
Array.from(matches).forEach(match => {
|
||||||
|
if (match[0] === '</span>') tokenStack.shift();
|
||||||
|
else tokenStack.unshift(match[2]);
|
||||||
|
});
|
||||||
|
const append = '</span>'.repeat(tokenStack.length);
|
||||||
|
return prepend + line + append;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sourced from https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md and
|
||||||
|
// https://github.com/exercism/v2-website/blob/main/config/initializers/prism.rb#L187-L315
|
||||||
|
const languagesToExt: { [_: string]: string } = {
|
||||||
|
'1c': '1c',
|
||||||
|
abnf: 'abnf',
|
||||||
|
accesslog: 'accesslog',
|
||||||
|
as: 'actionscript',
|
||||||
|
adb: 'ada',
|
||||||
|
ada: 'ada',
|
||||||
|
ads: 'ada',
|
||||||
|
angelscript: 'angelscript',
|
||||||
|
// asc: 'angelscript',
|
||||||
|
apache: 'apache',
|
||||||
|
applescript: 'applescript',
|
||||||
|
scpt: 'applescript',
|
||||||
|
arcade: 'arcade',
|
||||||
|
cpp: 'cpp',
|
||||||
|
hpp: 'cpp',
|
||||||
|
arduino: 'arduino',
|
||||||
|
ino: 'arduino',
|
||||||
|
armasm: 'armasm',
|
||||||
|
arm: 'armasm',
|
||||||
|
xml: 'xml',
|
||||||
|
html: 'xml',
|
||||||
|
xhtml: 'xml',
|
||||||
|
rss: 'xml',
|
||||||
|
atom: 'xml',
|
||||||
|
xjb: 'xml',
|
||||||
|
xsd: 'xml',
|
||||||
|
xsl: 'xml',
|
||||||
|
plist: 'xml',
|
||||||
|
svg: 'xml',
|
||||||
|
asciidoc: 'asciidoc',
|
||||||
|
adoc: 'asciidoc',
|
||||||
|
asc: 'asciidoc',
|
||||||
|
aspectj: 'aspectj',
|
||||||
|
ahk: 'autohotkey',
|
||||||
|
ahkl: 'autohotkey',
|
||||||
|
au3: 'autoit',
|
||||||
|
avrasm: 'avrasm',
|
||||||
|
awk: 'awk',
|
||||||
|
axapta: 'axapta',
|
||||||
|
'x++': 'axapta',
|
||||||
|
bash: 'bash',
|
||||||
|
sh: 'bash',
|
||||||
|
zsh: 'bash',
|
||||||
|
b: 'basic',
|
||||||
|
bnf: 'bnf',
|
||||||
|
bf: 'brainfuck',
|
||||||
|
c: 'c',
|
||||||
|
h: 'c',
|
||||||
|
cats: 'c',
|
||||||
|
idc: 'c',
|
||||||
|
cal: 'cal',
|
||||||
|
capnproto: 'capnproto',
|
||||||
|
capnp: 'capnproto',
|
||||||
|
ceylon: 'ceylon',
|
||||||
|
clean: 'clean',
|
||||||
|
clj: 'clojure',
|
||||||
|
boot: 'clojure',
|
||||||
|
cl2: 'clojure',
|
||||||
|
cljc: 'clojure',
|
||||||
|
cljs: 'clojure',
|
||||||
|
'cljs.hl': 'clojure',
|
||||||
|
cljscm: 'clojure',
|
||||||
|
cljx: 'clojure',
|
||||||
|
hic: 'clojure',
|
||||||
|
'clojure-repl': 'clojure-repl',
|
||||||
|
cmake: 'cmake',
|
||||||
|
'cmake.in': 'cmake',
|
||||||
|
coffee: 'coffeescript',
|
||||||
|
_coffee: 'coffeescript',
|
||||||
|
cake: 'coffeescript',
|
||||||
|
cjsx: 'coffeescript',
|
||||||
|
iced: 'coffeescript',
|
||||||
|
cson: 'coffeescript',
|
||||||
|
coq: 'coq',
|
||||||
|
cos: 'cos',
|
||||||
|
cls: 'cos',
|
||||||
|
crmsh: 'crmsh',
|
||||||
|
crm: 'crmsh',
|
||||||
|
pcmk: 'crmsh',
|
||||||
|
cr: 'crystal',
|
||||||
|
cs: 'csharp',
|
||||||
|
csx: 'csharp',
|
||||||
|
csp: 'csp',
|
||||||
|
css: 'css',
|
||||||
|
d: 'd',
|
||||||
|
di: 'd',
|
||||||
|
md: 'markdown',
|
||||||
|
markdown: 'markdown',
|
||||||
|
mdown: 'markdown',
|
||||||
|
mdwn: 'markdown',
|
||||||
|
mkd: 'markdown',
|
||||||
|
mkdn: 'markdown',
|
||||||
|
mkdown: 'markdown',
|
||||||
|
ronn: 'markdown',
|
||||||
|
workbook: 'markdown',
|
||||||
|
dart: 'dart',
|
||||||
|
dpr: 'delphi',
|
||||||
|
dfm: 'delphi',
|
||||||
|
pas: 'delphi',
|
||||||
|
pascal: 'delphi',
|
||||||
|
diff: 'diff',
|
||||||
|
patch: 'diff',
|
||||||
|
django: 'django',
|
||||||
|
jinja: 'django',
|
||||||
|
dns: 'dns',
|
||||||
|
zone: 'dns',
|
||||||
|
bind: 'dns',
|
||||||
|
dockerfile: 'dockerfile',
|
||||||
|
docker: 'dockerfile',
|
||||||
|
dos: 'dos',
|
||||||
|
bat: 'dos',
|
||||||
|
cmd: 'dos',
|
||||||
|
dsconfig: 'dsconfig',
|
||||||
|
dts: 'dts',
|
||||||
|
dust: 'dust',
|
||||||
|
dst: 'dust',
|
||||||
|
ebnf: 'ebnf',
|
||||||
|
ex: 'elixir',
|
||||||
|
exs: 'elixir',
|
||||||
|
elm: 'elm',
|
||||||
|
rb: 'ruby',
|
||||||
|
builder: 'ruby',
|
||||||
|
eye: 'ruby',
|
||||||
|
gemspec: 'ruby',
|
||||||
|
god: 'ruby',
|
||||||
|
jbuilder: 'ruby',
|
||||||
|
mspec: 'ruby',
|
||||||
|
pluginspec: 'ruby',
|
||||||
|
podspec: 'ruby',
|
||||||
|
rabl: 'ruby',
|
||||||
|
rake: 'ruby',
|
||||||
|
rbuild: 'ruby',
|
||||||
|
rbw: 'ruby',
|
||||||
|
rbx: 'ruby',
|
||||||
|
ru: 'ruby',
|
||||||
|
ruby: 'ruby',
|
||||||
|
spec: 'ruby',
|
||||||
|
thor: 'ruby',
|
||||||
|
watchr: 'ruby',
|
||||||
|
erb: 'erb',
|
||||||
|
'erlang-repl': 'erlang-repl',
|
||||||
|
erl: 'erlang',
|
||||||
|
'app.src': 'erlang',
|
||||||
|
escript: 'erlang',
|
||||||
|
hrl: 'erlang',
|
||||||
|
xrl: 'erlang',
|
||||||
|
yrl: 'erlang',
|
||||||
|
excel: 'excel',
|
||||||
|
xls: 'excel',
|
||||||
|
xlsx: 'excel',
|
||||||
|
fix: 'fix',
|
||||||
|
flix: 'flix',
|
||||||
|
f90: 'fortran',
|
||||||
|
f: 'fortran',
|
||||||
|
f03: 'fortran',
|
||||||
|
f08: 'fortran',
|
||||||
|
f77: 'fortran',
|
||||||
|
f95: 'fortran',
|
||||||
|
for: 'fortran',
|
||||||
|
fpp: 'fortran',
|
||||||
|
fs: 'fsharp',
|
||||||
|
fsx: 'fsharp',
|
||||||
|
gams: 'gams',
|
||||||
|
gms: 'gams',
|
||||||
|
gauss: 'gauss',
|
||||||
|
gss: 'gauss',
|
||||||
|
gcode: 'gcode',
|
||||||
|
nc: 'gcode',
|
||||||
|
gherkin: 'gherkin',
|
||||||
|
glsl: 'glsl',
|
||||||
|
fp: 'glsl',
|
||||||
|
frag: 'glsl',
|
||||||
|
frg: 'glsl',
|
||||||
|
fsh: 'glsl',
|
||||||
|
fshader: 'glsl',
|
||||||
|
geo: 'glsl',
|
||||||
|
geom: 'glsl',
|
||||||
|
glslv: 'glsl',
|
||||||
|
gshader: 'glsl',
|
||||||
|
shader: 'glsl',
|
||||||
|
tesc: 'glsl',
|
||||||
|
tese: 'glsl',
|
||||||
|
vert: 'glsl',
|
||||||
|
vrx: 'glsl',
|
||||||
|
vsh: 'glsl',
|
||||||
|
vshader: 'glsl',
|
||||||
|
gml: 'gml',
|
||||||
|
go: 'go',
|
||||||
|
bal: 'go',
|
||||||
|
golo: 'golo',
|
||||||
|
gololang: 'golo',
|
||||||
|
gradle: 'gradle',
|
||||||
|
groovy: 'groovy',
|
||||||
|
grt: 'groovy',
|
||||||
|
gtpl: 'groovy',
|
||||||
|
gvy: 'groovy',
|
||||||
|
haml: 'haml',
|
||||||
|
'haml.deface': 'haml',
|
||||||
|
handlebars: 'handlebars',
|
||||||
|
hbs: 'handlebars',
|
||||||
|
'html.hbs': 'handlebars',
|
||||||
|
'html.handlebars': 'handlebars',
|
||||||
|
hs: 'haskell',
|
||||||
|
hsc: 'haskell',
|
||||||
|
idr: 'haskell',
|
||||||
|
purs: 'haskell',
|
||||||
|
hx: 'haxe',
|
||||||
|
hxsl: 'haxe',
|
||||||
|
hsp: 'hsp',
|
||||||
|
htmlbars: 'htmlbars',
|
||||||
|
http: 'http',
|
||||||
|
https: 'http',
|
||||||
|
hy: 'hy',
|
||||||
|
inform7: 'inform7',
|
||||||
|
i7: 'inform7',
|
||||||
|
ini: 'ini',
|
||||||
|
toml: 'ini',
|
||||||
|
cfg: 'ini',
|
||||||
|
prefs: 'ini',
|
||||||
|
// properties: 'ini',
|
||||||
|
irpf90: 'irpf90',
|
||||||
|
isbl: 'isbl',
|
||||||
|
java: 'java',
|
||||||
|
jsp: 'java',
|
||||||
|
js: 'javascript',
|
||||||
|
jsx: 'javascript',
|
||||||
|
_js: 'javascript',
|
||||||
|
bones: 'javascript',
|
||||||
|
es: 'javascript',
|
||||||
|
es6: 'javascript',
|
||||||
|
gs: 'javascript',
|
||||||
|
jake: 'javascript',
|
||||||
|
jsb: 'javascript',
|
||||||
|
jscad: 'javascript',
|
||||||
|
jsfl: 'javascript',
|
||||||
|
jsm: 'javascript',
|
||||||
|
jss: 'javascript',
|
||||||
|
mjs: 'javascript',
|
||||||
|
njs: 'javascript',
|
||||||
|
pac: 'javascript',
|
||||||
|
sjs: 'javascript',
|
||||||
|
ssjs: 'javascript',
|
||||||
|
xsjs: 'javascript',
|
||||||
|
xsjslib: 'javascript',
|
||||||
|
cfc: 'javascript',
|
||||||
|
'jboss-cli': 'jboss-cli',
|
||||||
|
json: 'json',
|
||||||
|
avsc: 'json',
|
||||||
|
geojson: 'json',
|
||||||
|
gltf: 'json',
|
||||||
|
'JSON-tmLanguage': 'json',
|
||||||
|
jsonl: 'json',
|
||||||
|
tfstate: 'json',
|
||||||
|
'tfstate.backup': 'json',
|
||||||
|
topojson: 'json',
|
||||||
|
webapp: 'json',
|
||||||
|
webmanifest: 'json',
|
||||||
|
jl: 'julia',
|
||||||
|
'julia-repl': 'julia-repl',
|
||||||
|
kt: 'kotlin',
|
||||||
|
ktm: 'kotlin',
|
||||||
|
kts: 'kotlin',
|
||||||
|
lasso: 'lasso',
|
||||||
|
// ls: 'lasso',
|
||||||
|
lassoscript: 'lasso',
|
||||||
|
tex: 'latex',
|
||||||
|
ldif: 'ldif',
|
||||||
|
leaf: 'leaf',
|
||||||
|
less: 'less',
|
||||||
|
lisp: 'lisp',
|
||||||
|
factor: 'lisp',
|
||||||
|
livecodeserver: 'livecodeserver',
|
||||||
|
ls: 'livescript',
|
||||||
|
_ls: 'livescript',
|
||||||
|
llvm: 'llvm',
|
||||||
|
lsl: 'lsl',
|
||||||
|
lua: 'lua',
|
||||||
|
nse: 'lua',
|
||||||
|
p8: 'lua',
|
||||||
|
pd_lua: 'lua',
|
||||||
|
rbxs: 'lua',
|
||||||
|
wlua: 'lua',
|
||||||
|
mak: 'makefile',
|
||||||
|
make: 'makefile',
|
||||||
|
mk: 'makefile',
|
||||||
|
mkfile: 'makefile',
|
||||||
|
mathematica: 'mathematica',
|
||||||
|
mma: 'mathematica',
|
||||||
|
wl: 'mathematica',
|
||||||
|
matlab: 'matlab',
|
||||||
|
maxima: 'maxima',
|
||||||
|
mel: 'mel',
|
||||||
|
mercury: 'mercury',
|
||||||
|
mipsasm: 'mipsasm',
|
||||||
|
miz: 'mizar',
|
||||||
|
voc: 'mizar',
|
||||||
|
al: 'perl',
|
||||||
|
cgi: 'perl',
|
||||||
|
fcgi: 'perl',
|
||||||
|
perl: 'perl',
|
||||||
|
ph: 'perl',
|
||||||
|
plx: 'perl',
|
||||||
|
pl: 'perl',
|
||||||
|
pm: 'perl',
|
||||||
|
psgi: 'perl',
|
||||||
|
t: 'perl',
|
||||||
|
mojolicious: 'mojolicious',
|
||||||
|
monkey: 'monkey',
|
||||||
|
monkey2: 'monkey',
|
||||||
|
moonscript: 'moonscript',
|
||||||
|
moon: 'moonscript',
|
||||||
|
n1ql: 'n1ql',
|
||||||
|
nginxconf: 'nginx',
|
||||||
|
nim: 'nim',
|
||||||
|
nimrod: 'nim',
|
||||||
|
nix: 'nix',
|
||||||
|
nsi: 'nsis',
|
||||||
|
nsh: 'nsis',
|
||||||
|
m: 'objectivec',
|
||||||
|
objc: 'objectivec',
|
||||||
|
mm: 'objectivec',
|
||||||
|
'obj-c': 'objectivec',
|
||||||
|
'obj-c++': 'objectivec',
|
||||||
|
'objective-c++': 'objectivec',
|
||||||
|
fun: 'ocaml',
|
||||||
|
sig: 'ocaml',
|
||||||
|
// sml: 'ocaml',
|
||||||
|
ml: 'ocaml',
|
||||||
|
mli: 'ocaml',
|
||||||
|
eliom: 'ocaml',
|
||||||
|
eliomi: 'ocaml',
|
||||||
|
ml4: 'ocaml',
|
||||||
|
mll: 'ocaml',
|
||||||
|
mly: 'ocaml',
|
||||||
|
openscad: 'openscad',
|
||||||
|
oxygene: 'oxygene',
|
||||||
|
parser3: 'parser3',
|
||||||
|
pf: 'pf',
|
||||||
|
'pf.conf': 'pf',
|
||||||
|
pgsql: 'pgsql',
|
||||||
|
postgres: 'pgsql',
|
||||||
|
postgresql: 'pgsql',
|
||||||
|
php: 'php',
|
||||||
|
aw: 'php',
|
||||||
|
ctp: 'php',
|
||||||
|
inc: 'php',
|
||||||
|
php3: 'php',
|
||||||
|
php4: 'php',
|
||||||
|
php5: 'php',
|
||||||
|
phps: 'php',
|
||||||
|
phpt: 'php',
|
||||||
|
'php-template': 'php-template',
|
||||||
|
plaintext: 'plaintext',
|
||||||
|
txt: 'plaintext',
|
||||||
|
text: 'plaintext',
|
||||||
|
pony: 'pony',
|
||||||
|
ps: 'powershell',
|
||||||
|
ps1: 'powershell',
|
||||||
|
psd1: 'powershell',
|
||||||
|
psm1: 'powershell',
|
||||||
|
pde: 'processing',
|
||||||
|
profile: 'profile',
|
||||||
|
pro: 'prolog',
|
||||||
|
prolog: 'prolog',
|
||||||
|
yap: 'prolog',
|
||||||
|
properties: 'properties',
|
||||||
|
proto: 'protobuf',
|
||||||
|
puppet: 'puppet',
|
||||||
|
pp: 'puppet',
|
||||||
|
purebasic: 'purebasic',
|
||||||
|
py: 'python',
|
||||||
|
bzl: 'python',
|
||||||
|
gyp: 'python',
|
||||||
|
gypi: 'python',
|
||||||
|
lmi: 'python',
|
||||||
|
py3: 'python',
|
||||||
|
pyde: 'python',
|
||||||
|
pyi: 'python',
|
||||||
|
pyp: 'python',
|
||||||
|
pyt: 'python',
|
||||||
|
pyw: 'python',
|
||||||
|
rpy: 'python',
|
||||||
|
tac: 'python',
|
||||||
|
wsgi: 'python',
|
||||||
|
xpy: 'python',
|
||||||
|
'python-repl': 'python-repl',
|
||||||
|
pycon: 'python-repl',
|
||||||
|
q: 'q',
|
||||||
|
k: 'q',
|
||||||
|
kdb: 'q',
|
||||||
|
qml: 'qml',
|
||||||
|
r: 'r',
|
||||||
|
rd: 'r',
|
||||||
|
rsx: 'r',
|
||||||
|
reasonml: 'reasonml',
|
||||||
|
re: 'reasonml',
|
||||||
|
rib: 'rib',
|
||||||
|
roboconf: 'roboconf',
|
||||||
|
graph: 'roboconf',
|
||||||
|
instances: 'roboconf',
|
||||||
|
routeros: 'routeros',
|
||||||
|
rsl: 'rsl',
|
||||||
|
ruleslanguage: 'ruleslanguage',
|
||||||
|
rs: 'rust',
|
||||||
|
'rs.in': 'rust',
|
||||||
|
sas: 'sas',
|
||||||
|
// pony: 'scala',
|
||||||
|
scala: 'scala',
|
||||||
|
kojo: 'scala',
|
||||||
|
sbt: 'scala',
|
||||||
|
sc: 'scala',
|
||||||
|
scm: 'scheme',
|
||||||
|
sch: 'scheme',
|
||||||
|
sld: 'scheme',
|
||||||
|
sls: 'scheme',
|
||||||
|
sps: 'scheme',
|
||||||
|
ss: 'scheme',
|
||||||
|
rkt: 'scheme',
|
||||||
|
scilab: 'scilab',
|
||||||
|
scss: 'scss',
|
||||||
|
shell: 'shell',
|
||||||
|
smali: 'smali',
|
||||||
|
st: 'smalltalk',
|
||||||
|
sml: 'sml',
|
||||||
|
sqf: 'sqf',
|
||||||
|
sql: 'sql',
|
||||||
|
cql: 'sql',
|
||||||
|
ddl: 'sql',
|
||||||
|
mysql: 'sql',
|
||||||
|
prc: 'sql',
|
||||||
|
tab: 'sql',
|
||||||
|
udf: 'sql',
|
||||||
|
viw: 'sql',
|
||||||
|
stan: 'stan',
|
||||||
|
stanfuncs: 'stan',
|
||||||
|
stata: 'stata',
|
||||||
|
step21: 'step21',
|
||||||
|
step: 'step21',
|
||||||
|
stp: 'step21',
|
||||||
|
styl: 'stylus',
|
||||||
|
subunit: 'subunit',
|
||||||
|
swift: 'swift',
|
||||||
|
taggerscript: 'taggerscript',
|
||||||
|
yml: 'yaml',
|
||||||
|
mir: 'yaml',
|
||||||
|
reek: 'yaml',
|
||||||
|
rviz: 'yaml',
|
||||||
|
'sublime-syntax': 'yaml',
|
||||||
|
syntax: 'yaml',
|
||||||
|
yaml: 'yaml',
|
||||||
|
'yaml-tmlanguage': 'yaml',
|
||||||
|
'yml.mysql': 'yaml',
|
||||||
|
tap: 'tap',
|
||||||
|
tcl: 'tcl',
|
||||||
|
adp: 'tcl',
|
||||||
|
tm: 'tcl',
|
||||||
|
thrift: 'thrift',
|
||||||
|
tp: 'tp',
|
||||||
|
twig: 'twig',
|
||||||
|
craftcms: 'twig',
|
||||||
|
ts: 'typescript',
|
||||||
|
tsx: 'typescript',
|
||||||
|
vala: 'vala',
|
||||||
|
vbnet: 'vbnet',
|
||||||
|
vb: 'vbnet',
|
||||||
|
vbscript: 'vbscript',
|
||||||
|
vbs: 'vbscript',
|
||||||
|
'vbscript-html': 'vbscript-html',
|
||||||
|
v: 'verilog',
|
||||||
|
veo: 'verilog',
|
||||||
|
vhdl: 'vhdl',
|
||||||
|
vhd: 'vhdl',
|
||||||
|
vhf: 'vhdl',
|
||||||
|
vhi: 'vhdl',
|
||||||
|
vho: 'vhdl',
|
||||||
|
vhs: 'vhdl',
|
||||||
|
vht: 'vhdl',
|
||||||
|
vhw: 'vhdl',
|
||||||
|
vim: 'vim',
|
||||||
|
x86asm: 'x86asm',
|
||||||
|
xl: 'xl',
|
||||||
|
xquery: 'xquery',
|
||||||
|
xpath: 'xquery',
|
||||||
|
xq: 'xquery',
|
||||||
|
zephir: 'zephir',
|
||||||
|
zep: 'zephir',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getLanguage(fileExtension: string): string {
|
||||||
|
return languagesToExt[fileExtension] ?? 'plaintext';
|
||||||
|
}
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* highlight.js
|
|
||||||
* Author: isagalaev
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
function HighlightJS() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copied from Highlight.js Private API
|
|
||||||
* Will be removed when this part of the API is exposed
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Utility functions */
|
|
||||||
|
|
||||||
function escape(value) {
|
|
||||||
return value.replace(/&/gm, '&').replace(/</gm, '<').replace(/>/gm, '>');
|
|
||||||
}
|
|
||||||
|
|
||||||
function tag(node) {
|
|
||||||
return node.nodeName.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stream merging */
|
|
||||||
|
|
||||||
HighlightJS.prototype.nodeStream = function(node) {
|
|
||||||
var result = [];
|
|
||||||
(function _nodeStream(node, offset) {
|
|
||||||
for (var child = node.firstChild; child; child = child.nextSibling) {
|
|
||||||
if (child.nodeType == 3)
|
|
||||||
offset += child.nodeValue.length;
|
|
||||||
else if (child.nodeType == 1) {
|
|
||||||
result.push({
|
|
||||||
event: 'start',
|
|
||||||
offset: offset,
|
|
||||||
node: child
|
|
||||||
});
|
|
||||||
offset = _nodeStream(child, offset);
|
|
||||||
// Prevent void elements from having an end tag that would actually
|
|
||||||
// double them in the output. There are more void elements in HTML
|
|
||||||
// but we list only those realistically expected in code display.
|
|
||||||
if (!tag(child).match(/br|hr|img|input/)) {
|
|
||||||
result.push({
|
|
||||||
event: 'stop',
|
|
||||||
offset: offset,
|
|
||||||
node: child
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
})(node, 0);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
HighlightJS.prototype.mergeStreams = function(original, highlighted, value) {
|
|
||||||
var processed = 0;
|
|
||||||
var result = '';
|
|
||||||
var nodeStack = [];
|
|
||||||
|
|
||||||
function selectStream() {
|
|
||||||
if (!original.length || !highlighted.length) {
|
|
||||||
return original.length ? original : highlighted;
|
|
||||||
}
|
|
||||||
if (original[0].offset != highlighted[0].offset) {
|
|
||||||
return (original[0].offset < highlighted[0].offset) ? original : highlighted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
To avoid starting the stream just before it should stop the order is
|
|
||||||
ensured that original always starts first and closes last:
|
|
||||||
if (event1 == 'start' && event2 == 'start')
|
|
||||||
return original;
|
|
||||||
if (event1 == 'start' && event2 == 'stop')
|
|
||||||
return highlighted;
|
|
||||||
if (event1 == 'stop' && event2 == 'start')
|
|
||||||
return original;
|
|
||||||
if (event1 == 'stop' && event2 == 'stop')
|
|
||||||
return highlighted;
|
|
||||||
... which is collapsed to:
|
|
||||||
*/
|
|
||||||
return highlighted[0].event == 'start' ? original : highlighted;
|
|
||||||
}
|
|
||||||
|
|
||||||
function open(node) {
|
|
||||||
function attrStr(a) {
|
|
||||||
return ' ' + a.nodeName + '="' + escape(a.value) + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
result += '<' + tag(node) + Array.prototype.map.call(node.attributes, attrStr).join('') + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function close(node) {
|
|
||||||
result += '</' + tag(node) + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function render(event) {
|
|
||||||
(event.event == 'start' ? open : close)(event.node);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (original.length || highlighted.length) {
|
|
||||||
var stream = selectStream();
|
|
||||||
result += escape(value.substr(processed, stream[0].offset - processed));
|
|
||||||
processed = stream[0].offset;
|
|
||||||
if (stream == original) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
On any opening or closing tag of the original markup we first close
|
|
||||||
the entire highlighted node stack, then render the original tag along
|
|
||||||
with all the following original tags at the same offset and then
|
|
||||||
reopen all the tags on the highlighted stack.
|
|
||||||
*/
|
|
||||||
nodeStack.reverse().forEach(close);
|
|
||||||
do {
|
|
||||||
render(stream.splice(0, 1)[0]);
|
|
||||||
stream = selectStream();
|
|
||||||
} while (stream == original && stream.length && stream[0].offset == processed);
|
|
||||||
nodeStack.reverse().forEach(open);
|
|
||||||
} else {
|
|
||||||
if (stream[0].event == 'start') {
|
|
||||||
nodeStack.push(stream[0].node);
|
|
||||||
} else {
|
|
||||||
nodeStack.pop();
|
|
||||||
}
|
|
||||||
render(stream.splice(0, 1)[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result + escape(value.substr(processed));
|
|
||||||
};
|
|
||||||
|
|
||||||
/* **** Highlight.js Private API **** */
|
|
||||||
|
|
||||||
module.exports.HighlightJS = new HighlightJS();
|
|
||||||
|
|
||||||
})();
|
|
||||||
391
src/ui/js/highlight.js-slim.ts
Normal file
391
src/ui/js/highlight.js-slim.ts
Normal file
|
|
@ -0,0 +1,391 @@
|
||||||
|
// Require the highlight.js library without languages
|
||||||
|
import highlightJS from 'highlight.js/lib/core';
|
||||||
|
|
||||||
|
// Convert to imports
|
||||||
|
// ^hljs\.registerLanguage\('(.+)', require\('\./languages\/(.+)'\)\);$
|
||||||
|
// import $1 from 'highlight.js/lib/languages/$2';
|
||||||
|
|
||||||
|
// import _1c from 'highlight.js/lib/languages/1c';
|
||||||
|
// import abnf from 'highlight.js/lib/languages/abnf';
|
||||||
|
// import accesslog from 'highlight.js/lib/languages/accesslog';
|
||||||
|
// import actionscript from 'highlight.js/lib/languages/actionscript';
|
||||||
|
// import ada from 'highlight.js/lib/languages/ada';
|
||||||
|
// import angelscript from 'highlight.js/lib/languages/angelscript';
|
||||||
|
// import apache from 'highlight.js/lib/languages/apache';
|
||||||
|
// import applescript from 'highlight.js/lib/languages/applescript';
|
||||||
|
// import arcade from 'highlight.js/lib/languages/arcade';
|
||||||
|
import cpp from 'highlight.js/lib/languages/cpp';
|
||||||
|
// import arduino from 'highlight.js/lib/languages/arduino';
|
||||||
|
// import armasm from 'highlight.js/lib/languages/armasm';
|
||||||
|
import xml from 'highlight.js/lib/languages/xml';
|
||||||
|
// import asciidoc from 'highlight.js/lib/languages/asciidoc';
|
||||||
|
// import aspectj from 'highlight.js/lib/languages/aspectj';
|
||||||
|
// import autohotkey from 'highlight.js/lib/languages/autohotkey';
|
||||||
|
// import autoit from 'highlight.js/lib/languages/autoit';
|
||||||
|
// import avrasm from 'highlight.js/lib/languages/avrasm';
|
||||||
|
import awk from 'highlight.js/lib/languages/awk';
|
||||||
|
// import axapta from 'highlight.js/lib/languages/axapta';
|
||||||
|
import bash from 'highlight.js/lib/languages/bash';
|
||||||
|
// import basic from 'highlight.js/lib/languages/basic';
|
||||||
|
// import bnf from 'highlight.js/lib/languages/bnf';
|
||||||
|
// import brainfuck from 'highlight.js/lib/languages/brainfuck';
|
||||||
|
import c from 'highlight.js/lib/languages/c';
|
||||||
|
// import cal from 'highlight.js/lib/languages/cal';
|
||||||
|
// import capnproto from 'highlight.js/lib/languages/capnproto';
|
||||||
|
// import ceylon from 'highlight.js/lib/languages/ceylon';
|
||||||
|
// import clean from 'highlight.js/lib/languages/clean';
|
||||||
|
import clojure from 'highlight.js/lib/languages/clojure';
|
||||||
|
// import clojureRepl from 'highlight.js/lib/languages/clojure-repl';
|
||||||
|
// import cmake from 'highlight.js/lib/languages/cmake';
|
||||||
|
// import coffeescript from 'highlight.js/lib/languages/coffeescript';
|
||||||
|
// import coq from 'highlight.js/lib/languages/coq';
|
||||||
|
// import cos from 'highlight.js/lib/languages/cos';
|
||||||
|
// import crmsh from 'highlight.js/lib/languages/crmsh';
|
||||||
|
import crystal from 'highlight.js/lib/languages/crystal';
|
||||||
|
import csharp from 'highlight.js/lib/languages/csharp';
|
||||||
|
import csp from 'highlight.js/lib/languages/csp';
|
||||||
|
import css from 'highlight.js/lib/languages/css';
|
||||||
|
// import d from 'highlight.js/lib/languages/d';
|
||||||
|
import markdown from 'highlight.js/lib/languages/markdown';
|
||||||
|
import dart from 'highlight.js/lib/languages/dart';
|
||||||
|
// import delphi from 'highlight.js/lib/languages/delphi';
|
||||||
|
import diff from 'highlight.js/lib/languages/diff';
|
||||||
|
// import django from 'highlight.js/lib/languages/django';
|
||||||
|
// import dns from 'highlight.js/lib/languages/dns';
|
||||||
|
import dockerfile from 'highlight.js/lib/languages/dockerfile';
|
||||||
|
// import dos from 'highlight.js/lib/languages/dos';
|
||||||
|
// import dsconfig from 'highlight.js/lib/languages/dsconfig';
|
||||||
|
// import dts from 'highlight.js/lib/languages/dts';
|
||||||
|
// import dust from 'highlight.js/lib/languages/dust';
|
||||||
|
// import ebnf from 'highlight.js/lib/languages/ebnf';
|
||||||
|
import elixir from 'highlight.js/lib/languages/elixir';
|
||||||
|
import elm from 'highlight.js/lib/languages/elm';
|
||||||
|
import ruby from 'highlight.js/lib/languages/ruby';
|
||||||
|
// import erb from 'highlight.js/lib/languages/erb';
|
||||||
|
// import erlangRepl from 'highlight.js/lib/languages/erlang-repl';
|
||||||
|
import erlang from 'highlight.js/lib/languages/erlang';
|
||||||
|
// import excel from 'highlight.js/lib/languages/excel';
|
||||||
|
// import fix from 'highlight.js/lib/languages/fix';
|
||||||
|
// import flix from 'highlight.js/lib/languages/flix';
|
||||||
|
// import fortran from 'highlight.js/lib/languages/fortran';
|
||||||
|
import fsharp from 'highlight.js/lib/languages/fsharp';
|
||||||
|
// import gams from 'highlight.js/lib/languages/gams';
|
||||||
|
// import gauss from 'highlight.js/lib/languages/gauss';
|
||||||
|
// import gcode from 'highlight.js/lib/languages/gcode';
|
||||||
|
// import gherkin from 'highlight.js/lib/languages/gherkin';
|
||||||
|
// import glsl from 'highlight.js/lib/languages/glsl';
|
||||||
|
// import gml from 'highlight.js/lib/languages/gml';
|
||||||
|
import go from 'highlight.js/lib/languages/go';
|
||||||
|
// import golo from 'highlight.js/lib/languages/golo';
|
||||||
|
import gradle from 'highlight.js/lib/languages/gradle';
|
||||||
|
import groovy from 'highlight.js/lib/languages/groovy';
|
||||||
|
// import haml from 'highlight.js/lib/languages/haml';
|
||||||
|
import handlebars from 'highlight.js/lib/languages/handlebars';
|
||||||
|
import haskell from 'highlight.js/lib/languages/haskell';
|
||||||
|
// import haxe from 'highlight.js/lib/languages/haxe';
|
||||||
|
// import hsp from 'highlight.js/lib/languages/hsp';
|
||||||
|
// import htmlbars from 'highlight.js/lib/languages/htmlbars';
|
||||||
|
// import http from 'highlight.js/lib/languages/http';
|
||||||
|
// import hy from 'highlight.js/lib/languages/hy';
|
||||||
|
// import inform7 from 'highlight.js/lib/languages/inform7';
|
||||||
|
import ini from 'highlight.js/lib/languages/ini';
|
||||||
|
// import irpf90 from 'highlight.js/lib/languages/irpf90';
|
||||||
|
// import isbl from 'highlight.js/lib/languages/isbl';
|
||||||
|
import java from 'highlight.js/lib/languages/java';
|
||||||
|
import javascript from 'highlight.js/lib/languages/javascript';
|
||||||
|
// import jbossCli from 'highlight.js/lib/languages/jboss-cli';
|
||||||
|
import json from 'highlight.js/lib/languages/json';
|
||||||
|
// import julia from 'highlight.js/lib/languages/julia';
|
||||||
|
// import juliaRepl from 'highlight.js/lib/languages/julia-repl';
|
||||||
|
import kotlin from 'highlight.js/lib/languages/kotlin';
|
||||||
|
// import lasso from 'highlight.js/lib/languages/lasso';
|
||||||
|
// import latex from 'highlight.js/lib/languages/latex';
|
||||||
|
// import ldif from 'highlight.js/lib/languages/ldif';
|
||||||
|
// import leaf from 'highlight.js/lib/languages/leaf';
|
||||||
|
import less from 'highlight.js/lib/languages/less';
|
||||||
|
import lisp from 'highlight.js/lib/languages/lisp';
|
||||||
|
// import livecodeserver from 'highlight.js/lib/languages/livecodeserver';
|
||||||
|
// import livescript from 'highlight.js/lib/languages/livescript';
|
||||||
|
// import llvm from 'highlight.js/lib/languages/llvm';
|
||||||
|
// import lsl from 'highlight.js/lib/languages/lsl';
|
||||||
|
import lua from 'highlight.js/lib/languages/lua';
|
||||||
|
import makefile from 'highlight.js/lib/languages/makefile';
|
||||||
|
// import mathematica from 'highlight.js/lib/languages/mathematica';
|
||||||
|
// import matlab from 'highlight.js/lib/languages/matlab';
|
||||||
|
// import maxima from 'highlight.js/lib/languages/maxima';
|
||||||
|
// import mel from 'highlight.js/lib/languages/mel';
|
||||||
|
// import mercury from 'highlight.js/lib/languages/mercury';
|
||||||
|
// import mipsasm from 'highlight.js/lib/languages/mipsasm';
|
||||||
|
// import mizar from 'highlight.js/lib/languages/mizar';
|
||||||
|
import perl from 'highlight.js/lib/languages/perl';
|
||||||
|
// import mojolicious from 'highlight.js/lib/languages/mojolicious';
|
||||||
|
// import monkey from 'highlight.js/lib/languages/monkey';
|
||||||
|
// import moonscript from 'highlight.js/lib/languages/moonscript';
|
||||||
|
// import n1ql from 'highlight.js/lib/languages/n1ql';
|
||||||
|
import nginx from 'highlight.js/lib/languages/nginx';
|
||||||
|
// import nim from 'highlight.js/lib/languages/nim';
|
||||||
|
// import nix from 'highlight.js/lib/languages/nix';
|
||||||
|
// import nsis from 'highlight.js/lib/languages/nsis';
|
||||||
|
import objectivec from 'highlight.js/lib/languages/objectivec';
|
||||||
|
// import ocaml from 'highlight.js/lib/languages/ocaml';
|
||||||
|
// import openscad from 'highlight.js/lib/languages/openscad';
|
||||||
|
// import oxygene from 'highlight.js/lib/languages/oxygene';
|
||||||
|
// import parser3 from 'highlight.js/lib/languages/parser3';
|
||||||
|
// import pf from 'highlight.js/lib/languages/pf';
|
||||||
|
import pgsql from 'highlight.js/lib/languages/pgsql';
|
||||||
|
import php from 'highlight.js/lib/languages/php';
|
||||||
|
// import phpTemplate from 'highlight.js/lib/languages/php-template';
|
||||||
|
import plaintext from 'highlight.js/lib/languages/plaintext';
|
||||||
|
// import pony from 'highlight.js/lib/languages/pony';
|
||||||
|
import powershell from 'highlight.js/lib/languages/powershell';
|
||||||
|
// import processing from 'highlight.js/lib/languages/processing';
|
||||||
|
// import profile from 'highlight.js/lib/languages/profile';
|
||||||
|
// import prolog from 'highlight.js/lib/languages/prolog';
|
||||||
|
import properties from 'highlight.js/lib/languages/properties';
|
||||||
|
import protobuf from 'highlight.js/lib/languages/protobuf';
|
||||||
|
// import puppet from 'highlight.js/lib/languages/puppet';
|
||||||
|
// import purebasic from 'highlight.js/lib/languages/purebasic';
|
||||||
|
import python from 'highlight.js/lib/languages/python';
|
||||||
|
// import pythonRepl from 'highlight.js/lib/languages/python-repl';
|
||||||
|
// import q from 'highlight.js/lib/languages/q';
|
||||||
|
// import qml from 'highlight.js/lib/languages/qml';
|
||||||
|
// import r from 'highlight.js/lib/languages/r';
|
||||||
|
// import reasonml from 'highlight.js/lib/languages/reasonml';
|
||||||
|
// import rib from 'highlight.js/lib/languages/rib';
|
||||||
|
// import roboconf from 'highlight.js/lib/languages/roboconf';
|
||||||
|
// import routeros from 'highlight.js/lib/languages/routeros';
|
||||||
|
// import rsl from 'highlight.js/lib/languages/rsl';
|
||||||
|
// import ruleslanguage from 'highlight.js/lib/languages/ruleslanguage';
|
||||||
|
import rust from 'highlight.js/lib/languages/rust';
|
||||||
|
// import sas from 'highlight.js/lib/languages/sas';
|
||||||
|
import scala from 'highlight.js/lib/languages/scala';
|
||||||
|
// import scheme from 'highlight.js/lib/languages/scheme';
|
||||||
|
// import scilab from 'highlight.js/lib/languages/scilab';
|
||||||
|
import scss from 'highlight.js/lib/languages/scss';
|
||||||
|
import shell from 'highlight.js/lib/languages/shell';
|
||||||
|
// import smali from 'highlight.js/lib/languages/smali';
|
||||||
|
// import smalltalk from 'highlight.js/lib/languages/smalltalk';
|
||||||
|
// import sml from 'highlight.js/lib/languages/sml';
|
||||||
|
// import sqf from 'highlight.js/lib/languages/sqf';
|
||||||
|
import sql from 'highlight.js/lib/languages/sql';
|
||||||
|
// import stan from 'highlight.js/lib/languages/stan';
|
||||||
|
// import stata from 'highlight.js/lib/languages/stata';
|
||||||
|
// import step21 from 'highlight.js/lib/languages/step21';
|
||||||
|
// import stylus from 'highlight.js/lib/languages/stylus';
|
||||||
|
// import subunit from 'highlight.js/lib/languages/subunit';
|
||||||
|
import swift from 'highlight.js/lib/languages/swift';
|
||||||
|
// import taggerscript from 'highlight.js/lib/languages/taggerscript';
|
||||||
|
import yaml from 'highlight.js/lib/languages/yaml';
|
||||||
|
// import tap from 'highlight.js/lib/languages/tap';
|
||||||
|
// import tcl from 'highlight.js/lib/languages/tcl';
|
||||||
|
// import thrift from 'highlight.js/lib/languages/thrift';
|
||||||
|
// import tp from 'highlight.js/lib/languages/tp';
|
||||||
|
// import twig from 'highlight.js/lib/languages/twig';
|
||||||
|
import typescript from 'highlight.js/lib/languages/typescript';
|
||||||
|
// import vala from 'highlight.js/lib/languages/vala';
|
||||||
|
// import vbnet from 'highlight.js/lib/languages/vbnet';
|
||||||
|
// import vbscript from 'highlight.js/lib/languages/vbscript';
|
||||||
|
// import vbscriptHtml from 'highlight.js/lib/languages/vbscript-html';
|
||||||
|
// import verilog from 'highlight.js/lib/languages/verilog';
|
||||||
|
// import vhdl from 'highlight.js/lib/languages/vhdl';
|
||||||
|
// import vim from 'highlight.js/lib/languages/vim';
|
||||||
|
// import x86asm from 'highlight.js/lib/languages/x86asm';
|
||||||
|
// import xl from 'highlight.js/lib/languages/xl';
|
||||||
|
// import xquery from 'highlight.js/lib/languages/xquery';
|
||||||
|
// import zephir from 'highlight.js/lib/languages/zephir';
|
||||||
|
|
||||||
|
// Convert to registerLanguage
|
||||||
|
// ^hljs\.registerLanguage\('(.+)', require\('\./languages\/(.+)'\)\);$
|
||||||
|
// highlightJS.registerLanguage('$1', $1);
|
||||||
|
|
||||||
|
// Separately require languages
|
||||||
|
// highlightJS.registerLanguage('1c', _1c);
|
||||||
|
// highlightJS.registerLanguage('abnf', abnf);
|
||||||
|
// highlightJS.registerLanguage('accesslog', accesslog);
|
||||||
|
// highlightJS.registerLanguage('actionscript', actionscript);
|
||||||
|
// highlightJS.registerLanguage('ada', ada);
|
||||||
|
// highlightJS.registerLanguage('angelscript', angelscript);
|
||||||
|
// highlightJS.registerLanguage('apache', apache);
|
||||||
|
// highlightJS.registerLanguage('applescript', applescript);
|
||||||
|
// highlightJS.registerLanguage('arcade', arcade);
|
||||||
|
highlightJS.registerLanguage('cpp', cpp);
|
||||||
|
// highlightJS.registerLanguage('arduino', arduino);
|
||||||
|
// highlightJS.registerLanguage('armasm', armasm);
|
||||||
|
highlightJS.registerLanguage('xml', xml);
|
||||||
|
// highlightJS.registerLanguage('asciidoc', asciidoc);
|
||||||
|
// highlightJS.registerLanguage('aspectj', aspectj);
|
||||||
|
// highlightJS.registerLanguage('autohotkey', autohotkey);
|
||||||
|
// highlightJS.registerLanguage('autoit', autoit);
|
||||||
|
// highlightJS.registerLanguage('avrasm', avrasm);
|
||||||
|
highlightJS.registerLanguage('awk', awk);
|
||||||
|
// highlightJS.registerLanguage('axapta', axapta);
|
||||||
|
highlightJS.registerLanguage('bash', bash);
|
||||||
|
// highlightJS.registerLanguage('basic', basic);
|
||||||
|
// highlightJS.registerLanguage('bnf', bnf);
|
||||||
|
// highlightJS.registerLanguage('brainfuck', brainfuck);
|
||||||
|
highlightJS.registerLanguage('c', c);
|
||||||
|
// highlightJS.registerLanguage('cal', cal);
|
||||||
|
// highlightJS.registerLanguage('capnproto', capnproto);
|
||||||
|
// highlightJS.registerLanguage('ceylon', ceylon);
|
||||||
|
// highlightJS.registerLanguage('clean', clean);
|
||||||
|
highlightJS.registerLanguage('clojure', clojure);
|
||||||
|
// highlightJS.registerLanguage('clojure-repl', clojureRepl);
|
||||||
|
// highlightJS.registerLanguage('cmake', cmake);
|
||||||
|
// highlightJS.registerLanguage('coffeescript', coffeescript);
|
||||||
|
// highlightJS.registerLanguage('coq', coq);
|
||||||
|
// highlightJS.registerLanguage('cos', cos);
|
||||||
|
// highlightJS.registerLanguage('crmsh', crmsh);
|
||||||
|
highlightJS.registerLanguage('crystal', crystal);
|
||||||
|
highlightJS.registerLanguage('csharp', csharp);
|
||||||
|
highlightJS.registerLanguage('csp', csp);
|
||||||
|
highlightJS.registerLanguage('css', css);
|
||||||
|
// highlightJS.registerLanguage('d', d);
|
||||||
|
highlightJS.registerLanguage('markdown', markdown);
|
||||||
|
highlightJS.registerLanguage('dart', dart);
|
||||||
|
// highlightJS.registerLanguage('delphi', delphi);
|
||||||
|
highlightJS.registerLanguage('diff', diff);
|
||||||
|
// highlightJS.registerLanguage('django', django);
|
||||||
|
// highlightJS.registerLanguage('dns', dns);
|
||||||
|
highlightJS.registerLanguage('dockerfile', dockerfile);
|
||||||
|
// highlightJS.registerLanguage('dos', dos);
|
||||||
|
// highlightJS.registerLanguage('dsconfig', dsconfig);
|
||||||
|
// highlightJS.registerLanguage('dts', dts);
|
||||||
|
// highlightJS.registerLanguage('dust', dust);
|
||||||
|
// highlightJS.registerLanguage('ebnf', ebnf);
|
||||||
|
highlightJS.registerLanguage('elixir', elixir);
|
||||||
|
highlightJS.registerLanguage('elm', elm);
|
||||||
|
highlightJS.registerLanguage('ruby', ruby);
|
||||||
|
// highlightJS.registerLanguage('erb', erb);
|
||||||
|
// highlightJS.registerLanguage('erlang-repl', erlangRepl);
|
||||||
|
highlightJS.registerLanguage('erlang', erlang);
|
||||||
|
// highlightJS.registerLanguage('excel', excel);
|
||||||
|
// highlightJS.registerLanguage('fix', fix);
|
||||||
|
// highlightJS.registerLanguage('flix', flix);
|
||||||
|
// highlightJS.registerLanguage('fortran', fortran);
|
||||||
|
highlightJS.registerLanguage('fsharp', fsharp);
|
||||||
|
// highlightJS.registerLanguage('gams', gams);
|
||||||
|
// highlightJS.registerLanguage('gauss', gauss);
|
||||||
|
// highlightJS.registerLanguage('gcode', gcode);
|
||||||
|
// highlightJS.registerLanguage('gherkin', gherkin);
|
||||||
|
// highlightJS.registerLanguage('glsl', glsl);
|
||||||
|
// highlightJS.registerLanguage('gml', gml);
|
||||||
|
highlightJS.registerLanguage('go', go);
|
||||||
|
// highlightJS.registerLanguage('golo', golo);
|
||||||
|
highlightJS.registerLanguage('gradle', gradle);
|
||||||
|
highlightJS.registerLanguage('groovy', groovy);
|
||||||
|
// highlightJS.registerLanguage('haml', haml);
|
||||||
|
highlightJS.registerLanguage('handlebars', handlebars);
|
||||||
|
highlightJS.registerLanguage('haskell', haskell);
|
||||||
|
// highlightJS.registerLanguage('haxe', haxe);
|
||||||
|
// highlightJS.registerLanguage('hsp', hsp);
|
||||||
|
// highlightJS.registerLanguage('htmlbars', htmlbars);
|
||||||
|
// highlightJS.registerLanguage('http', http);
|
||||||
|
// highlightJS.registerLanguage('hy', hy);
|
||||||
|
// highlightJS.registerLanguage('inform7', inform7);
|
||||||
|
highlightJS.registerLanguage('ini', ini);
|
||||||
|
// highlightJS.registerLanguage('irpf90', irpf90);
|
||||||
|
// highlightJS.registerLanguage('isbl', isbl);
|
||||||
|
highlightJS.registerLanguage('java', java);
|
||||||
|
highlightJS.registerLanguage('javascript', javascript);
|
||||||
|
// highlightJS.registerLanguage('jboss-cli', jbossCli);
|
||||||
|
highlightJS.registerLanguage('json', json);
|
||||||
|
// highlightJS.registerLanguage('julia', julia);
|
||||||
|
// highlightJS.registerLanguage('julia-repl', juliaRepl);
|
||||||
|
highlightJS.registerLanguage('kotlin', kotlin);
|
||||||
|
// highlightJS.registerLanguage('lasso', lasso);
|
||||||
|
// highlightJS.registerLanguage('latex', latex);
|
||||||
|
// highlightJS.registerLanguage('ldif', ldif);
|
||||||
|
// highlightJS.registerLanguage('leaf', leaf);
|
||||||
|
highlightJS.registerLanguage('less', less);
|
||||||
|
highlightJS.registerLanguage('lisp', lisp);
|
||||||
|
// highlightJS.registerLanguage('livecodeserver', livecodeserver);
|
||||||
|
// highlightJS.registerLanguage('livescript', livescript);
|
||||||
|
// highlightJS.registerLanguage('llvm', llvm);
|
||||||
|
// highlightJS.registerLanguage('lsl', lsl);
|
||||||
|
highlightJS.registerLanguage('lua', lua);
|
||||||
|
highlightJS.registerLanguage('makefile', makefile);
|
||||||
|
// highlightJS.registerLanguage('mathematica', mathematica);
|
||||||
|
// highlightJS.registerLanguage('matlab', matlab);
|
||||||
|
// highlightJS.registerLanguage('maxima', maxima);
|
||||||
|
// highlightJS.registerLanguage('mel', mel);
|
||||||
|
// highlightJS.registerLanguage('mercury', mercury);
|
||||||
|
// highlightJS.registerLanguage('mipsasm', mipsasm);
|
||||||
|
// highlightJS.registerLanguage('mizar', mizar);
|
||||||
|
highlightJS.registerLanguage('perl', perl);
|
||||||
|
// highlightJS.registerLanguage('mojolicious', mojolicious);
|
||||||
|
// highlightJS.registerLanguage('monkey', monkey);
|
||||||
|
// highlightJS.registerLanguage('moonscript', moonscript);
|
||||||
|
// highlightJS.registerLanguage('n1ql', n1ql);
|
||||||
|
highlightJS.registerLanguage('nginx', nginx);
|
||||||
|
// highlightJS.registerLanguage('nim', nim);
|
||||||
|
// highlightJS.registerLanguage('nix', nix);
|
||||||
|
// highlightJS.registerLanguage('nsis', nsis);
|
||||||
|
highlightJS.registerLanguage('objectivec', objectivec);
|
||||||
|
// highlightJS.registerLanguage('ocaml', ocaml);
|
||||||
|
// highlightJS.registerLanguage('openscad', openscad);
|
||||||
|
// highlightJS.registerLanguage('oxygene', oxygene);
|
||||||
|
// highlightJS.registerLanguage('parser3', parser3);
|
||||||
|
// highlightJS.registerLanguage('pf', pf);
|
||||||
|
highlightJS.registerLanguage('pgsql', pgsql);
|
||||||
|
highlightJS.registerLanguage('php', php);
|
||||||
|
// highlightJS.registerLanguage('php-template', phpTemplate);
|
||||||
|
highlightJS.registerLanguage('plaintext', plaintext);
|
||||||
|
// highlightJS.registerLanguage('pony', pony);
|
||||||
|
highlightJS.registerLanguage('powershell', powershell);
|
||||||
|
// highlightJS.registerLanguage('processing', processing);
|
||||||
|
// highlightJS.registerLanguage('profile', profile);
|
||||||
|
// highlightJS.registerLanguage('prolog', prolog);
|
||||||
|
highlightJS.registerLanguage('properties', properties);
|
||||||
|
highlightJS.registerLanguage('protobuf', protobuf);
|
||||||
|
// highlightJS.registerLanguage('puppet', puppet);
|
||||||
|
// highlightJS.registerLanguage('purebasic', purebasic);
|
||||||
|
highlightJS.registerLanguage('python', python);
|
||||||
|
// highlightJS.registerLanguage('python-repl', pythonRepl);
|
||||||
|
// highlightJS.registerLanguage('q', q);
|
||||||
|
// highlightJS.registerLanguage('qml', qml);
|
||||||
|
// highlightJS.registerLanguage('r', r);
|
||||||
|
// highlightJS.registerLanguage('reasonml', reasonml);
|
||||||
|
// highlightJS.registerLanguage('rib', rib);
|
||||||
|
// highlightJS.registerLanguage('roboconf', roboconf);
|
||||||
|
// highlightJS.registerLanguage('routeros', routeros);
|
||||||
|
// highlightJS.registerLanguage('rsl', rsl);
|
||||||
|
// highlightJS.registerLanguage('ruleslanguage', ruleslanguage);
|
||||||
|
highlightJS.registerLanguage('rust', rust);
|
||||||
|
// highlightJS.registerLanguage('sas', sas);
|
||||||
|
highlightJS.registerLanguage('scala', scala);
|
||||||
|
// highlightJS.registerLanguage('scheme', scheme);
|
||||||
|
// highlightJS.registerLanguage('scilab', scilab);
|
||||||
|
highlightJS.registerLanguage('scss', scss);
|
||||||
|
highlightJS.registerLanguage('shell', shell);
|
||||||
|
// highlightJS.registerLanguage('smali', smali);
|
||||||
|
// highlightJS.registerLanguage('smalltalk', smalltalk);
|
||||||
|
// highlightJS.registerLanguage('sml', sml);
|
||||||
|
// highlightJS.registerLanguage('sqf', sqf);
|
||||||
|
highlightJS.registerLanguage('sql', sql);
|
||||||
|
// highlightJS.registerLanguage('stan', stan);
|
||||||
|
// highlightJS.registerLanguage('stata', stata);
|
||||||
|
// highlightJS.registerLanguage('step21', step21);
|
||||||
|
// highlightJS.registerLanguage('stylus', stylus);
|
||||||
|
// highlightJS.registerLanguage('subunit', subunit);
|
||||||
|
highlightJS.registerLanguage('swift', swift);
|
||||||
|
// highlightJS.registerLanguage('taggerscript', taggerscript);
|
||||||
|
highlightJS.registerLanguage('yaml', yaml);
|
||||||
|
// highlightJS.registerLanguage('tap', tap);
|
||||||
|
// highlightJS.registerLanguage('tcl', tcl);
|
||||||
|
// highlightJS.registerLanguage('thrift', thrift);
|
||||||
|
// highlightJS.registerLanguage('tp', tp);
|
||||||
|
// highlightJS.registerLanguage('twig', twig);
|
||||||
|
highlightJS.registerLanguage('typescript', typescript);
|
||||||
|
// highlightJS.registerLanguage('vala', vala);
|
||||||
|
// highlightJS.registerLanguage('vbnet', vbnet);
|
||||||
|
// highlightJS.registerLanguage('vbscript', vbscript);
|
||||||
|
// highlightJS.registerLanguage('vbscript-html', vbscriptHtml);
|
||||||
|
// highlightJS.registerLanguage('verilog', verilog);
|
||||||
|
// highlightJS.registerLanguage('vhdl', vhdl);
|
||||||
|
// highlightJS.registerLanguage('vim', vim);
|
||||||
|
// highlightJS.registerLanguage('x86asm', x86asm);
|
||||||
|
// highlightJS.registerLanguage('xl', xl);
|
||||||
|
// highlightJS.registerLanguage('xquery', xquery);
|
||||||
|
// highlightJS.registerLanguage('zephir', zephir);
|
||||||
|
|
||||||
|
export const hljs = highlightJS;
|
||||||
46
src/utils.js
46
src/utils.js
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Utils (utils.js)
|
|
||||||
* Author: rtfpessoa
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
function Utils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils.prototype.convertWhiteSpaceToNonBreakingSpace = function(str) {
|
|
||||||
return str.slice(0).replace(/ /g, ' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
Utils.prototype.escape = function(str) {
|
|
||||||
return str.slice(0)
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/\t/g, ' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
Utils.prototype.startsWith = function(str, start) {
|
|
||||||
if (typeof start === 'object') {
|
|
||||||
var result = false;
|
|
||||||
start.forEach(function(s) {
|
|
||||||
if (str.indexOf(s) === 0) {
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return str.indexOf(start) === 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
Utils.prototype.valueOrEmpty = function(value) {
|
|
||||||
return value ? value : '';
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.Utils = new Utils();
|
|
||||||
|
|
||||||
})();
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue