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

Commit0552af2

Browse files
Vigilansjdneo
authored andcommitted
Refactor solution webview to reuse markdown engine (LeetCode-OpenSource#224)
1 parent5f895a2 commit0552af2

File tree

2 files changed

+116
-53
lines changed

2 files changed

+116
-53
lines changed

‎src/leetCodeSolutionProvider.ts

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,28 @@
11
// Copyright (c) jdneo. All rights reserved.
22
// Licensed under the MIT license.
33

4-
import*ashljsfrom"highlight.js";
5-
import*asMarkdownItfrom"markdown-it";
6-
import*aspathfrom"path";
7-
import*asvscodefrom"vscode";
84
import{Disposable,ExtensionContext,ViewColumn,WebviewPanel,window}from"vscode";
9-
import{leetCodeChannel}from"./leetCodeChannel";
105
import{IProblem}from"./shared";
6+
import{MarkdownEngine}from"./webview/MarkdownEngine";
117

128
classLeetCodeSolutionProviderimplementsDisposable{
139

1410
privatecontext:ExtensionContext;
1511
privatepanel:WebviewPanel|undefined;
16-
privatemarkdown:MarkdownIt;
17-
privatemarkdownPath:string;// path of vscode built-in markdown extension
12+
privatemdEngine:MarkdownEngine;
1813
privatesolution:Solution;
1914

2015
publicinitialize(context:ExtensionContext):void{
2116
this.context=context;
22-
this.markdown=newMarkdownIt({
23-
linkify:true,
24-
typographer:true,
25-
highlight:this.codeHighlighter.bind(this),
26-
});
27-
this.markdownPath=path.join(vscode.env.appRoot,"extensions","markdown-language-features");
28-
29-
// Override code_block rule for highlighting in solution language
30-
// tslint:disable-next-line:typedef
31-
this.markdown.renderer.rules["code_block"]=(tokens,idx,options,_,self)=>{
32-
consthighlight:string=options.highlight(tokens[idx].content,undefined);
33-
return[
34-
`<pre><code${self.renderAttrs(tokens[idx])} >`,
35-
highlight||this.markdown.utils.escapeHtml(tokens[idx].content),
36-
"</code></pre>",
37-
].join("\n");
38-
};
17+
this.mdEngine=newMarkdownEngine();
3918
}
4019

4120
publicasyncshow(solutionString:string,problem:IProblem):Promise<void>{
4221
if(!this.panel){
4322
this.panel=window.createWebviewPanel("leetCode.solution","Top Voted Solution",ViewColumn.Active,{
4423
retainContextWhenHidden:true,
4524
enableFindWidget:true,
46-
localResourceRoots:[vscode.Uri.file(path.join(this.markdownPath,"media"))],
25+
localResourceRoots:this.mdEngine.localResourceRoots,
4726
});
4827

4928
this.panel.onDidDispose(()=>{
@@ -76,41 +55,20 @@ class LeetCodeSolutionProvider implements Disposable {
7655
returnsolution;
7756
}
7857

79-
privatecodeHighlighter(code:string,lang:string|undefined):string{
80-
if(!lang){
81-
lang=this.solution.lang;
82-
}
83-
if(hljs.getLanguage(lang)){
84-
try{
85-
returnhljs.highlight(lang,code,true).value;
86-
}catch(error){/* do not highlight */}
87-
}
88-
return"";// use external default escaping
89-
}
90-
91-
privategetMarkdownStyles():vscode.Uri[]{
92-
try{
93-
conststylePaths:string[]=require(path.join(this.markdownPath,"package.json"))["contributes"]["markdown.previewStyles"];
94-
returnstylePaths.map((p:string)=>vscode.Uri.file(path.join(this.markdownPath,p)).with({scheme:"vscode-resource"}));
95-
}catch(error){
96-
leetCodeChannel.appendLine("[Error] Fail to load built-in markdown style file.");
97-
return[];
98-
}
99-
}
100-
10158
privategetWebViewContent(solution:Solution):string{
102-
conststyles:string=this.getMarkdownStyles()
103-
.map((style:vscode.Uri)=>`<link rel="stylesheet" type="text/css" href="${style.toString()}">`)
104-
.join("\n");
59+
conststyles:string=this.mdEngine.getStylesHTML();
10560
const{ title, url, lang, author, votes}=solution;
106-
consthead:string=this.markdown.render(`# [${title}](${url})`);
61+
consthead:string=this.mdEngine.render(`# [${title}](${url})`);
10762
constauth:string=`[${author}](https://leetcode.com/${author}/)`;
108-
constinfo:string=this.markdown.render([
63+
constinfo:string=this.mdEngine.render([
10964
`| Language | Author | Votes |`,
11065
`| :------: | :------: | :------: |`,
11166
`|${lang} |${auth} |${votes} |`,
11267
].join("\n"));
113-
constbody:string=this.markdown.render(solution.body);
68+
constbody:string=this.mdEngine.render(solution.body,{
69+
lang:this.solution.lang,
70+
host:"https://discuss.leetcode.com/",
71+
});
11472
return`
11573
<!DOCTYPE html>
11674
<html>

‎src/webview/MarkdownEngine.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import*ashljsfrom"highlight.js";
2+
import*asMarkdownItfrom"markdown-it";
3+
import*asosfrom"os";
4+
import*aspathfrom"path";
5+
import*asvscodefrom"vscode";
6+
import{leetCodeChannel}from"../leetCodeChannel";
7+
8+
exportclassMarkdownEngine{
9+
10+
privatereadonlyengine:MarkdownIt;
11+
privatereadonlyextRoot:string;// root path of vscode built-in markdown extension
12+
13+
publicconstructor(){
14+
this.engine=this.initEngine();
15+
this.extRoot=path.join(vscode.env.appRoot,"extensions","markdown-language-features");
16+
}
17+
18+
publicgetlocalResourceRoots():vscode.Uri[]{
19+
return[vscode.Uri.file(path.join(this.extRoot,"media"))];
20+
}
21+
22+
publicgetstyles():vscode.Uri[]{
23+
try{
24+
conststylePaths:string[]=require(path.join(this.extRoot,"package.json"))["contributes"]["markdown.previewStyles"];
25+
returnstylePaths.map((p:string)=>vscode.Uri.file(path.join(this.extRoot,p)).with({scheme:"vscode-resource"}));
26+
}catch(error){
27+
leetCodeChannel.appendLine("[Error] Fail to load built-in markdown style file.");
28+
return[];
29+
}
30+
}
31+
32+
publicgetStylesHTML():string{
33+
returnthis.styles.map((style:vscode.Uri)=>`<link rel="stylesheet" type="text/css" href="${style.toString()}">`).join(os.EOL);
34+
}
35+
36+
publicrender(md:string,env?:any):string{
37+
returnthis.engine.render(md,env);
38+
}
39+
40+
privateinitEngine():MarkdownIt{
41+
constmd:MarkdownIt=newMarkdownIt({
42+
linkify:true,
43+
typographer:true,
44+
highlight:(code:string,lang?:string):string=>{
45+
switch(lang&&lang.toLowerCase()){
46+
case"mysql":
47+
lang="sql";break;
48+
case"json5":
49+
lang="json";break;
50+
case"python3":
51+
lang="python";break;
52+
}
53+
if(lang&&hljs.getLanguage(lang)){
54+
try{
55+
returnhljs.highlight(lang,code,true).value;
56+
}catch(error){/* do not highlight */}
57+
}
58+
return"";// use external default escaping
59+
},
60+
});
61+
62+
this.addCodeBlockHighlight(md);
63+
this.addImageUrlCompletion(md);
64+
this.addLinkValidator(md);
65+
returnmd;
66+
}
67+
68+
privateaddCodeBlockHighlight(md:MarkdownIt):void{
69+
constcodeBlock:MarkdownIt.TokenRender=md.renderer.rules["code_block"];
70+
// tslint:disable-next-line:typedef
71+
md.renderer.rules["code_block"]=(tokens,idx,options,env,self)=>{
72+
// if any token uses lang-specified code fence, then do not highlight code block
73+
if(tokens.some((token:any)=>token.type==="fence")){
74+
returncodeBlock(tokens,idx,options,env,self);
75+
}
76+
// otherwise, highlight with default lang in env object.
77+
consthighlighted:string=options.highlight(tokens[idx].content,env.lang);
78+
return[
79+
`<pre><code${self.renderAttrs(tokens[idx])} >`,
80+
highlighted||md.utils.escapeHtml(tokens[idx].content),
81+
"</code></pre>",
82+
].join(os.EOL);
83+
};
84+
}
85+
86+
privateaddImageUrlCompletion(md:MarkdownIt):void{
87+
constimage:MarkdownIt.TokenRender=md.renderer.rules["image"];
88+
// tslint:disable-next-line:typedef
89+
md.renderer.rules["image"]=(tokens,idx,options,env,self)=>{
90+
constimageSrc:string[]|undefined=tokens[idx].attrs.find((value:string[])=>value[0]==="src");
91+
if(env.host&&imageSrc&&imageSrc[1].startsWith("/")){
92+
imageSrc[1]=`${env.host}${imageSrc[1]}`;
93+
}
94+
returnimage(tokens,idx,options,env,self);
95+
};
96+
}
97+
98+
privateaddLinkValidator(md:MarkdownIt):void{
99+
constvalidateLink:(link:string)=>boolean=md.validateLink;
100+
md.validateLink=(link:string):boolean=>{
101+
// support file:// protocal link
102+
returnvalidateLink(link)||link.startsWith("file:");
103+
};
104+
}
105+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp