Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Refactor solution webview to reuse markdown engine#224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
jdneo merged 7 commits intoLeetCode-OpenSource:masterfromVigilans:markdown-engine
Mar 21, 2019
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 11 additions & 53 deletionssrc/leetCodeSolutionProvider.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,28 @@
// Copyright (c) jdneo. All rights reserved.
// Licensed under the MIT license.

import * as hljs from "highlight.js";
import * as MarkdownIt from "markdown-it";
import * as path from "path";
import * as vscode from "vscode";
import { Disposable, ExtensionContext, ViewColumn, WebviewPanel, window } from "vscode";
import { leetCodeChannel } from "./leetCodeChannel";
import { IProblem } from "./shared";
import { MarkdownEngine } from "./webview/MarkdownEngine";

class LeetCodeSolutionProvider implements Disposable {

private context: ExtensionContext;
private panel: WebviewPanel | undefined;
private markdown: MarkdownIt;
private markdownPath: string; // path of vscode built-in markdown extension
private mdEngine: MarkdownEngine;
private solution: Solution;

public initialize(context: ExtensionContext): void {
this.context = context;
this.markdown = new MarkdownIt({
linkify: true,
typographer: true,
highlight: this.codeHighlighter.bind(this),
});
this.markdownPath = path.join(vscode.env.appRoot, "extensions", "markdown-language-features");

// Override code_block rule for highlighting in solution language
// tslint:disable-next-line:typedef
this.markdown.renderer.rules["code_block"] = (tokens, idx, options, _, self) => {
const highlight: string = options.highlight(tokens[idx].content, undefined);
return [
`<pre><code ${self.renderAttrs(tokens[idx])} >`,
highlight || this.markdown.utils.escapeHtml(tokens[idx].content),
"</code></pre>",
].join("\n");
};
this.mdEngine = new MarkdownEngine();
}

public async show(solutionString: string, problem: IProblem): Promise<void> {
if (!this.panel) {
this.panel = window.createWebviewPanel("leetCode.solution", "Top Voted Solution", ViewColumn.Active, {
retainContextWhenHidden: true,
enableFindWidget: true,
localResourceRoots:[vscode.Uri.file(path.join(this.markdownPath, "media"))],
localResourceRoots: this.mdEngine.localResourceRoots,
});

this.panel.onDidDispose(() => {
Expand DownExpand Up@@ -76,41 +55,20 @@ class LeetCodeSolutionProvider implements Disposable {
return solution;
}

private codeHighlighter(code: string, lang: string | undefined): string {
if (!lang) {
lang = this.solution.lang;
}
if (hljs.getLanguage(lang)) {
try {
return hljs.highlight(lang, code, true).value;
} catch (error) { /* do not highlight */ }
}
return ""; // use external default escaping
}

private getMarkdownStyles(): vscode.Uri[] {
try {
const stylePaths: string[] = require(path.join(this.markdownPath, "package.json"))["contributes"]["markdown.previewStyles"];
return stylePaths.map((p: string) => vscode.Uri.file(path.join(this.markdownPath, p)).with({ scheme: "vscode-resource" }));
} catch (error) {
leetCodeChannel.appendLine("[Error] Fail to load built-in markdown style file.");
return [];
}
}

private getWebViewContent(solution: Solution): string {
const styles: string = this.getMarkdownStyles()
.map((style: vscode.Uri) => `<link rel="stylesheet" type="text/css" href="${style.toString()}">`)
.join("\n");
const styles: string = this.mdEngine.getStylesHTML();
const { title, url, lang, author, votes } = solution;
const head: string = this.markdown.render(`# [${title}](${url})`);
const head: string = this.mdEngine.render(`# [${title}](${url})`);
const auth: string = `[${author}](https://leetcode.com/${author}/)`;
const info: string = this.markdown.render([
const info: string = this.mdEngine.render([
`| Language | Author | Votes |`,
`| :------: | :------: | :------: |`,
`| ${lang} | ${auth} | ${votes} |`,
].join("\n"));
const body: string = this.markdown.render(solution.body);
const body: string = this.mdEngine.render(solution.body, {
lang: this.solution.lang,
host: "https://discuss.leetcode.com/",
});
return `
<!DOCTYPE html>
<html>
Expand Down
105 changes: 105 additions & 0 deletionssrc/webview/MarkdownEngine.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
import * as hljs from "highlight.js";
import * as MarkdownIt from "markdown-it";
import * as os from "os";
import * as path from "path";
import * as vscode from "vscode";
import { leetCodeChannel } from "../leetCodeChannel";

export class MarkdownEngine {

private readonly engine: MarkdownIt;
private readonly extRoot: string; // root path of vscode built-in markdown extension

public constructor() {
this.engine = this.initEngine();
this.extRoot = path.join(vscode.env.appRoot, "extensions", "markdown-language-features");
}

public get localResourceRoots(): vscode.Uri[] {
return [vscode.Uri.file(path.join(this.extRoot, "media"))];
}

public get styles(): vscode.Uri[] {
try {
const stylePaths: string[] = require(path.join(this.extRoot, "package.json"))["contributes"]["markdown.previewStyles"];
return stylePaths.map((p: string) => vscode.Uri.file(path.join(this.extRoot, p)).with({ scheme: "vscode-resource" }));
} catch (error) {
leetCodeChannel.appendLine("[Error] Fail to load built-in markdown style file.");
return [];
}
}

public getStylesHTML(): string {
return this.styles.map((style: vscode.Uri) => `<link rel="stylesheet" type="text/css" href="${style.toString()}">`).join(os.EOL);
}

public render(md: string, env?: any): string {
return this.engine.render(md, env);
}

private initEngine(): MarkdownIt {
const md: MarkdownIt = new MarkdownIt({
linkify: true,
typographer: true,
highlight: (code: string, lang?: string): string => {
switch (lang && lang.toLowerCase()) {
case "mysql":
lang = "sql"; break;
case "json5":
lang = "json"; break;
case "python3":
lang = "python"; break;
}
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(lang, code, true).value;
} catch (error) { /* do not highlight */ }
}
return ""; // use external default escaping
},
});

this.addCodeBlockHighlight(md);
this.addImageUrlCompletion(md);
this.addLinkValidator(md);
return md;
}

private addCodeBlockHighlight(md: MarkdownIt): void {
const codeBlock: MarkdownIt.TokenRender = md.renderer.rules["code_block"];
// tslint:disable-next-line:typedef
md.renderer.rules["code_block"] = (tokens, idx, options, env, self) => {
// if any token uses lang-specified code fence, then do not highlight code block
if (tokens.some((token: any) => token.type === "fence")) {
return codeBlock(tokens, idx, options, env, self);
}
// otherwise, highlight with default lang in env object.
const highlighted: string = options.highlight(tokens[idx].content, env.lang);
return [
`<pre><code ${self.renderAttrs(tokens[idx])} >`,
highlighted || md.utils.escapeHtml(tokens[idx].content),
"</code></pre>",
].join(os.EOL);
};
}

private addImageUrlCompletion(md: MarkdownIt): void {
const image: MarkdownIt.TokenRender = md.renderer.rules["image"];
// tslint:disable-next-line:typedef
md.renderer.rules["image"] = (tokens, idx, options, env, self) => {
const imageSrc: string[] | undefined = tokens[idx].attrs.find((value: string[]) => value[0] === "src");
if (env.host && imageSrc && imageSrc[1].startsWith("/")) {
imageSrc[1] = `${env.host}${imageSrc[1]}`;
}
return image(tokens, idx, options, env, self);
};
}

private addLinkValidator(md: MarkdownIt): void {
const validateLink: (link: string) => boolean = md.validateLink;
md.validateLink = (link: string): boolean => {
// support file:// protocal link
return validateLink(link) || link.startsWith("file:");
};
}
}

[8]ページ先頭

©2009-2025 Movatter.jp