Add support for displaying images in preview.

This commit is contained in:
CismonX 2020-10-05 01:26:21 +08:00
parent 06248c86d2
commit 9fb645a4d1
Signed by: cismonx
GPG Key ID: 3094873E29A482FB
7 changed files with 64 additions and 15 deletions

13
package-lock.json generated
View File

@ -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",

View File

@ -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"
}
}

View File

@ -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);
}
}

View File

@ -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(

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}