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|
119|
120|
121|
122|
123|
124|
125|
126|
127|
128|
129|
130|
131|
132|
133|
134|
135|
136|
137|
138|
139|
140|
141|
142|
143|
144|
145|
146|
147|
148|
149|
150|
import { readFile } from "fs/promises";
import { MetaData, Vertex, Edge, Id, Document, next } from "lsif-protocol";
export type Element = Vertex | Edge;
type SafeMap<A, B> = {
get: (x: A) => B,
have: (x: A) => boolean,
};
export type Lsif = {
items: Element[];
item: SafeMap<Id, Element>;
inV: SafeMap<Id, Element[]>;
outV: SafeMap<Id, Element[]>;
documents: Document[];
srcMap: {
raw: (x: string) => string;
lineSplitted: (x: string) => string[];
};
uriPath: (x: string) => string;
};
const parseMultiLineJs = (a: string) =>
a
.split("\n")
.filter((x) => x != "")
.map((x) => JSON.parse(x));
const getVertexWithLabel = (a: Element[], label: string) =>
a.filter((x) => x.type == "vertex" && x.label == label);
const getEdgeWithLabel = (a: Element[], label: string) =>
a.filter((x) => x.type == "edge" && x.label == label) as Edge[];
const buildItemMap = (items: Element[]) => {
const result: Map<Id, Element> = new Map();
items.forEach((item) => {
result.set(item.id, item);
});
return result;
};
const buildInVMap = (items: Element[]) => {
const result: Map<Id, Element[]> = new Map();
const insert = (x: Element, id: Id) => {
let cur = result.get(id) ?? [];
result.set(id, cur);
cur.push(x);
};
items.forEach((item: Element) => {
if (item.type != 'edge') {
return;
}
if ('inV' in item) insert(item, item.inV);
if ('inVs' in item) item.inVs.forEach((id: Id) => insert(item, id));
});
return result;
};
const buildOutVMap = (items: Element[]) => {
const result: Map<Id, Element[]> = new Map();
items.forEach((item) => {
if (item.type != 'edge' || !item.outV) {
return;
}
let cur = result.get(item.outV) ?? [];
result.set(item.outV, cur);
cur.push(item);
});
return result;
};
function getFromMap<A, B>(map: Map<A, B>): SafeMap<A, B> {
return {
get: (id: A) => {
const x = map.get(id);
if (!x) throw new Error(`map doesn't have ${id}`);
return x;
},
have: (id: A) => map.get(id) !== undefined,
};
}
const buildSrcMap = async (documents: Document[]) => {
const raw: Map<string, string> = new Map();
const lineSplitted: Map<string, string[]> = new Map();
await Promise.all(documents.map(async (doc) => {
const srcRaw = (await readFile(doc.uri.slice(7))).toString();
raw.set(doc.uri, srcRaw);
lineSplitted.set(doc.uri, srcRaw.split('\n'));
}));
return { raw: getFromMap(raw).get, lineSplitted: getFromMap(lineSplitted).get };
};
const buildUriMap = async (uriMapAddress: string | undefined, documents: Document[], projectRoot: string) => {
const r: Map<string, string> = new Map();
if (uriMapAddress) {
const f = JSON.parse((await readFile(uriMapAddress)).toString());
documents.map((doc) => doc.uri).forEach((x) => {
const y = f[x];
if (!y) {
r.set(x, `notmapped${x.slice(7)}`);
return;
}
r.set(x, y);
});
return getFromMap(r);
}
documents.map((doc) => doc.uri).forEach((x) => {
if (!x.startsWith(projectRoot)) {
r.set(x, `notmapped${x.slice(7)}`);
return;
}
const y = x.slice(projectRoot.length + 1);
r.set(x, y);
});
return getFromMap(r);
};
export const lsifParser = async (address: string, uriMapAddress: string | undefined): Promise<Lsif> => {
const text = (await readFile(address)).toString();
const items: Element[] = parseMultiLineJs(text);
const item = getFromMap(buildItemMap(items));
const inV = getFromMap(buildInVMap(items));
const outV = getFromMap(buildOutVMap(items));
const { projectRoot } = getVertexWithLabel(items, "metaData")[0] as MetaData;
const documents = getVertexWithLabel(items, "document") as Document[];
const srcMap = await buildSrcMap(documents);
const uriMap = await buildUriMap(uriMapAddress, documents, projectRoot);
return {
items, item, inV, outV, documents, srcMap, uriPath: uriMap.get,
};
};
export const findRecursiveEdge = (lsif: Lsif, v: Id, label: string) => {
const { outV, item } = lsif;
for (; ;) {
if (!outV.have(v)) return;
const out = outV.get(v);
const direct = getEdgeWithLabel(out, label).filter(Edge.is11);
if (direct.length > 0) return item.get(direct[0].inV);
const next = getEdgeWithLabel(out, 'next') as next[];
if (next.length == 0) return;
v = next[0].inV;
}
};