|
1 | 1 | // Copyright (c) jdneo. All rights reserved.
|
2 | 2 | // Licensed under the MIT license.
|
3 | 3 |
|
4 |
| -import * as fse from "fs-extra"; |
| 4 | +import * as _ from "lodash"; |
5 | 5 | import * as path from "path";
|
6 | 6 | import * as unescapeJS from "unescape-js";
|
7 | 7 | import * as vscode from "vscode";
|
@@ -11,7 +11,7 @@ import { leetCodeChannel } from "../leetCodeChannel";
|
11 | 11 | import { leetCodeExecutor } from "../leetCodeExecutor";
|
12 | 12 | import { leetCodeManager } from "../leetCodeManager";
|
13 | 13 | import { IProblem, IQuickItemEx, languages, ProblemState } from "../shared";
|
14 |
| -import { getNodeIdFromFile } from "../utils/problemUtils"; |
| 14 | +import { genFileExt, genFileName, getNodeIdFromFile } from "../utils/problemUtils"; |
15 | 15 | import { DialogOptions, DialogType, openSettingsEditor, promptForOpenOutputChannel, promptForSignIn, promptHintMessage } from "../utils/uiUtils";
|
16 | 16 | import { getActiveFilePath, selectWorkspaceFolder } from "../utils/workspaceUtils";
|
17 | 17 | import * as wsl from "../utils/wslUtils";
|
@@ -137,27 +137,38 @@ async function showProblemInternal(node: IProblem): Promise<void> {
|
137 | 137 | }
|
138 | 138 |
|
139 | 139 | 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) { |
142 | 142 | return;
|
143 | 143 | }
|
144 | 144 |
|
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) { |
149 | 162 | leetCodeChannel.appendLine("Showing problem canceled by user.");
|
150 | 163 | return;
|
151 | 164 | }
|
152 | 165 | }
|
153 | 166 |
|
154 |
| -outDir = path.join(outDir, relativePath); |
155 |
| -await fse.ensureDir(outDir); |
| 167 | +finalPath = wsl.useWsl() ? await wsl.toWinPath(finalPath) : finalPath; |
156 | 168 |
|
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")); |
159 | 170 | 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 }), |
161 | 172 | movePreviewAsideIfNeeded(node),
|
162 | 173 | promptHintMessage(
|
163 | 174 | "hint.commentDescription",
|
@@ -201,26 +212,49 @@ function parseProblemDecorator(state: ProblemState, locked: boolean): string {
|
201 | 212 | }
|
202 | 213 |
|
203 | 214 | async function resolveRelativePath(relativePath: string, node: IProblem, selectedLanguage: string): Promise<string> {
|
| 215 | +let tag: string = ""; |
204 | 216 | 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)) || ""; |
210 | 218 | }
|
211 | 219 |
|
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)) || ""; |
221 | 223 | }
|
222 | 224 |
|
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 | +}); |
224 | 258 | }
|
225 | 259 |
|
226 | 260 | async function resolveTagForProblem(problem: IProblem): Promise<string | undefined> {
|
@@ -236,3 +270,14 @@ async function resolveTagForProblem(problem: IProblem): Promise<string | undefin
|
236 | 270 | },
|
237 | 271 | );
|
238 | 272 | }
|
| 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