Skip to content

Commit 48444cb

Browse files
authored
Generate notes from in-source file links (#155)
* Add suggestion to fix the links. * Generate notes from links. * Re-export notev3.
1 parent e6608fe commit 48444cb

File tree

8 files changed

+118
-47
lines changed

8 files changed

+118
-47
lines changed

shared/links-metadata/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export { type Link, parseAndMigrateLink, parseLink, findLink } from "./src/link"
66
export * from "./src/metadata";
77
export * from "./src/types";
88
export * from "./src/utils";
9+
export * from "./src/notes";

shared/links-metadata/src/link.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { SynctexStore } from "./SynctexStore";
22
import type { TexStore } from "./TexStore";
33
import { type Metadata, ORIGIN } from "./metadata";
44
import { migrateSelection } from "./migrate";
5-
import type { ISynctexBlockId } from "./types";
5+
import type { ISelectionParams, ISynctexBlockId } from "./types";
66

77
export type Link = {
88
url: string;
@@ -12,7 +12,7 @@ export type Link = {
1212
version: string;
1313
versionName: string;
1414
isOutdated: boolean;
15-
};
15+
} & ISelectionParams;
1616

1717
export function findLink(line: string) {
1818
const linkStart = line.indexOf(ORIGIN);
@@ -73,6 +73,8 @@ export async function parseAndMigrateLink(
7373
versionName: "legacy",
7474
isOutdated: true,
7575
migrated: false,
76+
selectionStart: { pageNumber: 0, index: 0 },
77+
selectionEnd: { pageNumber: 0, index: 0 },
7678
};
7779
}
7880
const { version, versionName, selectionStart, selectionEnd } = linkData;
@@ -112,6 +114,8 @@ export async function parseAndMigrateLink(
112114
versionName,
113115
isOutdated,
114116
migrated,
117+
selectionStart,
118+
selectionEnd,
115119
};
116120
}
117121

shared/links-metadata/src/notes.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { ISelectionParams } from "./types";
2+
3+
/** Notes envelope. */
4+
export interface INotesEnvelopeV3 {
5+
version: 3;
6+
notes: INoteV3[];
7+
}
8+
9+
/** Version 3 of the note format. */
10+
export interface INoteV3 extends ISelectionParams {
11+
/** Constant value for this version of the note. */
12+
noteVersion: 3;
13+
content: string;
14+
/** local-tz timestamp (from Date.now()) */
15+
date: number;
16+
/** empty for local notes. */
17+
author: string;
18+
/** Full version number. */
19+
version: string;
20+
/** Labels. */
21+
labels: string[];
22+
}
Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,8 @@
1-
import type { ISelectionParams } from "@fluffylabs/links-metadata";
1+
import type { INoteV3, INotesEnvelopeV3 } from "@fluffylabs/links-metadata";
2+
export type { INoteV3 } from "@fluffylabs/links-metadata";
23

34
/** Latest version of the stored note. */
45
export type IStorageNote = INoteV3;
56

6-
/** Notes envelope. */
7-
export interface INotesEnvelope {
8-
version: 3;
9-
notes: INoteV3[];
10-
}
11-
12-
/** Version 3 of the note format. */
13-
export interface INoteV3 extends ISelectionParams {
14-
noteVersion: 3;
15-
content: string;
16-
date: number;
17-
author: string;
18-
version: string;
19-
labels: string[];
20-
}
7+
/** Versioned wrapper for notes. */
8+
export type INotesEnvelope = INotesEnvelopeV3;

tools/links-check/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { program } from "commander";
55
import fastGlob from "fast-glob";
66
import ignore from "ignore";
77
import { performMigrations } from "./migrate";
8+
import { generateNotes } from "./notes";
89
import { type Report, printReport } from "./report";
910
import { scan } from "./scan";
1011

