From 9fb645a4d15c16493fff5b4dd3c35d70ba5ff05b Mon Sep 17 00:00:00 2001 From: CismonX Date: Mon, 5 Oct 2020 01:26:21 +0800 Subject: [PATCH] Add support for displaying images in preview. --- package-lock.json | 13 +++++++++++++ package.json | 8 +++++++- src/converter.ts | 14 ++++++-------- src/extension.ts | 2 +- src/options.ts | 4 ++++ src/preview.ts | 17 +++++++++++++---- src/utils.ts | 21 ++++++++++++++++++++- 7 files changed, 64 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index ace5287..d27d80f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -646,6 +646,11 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -796,6 +801,14 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node-html-parser": { + "version": "1.2.21", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-1.2.21.tgz", + "integrity": "sha512-6vDhgen6J332syN5HUmeT4FfBG7m6bFRrPN+FXY8Am7FGuVpsIxTASVbeoO5PF2IHbX2s+WEIudb1hgxOjllNQ==", + "requires": { + "he": "1.2.0" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", diff --git a/package.json b/package.json index d3e787f..e1cbd28 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,11 @@ "type": "boolean", "default": false, "markdownDescription": "Suppress warnings." + }, + "texinfo.preview.displayImage": { + "type": "boolean", + "default": false, + "markdownDescription": "Whether to display images in in the preview." } } }, @@ -124,6 +129,7 @@ }, "dependencies": { "cson": "^7.20.0", - "language-texinfo": "^1.0.0" + "language-texinfo": "^1.0.0", + "node-html-parser": "^1.2.21" } } diff --git a/src/converter.ts b/src/converter.ts index a5dc185..075bf88 100644 --- a/src/converter.ts +++ b/src/converter.ts @@ -6,7 +6,7 @@ */ import { Options } from './options'; -import { exec } from './utils'; +import * as utils from './utils'; /** * Texinfo to HTML converter. @@ -19,9 +19,8 @@ export class Converter { * @param path Path to the Texinfo document. * @yields HTML code, or `undefined` if conversion fails. */ - static async convert(path: string) { - const converter = new Converter(path); - return await converter.convert(); + static async convertToHtml(path: string) { + return await new Converter().convert(path); } /** @@ -29,18 +28,17 @@ export class Converter { */ private readonly options = ['-o', '-', '--no-split', '--html']; - private constructor(path: string) { + private constructor() { Options.noHeaders && this.options.push('--no-headers'); Options.force && this.options.push('--force'); Options.noValidate && this.options.push('--no-validate'); Options.noWarn && this.options.push('--no-warn'); this.options.push(`--error-limit=${Options.errorLimit}`); - this.options.push(path); } - private async convert() { + private async convert(path: string) { const makeinfo = Options.makeinfo; const maxBuffer = Options.maxSize * 1024 * 1024; - return await exec(makeinfo, this.options, maxBuffer); + return await utils.exec(makeinfo, this.options.concat(path), maxBuffer); } } diff --git a/src/extension.ts b/src/extension.ts index ac4af2a..5593080 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,8 +6,8 @@ */ import * as vscode from 'vscode'; -import { Preview } from './preview'; import { Options } from './options'; +import { Preview } from './preview'; export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( diff --git a/src/options.ts b/src/options.ts index 92d6363..ee42124 100644 --- a/src/options.ts +++ b/src/options.ts @@ -55,6 +55,10 @@ export class Options { return Options.instance.getBoolean('preview.noWarn'); } + static get displayImage() { + return Options.instance.getBoolean('preview.displayImage'); + } + private readonly configuration: vscode.WorkspaceConfiguration; private constructor(section: string) { diff --git a/src/preview.ts b/src/preview.ts index 1c8b1a9..a1c294c 100644 --- a/src/preview.ts +++ b/src/preview.ts @@ -5,10 +5,11 @@ * @license MIT */ -import * as vscode from 'vscode'; import * as path from 'path'; +import * as vscode from 'vscode'; import { Converter } from './converter'; -import { prompt } from './utils'; +import { Options } from './options'; +import * as utils from './utils'; /** * Texinfo document preview. @@ -25,7 +26,7 @@ export class Preview { static async show(editor: vscode.TextEditor) { const document = editor.document; if (document.isUntitled) { - if (!await prompt('Save this document to display preview.', 'Save')) { + if (!await utils.prompt('Save this document to display preview.', 'Save')) { return; } if (!await document.save()) { @@ -108,10 +109,18 @@ export class Preview { this.pendingUpdate = false; this.updateTitle(); - const htmlCode = await Converter.convert(this.document.fileName); + let htmlCode = await Converter.convertToHtml(this.document.fileName); if (htmlCode === undefined) { vscode.window.showErrorMessage(`Failed to show preview for ${this.document.fileName}.`); } else { + if (Options.displayImage) { + const pathName = path.dirname(this.document.fileName); + // To display images in webviews, image URIs in HTML should be converted to VSCode-recognizable ones. + htmlCode = utils.transformHtmlImageUri(htmlCode, (src) => { + const srcUri = vscode.Uri.file(pathName + '/' + src); + return this.panel.webview.asWebviewUri(srcUri).toString(); + }); + } this.panel.webview.html = htmlCode; } this.updating = false; diff --git a/src/utils.ts b/src/utils.ts index 3fcc5ea..d2cf254 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,8 +5,9 @@ * @license MIT */ -import * as vscode from 'vscode'; import * as child_process from 'child_process'; +import * as htmlparser from 'node-html-parser'; +import * as vscode from 'vscode'; /** * Open a prompt with two buttons, "Confirm" and "Cancel", and wait for user action. @@ -42,3 +43,21 @@ export function exec(path: string, args: string[], maxBuffer: number) { }); }); } + +/** + * Transform and replace the `src` attribute value of all `img` elements from given HTML code using given function. + * + * @param htmlCode + * @param transformer + * @returns The HTML code after transformation. + */ +export function transformHtmlImageUri(htmlCode: string, transformer: (src: string) => string) { + const dom = htmlparser.parse(htmlCode); + const elements = dom.querySelectorAll('img'); + elements.forEach((element) => { + const src = element.getAttribute('src'); + src && element.setAttribute('src', transformer(src)); + }) + // If nothing is transformed, return the original HTML code, for better performance. + return elements.length === 0 ? htmlCode : dom.outerHTML; +}