paperlint 6.3.0 → 7.1.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/CONTRIBUTING.md +4 -2
- package/README.md +20 -13
- package/dist/adapters/latex/conditionals.d.ts +21 -0
- package/dist/adapters/latex/conditionals.d.ts.map +1 -0
- package/dist/adapters/latex/conditionals.js +75 -0
- package/dist/adapters/latex/conditionals.js.map +1 -0
- package/dist/adapters/latex/headings.d.ts +10 -0
- package/dist/adapters/latex/headings.d.ts.map +1 -0
- package/dist/adapters/latex/headings.js +129 -0
- package/dist/adapters/latex/headings.js.map +1 -0
- package/dist/adapters/latex/index.d.ts +1 -0
- package/dist/adapters/latex/index.d.ts.map +1 -1
- package/dist/adapters/latex/index.js +3 -0
- package/dist/adapters/latex/index.js.map +1 -1
- package/dist/adapters/latex/parse.d.ts +1 -1
- package/dist/adapters/latex/parse.d.ts.map +1 -1
- package/dist/adapters/latex/parse.js +3 -2
- package/dist/adapters/latex/parse.js.map +1 -1
- package/dist/build.d.ts +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +1 -1
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +19 -5
- package/dist/cli.js.map +1 -1
- package/dist/domain/body-pages.d.ts +29 -0
- package/dist/domain/body-pages.d.ts.map +1 -1
- package/dist/domain/body-pages.js +25 -0
- package/dist/domain/body-pages.js.map +1 -1
- package/dist/domain/heading-case.d.ts +100 -0
- package/dist/domain/heading-case.d.ts.map +1 -0
- package/dist/domain/heading-case.js +365 -0
- package/dist/domain/heading-case.js.map +1 -0
- package/dist/domain/tex-document.d.ts +32 -0
- package/dist/domain/tex-document.d.ts.map +1 -1
- package/dist/domain/tex-document.js +11 -0
- package/dist/domain/tex-document.js.map +1 -1
- package/dist/eslint-rules/latex-language.d.ts.map +1 -1
- package/dist/eslint-rules/latex-language.js +16 -3
- package/dist/eslint-rules/latex-language.js.map +1 -1
- package/dist/facts-file.d.ts +3 -3
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +3 -3
- package/dist/facts-file.js.map +1 -1
- package/dist/folder-venue-rule.js +6 -6
- package/dist/folder-venue-rule.js.map +1 -1
- package/dist/heading-case.d.ts +48 -0
- package/dist/heading-case.d.ts.map +1 -0
- package/dist/heading-case.js +191 -0
- package/dist/heading-case.js.map +1 -0
- package/dist/paper-includes.d.ts +3 -0
- package/dist/paper-includes.d.ts.map +1 -1
- package/dist/paper-includes.js +17 -2
- package/dist/paper-includes.js.map +1 -1
- package/dist/ports/latex.d.ts +7 -1
- package/dist/ports/latex.d.ts.map +1 -1
- package/dist/preset-card-rule.d.ts +100 -0
- package/dist/preset-card-rule.d.ts.map +1 -0
- package/dist/preset-card-rule.js +221 -0
- package/dist/preset-card-rule.js.map +1 -0
- package/dist/presets.d.ts +29 -5
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +104 -68
- package/dist/presets.js.map +1 -1
- package/dist/submission.d.ts +8 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +19 -7
- package/dist/submission.js.map +1 -1
- package/dist/tex-requirements.d.ts +42 -3
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +34 -5
- package/dist/tex-requirements.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +6 -3
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js +29 -10
- package/dist/tex-venue-rules.js.map +1 -1
- package/dist/venue-rules.d.ts +2 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +55 -7
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +13 -1
- package/docs/e2e.md +14 -12
- package/docs/optional-rules.md +4 -3
- package/docs/rules/format/page-limit.md +19 -2
- package/docs/rules/paper/folder-venue-leftover.md +1 -1
- package/docs/rules/talk/profile.md +1 -1
- package/docs/rules/tex/heading-case.md +424 -0
- package/docs/rules/tex/template.md +25 -5
- package/docs/rules/tex/venue-leftover.md +13 -0
- package/docs/rules.md +74 -57
- package/docs/submission.md +5 -1
- package/eslint-rules/latex-language.test.mjs +12 -0
- package/eslint-rules/latex-language.ts +17 -4
- package/eslint-rules/paper-typography.test.mjs +12 -0
- package/eslint.config.mjs +59 -1
- package/fixtures/build-e2e/acmart/paper.tex +1 -1
- package/fixtures/build-e2e/fallback/expect.json +3 -3
- package/fixtures/build-e2e/msr/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/msr/expect.json +18 -0
- package/fixtures/build-e2e/msr/paper.tex +49 -0
- package/fixtures/build-e2e/msr/paperlint.json +1 -0
- package/fixtures/build-e2e/msr-appendix-after/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/msr-appendix-after/expect.json +20 -0
- package/fixtures/build-e2e/msr-appendix-after/paper.tex +48 -0
- package/fixtures/build-e2e/msr-appendix-after/paperlint.json +1 -0
- package/lib/skill-checks.mjs +31 -0
- package/package.json +7 -1
- package/presets/acm-sigconf.jsonc +5 -0
- package/presets/acm-sigconf.md +43 -0
- package/presets/agenticdev.jsonc +27 -6
- package/presets/agenticdev.md +151 -0
- package/presets/aidc.jsonc +6 -0
- package/presets/aidc.md +75 -0
- package/presets/aisec.jsonc +5 -1
- package/presets/aisec.md +117 -0
- package/presets/ieee-conference.jsonc +5 -0
- package/presets/ieee-conference.md +38 -0
- package/presets/msr.jsonc +71 -0
- package/presets/msr.md +94 -0
- package/presets/realm.jsonc +7 -2
- package/presets/realm.md +166 -0
- package/presets/tex/agenticdev.tex +1 -1
- package/presets/tex-base.jsonc +6 -0
- package/presets/tex-base.md +30 -0
- package/presets/venue-profile.schema.json +65 -1
- package/skills/camera-ready/SKILL.md +8 -1
- package/skills/camera-ready/SKILL.md.spec.ts +7 -0
- package/skills/find-venue/SKILL.md +6 -5
- package/skills/find-venue/SKILL.md.spec.ts +5 -4
- package/skills/paper-pipeline/SKILL.md +5 -6
- package/skills/paper-pipeline/SKILL.md.spec.ts +4 -5
- package/skills/paper-pipeline/references/acl-venue-rules.md +1 -1
- package/skills/study-accepted-papers/SKILL.md +3 -3
- package/skills/study-accepted-papers/SKILL.md.spec.ts +2 -2
- package/skills/submission-portal/SKILL.md +2 -2
- package/skills/submission-portal/SKILL.md.spec.ts +1 -1
- package/skills/submit-paper/SKILL.md +34 -22
- package/skills/submit-paper/SKILL.md.spec.ts +33 -21
- package/skills/submit-paper/references/publishers/acm.md +1 -1
- package/src/adapters/latex/conditionals.ts +130 -0
- package/src/adapters/latex/headings.ts +163 -0
- package/src/adapters/latex/index.test.ts +65 -0
- package/src/adapters/latex/index.ts +3 -0
- package/src/adapters/latex/parse.ts +3 -2
- package/src/build.ts +2 -2
- package/src/cli.test.ts +50 -7
- package/src/cli.ts +28 -6
- package/src/domain/body-pages.test.ts +144 -0
- package/src/domain/body-pages.ts +61 -1
- package/src/domain/heading-case.test.ts +660 -0
- package/src/domain/heading-case.ts +538 -0
- package/src/domain/tex-document.ts +36 -0
- package/src/domain/venue-name.test.ts +13 -7
- package/src/facts-file.test.ts +4 -4
- package/src/facts-file.ts +6 -6
- package/src/folder-venue-rule.test.ts +10 -10
- package/src/folder-venue-rule.ts +6 -6
- package/src/heading-case.test.ts +500 -0
- package/src/heading-case.ts +275 -0
- package/src/init.test.ts +1 -1
- package/src/new-paper.test.ts +37 -6
- package/src/paper-includes.test.ts +26 -0
- package/src/paper-includes.ts +20 -2
- package/src/paper-settings-commands.test.ts +26 -11
- package/src/ports/latex.ts +7 -0
- package/src/preset-card-rule.test.ts +267 -0
- package/src/preset-card-rule.ts +384 -0
- package/src/presets.test.ts +259 -41
- package/src/presets.ts +161 -75
- package/src/rule-cases.test.ts +136 -50
- package/src/submission.test.ts +44 -4
- package/src/submission.ts +28 -7
- package/src/talk-rules.test.ts +6 -3
- package/src/tex-requirements.harness.ts +25 -11
- package/src/tex-requirements.test.ts +127 -1
- package/src/tex-requirements.ts +91 -12
- package/src/tex-venue-rules.test.ts +196 -21
- package/src/tex-venue-rules.ts +47 -16
- package/src/venue-rules.test.ts +180 -8
- package/src/venue-rules.ts +71 -10
- package/templates/paper/paper.tex +1 -1
- package/skills/submit-paper/references/venues/agenticdev.md +0 -171
- package/skills/submit-paper/references/venues/aisec.md +0 -105
- package/skills/submit-paper/references/venues/realm.md +0 -155
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
bodyEnd,
|
|
4
4
|
isAppendixHeading,
|
|
5
5
|
isReferencesHeading,
|
|
6
|
+
pageSplit,
|
|
7
|
+
type Anchors,
|
|
6
8
|
} from "./body-pages.ts";
|
|
7
9
|
|
|
8
10
|
describe("isReferencesHeading", () => {
|
|
@@ -298,3 +300,145 @@ describe("bodyEnd: where the appendix is not taken, or not found", () => {
|
|
|
298
300
|
});
|
|
299
301
|
});
|
|
300
302
|
});
|
|
303
|
+
|
|
304
|
+
describe("pageSplit: the appendix counts as body, the reference pages are the ones holding only references", () => {
|
|
305
|
+
it("no appendix: the body up to the references, the rest references", () => {
|
|
306
|
+
expect(
|
|
307
|
+
pageSplit(["body", "end\nReferences\n[1] A.", "[2] B.", "[3] C."], {
|
|
308
|
+
bib: 2,
|
|
309
|
+
appendix: null,
|
|
310
|
+
}),
|
|
311
|
+
).toEqual({
|
|
312
|
+
kind: "found",
|
|
313
|
+
page: 2,
|
|
314
|
+
bodyPages: 2,
|
|
315
|
+
refPages: 2,
|
|
316
|
+
appendixFrom: null,
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("🔴 an appendix before the references is body: it does not end the body", () => {
|
|
321
|
+
expect(
|
|
322
|
+
pageSplit(
|
|
323
|
+
[
|
|
324
|
+
"body",
|
|
325
|
+
"Appendix A.\nDetails",
|
|
326
|
+
"more details\nReferences\n[1] A.",
|
|
327
|
+
"[2] B.",
|
|
328
|
+
],
|
|
329
|
+
{ bib: 3, appendix: 2 },
|
|
330
|
+
),
|
|
331
|
+
).toEqual({
|
|
332
|
+
kind: "found",
|
|
333
|
+
page: 3,
|
|
334
|
+
bodyPages: 3,
|
|
335
|
+
refPages: 1,
|
|
336
|
+
appendixFrom: null,
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("🔴 an appendix after the references is body: the pages from it to the end", () => {
|
|
341
|
+
expect(
|
|
342
|
+
pageSplit(
|
|
343
|
+
[
|
|
344
|
+
"body",
|
|
345
|
+
"end\nReferences\n[1] A.",
|
|
346
|
+
"[2] B.",
|
|
347
|
+
"[3] C.\nAppendix A.\nx",
|
|
348
|
+
"y",
|
|
349
|
+
],
|
|
350
|
+
{ bib: 2, appendix: 4 },
|
|
351
|
+
),
|
|
352
|
+
).toEqual({
|
|
353
|
+
kind: "found",
|
|
354
|
+
page: 2,
|
|
355
|
+
bodyPages: 4,
|
|
356
|
+
refPages: 1,
|
|
357
|
+
appendixFrom: 4,
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
describe("pageSplit: the appendix and the references on one page", () => {
|
|
363
|
+
it("an appendix after the references on their own page: that page is counted once", () => {
|
|
364
|
+
expect(
|
|
365
|
+
pageSplit(["body", "end\nReferences\n[1] A.\nAppendix A.\nx"], {
|
|
366
|
+
bib: 2,
|
|
367
|
+
appendix: 2,
|
|
368
|
+
}),
|
|
369
|
+
).toEqual({
|
|
370
|
+
kind: "found",
|
|
371
|
+
page: 2,
|
|
372
|
+
bodyPages: 2,
|
|
373
|
+
refPages: 0,
|
|
374
|
+
appendixFrom: 2,
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("an appendix above the references on their page is body before them, like any text", () => {
|
|
379
|
+
expect(
|
|
380
|
+
pageSplit(["body", "end\nAppendix A.\nx\nReferences\n[1] A.", "[2] B."], {
|
|
381
|
+
bib: 2,
|
|
382
|
+
appendix: 2,
|
|
383
|
+
}),
|
|
384
|
+
).toEqual({
|
|
385
|
+
kind: "found",
|
|
386
|
+
page: 2,
|
|
387
|
+
bodyPages: 2,
|
|
388
|
+
refPages: 1,
|
|
389
|
+
appendixFrom: null,
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it("the references at the top of their page: that page holds only references", () => {
|
|
394
|
+
expect(
|
|
395
|
+
pageSplit(["body", "References\n[1] A.", "Appendix A.\nx"], {
|
|
396
|
+
bib: 2,
|
|
397
|
+
appendix: 3,
|
|
398
|
+
}),
|
|
399
|
+
).toEqual({
|
|
400
|
+
kind: "found",
|
|
401
|
+
page: 2,
|
|
402
|
+
bodyPages: 2,
|
|
403
|
+
refPages: 1,
|
|
404
|
+
appendixFrom: 3,
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
describe("pageSplit: what is said rather than counted, and what is not looked for", () => {
|
|
410
|
+
it.each<[string, readonly string[], Anchors, string]>([
|
|
411
|
+
[
|
|
412
|
+
"no references heading",
|
|
413
|
+
["body", "Appendix\nx"],
|
|
414
|
+
{ bib: null, appendix: 2 },
|
|
415
|
+
"missing",
|
|
416
|
+
],
|
|
417
|
+
[
|
|
418
|
+
"an appendix anchor with no heading",
|
|
419
|
+
["body", "References\n[1] A.", "x"],
|
|
420
|
+
{ bib: 2, appendix: 3 },
|
|
421
|
+
"disagree",
|
|
422
|
+
],
|
|
423
|
+
[
|
|
424
|
+
"a references anchor with no heading",
|
|
425
|
+
["body", "x"],
|
|
426
|
+
{ bib: 2, appendix: null },
|
|
427
|
+
"disagree",
|
|
428
|
+
],
|
|
429
|
+
])("%s: said, not counted", (_, pages, anchors, kind) => {
|
|
430
|
+
expect(pageSplit(pages, anchors).kind).toBe(kind);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it("without anchors the references heading is found in the text, and no appendix is looked for", () => {
|
|
434
|
+
expect(
|
|
435
|
+
pageSplit(["body", "end\nReferences\n[1] A.", "Appendix A.\nx"]),
|
|
436
|
+
).toEqual({
|
|
437
|
+
kind: "found",
|
|
438
|
+
page: 2,
|
|
439
|
+
bodyPages: 2,
|
|
440
|
+
refPages: 1,
|
|
441
|
+
appendixFrom: null,
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
});
|
package/src/domain/body-pages.ts
CHANGED
|
@@ -186,8 +186,11 @@ function appendixAt(
|
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
/** A body end that was found. */
|
|
190
|
+
type Found = Extract<BodyEnd, { readonly kind: "found" }>;
|
|
191
|
+
|
|
189
192
|
/** The body's end at a heading: the heading's page counts when body text stands above it. */
|
|
190
|
-
function endAt(pages: readonly PageReading[], at: At):
|
|
193
|
+
function endAt(pages: readonly PageReading[], at: At): Found {
|
|
191
194
|
const running = runningLines(pages);
|
|
192
195
|
const body = at.above.some((l) => /\p{L}/u.test(l) && !running.has(shape(l)));
|
|
193
196
|
return {
|
|
@@ -221,3 +224,60 @@ export function bodyEnd(
|
|
|
221
224
|
.toSorted((a, b) => a.page - b.page || a.line - b.line)[0];
|
|
222
225
|
return first === undefined ? { kind: "missing" } : endAt(pages, first);
|
|
223
226
|
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The pages of a paper split as a venue counts them when its body limit includes the appendices
|
|
230
|
+
* and only the pages holding nothing but references are reference pages (MSR: «10 pages for the
|
|
231
|
+
* main text, inclusive of all figures, tables, appendices, etc. Two more pages containing only
|
|
232
|
+
* references are permitted.»):
|
|
233
|
+
*
|
|
234
|
+
* body the pages up to the references — their page too when body text stands above the
|
|
235
|
+
* heading — and, when the appendix starts after the references, every page from the
|
|
236
|
+
* appendix's to the last, which holds appendix text
|
|
237
|
+
* references the pages between, which hold only references
|
|
238
|
+
*
|
|
239
|
+
* An appendix before the references is body like any other text, and ends nothing. The appendix is
|
|
240
|
+
* found by hyperref's anchor only (`appendixAt`): without hyperref, an appendix after the references
|
|
241
|
+
* is not told from them and is counted with them. Pure.
|
|
242
|
+
*/
|
|
243
|
+
export type PageSplit =
|
|
244
|
+
| {
|
|
245
|
+
readonly kind: "found";
|
|
246
|
+
/** 1-based page holding the references heading. */
|
|
247
|
+
readonly page: number;
|
|
248
|
+
readonly bodyPages: number;
|
|
249
|
+
readonly refPages: number;
|
|
250
|
+
/** 1-based page the appendix starts on, when it follows the references; else null. */
|
|
251
|
+
readonly appendixFrom: number | null;
|
|
252
|
+
}
|
|
253
|
+
| { readonly kind: "missing" }
|
|
254
|
+
| Extract<BodyEnd, { readonly kind: "disagree" }>;
|
|
255
|
+
|
|
256
|
+
/** Whether heading `a` stands after heading `b` in the document. */
|
|
257
|
+
const after = (a: At, b: At): boolean =>
|
|
258
|
+
a.page > b.page || (a.page === b.page && a.line > b.line);
|
|
259
|
+
|
|
260
|
+
export function pageSplit(
|
|
261
|
+
texts: readonly string[],
|
|
262
|
+
anchors: Anchors = { bib: null, appendix: null },
|
|
263
|
+
): PageSplit {
|
|
264
|
+
const pages = texts.map(readPage);
|
|
265
|
+
const refs = referencesAt(pages, anchors.bib);
|
|
266
|
+
const appendix = appendixAt(pages, anchors.appendix);
|
|
267
|
+
if (refs.kind === "disagree") return refs;
|
|
268
|
+
if (appendix.kind === "disagree") return appendix;
|
|
269
|
+
if (refs.kind === "missing") return { kind: "missing" };
|
|
270
|
+
const upTo = endAt(pages, refs).bodyPages;
|
|
271
|
+
const tail =
|
|
272
|
+
appendix.kind === "at" && after(appendix, refs) ? appendix.page : null;
|
|
273
|
+
// The pages from the appendix to the end, less the ones already counted up to the references.
|
|
274
|
+
const appendixPages = tail === null ? 0 : pages.length - Math.max(tail, upTo);
|
|
275
|
+
const bodyPages = upTo + appendixPages;
|
|
276
|
+
return {
|
|
277
|
+
kind: "found",
|
|
278
|
+
page: refs.page + 1,
|
|
279
|
+
bodyPages,
|
|
280
|
+
refPages: pages.length - bodyPages,
|
|
281
|
+
appendixFrom: tail === null ? null : tail + 1,
|
|
282
|
+
};
|
|
283
|
+
}
|