@contractkit/prettier-plugin 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$ci.log +5 -5
- package/.turbo/turbo-test$colon$ci.log +13 -10
- package/CHANGELOG.md +25 -0
- package/README.md +12 -3
- package/dist/index.js +170 -52
- package/dist/index.js.map +1 -1
- package/dist/print-ck.d.ts +10 -3
- package/dist/print-ck.d.ts.map +1 -1
- package/dist/print-contract.d.ts +10 -0
- package/dist/print-contract.d.ts.map +1 -1
- package/dist/print-operation.d.ts +13 -0
- package/dist/print-operation.d.ts.map +1 -1
- package/dist/print-type.d.ts +3 -1
- package/dist/print-type.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/print-ck.ts +60 -13
- package/src/print-contract.ts +19 -2
- package/src/print-operation.ts +124 -41
- package/src/print-type.ts +8 -2
- package/tests/print-ck.test.ts +318 -1
- package/tests/round-trip.test.ts +268 -0
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -358
- package/coverage/coverage-final.json +0 -6
- package/coverage/favicon.png +0 -0
- package/coverage/indent.ts.html +0 -88
- package/coverage/index.html +0 -176
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/print-ck.ts.html +0 -424
- package/coverage/print-contract.ts.html +0 -292
- package/coverage/print-operation.ts.html +0 -1078
- package/coverage/print-type.ts.html +0 -532
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { printCk } from '../src/print-ck.js';
|
|
5
|
+
import { parseCk, DiagnosticCollector } from '@contractkit/core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Formatting a `.ck` file must not change it.
|
|
9
|
+
*
|
|
10
|
+
* The prettier plugin used to fold standalone `#` comment blocks into trailing comments on the
|
|
11
|
+
* following declaration, reorder operation body keys, drop blank lines between operations, and
|
|
12
|
+
* expand single-line response bodies — all of which silently rewrote a user's file on
|
|
13
|
+
* `pnpm format`. These tests pin the guarantee: parse → print is the identity on well-formed
|
|
14
|
+
* source, and printing is idempotent.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
function format(source: string, file = 'test.ck'): string {
|
|
18
|
+
const diag = new DiagnosticCollector();
|
|
19
|
+
const ast = parseCk(source, file, diag);
|
|
20
|
+
expect(diag.hasErrors()).toBe(false);
|
|
21
|
+
return printCk(ast);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ─── Repository contracts ────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
const CONTRACTS_DIR = new URL('../../../contracts', import.meta.url).pathname;
|
|
27
|
+
const ckFiles = readdirSync(CONTRACTS_DIR).filter(f => f.endsWith('.ck'));
|
|
28
|
+
|
|
29
|
+
describe('round-trip — repository .ck files', () => {
|
|
30
|
+
it('finds .ck files to check', () => {
|
|
31
|
+
expect(ckFiles.length).toBeGreaterThan(0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
for (const name of ckFiles) {
|
|
35
|
+
it(`formats ${name} to itself`, () => {
|
|
36
|
+
const source = readFileSync(join(CONTRACTS_DIR, name), 'utf8');
|
|
37
|
+
expect(format(source, name)).toBe(source);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// ─── Constructs that previously round-tripped lossily ────────────────────────
|
|
43
|
+
|
|
44
|
+
describe('round-trip — comment placement', () => {
|
|
45
|
+
it('keeps a standalone comment block above the declaration it precedes', () => {
|
|
46
|
+
const source = `# ─── Pet endpoints ───
|
|
47
|
+
|
|
48
|
+
operation /pet: {
|
|
49
|
+
get: {
|
|
50
|
+
response: {
|
|
51
|
+
200:
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
expect(format(source)).toBe(source);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('keeps a contract doc comment on its own line', () => {
|
|
60
|
+
const source = `# A pet for sale
|
|
61
|
+
contract Pet: {
|
|
62
|
+
id: int
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
expect(format(source)).toBe(source);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('keeps a contract doc comment inline when written inline', () => {
|
|
69
|
+
const source = `contract Pet: { # A pet for sale
|
|
70
|
+
id: int
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
expect(format(source)).toBe(source);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('distinguishes a divider from the doc comment below it', () => {
|
|
77
|
+
const source = `# ─── Models ───
|
|
78
|
+
|
|
79
|
+
# A pet for sale
|
|
80
|
+
contract Pet: {
|
|
81
|
+
id: int
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
expect(format(source)).toBe(source);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('keeps an operation doc comment on its own line', () => {
|
|
88
|
+
const source = `operation /pet: {
|
|
89
|
+
# update an existing pet
|
|
90
|
+
put: {
|
|
91
|
+
response: {
|
|
92
|
+
200:
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
97
|
+
expect(format(source)).toBe(source);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('keeps an operation doc comment inline when written inline', () => {
|
|
101
|
+
const source = `operation /pet: {
|
|
102
|
+
put: { # update an existing pet
|
|
103
|
+
response: {
|
|
104
|
+
200:
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
109
|
+
expect(format(source)).toBe(source);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('round-trip — comments in the options block', () => {
|
|
114
|
+
it('keeps a comment above a sub-block', () => {
|
|
115
|
+
const source = `options {
|
|
116
|
+
# where these come from
|
|
117
|
+
keys: {
|
|
118
|
+
area: ledger
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
`;
|
|
122
|
+
expect(format(source)).toBe(source);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('keeps a comment run above the sub-block it precedes', () => {
|
|
126
|
+
const source = `options {
|
|
127
|
+
keys: {
|
|
128
|
+
area: ledger
|
|
129
|
+
}
|
|
130
|
+
# service wiring
|
|
131
|
+
# one per module
|
|
132
|
+
services: {
|
|
133
|
+
UserService: "#src/user.js"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
expect(format(source)).toBe(source);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('keeps a trailing comment before the closing brace', () => {
|
|
141
|
+
const source = `options {
|
|
142
|
+
keys: {
|
|
143
|
+
area: ledger
|
|
144
|
+
}
|
|
145
|
+
# nothing below
|
|
146
|
+
}
|
|
147
|
+
`;
|
|
148
|
+
expect(format(source)).toBe(source);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('keeps an options block that holds nothing but a comment', () => {
|
|
152
|
+
const source = `options {
|
|
153
|
+
# a note
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
156
|
+
expect(format(source)).toBe(source);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('round-trip — operation body key order', () => {
|
|
161
|
+
it('does not reorder keys into a canonical order', () => {
|
|
162
|
+
const source = `operation /pet: {
|
|
163
|
+
put: {
|
|
164
|
+
sdk: updatePet
|
|
165
|
+
service: PetService.update
|
|
166
|
+
response: {
|
|
167
|
+
200:
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
expect(format(source)).toBe(source);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('preserves the opposite order just as faithfully', () => {
|
|
176
|
+
const source = `operation /pet: {
|
|
177
|
+
put: {
|
|
178
|
+
service: PetService.update
|
|
179
|
+
sdk: updatePet
|
|
180
|
+
response: {
|
|
181
|
+
200:
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
expect(format(source)).toBe(source);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('round-trip — layout', () => {
|
|
191
|
+
it('keeps blank lines between operations', () => {
|
|
192
|
+
const source = `operation /pet: {
|
|
193
|
+
get: {
|
|
194
|
+
response: {
|
|
195
|
+
200:
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
post: {
|
|
200
|
+
response: {
|
|
201
|
+
201:
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
`;
|
|
206
|
+
expect(format(source)).toBe(source);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('keeps operations packed when the source has no blank line', () => {
|
|
210
|
+
const source = `operation /pet: {
|
|
211
|
+
get: {
|
|
212
|
+
response: {
|
|
213
|
+
200:
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
post: {
|
|
217
|
+
response: {
|
|
218
|
+
201:
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
`;
|
|
223
|
+
expect(format(source)).toBe(source);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('keeps a single-line response body on one line', () => {
|
|
227
|
+
const source = `operation /pet: {
|
|
228
|
+
get: {
|
|
229
|
+
response: {
|
|
230
|
+
200: { application/json: Pet }
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
expect(format(source)).toBe(source);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('keeps an expanded response body expanded', () => {
|
|
239
|
+
const source = `operation /pet: {
|
|
240
|
+
get: {
|
|
241
|
+
response: {
|
|
242
|
+
200: {
|
|
243
|
+
application/json: Pet
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
`;
|
|
249
|
+
expect(format(source)).toBe(source);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// ─── Idempotence ─────────────────────────────────────────────────────────────
|
|
254
|
+
|
|
255
|
+
describe('round-trip — idempotence', () => {
|
|
256
|
+
const sources = [
|
|
257
|
+
...ckFiles.map(name => readFileSync(join(CONTRACTS_DIR, name), 'utf8')),
|
|
258
|
+
// Non-canonical spacing: formatting once must reach a fixed point.
|
|
259
|
+
`operation /pet: {\n get: {\n response: {\n 200:\n }\n }\n}\n`,
|
|
260
|
+
];
|
|
261
|
+
|
|
262
|
+
for (const [i, source] of sources.entries()) {
|
|
263
|
+
it(`formatting is a fixed point for source #${i}`, () => {
|
|
264
|
+
const once = format(source);
|
|
265
|
+
expect(format(once)).toBe(once);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
});
|
package/coverage/base.css
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
body, html {
|
|
2
|
-
margin:0; padding: 0;
|
|
3
|
-
height: 100%;
|
|
4
|
-
}
|
|
5
|
-
body {
|
|
6
|
-
font-family: Helvetica Neue, Helvetica, Arial;
|
|
7
|
-
font-size: 14px;
|
|
8
|
-
color:#333;
|
|
9
|
-
}
|
|
10
|
-
.small { font-size: 12px; }
|
|
11
|
-
*, *:after, *:before {
|
|
12
|
-
-webkit-box-sizing:border-box;
|
|
13
|
-
-moz-box-sizing:border-box;
|
|
14
|
-
box-sizing:border-box;
|
|
15
|
-
}
|
|
16
|
-
h1 { font-size: 20px; margin: 0;}
|
|
17
|
-
h2 { font-size: 14px; }
|
|
18
|
-
pre {
|
|
19
|
-
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
|
20
|
-
margin: 0;
|
|
21
|
-
padding: 0;
|
|
22
|
-
-moz-tab-size: 2;
|
|
23
|
-
-o-tab-size: 2;
|
|
24
|
-
tab-size: 2;
|
|
25
|
-
}
|
|
26
|
-
a { color:#0074D9; text-decoration:none; }
|
|
27
|
-
a:hover { text-decoration:underline; }
|
|
28
|
-
.strong { font-weight: bold; }
|
|
29
|
-
.space-top1 { padding: 10px 0 0 0; }
|
|
30
|
-
.pad2y { padding: 20px 0; }
|
|
31
|
-
.pad1y { padding: 10px 0; }
|
|
32
|
-
.pad2x { padding: 0 20px; }
|
|
33
|
-
.pad2 { padding: 20px; }
|
|
34
|
-
.pad1 { padding: 10px; }
|
|
35
|
-
.space-left2 { padding-left:55px; }
|
|
36
|
-
.space-right2 { padding-right:20px; }
|
|
37
|
-
.center { text-align:center; }
|
|
38
|
-
.clearfix { display:block; }
|
|
39
|
-
.clearfix:after {
|
|
40
|
-
content:'';
|
|
41
|
-
display:block;
|
|
42
|
-
height:0;
|
|
43
|
-
clear:both;
|
|
44
|
-
visibility:hidden;
|
|
45
|
-
}
|
|
46
|
-
.fl { float: left; }
|
|
47
|
-
@media only screen and (max-width:640px) {
|
|
48
|
-
.col3 { width:100%; max-width:100%; }
|
|
49
|
-
.hide-mobile { display:none!important; }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.quiet {
|
|
53
|
-
color: #7f7f7f;
|
|
54
|
-
color: rgba(0,0,0,0.5);
|
|
55
|
-
}
|
|
56
|
-
.quiet a { opacity: 0.7; }
|
|
57
|
-
|
|
58
|
-
.fraction {
|
|
59
|
-
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
|
60
|
-
font-size: 10px;
|
|
61
|
-
color: #555;
|
|
62
|
-
background: #E8E8E8;
|
|
63
|
-
padding: 4px 5px;
|
|
64
|
-
border-radius: 3px;
|
|
65
|
-
vertical-align: middle;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
div.path a:link, div.path a:visited { color: #333; }
|
|
69
|
-
table.coverage {
|
|
70
|
-
border-collapse: collapse;
|
|
71
|
-
margin: 10px 0 0 0;
|
|
72
|
-
padding: 0;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
table.coverage td {
|
|
76
|
-
margin: 0;
|
|
77
|
-
padding: 0;
|
|
78
|
-
vertical-align: top;
|
|
79
|
-
}
|
|
80
|
-
table.coverage td.line-count {
|
|
81
|
-
text-align: right;
|
|
82
|
-
padding: 0 5px 0 20px;
|
|
83
|
-
}
|
|
84
|
-
table.coverage td.line-coverage {
|
|
85
|
-
text-align: right;
|
|
86
|
-
padding-right: 10px;
|
|
87
|
-
min-width:20px;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
table.coverage td span.cline-any {
|
|
91
|
-
display: inline-block;
|
|
92
|
-
padding: 0 5px;
|
|
93
|
-
width: 100%;
|
|
94
|
-
}
|
|
95
|
-
.missing-if-branch {
|
|
96
|
-
display: inline-block;
|
|
97
|
-
margin-right: 5px;
|
|
98
|
-
border-radius: 3px;
|
|
99
|
-
position: relative;
|
|
100
|
-
padding: 0 4px;
|
|
101
|
-
background: #333;
|
|
102
|
-
color: yellow;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.skip-if-branch {
|
|
106
|
-
display: none;
|
|
107
|
-
margin-right: 10px;
|
|
108
|
-
position: relative;
|
|
109
|
-
padding: 0 4px;
|
|
110
|
-
background: #ccc;
|
|
111
|
-
color: white;
|
|
112
|
-
}
|
|
113
|
-
.missing-if-branch .typ, .skip-if-branch .typ {
|
|
114
|
-
color: inherit !important;
|
|
115
|
-
}
|
|
116
|
-
.coverage-summary {
|
|
117
|
-
border-collapse: collapse;
|
|
118
|
-
width: 100%;
|
|
119
|
-
}
|
|
120
|
-
.coverage-summary tr { border-bottom: 1px solid #bbb; }
|
|
121
|
-
.keyline-all { border: 1px solid #ddd; }
|
|
122
|
-
.coverage-summary td, .coverage-summary th { padding: 10px; }
|
|
123
|
-
.coverage-summary tbody { border: 1px solid #bbb; }
|
|
124
|
-
.coverage-summary td { border-right: 1px solid #bbb; }
|
|
125
|
-
.coverage-summary td:last-child { border-right: none; }
|
|
126
|
-
.coverage-summary th {
|
|
127
|
-
text-align: left;
|
|
128
|
-
font-weight: normal;
|
|
129
|
-
white-space: nowrap;
|
|
130
|
-
}
|
|
131
|
-
.coverage-summary th.file { border-right: none !important; }
|
|
132
|
-
.coverage-summary th.pct { }
|
|
133
|
-
.coverage-summary th.pic,
|
|
134
|
-
.coverage-summary th.abs,
|
|
135
|
-
.coverage-summary td.pct,
|
|
136
|
-
.coverage-summary td.abs { text-align: right; }
|
|
137
|
-
.coverage-summary td.file { white-space: nowrap; }
|
|
138
|
-
.coverage-summary td.pic { min-width: 120px !important; }
|
|
139
|
-
.coverage-summary tfoot td { }
|
|
140
|
-
|
|
141
|
-
.coverage-summary .sorter {
|
|
142
|
-
height: 10px;
|
|
143
|
-
width: 7px;
|
|
144
|
-
display: inline-block;
|
|
145
|
-
margin-left: 0.5em;
|
|
146
|
-
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
|
|
147
|
-
}
|
|
148
|
-
.coverage-summary .sorted .sorter {
|
|
149
|
-
background-position: 0 -20px;
|
|
150
|
-
}
|
|
151
|
-
.coverage-summary .sorted-desc .sorter {
|
|
152
|
-
background-position: 0 -10px;
|
|
153
|
-
}
|
|
154
|
-
.status-line { height: 10px; }
|
|
155
|
-
/* yellow */
|
|
156
|
-
.cbranch-no { background: yellow !important; color: #111; }
|
|
157
|
-
/* dark red */
|
|
158
|
-
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
|
|
159
|
-
.low .chart { border:1px solid #C21F39 }
|
|
160
|
-
.highlighted,
|
|
161
|
-
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
|
|
162
|
-
background: #C21F39 !important;
|
|
163
|
-
}
|
|
164
|
-
/* medium red */
|
|
165
|
-
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
|
|
166
|
-
/* light red */
|
|
167
|
-
.low, .cline-no { background:#FCE1E5 }
|
|
168
|
-
/* light green */
|
|
169
|
-
.high, .cline-yes { background:rgb(230,245,208) }
|
|
170
|
-
/* medium green */
|
|
171
|
-
.cstat-yes { background:rgb(161,215,106) }
|
|
172
|
-
/* dark green */
|
|
173
|
-
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
|
|
174
|
-
.high .chart { border:1px solid rgb(77,146,33) }
|
|
175
|
-
/* dark yellow (gold) */
|
|
176
|
-
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
|
|
177
|
-
.medium .chart { border:1px solid #f9cd0b; }
|
|
178
|
-
/* light yellow */
|
|
179
|
-
.medium { background: #fff4c2; }
|
|
180
|
-
|
|
181
|
-
.cstat-skip { background: #ddd; color: #111; }
|
|
182
|
-
.fstat-skip { background: #ddd; color: #111 !important; }
|
|
183
|
-
.cbranch-skip { background: #ddd !important; color: #111; }
|
|
184
|
-
|
|
185
|
-
span.cline-neutral { background: #eaeaea; }
|
|
186
|
-
|
|
187
|
-
.coverage-summary td.empty {
|
|
188
|
-
opacity: .5;
|
|
189
|
-
padding-top: 4px;
|
|
190
|
-
padding-bottom: 4px;
|
|
191
|
-
line-height: 1;
|
|
192
|
-
color: #888;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.cover-fill, .cover-empty {
|
|
196
|
-
display:inline-block;
|
|
197
|
-
height: 12px;
|
|
198
|
-
}
|
|
199
|
-
.chart {
|
|
200
|
-
line-height: 0;
|
|
201
|
-
}
|
|
202
|
-
.cover-empty {
|
|
203
|
-
background: white;
|
|
204
|
-
}
|
|
205
|
-
.cover-full {
|
|
206
|
-
border-right: none !important;
|
|
207
|
-
}
|
|
208
|
-
pre.prettyprint {
|
|
209
|
-
border: none !important;
|
|
210
|
-
padding: 0 !important;
|
|
211
|
-
margin: 0 !important;
|
|
212
|
-
}
|
|
213
|
-
.com { color: #999 !important; }
|
|
214
|
-
.ignore-none { color: #999; font-weight: normal; }
|
|
215
|
-
|
|
216
|
-
.wrapper {
|
|
217
|
-
min-height: 100%;
|
|
218
|
-
height: auto !important;
|
|
219
|
-
height: 100%;
|
|
220
|
-
margin: 0 auto -48px;
|
|
221
|
-
}
|
|
222
|
-
.footer, .push {
|
|
223
|
-
height: 48px;
|
|
224
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
var jumpToCode = (function init() {
|
|
3
|
-
// Classes of code we would like to highlight in the file view
|
|
4
|
-
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
|
|
5
|
-
|
|
6
|
-
// Elements to highlight in the file listing view
|
|
7
|
-
var fileListingElements = ['td.pct.low'];
|
|
8
|
-
|
|
9
|
-
// We don't want to select elements that are direct descendants of another match
|
|
10
|
-
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
|
|
11
|
-
|
|
12
|
-
// Selector that finds elements on the page to which we can jump
|
|
13
|
-
var selector =
|
|
14
|
-
fileListingElements.join(', ') +
|
|
15
|
-
', ' +
|
|
16
|
-
notSelector +
|
|
17
|
-
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
|
|
18
|
-
|
|
19
|
-
// The NodeList of matching elements
|
|
20
|
-
var missingCoverageElements = document.querySelectorAll(selector);
|
|
21
|
-
|
|
22
|
-
var currentIndex;
|
|
23
|
-
|
|
24
|
-
function toggleClass(index) {
|
|
25
|
-
missingCoverageElements
|
|
26
|
-
.item(currentIndex)
|
|
27
|
-
.classList.remove('highlighted');
|
|
28
|
-
missingCoverageElements.item(index).classList.add('highlighted');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function makeCurrent(index) {
|
|
32
|
-
toggleClass(index);
|
|
33
|
-
currentIndex = index;
|
|
34
|
-
missingCoverageElements.item(index).scrollIntoView({
|
|
35
|
-
behavior: 'smooth',
|
|
36
|
-
block: 'center',
|
|
37
|
-
inline: 'center'
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function goToPrevious() {
|
|
42
|
-
var nextIndex = 0;
|
|
43
|
-
if (typeof currentIndex !== 'number' || currentIndex === 0) {
|
|
44
|
-
nextIndex = missingCoverageElements.length - 1;
|
|
45
|
-
} else if (missingCoverageElements.length > 1) {
|
|
46
|
-
nextIndex = currentIndex - 1;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
makeCurrent(nextIndex);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function goToNext() {
|
|
53
|
-
var nextIndex = 0;
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
typeof currentIndex === 'number' &&
|
|
57
|
-
currentIndex < missingCoverageElements.length - 1
|
|
58
|
-
) {
|
|
59
|
-
nextIndex = currentIndex + 1;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
makeCurrent(nextIndex);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return function jump(event) {
|
|
66
|
-
if (
|
|
67
|
-
document.getElementById('fileSearch') === document.activeElement &&
|
|
68
|
-
document.activeElement != null
|
|
69
|
-
) {
|
|
70
|
-
// if we're currently focused on the search input, we don't want to navigate
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
switch (event.which) {
|
|
75
|
-
case 78: // n
|
|
76
|
-
case 74: // j
|
|
77
|
-
goToNext();
|
|
78
|
-
break;
|
|
79
|
-
case 66: // b
|
|
80
|
-
case 75: // k
|
|
81
|
-
case 80: // p
|
|
82
|
-
goToPrevious();
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
})();
|
|
87
|
-
window.addEventListener('keydown', jumpToCode);
|