22// Licensed under the MIT license.
33
44import * as fse from "fs-extra" ;
5+ import * as path from "path" ;
56import * as vscode from "vscode" ;
67import { LeetCodeNode } from "../explorer/LeetCodeNode" ;
8+ import { leetCodeChannel } from "../leetCodeChannel" ;
79import { leetCodeExecutor } from "../leetCodeExecutor" ;
810import { leetCodeManager } from "../leetCodeManager" ;
911import { IProblem , IQuickItemEx , languages , ProblemState } from "../shared" ;
@@ -16,15 +18,15 @@ export async function showProblem(node?: LeetCodeNode): Promise<void> {
1618if ( ! node ) {
1719return ;
1820}
19- await showProblemInternal ( node . id ) ;
21+ await showProblemInternal ( node ) ;
2022}
2123
2224export async function searchProblem ( ) :Promise < void > {
2325if ( ! leetCodeManager . getUser ( ) ) {
2426promptForSignIn ( ) ;
2527return ;
2628}
27- const choice :IQuickItemEx < string > | undefined = await vscode . window . showQuickPick (
29+ const choice :IQuickItemEx < IProblem > | undefined = await vscode . window . showQuickPick (
2830parseProblemsToPicks ( list . listProblems ( ) ) ,
2931{
3032matchOnDetail :true ,
@@ -37,7 +39,7 @@ export async function searchProblem(): Promise<void> {
3739await showProblemInternal ( choice . value ) ;
3840}
3941
40- async function showProblemInternal ( id : string ) :Promise < void > {
42+ async function showProblemInternal ( node : IProblem ) :Promise < void > {
4143try {
4244const leetCodeConfig :vscode . WorkspaceConfiguration = vscode . workspace . getConfiguration ( "leetcode" ) ;
4345let defaultLanguage :string | undefined = leetCodeConfig . get < string > ( "defaultLanguage" ) ;
@@ -49,9 +51,21 @@ async function showProblemInternal(id: string): Promise<void> {
4951return ;
5052}
5153
52- const outDir :string = await selectWorkspaceFolder ( ) ;
54+ let outDir :string = await selectWorkspaceFolder ( ) ;
55+ let relativePath :string = ( leetCodeConfig . get < string > ( "outputFolder" ) || "" ) . trim ( ) ;
56+ const matchResult :RegExpMatchArray | null = relativePath . match ( / \$ \{ ( .* ?) \} / ) ;
57+ if ( matchResult ) {
58+ const resolvedPath :string | undefined = await resolveRelativePath ( matchResult [ 1 ] . toLocaleLowerCase ( ) , node , language ) ;
59+ if ( ! resolvedPath ) {
60+ leetCodeChannel . appendLine ( "Showing problem canceled by user." ) ;
61+ return ;
62+ }
63+ relativePath = resolvedPath ;
64+ }
65+
66+ outDir = path . join ( outDir , relativePath ) ;
5367await fse . ensureDir ( outDir ) ;
54- const result :string = await leetCodeExecutor . showProblem ( id , language , outDir ) ;
68+ const result :string = await leetCodeExecutor . showProblem ( node . id , language , outDir ) ;
5569const reg :RegExp = / \* S o u r c e C o d e : \s * ( .* ) / ;
5670const match :RegExpMatchArray | null = result . match ( reg ) ;
5771if ( match && match . length >= 2 ) {
@@ -76,17 +90,17 @@ async function showProblemInternal(id: string): Promise<void> {
7690}
7791}
7892} catch ( error ) {
79- await promptForOpenOutputChannel ( "Failed tofetch the problem information . Please open the output channel for details." , DialogType . error ) ;
93+ await promptForOpenOutputChannel ( "Failed toshow the problem. Please open the output channel for details." , DialogType . error ) ;
8094}
8195}
8296
83- async function parseProblemsToPicks ( p :Promise < IProblem [ ] > ) :Promise < Array < IQuickItemEx < string > > > {
84- return new Promise ( async ( resolve :( res :Array < IQuickItemEx < string > > ) => void ) :Promise < void > => {
85- const picks :Array < IQuickItemEx < string > > = ( await p ) . map ( ( problem :IProblem ) => Object . assign ( { } , {
97+ async function parseProblemsToPicks ( p :Promise < IProblem [ ] > ) :Promise < Array < IQuickItemEx < IProblem > > > {
98+ return new Promise ( async ( resolve :( res :Array < IQuickItemEx < IProblem > > ) => void ) :Promise < void > => {
99+ const picks :Array < IQuickItemEx < IProblem > > = ( await p ) . map ( ( problem :IProblem ) => Object . assign ( { } , {
86100label :`${ parseProblemDecorator ( problem . state , problem . locked ) } ${ problem . id } .${ problem . name } ` ,
87101description :"" ,
88102detail :`AC rate:${ problem . passRate } , Difficulty:${ problem . difficulty } ` ,
89- value :problem . id ,
103+ value :problem ,
90104} ) ) ;
91105resolve ( picks ) ;
92106} ) ;
@@ -102,3 +116,28 @@ function parseProblemDecorator(state: ProblemState, locked: boolean): string {
102116return locked ?"$(lock) " :"" ;
103117}
104118}
119+
120+ async function resolveRelativePath ( value :string , node :IProblem , selectedLanguage :string ) :Promise < string | undefined > {
121+ switch ( value ) {
122+ case "tag" :
123+ if ( node . tags . length === 1 ) {
124+ return node . tags [ 0 ] ;
125+ }
126+ return await vscode . window . showQuickPick (
127+ node . tags ,
128+ {
129+ matchOnDetail :true ,
130+ placeHolder :"Multiple tags available, please select one" ,
131+ ignoreFocusOut :true ,
132+ } ,
133+ ) ;
134+ case "language" :
135+ return selectedLanguage ;
136+ case "difficulty" :
137+ return node . difficulty ;
138+ default :
139+ const errorMsg :string = `The config '${ value } ' is not supported.` ;
140+ leetCodeChannel . appendLine ( errorMsg ) ;
141+ throw new Error ( errorMsg ) ;
142+ }
143+ }