forked from asciidoctor/asciidoctor-vscode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tableOfContentsProvider.ts
84 lines (73 loc) · 2.45 KB
/
tableOfContentsProvider.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
/*---------------------------------------------------------------------------------------------
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { AsciidocEngine } from './asciidocEngine';
import { Slug, githubSlugifier } from './slugify';
export interface TocEntry {
readonly slug: Slug;
readonly text: string;
readonly level: number;
readonly line: number;
readonly location: vscode.Location;
}
export interface SkinnyTextDocument {
readonly uri: vscode.Uri;
readonly lineCount: number;
getText(): string;
lineAt(line: number): vscode.TextLine;
}
export class TableOfContentsProvider {
private toc?: TocEntry[];
public constructor(
private engine: AsciidocEngine,
private document: SkinnyTextDocument
) { }
public async getToc(): Promise<TocEntry[]> {
if (!this.toc) {
try {
this.toc = await this.buildToc(this.document);
} catch (e) {
this.toc = [];
}
}
return this.toc;
}
public async lookup(fragment: string): Promise<TocEntry | undefined> {
const toc = await this.getToc();
const slug = githubSlugifier.fromHeading(fragment);
return toc.find((entry) => entry.slug.equals(slug));
}
private async buildToc(document: SkinnyTextDocument): Promise<TocEntry[]> {
let toc: TocEntry[] = [];
const adoc = await this.engine.parse(document.uri, document.getText());
adoc.findBy({ 'context': 'section' }, function (section) {
toc.push({
slug: section.getId(),
text: section.getTitle(),
level: section.getLevel(),
line: section.getLineNumber()-1,
location: new vscode.Location(document.uri,
new vscode.Position(section.getLineNumber()-1, 1)),
})
})
// Get full range of section
return toc.map((entry, startIndex): TocEntry => {
let end: number | undefined = undefined;
for (let i = startIndex + 1; i < toc.length; ++i) {
if (toc[i].level <= entry.level) {
end = toc[i].line - 1;
break;
}
}
const endLine = typeof end === 'number' ? end : document.lineCount - 1;
return {
...entry,
location: new vscode.Location(document.uri,
new vscode.Range(
entry.location.range.start,
new vscode.Position(endLine, document.lineAt(endLine).range.end.character))),
};
});
}
}