Skip to content

Commit 2d4266f

Browse files
magic-akarijdneo
authored andcommitted
implement leetcode.relativeFilePath (LeetCode-OpenSource#380)
1 parent a352850 commit 2d4266f

File tree

3 files changed

+317
-35
lines changed

3 files changed

+317
-35
lines changed

package.json

Lines changed: 242 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,248 @@
367367
"leetcode.outputFolder": {
368368
"type": "string",
369369
"scope": "application",
370-
"description": "The relative path to save the problem files."
370+
"description": "[Deprecated] The output folder to save the problem files."
371+
},
372+
"leetcode.filePath": {
373+
"type": "object",
374+
"scope": "application",
375+
"description": "The output folder and filename to save the problem files.",
376+
"properties": {
377+
"default": {
378+
"type": "object",
379+
"properties": {
380+
"folder": {
381+
"type": "string",
382+
"examples": [
383+
"src"
384+
]
385+
},
386+
"filename": {
387+
"type": "string",
388+
"examples": [
389+
"${camelCaseName}.${ext}",
390+
"${PascalCaseName}.${ext}",
391+
"${id}-${kebab-case-name}.${ext}",
392+
"${id}_${snake_case_name}.${ext}"
393+
]
394+
}
395+
},
396+
"required": [
397+
"folder",
398+
"filename"
399+
]
400+
},
401+
"bash": {
402+
"type": "object",
403+
"properties": {
404+
"folder": {
405+
"type": "string"
406+
},
407+
"filename": {
408+
"type": "string"
409+
}
410+
},
411+
"minProperties": 1
412+
},
413+
"c": {
414+
"type": "object",
415+
"properties": {
416+
"folder": {
417+
"type": "string"
418+
},
419+
"filename": {
420+
"type": "string"
421+
}
422+
},
423+
"minProperties": 1
424+
},
425+
"cpp": {
426+
"type": "object",
427+
"properties": {
428+
"folder": {
429+
"type": "string"
430+
},
431+
"filename": {
432+
"type": "string"
433+
}
434+
},
435+
"minProperties": 1
436+
},
437+
"csharp": {
438+
"type": "object",
439+
"properties": {
440+
"folder": {
441+
"type": "string"
442+
},
443+
"filename": {
444+
"type": "string"
445+
}
446+
},
447+
"minProperties": 1
448+
},
449+
"golang": {
450+
"type": "object",
451+
"properties": {
452+
"folder": {
453+
"type": "string"
454+
},
455+
"filename": {
456+
"type": "string"
457+
}
458+
},
459+
"minProperties": 1
460+
},
461+
"java": {
462+
"type": "object",
463+
"properties": {
464+
"folder": {
465+
"type": "string"
466+
},
467+
"filename": {
468+
"type": "string"
469+
}
470+
},
471+
"minProperties": 1
472+
},
473+
"javascript": {
474+
"type": "object",
475+
"properties": {
476+
"folder": {
477+
"type": "string"
478+
},
479+
"filename": {
480+
"type": "string"
481+
}
482+
},
483+
"minProperties": 1
484+
},
485+
"kotlin": {
486+
"type": "object",
487+
"properties": {
488+
"folder": {
489+
"type": "string"
490+
},
491+
"filename": {
492+
"type": "string"
493+
}
494+
},
495+
"minProperties": 1
496+
},
497+
"mysql": {
498+
"type": "object",
499+
"properties": {
500+
"folder": {
501+
"type": "string"
502+
},
503+
"filename": {
504+
"type": "string"
505+
}
506+
},
507+
"minProperties": 1
508+
},
509+
"php": {
510+
"type": "object",
511+
"properties": {
512+
"folder": {
513+
"type": "string"
514+
},
515+
"filename": {
516+
"type": "string"
517+
}
518+
},
519+
"minProperties": 1
520+
},
521+
"python": {
522+
"type": "object",
523+
"properties": {
524+
"folder": {
525+
"type": "string"
526+
},
527+
"filename": {
528+
"type": "string"
529+
}
530+
},
531+
"minProperties": 1
532+
},
533+
"python3": {
534+
"type": "object",
535+
"properties": {
536+
"folder": {
537+
"type": "string"
538+
},
539+
"filename": {
540+
"type": "string"
541+
}
542+
},
543+
"minProperties": 1
544+
},
545+
"ruby": {
546+
"type": "object",
547+
"properties": {
548+
"folder": {
549+
"type": "string"
550+
},
551+
"filename": {
552+
"type": "string"
553+
}
554+
},
555+
"minProperties": 1
556+
},
557+
"rust": {
558+
"type": "object",
559+
"properties": {
560+
"folder": {
561+
"type": "string"
562+
},
563+
"filename": {
564+
"type": "string"
565+
}
566+
},
567+
"minProperties": 1
568+
},
569+
"scala": {
570+
"type": "object",
571+
"properties": {
572+
"folder": {
573+
"type": "string"
574+
},
575+
"filename": {
576+
"type": "string"
577+
}
578+
},
579+
"minProperties": 1
580+
},
581+
"swift": {
582+
"type": "object",
583+
"properties": {
584+
"folder": {
585+
"type": "string"
586+
},
587+
"filename": {
588+
"type": "string"
589+
}
590+
},
591+
"minProperties": 1
592+
}
593+
},
594+
"additionalProperties": {
595+
"type": "object",
596+
"properties": {
597+
"folder": {
598+
"type": "string"
599+
},
600+
"filename": {
601+
"type": "string"
602+
}
603+
},
604+
"minProperties": 1
605+
},
606+
"default": {
607+
"default": {
608+
"folder": "",
609+
"filename": "${id}.${kebab-case-name}.${ext}"
610+
}
611+
}
371612
},
372613
"leetcode.enableStatusBar": {
373614
"type": "boolean",

src/commands/show.ts

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

4-
import * as fse from "fs-extra";
4+
import * as _ from "lodash";
55
import * as path from "path";
66
import * as unescapeJS from "unescape-js";
77
import * as vscode from "vscode";
@@ -11,7 +11,7 @@ import { leetCodeChannel } from "../leetCodeChannel";
1111
import { leetCodeExecutor } from "../leetCodeExecutor";
1212
import { leetCodeManager } from "../leetCodeManager";
1313
import { IProblem, IQuickItemEx, languages, ProblemState } from "../shared";
14-
import { getNodeIdFromFile } from "../utils/problemUtils";
14+
import { genFileExt, genFileName, getNodeIdFromFile } from "../utils/problemUtils";
1515
import { DialogOptions, DialogType, openSettingsEditor, promptForOpenOutputChannel, promptForSignIn, promptHintMessage } from "../utils/uiUtils";
1616
import { getActiveFilePath, selectWorkspaceFolder } from "../utils/workspaceUtils";
1717
import * as wsl from "../utils/wslUtils";
@@ -137,27 +137,38 @@ async function showProblemInternal(node: IProblem): Promise<void> {
137137
}
138138

139139
const leetCodeConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("leetcode");
140-
let outDir: string = await selectWorkspaceFolder();
141-
if (!outDir) {
140+
const workspaceFolder: string = await selectWorkspaceFolder();
141+
if (!workspaceFolder) {
142142
return;
143143
}
144144

145-
let relativePath: string = (leetCodeConfig.get<string>("outputFolder", "")).trim();
146-
if (relativePath) {
147-
relativePath = await resolveRelativePath(relativePath, node, language);
148-
if (!relativePath) {
145+
const outputFolder: string = leetCodeConfig.get<string>("outputFolder", "").trim();
146+
147+
const fileFolder: string = leetCodeConfig
148+
.get<string>(`filePath.${language}.folder`, leetCodeConfig.get<string>(`filePath.default.folder`, outputFolder))
149+
.trim();
150+
const fileName: string = leetCodeConfig
151+
.get<string>(
152+
`filePath.${language}.filename`,
153+
leetCodeConfig.get<string>(`filePath.default.filename`, genFileName(node, language)),
154+
)
155+
.trim();
156+
157+
let finalPath: string = path.join(workspaceFolder, fileFolder, fileName);
158+
159+
if (finalPath) {
160+
finalPath = await resolveRelativePath(finalPath, node, language);
161+
if (!finalPath) {
149162
leetCodeChannel.appendLine("Showing problem canceled by user.");
150163
return;
151164
}
152165
}
153166

154-
outDir = path.join(outDir, relativePath);
155-
await fse.ensureDir(outDir);
167+
finalPath = wsl.useWsl() ? await wsl.toWinPath(finalPath) : finalPath;
156168

157-
const originFilePath: string = await leetCodeExecutor.showProblem(node, language, outDir, leetCodeConfig.get<boolean>("showCommentDescription"));
158-
const filePath: string = wsl.useWsl() ? await wsl.toWinPath(originFilePath) : originFilePath;
169+
await leetCodeExecutor.showProblem(node, language, finalPath, leetCodeConfig.get<boolean>("showCommentDescription"));
159170
await Promise.all([
160-
vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false, viewColumn: vscode.ViewColumn.One }),
171+
vscode.window.showTextDocument(vscode.Uri.file(finalPath), { preview: false, viewColumn: vscode.ViewColumn.One }),
161172
movePreviewAsideIfNeeded(node),
162173
promptHintMessage(
163174
"hint.commentDescription",
@@ -201,26 +212,49 @@ function parseProblemDecorator(state: ProblemState, locked: boolean): string {
201212
}
202213

203214
async function resolveRelativePath(relativePath: string, node: IProblem, selectedLanguage: string): Promise<string> {
215+
let tag: string = "";
204216
if (/\$\{tag\}/i.test(relativePath)) {
205-
const tag: string | undefined = await resolveTagForProblem(node);
206-
if (!tag) {
207-
return "";
208-
}
209-
relativePath = relativePath.replace(/\$\{tag\}/ig, tag);
217+
tag = (await resolveTagForProblem(node)) || "";
210218
}
211219

212-
relativePath = relativePath.replace(/\$\{language\}/ig, selectedLanguage);
213-
relativePath = relativePath.replace(/\$\{difficulty\}/ig, node.difficulty.toLocaleLowerCase());
214-
215-
// Check if there is any unsupported configuration
216-
const matchResult: RegExpMatchArray | null = relativePath.match(/\$\{(.*?)\}/);
217-
if (matchResult && matchResult.length >= 1) {
218-
const errorMsg: string = `The config '${matchResult[1]}' is not supported.`;
219-
leetCodeChannel.appendLine(errorMsg);
220-
throw new Error(errorMsg);
220+
let company: string = "";
221+
if (/\$\{company\}/i.test(relativePath)) {
222+
company = (await resolveCompanyForProblem(node)) || "";
221223
}
222224

223-
return relativePath;
225+
return relativePath.replace(/\$\{(.*?)\}/g, (_substring: string, ...args: string[]) => {
226+
const placeholder: string = args[0].toLowerCase().trim();
227+
switch (placeholder) {
228+
case "id":
229+
return node.id;
230+
case "name":
231+
return node.name;
232+
case "camelcasename":
233+
return _.camelCase(node.name);
234+
case "pascalcasename":
235+
return _.upperFirst(_.camelCase(node.name));
236+
case "kebabcasename":
237+
case "kebab-case-name":
238+
return _.kebabCase(node.name);
239+
case "snakecasename":
240+
case "snake_case_name":
241+
return _.snakeCase(node.name);
242+
case "ext":
243+
return genFileExt(selectedLanguage);
244+
case "language":
245+
return selectedLanguage;
246+
case "difficulty":
247+
return node.difficulty.toLocaleLowerCase();
248+
case "tag":
249+
return tag;
250+
case "company":
251+
return company;
252+
default:
253+
const errorMsg: string = `The config '${placeholder}' is not supported.`;
254+
leetCodeChannel.appendLine(errorMsg);
255+
throw new Error(errorMsg);
256+
}
257+
});
224258
}
225259

226260
async function resolveTagForProblem(problem: IProblem): Promise<string | undefined> {
@@ -236,3 +270,14 @@ async function resolveTagForProblem(problem: IProblem): Promise<string | undefin
236270
},
237271
);
238272
}
273+
274+
async function resolveCompanyForProblem(problem: IProblem): Promise<string | undefined> {
275+
if (problem.companies.length === 1) {
276+
return problem.companies[0];
277+
}
278+
return await vscode.window.showQuickPick(problem.companies, {
279+
matchOnDetail: true,
280+
placeHolder: "Multiple tags available, please select one",
281+
ignoreFocusOut: true,
282+
});
283+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy