/
parsing.ts
118 lines (96 loc) · 3.35 KB
/
parsing.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import * as vscode from "vscode";
import * as path from "path";
import * as unified from "unified";
import * as markdown from "remark-parse";
import * as wikiLinkPlugin from "remark-wiki-link";
import * as frontmatter from "remark-frontmatter";
import { MarkdownNode, Graph } from "./types";
import { TextDecoder } from "util";
import {
findTitle,
findLinks,
id,
FILE_ID_REGEXP,
getFileTypesSetting,
getConfiguration,
getTitleMaxLength,
} from "./utils";
import { basename } from "path";
let idToPath: Record<string, string> = {};
export const idResolver = (id: string) => {
const filePath = idToPath[id];
if (filePath === undefined) {
return [id];
} else {
return [filePath];
}
};
const parser = unified()
.use(markdown)
.use(wikiLinkPlugin, { pageResolver: idResolver })
.use(frontmatter);
export const parseFile = async (graph: Graph, filePath: string) => {
filePath = path.normalize(filePath);
const buffer = await vscode.workspace.fs.readFile(vscode.Uri.file(filePath));
const content = new TextDecoder("utf-8").decode(buffer);
const ast: MarkdownNode = parser.parse(content);
let title: string | null = findTitle(ast);
const index = graph.nodes.findIndex((node) => node.path === filePath);
if (!title) {
if (index !== -1) {
graph.nodes.splice(index, 1);
}
return;
}
if (index !== -1) {
graph.nodes[index].label = title;
} else {
graph.nodes.push({ id: id(filePath), path: filePath, label: title });
}
// Remove edges based on an old version of this file.
graph.edges = graph.edges.filter((edge) => edge.source !== id(filePath));
// Returns a list of decoded links (by default markdown only supports encoded URI)
const links = findLinks(ast).map(uri => decodeURI(uri));
const parentDirectory = filePath.split(path.sep).slice(0, -1).join(path.sep);
for (const link of links) {
let target = path.normalize(link);
if (!path.isAbsolute(link)) {
target = path.normalize(`${parentDirectory}/${link}`);
}
graph.edges.push({ source: id(filePath), target: id(target) });
}
};
export const findFileId = async (filePath: string): Promise<string | null> => {
const buffer = await vscode.workspace.fs.readFile(vscode.Uri.file(filePath));
const content = new TextDecoder("utf-8").decode(buffer);
const match = content.match(FILE_ID_REGEXP);
return match ? match[1] : null;
};
export const learnFileId = async (_graph: Graph, filePath: string) => {
const id = await findFileId(filePath);
if (id !== null) {
idToPath[id] = filePath;
}
const fileName = basename(filePath);
idToPath[fileName] = filePath;
const fileNameWithoutExt = fileName.split(".").slice(0, -1).join(".");
idToPath[fileNameWithoutExt] = filePath;
};
export const parseDirectory = async (
graph: Graph,
fileCallback: (graph: Graph, path: string) => Promise<void>
) => {
// `findFiles` is used here since it respects files excluded by either the
// global or workspace level files.exclude config option.
const files = await vscode.workspace.findFiles(
`**/*{${(getFileTypesSetting() as string[]).map((f) => `.${f}`).join(",")}}`
);
const promises: Promise<void>[] = [];
for (const file of files) {
const hiddenFile = path.basename(file.path).startsWith(".");
if (!hiddenFile) {
promises.push(fileCallback(graph, file.path));
}
}
await Promise.all(promises);
};