diff --git a/package-lock.json b/package-lock.json index 608b074f..5ea13690 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "asciidoctor-vscode", - "version": "3.1.5", + "version": "3.1.6-dev", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "asciidoctor-vscode", - "version": "3.1.5", + "version": "3.1.6-dev", "license": "MIT", "dependencies": { "@antora/content-classifier": "3.1.2", @@ -14,6 +14,7 @@ "@asciidoctor/docbook-converter": "2.0.0", "@orcid/bibtex-parse-js": "0.0.25", "asciidoctor-kroki": "^0.17.0", + "html-entities": "^2.4.0", "js-yaml": "^4.1.0", "querystring": "^0.2.1", "tty-browserify": "^0.0.1", @@ -5192,6 +5193,21 @@ "node": ">=10" } }, + "node_modules/html-entities": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", + "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, "node_modules/htmlparser2": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", diff --git a/package.json b/package.json index 283826f2..157d37f2 100644 --- a/package.json +++ b/package.json @@ -656,6 +656,7 @@ "@asciidoctor/docbook-converter": "2.0.0", "@orcid/bibtex-parse-js": "0.0.25", "asciidoctor-kroki": "^0.17.0", + "html-entities": "^2.4.0", "js-yaml": "^4.1.0", "querystring": "^0.2.1", "tty-browserify": "^0.0.1", diff --git a/src/tableOfContentsProvider.ts b/src/tableOfContentsProvider.ts index 56b4f366..91ba1191 100644 --- a/src/tableOfContentsProvider.ts +++ b/src/tableOfContentsProvider.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode' +import { decode as htmlEntitiesDecode } from 'html-entities' import { githubSlugifier, Slug } from './slugify' import { SkinnyTextDocument } from './util/document' import { AsciidocLoader } from './asciidocLoader' @@ -48,7 +49,7 @@ export class TableOfContentsProvider { } return { slug: new Slug(section.getId()), - text: section.getTitle(), + text: htmlEntitiesDecode(section.getTitle()), level: section.getLevel(), line: lineNumber, location: new vscode.Location(textDocument.uri, diff --git a/src/test/tableOfContentsProvider.test.ts b/src/test/tableOfContentsProvider.test.ts index b3ec0854..17eafc54 100644 --- a/src/test/tableOfContentsProvider.test.ts +++ b/src/test/tableOfContentsProvider.test.ts @@ -85,4 +85,38 @@ content` const documentTitleEntry = toc.find((entry) => entry.text === 'test' && entry.line === 0) assert.deepStrictEqual(documentTitleEntry !== undefined, true, 'should include the document title in the TOC') }) + + test('Should properly decode HTML entities', async () => { + const doc = new InMemoryDocument(vscode.Uri.file('test.adoc'), `= Title + +== Dungeons & Dragons + +== Let's do it!`) + const provider = new TableOfContentsProvider(doc, new AsciidocLoader( + new AsciidoctorConfig(), + new AsciidoctorExtensions(AsciidoctorExtensionsSecurityPolicyArbiter.activate(extensionContext)), + new AsciidoctorDiagnostic('test') + )) + + const toc = await provider.getToc() + const ddEntry = toc.find((t) => t.text === 'Dungeons & Dragons') + assert.strictEqual(ddEntry !== null, true, 'should find an entry with title: Dungeons & Dragons') + assert.deepStrictEqual({ + text: ddEntry.text, + slug: ddEntry.slug.value, + }, { + text: 'Dungeons & Dragons', + slug: '_dungeons_dragons', + }) + console.log(toc.map((t) => t.text)) + const ldiEntry = toc.find((t) => t.text === 'Let’s do it!') + assert.strictEqual(ldiEntry !== null, true, 'should find an entry with title: Let’s do it!') + assert.deepStrictEqual({ + text: ldiEntry.text, + slug: ldiEntry.slug.value, + }, { + text: 'Let’s do it!', + slug: '_lets_do_it', + }) + }) })