forked from asciidoctor/asciidoctor-vscode
/
tableOfContentsProvider.ts
85 lines (73 loc) · 2.53 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
85
/*---------------------------------------------------------------------------------------------
* 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 { githubSlugifier, Slug } 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) {
this.engine = engine
this.document = document
}
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[]> {
const toc: TocEntry[] = []
const adoc = await this.engine.load(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
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))),
}
})
}
}