@@ -21,8 +22,9 @@ async function main() {
2122
"--ignore-file <path>",
2223
"Path to a file containing patterns to ignore. Gitignore format applies. Patterns are resolved according to current working directory.",
2324
)
24-
.option("--write", "Perform link migrations in provided source files.")
25+
.option("--write", "Modify the files and update reader links to their newest versions.")
2526
.option("--fix", "Alias for --write.")
27+
.option("--generate-notes <file.json>", "Generate notes for the Gray Paper Reader")
2628
.action(async (paths, options) => {
2729
let files = await fastGlob(paths);
2830
const globExpandedFileCount = files.length;
@@ -73,6 +75,13 @@ async function main() {
7375
return;
7476
}
7577

78+
if (options.generateNotes !== undefined) {
79+
console.info(`📓 Generating notes to ${options.generateNotes}`);
80+
const notes = generateNotes(report);
81+
const notesStr = JSON.stringify(notes, null, 2);
82+
fs.writeFileSync(options.generateNotes, notesStr);
83+
}
84+
7685
const summary = printReport(report);
7786

7887
if (options.write || options.fix) {

tools/links-check/notes.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import type { INoteV3, INotesEnvelopeV3, Link } from "@fluffylabs/links-metadata";
2+
import type { Report } from "./report";
3+
4+
export function generateNotes(report: Report): INotesEnvelopeV3 {
5+
const perLink = new Map<string, { file: string; link: Link }[]>();
6+
const commonPath: Map<string, number>[] = [];
7+
8+
for (const [file, links] of report.detected) {
9+
const parts = file.split("/");
10+
for (let i = 0; i < parts.length; i++) {
11+
commonPath[i] = commonPath[i] || new Map();
12+
commonPath[i].set(parts[i], (commonPath[i].get(parts[i]) || 0) + 1);
13+
}
14+
15+
for (const link of links) {
16+
// consolidate urls to the newest version
17+
const url = link.updated || link.url;
18+
const list = perLink.get(url) ?? [];
19+
list.push({
20+
file,
21+
link,
22+
});
23+
perLink.set(url, list);
24+
}
25+
}
26+
27+
const noOfFiles = report.detected.size;
28+
const path = commonPath
29+
.map((x) => {
30+
for (const [k, count] of x.entries()) {
31+
if (count === noOfFiles) {
32+
return k;
33+
}
34+
return null;
35+
}
36+
})
37+
.filter((x) => x)
38+
.join("/");
39+
40+
const notes = [];
41+
for (const [_, linkData] of perLink) {
42+
notes.push(linkToNote(linkData, path));
43+
}
44+
45+
return {
46+
version: 3,
47+
notes,
48+
};
49+
}
50+
51+
function linkToNote(linkData: { file: string; link: Link }[], path: string): INoteV3 {
52+
const { link } = linkData[0];
53+
// if we knew the github repository address this could actually be links to browse the files.
54+
const content = linkData.map((x) => `${x.file.replace(path, "")}:${x.link.lineNumber}`).join("\n");
55+
56+
return {
57+
noteVersion: 3,
58+
content,
59+
date: Date.now(),
60+
author: "",
61+
version: link.version,
62+
labels: [],
63+
selectionStart: link.selectionStart,
64+
selectionEnd: link.selectionEnd,
65+
};
66+
}

tools/links-check/report.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export function printReport(report: Report): Summary {
7070
console.info(`✅ No broken links found amongst ${total} total.`);
7171
if (outdated > 0) {
7272
console.info(`⚠️ Yet there are ${outdated} outdated links. See above.`);
73+
console.info(`⚠️ You can use '--fix' flag to automatically update these links with suggestions.`);
7374
}
7475
} else {
7576
console.info(`⁉️ Detected some potentially broken links ${broken.length}/${total}:`);
@@ -83,6 +84,12 @@ export function printReport(report: Report): Summary {
8384
}
8485
console.info();
8586
}
87+
88+
if (outdated !== broken.length) {
89+
console.info(
90+
`⚠️ You can use '--fix' flag to automatically update outdated links. Broken links are not going to be changed.`,
91+
);
92+
}
8693
}
8794

8895
return {

tools/matrix-bot/convert-to-notes.ts

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import fs from "node:fs";
22
import path from "node:path";
3-
import type { ISynctexBlockId, Metadata } from "@fluffylabs/links-metadata";
3+
import type { INoteV3, INotesEnvelopeV3, Metadata } from "@fluffylabs/links-metadata";
44
import { fetchMetadata, findLink, parseLink } from "@fluffylabs/links-metadata";
55

66
/**
77
* Convert messages.json file generated by the tool into Reader notes format
88
* so it can be imported or loaded as remote content.
99
*/
10-
1110
type Message = {
1211
/** ISO date. */
1312
date: string;
@@ -19,31 +18,6 @@ type Message = {
1918
msg: string;
2019
};
2120

22-
// TODO [ToDr] Note these types could be shared between the `matrix-bot`
23-
// and the reader, however since we want to avoid having too many shared
24-
// packages (to simplify publishing to npm) and this type does not really
25-
// fit the `links-metadata`, I've decided to duplicate it here.
26-
// Note it's a historical format, so it SHOULD NOT change anyway.
27-
type NoteV3 = {
28-
noteVersion: 3;
29-
content: string;
30-
/** local-tz timestamp (from Date.now()) */
31-
date: number;
32-
/** empty for local notes. */
33-
author: string;
34-
/** Full version number. */
35-
version: string;
36-
/** Labels. */
37-
labels: string[];
38-
39-
selectionStart: ISynctexBlockId;
40-
selectionEnd: ISynctexBlockId;
41-
};
42-
type INotesEnvelopeV3 = {
43-
version: 3;
44-
notes: NoteV3[];
45-
};
46-
4721
async function main(file = "./output/messages.json") {
4822
const content = fs.readFileSync(path.resolve(file), "utf-8");
4923
// note that the file is not a valid JSON as-is (it's appended to),
@@ -54,7 +28,7 @@ async function main(file = "./output/messages.json") {
5428
// get metadata and synctex data.
5529
const meta = await fetchMetadata();
5630

57-
const notes = new Map<string, NoteV3>();
31+
const notes = new Map<string, INoteV3>();
5832
for (const msg of data) {
5933
const linkData = await findAndParseLink(msg.msg, meta);
6034
if (linkData === null) {

0 commit comments

Comments
 (0)