Skip to content

Commit

Permalink
Add code folding based on heading level
Browse files Browse the repository at this point in the history
  • Loading branch information
marieflorescontact committed Mar 15, 2022
1 parent 1c96606 commit a4e16a4
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 9 deletions.
1 change: 1 addition & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--disable-extensions",
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/dist/src/test/suite"
],
Expand Down
6 changes: 3 additions & 3 deletions src/asciidocEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AsciidocContributions } from './asciidocExtensions'
import { Slugifier } from './slugify'
import { AsciidocParser } from './asciidocParser'
import { Asciidoctor } from '@asciidoctor/core'
import { SkinnyTextDocument } from './util/document'

const FrontMatterRegex = /^---\s*[^]*?(-{3}|\.{3})\s*/

Expand Down Expand Up @@ -64,9 +65,8 @@ export class AsciidocEngine {
return { output, document }
}

public async load (documentUri: vscode.Uri, source: string): Promise<any> {
const textDocument = await vscode.workspace.openTextDocument(documentUri)
const { document } = await this.getEngine().parseText(source, textDocument)
public async load (textDocument: SkinnyTextDocument): Promise<any> {
const { document } = await this.getEngine().parseText(textDocument.getText(), textDocument)
return document
}
}
6 changes: 3 additions & 3 deletions src/asciidocParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class AsciidocParser {

public convertUsingJavascript (
text: string,
doc: vscode.TextDocument,
doc: SkinnyTextDocument,
forHTMLSave: boolean,
backend: string,
context?: vscode.ExtensionContext,
Expand Down Expand Up @@ -176,7 +176,7 @@ export class AsciidocParser {

public async parseText (
text: string,
doc: vscode.TextDocument,
doc: SkinnyTextDocument,
forHTMLSave: boolean = false,
backend: string = 'webview-html5',
context?: vscode.ExtensionContext,
Expand All @@ -185,7 +185,7 @@ export class AsciidocParser {
return this.convertUsingJavascript(text, doc, forHTMLSave, backend, context, editor)
}

private reportErrors (memoryLogger: Asciidoctor.MemoryLogger, textDocument: vscode.TextDocument) {
private reportErrors (memoryLogger: Asciidoctor.MemoryLogger, textDocument: SkinnyTextDocument) {
const diagnostics = []
memoryLogger.getMessages().forEach((error) => {
//console.log(error); //Error from asciidoctor.js
Expand Down
3 changes: 2 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { githubSlugifier } from './slugify'
import { AsciidocFileIncludeAutoCompletionMonitor } from './util/includeAutoCompletion'
import { AttributeReferenceProvider } from './features/attributeReferenceProvider'
import { BuiltinDocumentAttributeProvider } from './features/builtinDocumentAttributeProvider'
import AsciidocFoldingRangeProvider from './features/foldingProvider'

export function activate (context: vscode.ExtensionContext) {
const contributions = getAsciidocExtensionContributions(context)
Expand Down Expand Up @@ -47,8 +48,8 @@ export function activate (context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.languages.registerWorkspaceSymbolProvider(new AsciidocWorkspaceSymbolProvider(symbolProvider)))
context.subscriptions.push(vscode.languages.registerCompletionItemProvider(selector, new AttributeReferenceProvider(contributions.extensionUri), '{'))
context.subscriptions.push(vscode.languages.registerCompletionItemProvider(selector, new BuiltinDocumentAttributeProvider(contributions.extensionUri), ':'))
context.subscriptions.push(vscode.languages.registerFoldingRangeProvider(selector, new AsciidocFoldingRangeProvider(engine)))
const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, previewManager)

const commandManager = new CommandManager()
context.subscriptions.push(commandManager)
commandManager.register(new commands.ShowPreviewCommand(previewManager))
Expand Down
37 changes: 37 additions & 0 deletions src/features/foldingProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* 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 { TableOfContentsProvider } from '../tableOfContentsProvider'

export default class AsciidocFoldingRangeProvider implements vscode.FoldingRangeProvider {
constructor (
private readonly engine: AsciidocEngine
) {
}

public async provideFoldingRanges (
document: vscode.TextDocument,
_token: vscode.CancellationToken
): Promise<vscode.FoldingRange[]> {
const tableOfContentsProvider = new TableOfContentsProvider(this.engine, document)
const tableOfContents = await tableOfContentsProvider.getToc()

return tableOfContents.map((entry, startIndex) => {
const start = entry.line
let end: number | undefined
for (let i = startIndex + 1; i < tableOfContents.length; ++i) {
if (tableOfContents[i].level <= entry.level) {
end = tableOfContents[i].line - 1
break
}
}
return new vscode.FoldingRange(
start,
typeof end === 'number' ? end : document.lineCount - 1)
})
}
}
3 changes: 2 additions & 1 deletion src/tableOfContentsProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class TableOfContentsProvider {
try {
this.toc = await this.buildToc(this.document)
} catch (e) {
console.log(e)
this.toc = []
}
}
Expand All @@ -42,7 +43,7 @@ export class TableOfContentsProvider {

private async buildToc (document: SkinnyTextDocument): Promise<TocEntry[]> {
const toc: TocEntry[] = []
const adoc = await this.engine.load(document.uri, document.getText())
const adoc = await this.engine.load(document)

adoc.findBy({ context: 'section' }, function (section) {
toc.push({
Expand Down
2 changes: 1 addition & 1 deletion src/test/documentSymbolProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import SymbolProvider from '../features/documentSymbolProvider'
import { InMemoryDocument } from './inMemoryDocument'
import { createNewAsciidocEngine } from './engine'

const testFileName = vscode.Uri.file('test.md')
const testFileName = vscode.Uri.file('test.adoc')

function getSymbolsForFile (fileContents: string) {
const doc = new InMemoryDocument(testFileName, fileContents)
Expand Down
87 changes: 87 additions & 0 deletions src/test/foldingProvider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as assert from 'assert'
import 'mocha'
import * as vscode from 'vscode'
import AsciidocFoldingProvider from '../features/foldingProvider'
import { createNewAsciidocEngine } from './engine'
import { InMemoryDocument } from './inMemoryDocument'

const testFileName = vscode.Uri.file('test.adoc')

suite('asciidoc.FoldingProvider', () => {
test('Should not return anything for empty document', async () => {
const folds = await getFoldsForDocument('')
assert.strictEqual(folds.length, 0)
})

test('Should not return anything for document without headers', async () => {
const folds = await getFoldsForDocument(`a
*b* afas
a=b
a`)
assert.strictEqual(folds.length, 0)
})

test('Should fold from header to end of document', async () => {
const folds = await getFoldsForDocument(`= a
== b
c
d`)
assert.strictEqual(folds.length, 2)
const firstFold = folds[0]
assert.strictEqual(firstFold.start, 0)
assert.strictEqual(firstFold.end, 5)
})

test('Should leave single newline before next header', async () => {
const folds = await getFoldsForDocument(`
== a
x
== b
y`)
assert.strictEqual(folds.length, 2)
const firstFold = folds[0]
assert.strictEqual(firstFold.start, 1)
assert.strictEqual(firstFold.end, 3)
})

test('Should collapse multiple newlines to single newline before next header', async () => {
const folds = await getFoldsForDocument(`
= a
x
= b
y`)
assert.strictEqual(folds.length, 2)
const firstFold = folds[0]
assert.strictEqual(firstFold.start, 1)
assert.strictEqual(firstFold.end, 5)
})

test('Should not collapse if there is no newline before next header', async () => {
const folds = await getFoldsForDocument(`
= a
x
== b
y`)
assert.strictEqual(folds.length, 1)
const firstFold = folds[0]
assert.strictEqual(firstFold.start, 1)
assert.strictEqual(firstFold.end, 4)
})
})

async function getFoldsForDocument (contents: string) {
const doc = new InMemoryDocument(testFileName, contents)
const provider = new AsciidocFoldingProvider(createNewAsciidocEngine())
return await provider.provideFoldingRanges(doc, new vscode.CancellationTokenSource().token)
}

0 comments on commit a4e16a4

Please sign in to comment.