From f1abc6a880e7dfc149346c1e7560f10f43bced4c Mon Sep 17 00:00:00 2001 From: A23187 Date: Sun, 15 Dec 2024 15:05:05 +0800 Subject: [PATCH] support moonbit language --- package-lock.json | 4 +-- package.json | 25 +++++++++++++--- src/leetCodeExecutor.ts | 12 +++++++- src/shared.ts | 6 ++-- src/utils/moonbitUtils.ts | 63 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 src/utils/moonbitUtils.ts diff --git a/package-lock.json b/package-lock.json index 2ac17cf..aa5d515 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-leetcode", - "version": "0.18.1", + "version": "0.18.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-leetcode", - "version": "0.18.1", + "version": "0.18.4", "license": "MIT", "dependencies": { "axios": "^1.6.8", diff --git a/package.json b/package.json index 53552b7..2298d3e 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "displayName": "LeetCode", "description": "Solve LeetCode problems in VS Code", "version": "0.18.4", - "author": "LeetCode", - "publisher": "LeetCode", + "author": "A-23187", + "publisher": "A-23187", "license": "MIT", "icon": "resources/LeetCode.png", "engines": { @@ -12,9 +12,9 @@ }, "repository": { "type": "git", - "url": "https://github.com/LeetCode-OpenSource/vscode-leetcode" + "url": "https://github.com/A-23187/vscode-leetcode" }, - "homepage": "https://github.com/LeetCode-OpenSource/vscode-leetcode/blob/master/README.md", + "homepage": "https://github.com/A-23187/vscode-leetcode/blob/master/README.md", "categories": [ "Other", "Snippets" @@ -310,6 +310,7 @@ "java", "javascript", "kotlin", + "moonbit", "mysql", "php", "python", @@ -523,6 +524,18 @@ }, "minProperties": 1 }, + "moonbit": { + "type": "object", + "properties": { + "folder": { + "type": "string" + }, + "filename": { + "type": "string" + } + }, + "minProperties": 1 + }, "mysql": { "type": "object", "properties": { @@ -636,6 +649,10 @@ "default": { "folder": "", "filename": "${id}.${kebab-case-name}.${ext}" + }, + "moonbit": { + "folder": "moonbit", + "filename": "${id}/${id}.${ext}" } } }, diff --git a/src/leetCodeExecutor.ts b/src/leetCodeExecutor.ts index d2332c7..9d22e0a 100644 --- a/src/leetCodeExecutor.ts +++ b/src/leetCodeExecutor.ts @@ -13,6 +13,7 @@ import { executeCommand, executeCommandWithProgress } from "./utils/cpUtils"; import { DialogOptions, openUrl } from "./utils/uiUtils"; import * as wsl from "./utils/wslUtils"; import { toWslPath, useWsl } from "./utils/wslUtils"; +import { genMoonbitCodeTemplateAndWrite, isMoonbitFile, moonbitBuild } from "./utils/moonbitUtils"; class LeetCodeExecutor implements Disposable { private leetCodeRootPath: string; @@ -102,7 +103,7 @@ class LeetCodeExecutor implements Disposable { public async showProblem(problemNode: IProblem, language: string, filePath: string, showDescriptionInComment: boolean = false, needTranslation: boolean): Promise { const templateType: string = showDescriptionInComment ? "-cx" : "-c"; - const cmd: string[] = [await this.getLeetCodeBinaryPath(), "show", problemNode.id, templateType, "-l", language]; + const cmd: string[] = [await this.getLeetCodeBinaryPath(), "show", problemNode.id, templateType, "-l", language === "moonbit" ? "javascript" : language]; if (!needTranslation) { cmd.push("-T"); // use -T to force English version @@ -111,6 +112,9 @@ class LeetCodeExecutor implements Disposable { if (!await fse.pathExists(filePath)) { await fse.createFile(filePath); const codeTemplate: string = await this.executeCommandWithProgressEx("Fetching problem data...", this.nodeExecutable, cmd); + if (isMoonbitFile(filePath, language)) { + return await genMoonbitCodeTemplateAndWrite(codeTemplate, filePath); + } await fse.writeFile(filePath, codeTemplate); } } @@ -163,6 +167,9 @@ class LeetCodeExecutor implements Disposable { } public async submitSolution(filePath: string): Promise { + if (isMoonbitFile(filePath, null)) { + filePath = await moonbitBuild(filePath); + } try { return await this.executeCommandWithProgressEx("Submitting to LeetCode...", this.nodeExecutable, [await this.getLeetCodeBinaryPath(), "submit", `"${filePath}"`]); } catch (error) { @@ -174,6 +181,9 @@ class LeetCodeExecutor implements Disposable { } public async testSolution(filePath: string, testString?: string): Promise { + if (isMoonbitFile(filePath, null)) { + filePath = await moonbitBuild(filePath); + } if (testString) { return await this.executeCommandWithProgressEx("Submitting to LeetCode...", this.nodeExecutable, [await this.getLeetCodeBinaryPath(), "test", `"${filePath}"`, "-t", `${testString}`]); } diff --git a/src/shared.ts b/src/shared.ts index e8b59d8..ba7b873 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -28,6 +28,7 @@ export const languages: string[] = [ "java", "javascript", "kotlin", + "moonbit", "mysql", "php", "python", @@ -48,6 +49,7 @@ export const langExt: Map = new Map([ ["java", "java"], ["javascript", "js"], ["kotlin", "kt"], + ["moonbit", "mbt"], ["mysql", "sql"], ["php", "php"], ["python", "py"], @@ -133,7 +135,7 @@ export const urls = { graphql: "https://leetcode.com/graphql", userGraphql: "https://leetcode.com/graphql", login: "https://leetcode.com/accounts/login/", - authLoginUrl: `https://leetcode.com/authorize-login/${protocol}/?path=leetcode.vscode-leetcode`, + authLoginUrl: `https://leetcode.com/authorize-login/${protocol}/?path=A-23187.vscode-leetcode`, }; export const urlsCn = { @@ -142,7 +144,7 @@ export const urlsCn = { graphql: "https://leetcode.cn/graphql", userGraphql: "https://leetcode.cn/graphql/", login: "https://leetcode.cn/accounts/login/", - authLoginUrl: `https://leetcode.cn/authorize-login/${protocol}/?path=leetcode.vscode-leetcode`, + authLoginUrl: `https://leetcode.cn/authorize-login/${protocol}/?path=A-23187.vscode-leetcode`, }; export const getUrl = (key: string) => { diff --git a/src/utils/moonbitUtils.ts b/src/utils/moonbitUtils.ts new file mode 100644 index 0000000..1ef032a --- /dev/null +++ b/src/utils/moonbitUtils.ts @@ -0,0 +1,63 @@ +import * as fse from "fs-extra"; +import * as path from "path"; +import { WorkspaceConfiguration } from "vscode"; +import { executeCommand } from "./cpUtils"; +import { getNodeIdFromFile } from "./problemUtils"; +import { getWorkspaceConfiguration, getWorkspaceFolder } from "./settingUtils"; +import { langExt } from "../shared"; + +export function getMoonbitFolder(): string { + const leetCodeConfig: WorkspaceConfiguration = getWorkspaceConfiguration(); + return path.join(getWorkspaceFolder(), + leetCodeConfig.get("filePath.moonbit.folder", leetCodeConfig.get("filePath.default.folder", ""))); +} + +export function getMoonbitExt(): string { + return langExt.get("moonbit") || ".mbt"; +} + +export function isMoonbitFile(filePath: string, language: string | null): boolean { + return filePath.startsWith(getMoonbitFolder()) && filePath.endsWith(`.${getMoonbitExt()}`) + && (language == null || language === "moonbit"); +} + +export async function writeMoonbitModuleJson(): Promise { + const modJsonPath: string = path.join(getMoonbitFolder(), "moon.mod.json"); + if (await fse.pathExists(modJsonPath)) { + return; + } + await fse.createFile(modJsonPath); + await fse.writeFile(modJsonPath, `{"name":"moonbit-leetcode","source":"."}`); +} + +export async function genMoonbitCodeTemplateAndWrite(jsCodeTemplate: string, moonbitFilePath: string): Promise { + const functionReg: RegExp = /^\s*var\s+([^\s]+)\s*=\s*function\s*\((.*)\)/; + let functionName: string = "functionName"; + let functionParam: string = ""; + const lines: string[] = []; + for (const line of jsCodeTemplate.split("\n")) { + const match: RegExpMatchArray | null = line.match(functionReg); + if (match && match.length === 3) { + functionName = match[1]; + functionParam = match[2]; + } + if (line && line.indexOf("@lc code=end") < 0) { + lines.push(`// ${line}`); + } + } + // TODO support functionParam with type and return type + lines.push(`pub fn ${functionName}(${functionParam}) -> Unit {\n}\n// @lc code=end\n`); + await writeMoonbitModuleJson(); + await fse.writeFile(moonbitFilePath, lines.join("\n")); + await fse.writeFile(path.join(path.dirname(moonbitFilePath), "moon.pkg.json"), + `{"is-main":false,"link":{"js":{"exports":["${functionName}"],"format":"iife"}}}`); +} + +export async function moonbitBuild(moonbitFilePath: string): Promise { + const moonbitFolder: string = getMoonbitFolder(); + const moonbitExt: string = getMoonbitExt(); + await executeCommand("moon", ["build", "--release", "--target", "js", "--directory", moonbitFolder, "--verbose"]); + const jsFilePath: string = path.join(moonbitFolder, "target", "js", "release", "build", `${moonbitFilePath.substring(moonbitFolder.length, moonbitFilePath.length - moonbitExt.length)}js`); + await fse.appendFile(jsFilePath, `// @lc app=leetcode.cn id=${await getNodeIdFromFile(moonbitFilePath)} lang=javascript`); + return jsFilePath; +}