├── .eslintignore ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── auto_build.yml │ ├── codeql-analysis.yml │ ├── release.yml │ └── update_version.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── _config.yml ├── c.sh ├── check_list_case.ts ├── delete.sh ├── hot.sh ├── init_env.sh ├── nls.ts ├── p.sh ├── package-lock.json ├── package.json ├── package.nls.json ├── package.nls.zh-cn.json ├── processdatajson.ts ├── resources ├── LCPR.png ├── LCPR.svg ├── bin │ └── leetcode ├── blank.png ├── check.png ├── close.svg ├── close_inverse.svg ├── dark │ ├── dislike.png │ └── like.png ├── data.json ├── debug │ ├── entry │ │ ├── cpp │ │ │ ├── entry.cpp │ │ │ └── problems │ │ │ │ ├── 116.cpp │ │ │ │ ├── 116.h │ │ │ │ ├── 133.cpp │ │ │ │ ├── 133.h │ │ │ │ ├── 138.cpp │ │ │ │ ├── 138.h │ │ │ │ ├── 278.cpp │ │ │ │ ├── 278.h │ │ │ │ ├── 429.cpp │ │ │ │ ├── 429.h │ │ │ │ ├── 843.cpp │ │ │ │ ├── 843.h │ │ │ │ ├── common.cpp │ │ │ │ └── common.h │ │ ├── javascript │ │ │ ├── entry.js │ │ │ └── problems │ │ │ │ ├── 1095.js │ │ │ │ ├── 116.js │ │ │ │ ├── 117.js │ │ │ │ ├── 133.js │ │ │ │ ├── 138.js │ │ │ │ ├── 278.js │ │ │ │ ├── 341.js │ │ │ │ ├── 429.js │ │ │ │ ├── 559.js │ │ │ │ ├── 589.js │ │ │ │ ├── 590.js │ │ │ │ ├── 843.js │ │ │ │ └── common.js │ │ └── python3 │ │ │ ├── entry.py │ │ │ └── problems │ │ │ ├── 1095.py │ │ │ ├── 116.py │ │ │ ├── 117.py │ │ │ ├── 133.py │ │ │ ├── 138.py │ │ │ ├── 278.py │ │ │ ├── 429.py │ │ │ ├── 559.py │ │ │ ├── 589.py │ │ │ ├── 590.py │ │ │ ├── 843.py │ │ │ └── common.py │ └── thirdparty │ │ └── c │ │ ├── cJSON.c │ │ └── cJSON.h ├── edit.svg ├── edit_inverse.svg ├── katexcss │ ├── github-markdown.min.css │ └── kates.min.css ├── leetcode-extension-recentcontests.gif ├── light │ ├── dislike.png │ └── like.png ├── lock.png ├── mygif │ ├── quickstart.gif │ ├── search.gif │ └── tag.gif ├── templates │ ├── codeonly.tpl │ └── detailed.tpl └── x.png ├── src ├── BABA.ts ├── bricksData │ └── BricksDataService.ts ├── childCall │ └── childCallModule.ts ├── commitResult │ └── CommitResultModule.ts ├── controller │ ├── BricksViewController.ts │ └── TreeViewController.ts ├── dao │ ├── bricksDao.ts │ ├── choiceDao.ts │ ├── debugArgDao.ts │ ├── groupDao.ts │ ├── remarkDao.ts │ └── tagsDao.ts ├── debug │ ├── DebugModule.ts │ ├── DoCpp.ts │ ├── DoJs.ts │ └── DoPy3.ts ├── extension.ts ├── fileButton │ └── FileButtonModule.ts ├── logOutput │ └── logOutputModule.ts ├── model │ ├── ConstDefind.ts │ └── TreeNodeModel.ts ├── preView │ └── PreviewModule.ts ├── questionData │ └── QuestionDataModule.ts ├── rankScore │ └── RankScoreDataModule.ts ├── recentContestData │ ├── ContestQuestionDataModule.ts │ └── RecentContestDataModule.ts ├── remark │ └── RemarkServiceModule.ts ├── rpc │ ├── actionChain │ │ ├── chainManager.ts │ │ ├── chainNode │ │ │ ├── cache.ts │ │ │ ├── core.ts │ │ │ ├── leetcode.cn.ts │ │ │ ├── leetcode.ts │ │ │ └── retry.ts │ │ └── chainNodeBase.ts │ ├── childMain.ts │ ├── factory │ │ ├── api │ │ │ ├── cacheApi.ts │ │ │ ├── pluginApi.ts │ │ │ ├── queryApi.ts │ │ │ ├── showApi.ts │ │ │ ├── starApi.ts │ │ │ ├── submitApi.ts │ │ │ ├── testApi.ts │ │ │ └── userApi.ts │ │ ├── apiBase.ts │ │ └── apiFactory.ts │ └── utils │ │ ├── ReplyUtils.ts │ │ ├── commUtils.ts │ │ ├── configUtils.ts │ │ ├── queueUtils.ts │ │ ├── sessionUtils.ts │ │ └── storageUtils.ts ├── service │ ├── BaseWebviewService.ts │ └── MarkdownService.ts ├── solution │ └── SolutionModule.ts ├── statusBar │ └── StatusBarModule.ts ├── statusBarTime │ └── StatusBarTimeModule.ts ├── todayData │ └── TodayDataModule.ts ├── treeColor │ └── TreeColorModule.ts ├── treeData │ └── TreeDataService.ts └── utils │ ├── BaseCC.ts │ ├── ConfigUtils.ts │ ├── OutputUtils.ts │ ├── SystemUtils.ts │ ├── problemUtils.ts │ └── testHot.ts ├── thirdpartynotice.txt ├── tsconfig.json └── tslint.json /.eslintignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2020: true, 5 | node: true, 6 | }, 7 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | parser: "@typescript-eslint/parser", 9 | parserOptions: { 10 | ecmaVersion: 6, 11 | sourceType: "module", 12 | // 'project': './tsconfig.json', 13 | }, 14 | plugins: ["@typescript-eslint"], 15 | 16 | /** 17 | * 规则写法 18 | * 1、'quotes': 0; -- 0关闭,1警告,2错误 19 | * 2、'quotes': 'off'; -- off关闭,warn警告,error错误 20 | * 3、'quotes': ['error', 'single']; 'error'是提示类型,'single'是参数。参数不止一个的时候写成{} 21 | */ 22 | rules: { 23 | // 是否检查变量已申明但未使用:警告。 24 | "@typescript-eslint/no-unused-vars": ["warn"], 25 | // 强制单引号:开启,自动修正 26 | quotes: 0, 27 | // 强制分号:js 关闭,ts 强制分号。 28 | semi: ["off"], 29 | // 'semi': ['off'], 30 | // 定义变量时自动类型推断:关闭 31 | "@typescript-eslint/no-inferrable-types": ["off"], 32 | // 强制const:关闭 33 | "prefer-const": ["off"], 34 | // 不允许空函数:关闭 35 | "@typescript-eslint/no-empty-function": ["off"], 36 | // 禁止特定类型:关闭。 PS:如果打开 Function 类型会报错 37 | "@typescript-eslint/ban-types": ["off"], 38 | // 禁止多余的分号:关闭。 PS:打开后,某些大括号结束加分号会报错 39 | "@typescript-eslint/no-extra-semi": ["off"], 40 | // 检查函数是否有返回值:警告。 PS:有些老代码没有返回值,历史包袱重,暂时不强制报错 41 | "@typescript-eslint/explicit-module-boundary-types": ["warn"], 42 | // 禁止给变量赋值为 this:关闭。 43 | "@typescript-eslint/no-this-alias": ["off"], 44 | // 禁止使用 requires:关闭。 45 | "@typescript-eslint/no-var-requires": ["off"], 46 | // 检测无法访问的代码:关闭。 PS:有时候需要用 return 注释掉后面的代码 47 | "no-unreachable": ["off"], 48 | /** 49 | * 是否可以直接调用对象方法:关闭。 50 | * PS:暂时关闭。目前写法:myObject.hasOwnProperty('name') ,推荐写法:Object.prototype.hasOwnProperty.call(foo, "bar") 51 | */ 52 | "no-prototype-builtins": ["off"], 53 | // 是否允许函数内定义函数:关闭。 54 | "no-inner-declarations": ["off"], 55 | // 不允许判断条件写死:关闭。 PS:打开后,if(false){} 这种判断语句会报错 56 | "no-constant-condition": ["off"], 57 | // get 和 set 是否必须放在一起:关闭。 58 | "@typescript-eslint/adjacent-overload-signatures": ["off"], 59 | "no-async-promise-executor": ["off"], 60 | "prefer-spread": ["off"], 61 | "prefer-rest-params": ["off"], 62 | "@typescript-eslint/no-explicit-any": ["off"], 63 | // "function-paren-newline": ["off", { minItems: 5 }], 64 | "max-len": ["warn", { code: 120 }], 65 | }, 66 | // 如果有 js 和 ts 需要分开指定的规则,就 js 写 rules 里,ts 写 overrides 里 67 | overrides: [ 68 | { 69 | // enable the rule specifically for TypeScript files 70 | files: ["*.ts", "*.tsx"], 71 | rules: { 72 | // 强制分号:开启,自动修正 73 | semi: ["error", "always"], 74 | // '@typescript-eslint/explicit-module-boundary-types': ['error'] 75 | }, 76 | }, 77 | ], 78 | // 定义全局变量 79 | globals: { 80 | Global: true, 81 | }, 82 | }; 83 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | --- 10 | 11 | name: 🐛 Bug report(错误报告) 12 | about: Create a report to help us improve(创建报告以帮助我们改进) 13 | 14 | --- 15 | 16 | ## 🐛 Bug Report(错误报告) 17 | 18 | A clear and concise description of what the bug is.(清晰简洁地描述错误是什么。) 19 | 20 | ## To Reproduce(重现) 21 | 22 | Steps to reproduce the behavior.(重现的步骤。) 23 | 24 | ## Expected behavior(预期行为) 25 | 26 | A clear and concise description of what you expected to happen.(对您期望发生的事情进行清晰简洁的描述。) 27 | 28 | ## Extension Output(扩展输出) 29 | 30 | Paste here the LeetCode extension log from output channel.(将输出通道中的 LeetCode 扩展日志粘贴到此处。) 31 | 32 | Guidance: Press `Ctrl+Shift+U`, and toggle the channel to `LeetCode`.(按`Ctrl+Shift+U`,将频道切换到`LeetCode`。) 33 | 34 | ## Your Environment 35 | 36 | - _os(操作系统)_: 37 | - _extension settings(扩展设置)_: 38 | - _nodejs version(nodejs 版本)_: 39 | - _vscode version(vscode 版本)_: 40 | - _extension version(扩展版本)_: 41 | -------------------------------------------------------------------------------- /.github/workflows/auto_build.yml: -------------------------------------------------------------------------------- 1 | name: auto_build 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | tag: 7 | description: "版本号" 8 | type: string 9 | 10 | jobs: 11 | linux: 12 | name: Linux 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 30 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Setup Node.js environment 19 | uses: actions/setup-node@v2 20 | with: 21 | node-version: 16 22 | 23 | - name: Install Node.js modules 24 | run: npm install 25 | 26 | - name: Update Node.js modules 27 | run: npm update 28 | 29 | - name: Install vsce 30 | run: npm i -g vsce 31 | 32 | - name: VSCE Packge 33 | run: | 34 | vsce package 35 | VSIX_FILE=$(find ./ -name '*vscode-leetcode-problem-rating*.vsix') 36 | echo "VSIX_FILE=$VSIX_FILE" >> $GITHUB_ENV 37 | echo $VSIX_FILE 38 | - uses: actions/upload-artifact@v2 39 | with: 40 | name: vsix_file 41 | path: ${{ env.VSIX_FILE }} 42 | 43 | windows: 44 | name: Windows 45 | runs-on: windows-latest 46 | timeout-minutes: 30 47 | steps: 48 | - uses: actions/checkout@v2 49 | 50 | - name: Setup Node.js environment 51 | uses: actions/setup-node@v2 52 | with: 53 | node-version: 16 54 | 55 | - name: Install Node.js modules 56 | run: npm install 57 | 58 | - name: Update Node.js modules 59 | run: npm update 60 | 61 | - name: Install vsce 62 | run: npm i -g vsce 63 | 64 | - name: VSCE Packge 65 | run: vsce package 66 | 67 | darwin: 68 | name: macOS 69 | runs-on: macos-latest 70 | timeout-minutes: 30 71 | steps: 72 | - uses: actions/checkout@v2 73 | 74 | - name: Setup Node.js environment 75 | uses: actions/setup-node@v2 76 | with: 77 | node-version: 16 78 | 79 | - name: Install Node.js modules 80 | run: npm install 81 | 82 | - name: Update Node.js modules 83 | run: npm update 84 | 85 | - name: Install vsce 86 | run: npm i -g vsce 87 | 88 | - name: VSCE Packge 89 | run: vsce package 90 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | workflow_dispatch: 16 | inputs: 17 | tag: 18 | description: "版本号" 19 | type: string 20 | 21 | jobs: 22 | analyze: 23 | name: Analyze 24 | runs-on: ubuntu-latest 25 | permissions: 26 | actions: read 27 | contents: read 28 | security-events: write 29 | 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | language: ["javascript"] 34 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 35 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 36 | 37 | steps: 38 | - name: Checkout repository 39 | uses: actions/checkout@v3 40 | 41 | # Initializes the CodeQL tools for scanning. 42 | - name: Initialize CodeQL 43 | uses: github/codeql-action/init@v2 44 | with: 45 | languages: ${{ matrix.language }} 46 | # If you wish to specify custom queries, you can do so here or in a config file. 47 | # By default, queries listed here will override any specified in a config file. 48 | # Prefix the list here with "+" to use these queries and those in the config file. 49 | 50 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 51 | # queries: security-extended,security-and-quality 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v2 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 60 | 61 | # If the Autobuild fails above, remove it and uncomment the following three lines. 62 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 63 | 64 | # - run: | 65 | # echo "Run, Build Application using script" 66 | # ./location_of_script_within_repo/buildscript.sh 67 | 68 | - name: Perform CodeQL Analysis 69 | uses: github/codeql-action/analyze@v2 70 | with: 71 | category: "/language:${{matrix.language}}" 72 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: [release] 6 | inputs: 7 | tag: 8 | description: "版本号" 9 | type: string 10 | change: 11 | description: "提交内容" 12 | type: string 13 | pr: 14 | description: "需要合并提交" 15 | type: string 16 | mt: 17 | description: "提交到市场" 18 | type: string 19 | 20 | jobs: 21 | first: 22 | name: version 23 | runs-on: ubuntu-latest 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | steps: 28 | - uses: actions/checkout@v2 29 | - name: updatepackage 30 | if: github.event.inputs.tag != '' 31 | run: | 32 | sed -i "s/\"version\":.*$/\"version\": \"${{ github.event.inputs.tag }}\",/" package.json 33 | - name: change 34 | if: github.event.inputs.change != '' 35 | run: | 36 | echo -e "## version ${{ github.event.inputs.tag }}\n\n- ${{ github.event.inputs.change }}\n\n$(cat CHANGELOG.md)" > CHANGELOG.md 37 | - name: commit 38 | if: github.event.inputs.tag != '' || github.event.inputs.change != '' 39 | run: | 40 | git config user.name ccagml 41 | git config user.email ccagml@163.com 42 | git add . 43 | git commit -m ${{ github.event.inputs.change }} 44 | git push 45 | - name: needpr 46 | if: github.event.inputs.pr != '' 47 | run: | 48 | gh pr create -B main --title 'Merge release${{ github.event.inputs.tag }} into main' --body 'Created by Github action' 49 | 50 | - name: finish 51 | run: | 52 | ls 53 | 54 | market: 55 | name: market 56 | needs: first 57 | runs-on: ubuntu-latest 58 | timeout-minutes: 30 59 | if: github.event.inputs.mt != '' 60 | steps: 61 | - uses: actions/checkout@v2 62 | - name: updatepackage 63 | if: github.event.inputs.tag != '' 64 | run: | 65 | sed -i "s/\"version\":.*$/\"version\": \"${{ github.event.inputs.tag }}\",/" package.json 66 | 67 | - name: change 68 | if: github.event.inputs.change != '' 69 | run: | 70 | echo -e "## version ${{ github.event.inputs.tag }}\n\n- ${{ github.event.inputs.change }}\n\n$(cat CHANGELOG.md)" > CHANGELOG.md 71 | 72 | - name: Setup Node.js environment 73 | uses: actions/setup-node@v2 74 | with: 75 | node-version: 16 76 | 77 | - name: Install Node.js modules 78 | run: npm install 79 | 80 | # - name: Update Node.js modules 81 | # run: npm update 82 | 83 | - name: Install vsce 84 | run: npm i -g vsce 85 | 86 | - name: Publish 87 | run: vsce publish -p ${{ secrets.VSCE_PAT }} 88 | -------------------------------------------------------------------------------- /.github/workflows/update_version.yml: -------------------------------------------------------------------------------- 1 | name: update_version 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | tag: 7 | description: "版本号" 8 | type: string 9 | change: 10 | description: "提交内容" 11 | type: string 12 | pr: 13 | description: "需要合并提交" 14 | type: boolean 15 | default: false 16 | 17 | jobs: 18 | jobA: 19 | name: Linux 20 | runs-on: ubuntu-latest 21 | if: github.event.inputs.tag != '' && github.event.inputs.change != '' 22 | steps: 23 | - uses: actions/checkout@v2 24 | - run: | 25 | ls 26 | sed -i "s/\"version\":.*$/\"version\": \"${{ github.event.inputs.tag }}\",/" package.json 27 | echo -e "## version ${{ github.event.inputs.tag }}\n\n- ${{ github.event.inputs.change }}\n\n$(cat CHANGELOG.md)" > CHANGELOG.md 28 | git config user.name ccagml 29 | git config user.email ccagml@163.com 30 | git add . 31 | git commit -m ${{ github.event.inputs.change }} 32 | git push 33 | jobB: 34 | name: createPr 35 | runs-on: ubuntu-latest 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | if: ${{ github.event.inputs.pr }} && github.event.inputs.tag != '' 40 | steps: 41 | - uses: actions/checkout@v2 42 | - run: | 43 | gh pr create -B main --title 'Merge release${{ github.event.inputs.tag }} into main' --body 'Created by Github action' 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # Output 61 | out 62 | *.vsix 63 | .vscode-test 64 | 65 | # Mac 66 | .DS_Store 67 | nls.js 68 | nls.json 69 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "master", 7 | // "type": "extensionHost", 8 | "type": "extensionHost", 9 | "request": "launch", 10 | "runtimeExecutable": "${execPath}", 11 | "args": [ 12 | "--extensionDevelopmentPath=${workspaceRoot}", 13 | "--no-warnings" 14 | ], 15 | "autoAttachChildProcesses": true, 16 | // "stopOnEntry": false, 17 | "sourceMaps": true, 18 | "outFiles": [ 19 | "${workspaceRoot}/out/src/**/*.js" 20 | ], 21 | "trace": false, 22 | "preLaunchTask": "npm", 23 | "env": { 24 | "PATH": "${env:PATH}" 25 | } 26 | }, 27 | { 28 | "type": "node", 29 | "request": "attach", 30 | "name": "attach process", 31 | "processId": "${command:PickProcess}", 32 | "continueOnAttach": true, 33 | }, 34 | { 35 | "type": "node", 36 | "request": "attach", 37 | "name": "attach 9229", 38 | "port": 9229, // 与子进程中的 --inspect 端口一致 39 | "continueOnAttach": true 40 | }, 41 | { 42 | "name": "Launch Tests", 43 | "type": "extensionHost", 44 | "request": "launch", 45 | "runtimeExecutable": "${execPath}", 46 | "args": [ 47 | "--extensionDevelopmentPath=${workspaceRoot}", 48 | "--extensionTestsPath=${workspaceRoot}/out/test", 49 | "--trace-warnings" 50 | ], 51 | // "stopOnEntry": false, 52 | "sourceMaps": true, 53 | "outFiles": [ 54 | "${workspaceRoot}/out/test/**/*.js" 55 | ], 56 | "preLaunchTask": "npm" 57 | }, 58 | { 59 | "name": "windows_master", 60 | // "type": "extensionHost", 61 | "type": "extensionHost", 62 | "request": "launch", 63 | "runtimeExecutable": "${execPath}", 64 | "args": [ 65 | "--extensionDevelopmentPath=${workspaceRoot}", 66 | "--trace-warnings" 67 | ], 68 | "autoAttachChildProcesses": true, 69 | // "stopOnEntry": false, 70 | "sourceMaps": true, 71 | "outFiles": [ 72 | "${workspaceRoot}/out/src/**/*.js" 73 | ], 74 | "trace": false, 75 | "preLaunchTask": "window_npm" 76 | }, 77 | { 78 | "name": "debugcheck", 79 | "program": "${workspaceFolder}/check_list_case.js", 80 | "request": "launch", 81 | "sourceMaps": true, 82 | "outFiles": [ 83 | "${workspaceRoot}/check_list_case.js" 84 | ], 85 | "type": "node" 86 | }, 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.insertSpaces": true, 4 | "editor.tabSize": 4, 5 | "files.insertFinalNewline": true, 6 | "files.trimTrailingWhitespace": true, 7 | "search.exclude": { 8 | "out": true, 9 | "**/node_modules": true, 10 | ".vscode-test": true 11 | }, 12 | "files.associations": { 13 | "*.gafq": "lua", 14 | "functional": "cpp", 15 | "iostream": "cpp" 16 | }, 17 | "leetcode-problem-rating.filePath": { 18 | "default": { 19 | "folder": "", 20 | "filename": "${id}.${kebab-case-name}_${yyyymmdd}.${ext}" 21 | } 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "npm", 6 | "dependsOn": [ 7 | "npm_rm", 8 | "npm_do" 9 | ] 10 | }, 11 | { 12 | "label": "window_npm", 13 | "dependsOn": [ 14 | "window_npm_rm", 15 | "window_npm_do" 16 | ] 17 | }, 18 | { 19 | "label": "window_npm_rm", 20 | "type": "shell", 21 | "command": "Remove-item", 22 | "args": [ 23 | "out", 24 | "-recurse" 25 | ], 26 | // "isBackground": true, 27 | }, 28 | { 29 | "label": "window_npm_do", 30 | "type": "shell", 31 | "command": "npm", 32 | "group": { 33 | "kind": "build", 34 | "isDefault": true 35 | }, 36 | "args": [ 37 | "run", 38 | "compile", 39 | "--loglevel", 40 | "silent" 41 | ], 42 | "isBackground": true, 43 | "presentation": { 44 | "reveal": "silent" 45 | }, 46 | "problemMatcher": "$tsc-watch" 47 | }, 48 | { 49 | "label": "npm_rm", 50 | "type": "shell", 51 | "command": "rm", 52 | "args": [ 53 | "-rf", 54 | "out" 55 | ], 56 | "windows": { 57 | "command": "Remove-item", 58 | "args": [ 59 | "out", 60 | "-recurse" 61 | ], 62 | } 63 | // "isBackground": true, 64 | }, 65 | { 66 | "label": "npm_do", 67 | "type": "shell", 68 | "command": "npm", 69 | "group": { 70 | "kind": "build", 71 | "isDefault": true 72 | }, 73 | "args": [ 74 | "run", 75 | "compile", 76 | "--loglevel", 77 | "silent" 78 | ], 79 | "isBackground": true, 80 | "presentation": { 81 | "reveal": "silent" 82 | }, 83 | "problemMatcher": "$tsc-watch" 84 | }, 85 | { 86 | "type": "npm", 87 | "script": "lint", 88 | // "problemMatcher": { 89 | // "base": "$tslint5", 90 | // "fileLocation": "absolute" 91 | // } 92 | } 93 | ] 94 | } 95 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | package-lock.json 9 | tsconfig.json 10 | tslint.json 11 | **/*.gif 12 | ACKNOWLEDGEMENTS.md 13 | docs 14 | .github 15 | c.sh 16 | delete.sh 17 | init_env.sh 18 | p.sh 19 | nls.ts 20 | nls.js 21 | processdatajson.ts 22 | .eslintignore 23 | .eslintrc.js 24 | _config.yml 25 | check_list_case.ts 26 | hot.sh 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-present ccagml 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | -------------------------------------------------------------------------------- /c.sh: -------------------------------------------------------------------------------- 1 | tsc ./check_list_case.ts 2 | node ./check_list_case.js 3 | rm -rf ./check_list_case.js 4 | -------------------------------------------------------------------------------- /check_list_case.ts: -------------------------------------------------------------------------------- 1 | // const fs = require("fs"); 2 | // const path = require("path"); 3 | 4 | // // 指定要遍历的目录路径 5 | // const directoryPath = "./src"; 6 | 7 | // // 递归遍历目录并获取所有.ts文件 8 | // function getTsFiles(dir) { 9 | // const files = fs.readdirSync(dir); 10 | // const tsFiles: any = []; 11 | 12 | // files.forEach((file) => { 13 | // const filePath = path.join(dir, file); 14 | // const stat = fs.statSync(filePath); 15 | 16 | // if (stat.isDirectory()) { 17 | // tsFiles.push(...getTsFiles(filePath)); // 递归处理子目录 18 | // } else if (file.endsWith(".ts")) { 19 | // tsFiles.push(filePath); 20 | // } 21 | // }); 22 | 23 | // return tsFiles; 24 | // } 25 | 26 | // const tsFiles = getTsFiles(directoryPath); 27 | 28 | // console.log("所有.ts文件:", tsFiles); 29 | const fs = require("fs"); 30 | const path = require("path"); 31 | 32 | // 指定要遍历的目录路径 33 | const directoryPath = "./src"; 34 | 35 | // 递归遍历目录并获取所有.ts文件 36 | function getTsFiles(dir) { 37 | const files = fs.readdirSync(dir); 38 | const tsFiles: any = []; 39 | 40 | files.forEach((file) => { 41 | const filePath = path.join(dir, file); 42 | const stat = fs.statSync(filePath); 43 | 44 | if (stat.isDirectory()) { 45 | tsFiles.push(...getTsFiles(filePath)); // 递归处理子目录 46 | } else if (file.endsWith(".ts")) { 47 | tsFiles.push(filePath); 48 | } 49 | }); 50 | 51 | return tsFiles; 52 | } 53 | 54 | // 读取文件内容并查找listNotificationInterests函数 55 | function findListNotificationInterestsContent(filePath) { 56 | const fileContent = fs.readFileSync(filePath, "utf8"); 57 | const regex = /listNotificationInterests\(\): string\[\] {\s*return\s*\[([\s\S]*?)\s*\];/m; 58 | const matches = fileContent.match(regex); 59 | 60 | if (matches && matches[1]) { 61 | // 获取返回内容 62 | const content = matches[1] 63 | .replace(/['"]+/g, "") // 去除引号 64 | .split(",") // 分割成数组 65 | .map((item) => item.trim()); // 移除空白字符 66 | 67 | return content; 68 | } 69 | 70 | return null; 71 | } 72 | 73 | const tsFiles = getTsFiles(directoryPath); 74 | 75 | // 读取文件内容并查找listNotificationInterests函数 76 | function findListContent(filePath) { 77 | const fileContent = fs.readFileSync(filePath, "utf8"); 78 | // 匹配 handleNotification 函数 79 | const handleNotificationRegex = /async\s+handleNotification\s*\(.*\)\s*{([\s\S]*?)}/; 80 | const handleNotificationMatches = handleNotificationRegex.exec(fileContent); 81 | 82 | if (handleNotificationMatches && handleNotificationMatches[1]) { 83 | const handleNotificationContent = handleNotificationMatches[1]; 84 | 85 | let caseRegex = /case\s+.*?:([\s\S]*?)(?=case\s+|$)/g; 86 | let caseMatches = handleNotificationContent.match(caseRegex); 87 | let new_result: any = []; 88 | if (caseMatches) { 89 | caseMatches.forEach(function (caseMatch) { 90 | let trimmedCaseMatch = caseMatch.match(/case\s+(.*?):([\s\S]*)/); 91 | if (trimmedCaseMatch) { 92 | new_result.push(trimmedCaseMatch[1]); 93 | } 94 | 95 | // if (trimmedCaseMatch && !content.includes(trimmedCaseMatch[1])) { 96 | // console.log(filePath, "没有:", trimmedCaseMatch[1], "\n"); 97 | // } 98 | }); 99 | } 100 | return new_result; 101 | } 102 | } 103 | 104 | tsFiles.forEach((filePath) => { 105 | const all_listen = findListNotificationInterestsContent(filePath); 106 | let all_case = findListContent(filePath); 107 | if (all_case) { 108 | all_case.forEach((element) => { 109 | if (!all_listen.includes(element)) { 110 | console.log(filePath, "没有:", element, "\n"); 111 | } 112 | }); 113 | } 114 | }); 115 | -------------------------------------------------------------------------------- /delete.sh: -------------------------------------------------------------------------------- 1 | rm -rf ~/.lcpr 2 | rm -rf ~/.vscode-server/extensions/ccagml* 3 | -------------------------------------------------------------------------------- /hot.sh: -------------------------------------------------------------------------------- 1 | rm -rf out/ 2 | tsc -p ./ 3 | -------------------------------------------------------------------------------- /init_env.sh: -------------------------------------------------------------------------------- 1 | sudo apt install nodejs 2 | sudo apt install npm 3 | 4 | sudo npm cache clean -f 5 | sudo npm install -g n 6 | sudo n stable 7 | 8 | sudo apt install node-typescript 9 | sudo npm i vsce -g 10 | 11 | 12 | -------------------------------------------------------------------------------- /nls.ts: -------------------------------------------------------------------------------- 1 | import * as fse from "fs-extra"; 2 | 3 | let result_json = {}; 4 | 5 | function check_key(object, father: Array) { 6 | let main_join = father.join("."); 7 | 8 | if (object["description"]) { 9 | let key = main_join + ".description"; 10 | 11 | if (result_json[key]) { 12 | console.log("重复", key, object); 13 | } 14 | 15 | result_json[key] = object["description"]; 16 | object["description"] = `%${key}%`; 17 | } 18 | if (object["title"]) { 19 | let key = main_join + ".title"; 20 | if (result_json[key]) { 21 | console.log("重复", key); 22 | } 23 | result_json[key] = object["title"]; 24 | object["title"] = `%${key}%`; 25 | } 26 | if (object["name"]) { 27 | let key = main_join + ".name"; 28 | if (result_json[key]) { 29 | console.log("重复", key); 30 | } 31 | result_json[key] = object["name"]; 32 | object["name"] = `%${key}%`; 33 | } 34 | if (object["label"]) { 35 | let key = main_join + ".label"; 36 | if (result_json[key]) { 37 | console.log("重复", key); 38 | } 39 | result_json[key] = object["label"]; 40 | object["label"] = `%${key}%`; 41 | } 42 | if (object["enumDescriptions"]) { 43 | let key = main_join + ".enumDescriptions"; 44 | if (result_json[key]) { 45 | console.log("重复", key); 46 | } 47 | for (let e_index = 0; e_index < object["enumDescriptions"].length; e_index++) { 48 | let b_key = key + `.${e_index}`; 49 | result_json[b_key] = object["enumDescriptions"][e_index]; 50 | } 51 | // object["enumDescriptions"] = `%${key}%`; 52 | } 53 | } 54 | 55 | function print_obj(object, father) { 56 | let obj_key = object["command"] || object["id"]; 57 | if (obj_key) { 58 | father.push(obj_key); 59 | } 60 | check_key(object, father); 61 | for (const key in object) { 62 | if (Object.prototype.hasOwnProperty.call(object, key)) { 63 | const element = object[key]; 64 | father.push(key); 65 | if (Array.isArray(element)) { 66 | print_arr(element, father); 67 | } else if (typeof element == "object") { 68 | print_obj(element, father); 69 | } 70 | father.pop(); 71 | } 72 | } 73 | if (obj_key) { 74 | father.pop(); 75 | } 76 | } 77 | function print_arr(object, father) { 78 | for (let i = 0; i < object.length; i++) { 79 | const element = object[i]; 80 | 81 | if (Array.isArray(element)) { 82 | print_arr(element, father); 83 | } else if (typeof element == "object") { 84 | print_obj(element, father); 85 | } 86 | } 87 | } 88 | 89 | async function test() { 90 | let temp_data = await fse.readFile("./package.json", "utf8"); 91 | let ob = JSON.parse(temp_data); 92 | print_obj(ob, ["main"]); 93 | await fse.writeFile("./nls.json", JSON.stringify(result_json)); 94 | await fse.writeFile("./package.json", JSON.stringify(ob)); 95 | } 96 | 97 | test(); 98 | -------------------------------------------------------------------------------- /p.sh: -------------------------------------------------------------------------------- 1 | ./c.sh 2 | rm -rf out/ 3 | vsce package 4 | rm -rf out 5 | -------------------------------------------------------------------------------- /processdatajson.ts: -------------------------------------------------------------------------------- 1 | import * as fse from "fs-extra"; 2 | 3 | async function test() { 4 | let temp_data = await fse.readFile("./resources/data.json", "utf8"); 5 | let ob = JSON.parse(temp_data); 6 | await fse.writeFile("./resources/data.json", JSON.stringify(ob)); 7 | } 8 | 9 | test(); 10 | -------------------------------------------------------------------------------- /resources/LCPR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/LCPR.png -------------------------------------------------------------------------------- /resources/LCPR.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 60 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /resources/bin/leetcode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require("../../out/src/rpc/childMain"); 4 | -------------------------------------------------------------------------------- /resources/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/blank.png -------------------------------------------------------------------------------- /resources/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/check.png -------------------------------------------------------------------------------- /resources/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/close_inverse.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/dislike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/dark/dislike.png -------------------------------------------------------------------------------- /resources/dark/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/dark/like.png -------------------------------------------------------------------------------- /resources/debug/entry/cpp/entry.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "cJSON.h" 8 | 9 | using namespace std; 10 | 11 | // @@stub-for-include-code@@ 12 | 13 | int main(int argc, char **argv) 14 | { 15 | for (int i = 0; i < argc; i++) 16 | { 17 | cout << "arg " << i << " is " << argv[i] << endl; 18 | } 19 | 20 | // @@stub-for-body-code@@ 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/116.cpp: -------------------------------------------------------------------------------- 1 | Node *parseNodeElement(cJSON *node) 2 | { 3 | if (node->type != cJSON_Number) 4 | { 5 | return nullptr; 6 | } 7 | return new Node(node->valueint); 8 | } 9 | 10 | Node *parseNode(const cJSON *node) 11 | { 12 | if (node->type != cJSON_Array) 13 | { 14 | throw "Parse parameter error, expect NumberArray"; 15 | } 16 | 17 | int i = 0; 18 | int isLeft = true; 19 | Node *first = parseNodeElement(cJSON_GetArrayItem(node, i)); 20 | queue q; 21 | q.push(first); 22 | int size = cJSON_GetArraySize(node); 23 | 24 | for (i = 1; i < size; i++) 25 | { 26 | Node *top = q.front(); 27 | Node *child = parseNodeElement(cJSON_GetArrayItem(node, i)); 28 | if (isLeft) 29 | { 30 | top->left = child; 31 | isLeft = false; 32 | } 33 | else 34 | { 35 | top->right = child; 36 | isLeft = true; 37 | q.pop(); 38 | } 39 | if (child != nullptr) 40 | { 41 | q.push(child); 42 | } 43 | } 44 | 45 | return first; 46 | } 47 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/116.h: -------------------------------------------------------------------------------- 1 | class Node 2 | { 3 | public: 4 | int val; 5 | Node *left; 6 | Node *right; 7 | Node *next; 8 | 9 | Node() : val(0), left(NULL), right(NULL), next(NULL) {} 10 | 11 | Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} 12 | 13 | Node(int _val, Node *_left, Node *_right, Node *_next) 14 | : val(_val), left(_left), right(_right), next(_next) {} 15 | }; 16 | 17 | Node *parseNode(const cJSON *node); 18 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/133.cpp: -------------------------------------------------------------------------------- 1 | Node *parseNode(const vector> vec) 2 | { 3 | vector arr{}; 4 | int i = 0; 5 | int j = 0; 6 | for (i = 0; i < vec.size(); i++) 7 | { 8 | vector nei{}; 9 | arr.push_back(new Node(i + 1, nei)); 10 | } 11 | for (i = 0; i < vec.size(); i++) 12 | { 13 | for (j = 0; j < vec[i].size(); j++) 14 | { 15 | arr[i]->neighbors.push_back(arr[vec[i][j] - 1]); 16 | } 17 | } 18 | return arr[0]; 19 | } 20 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/133.h: -------------------------------------------------------------------------------- 1 | class Node 2 | { 3 | public: 4 | int val; 5 | vector neighbors; 6 | 7 | Node() {} 8 | 9 | Node(int _val, vector _neighbors) 10 | { 11 | val = _val; 12 | neighbors = _neighbors; 13 | } 14 | }; 15 | 16 | Node *parseNode(const vector> vec); 17 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/138.cpp: -------------------------------------------------------------------------------- 1 | Node *parseValueElement(cJSON *node) 2 | { 3 | int size = cJSON_GetArraySize(node); 4 | if (node->type != cJSON_Array && size == 2) 5 | { 6 | throw "Parse parameter error, expect NumberArray with length: 2"; 7 | } 8 | int value = parseNumber(cJSON_GetArrayItem(node, 0)); 9 | return new Node(value); 10 | } 11 | 12 | int parseRandomElement(cJSON *node) 13 | { 14 | cJSON *child = cJSON_GetArrayItem(node, 1); 15 | 16 | if (child->type != cJSON_Number) 17 | { 18 | return -1; 19 | } 20 | return child->valueint; 21 | } 22 | 23 | Node *parseNode(vector vec) 24 | { 25 | vector arr{}; 26 | int i = 0; 27 | for (i = 0; i < vec.size(); i++) 28 | { 29 | arr.push_back(parseValueElement(vec[i])); 30 | } 31 | for (i = 0; i < vec.size(); i++) 32 | { 33 | if (i != vec.size()) 34 | { 35 | arr[i]->next = arr[i + 1]; 36 | } 37 | int el = parseRandomElement(vec[i]); 38 | if (el != -1) 39 | { 40 | arr[i]->random = arr[el]; 41 | } 42 | } 43 | 44 | return arr[0]; 45 | } 46 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/138.h: -------------------------------------------------------------------------------- 1 | class Node 2 | { 3 | public: 4 | int val; 5 | Node *next; 6 | Node *random; 7 | 8 | Node(int _val) 9 | { 10 | val = _val; 11 | next = NULL; 12 | random = NULL; 13 | } 14 | }; 15 | 16 | Node *parseNode(vector vec); 17 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/278.cpp: -------------------------------------------------------------------------------- 1 | int badVersion = 0; 2 | 3 | bool isBadVersion(int version) 4 | { 5 | if (version >= badVersion) 6 | { 7 | return true; 8 | } 9 | return false; 10 | } 11 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/278.h: -------------------------------------------------------------------------------- 1 | extern int badVersion; 2 | bool isBadVersion(int version); 3 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/429.cpp: -------------------------------------------------------------------------------- 1 | Node *parseNodeElement(cJSON *node) 2 | { 3 | if (node->type != cJSON_Number) 4 | { 5 | return nullptr; 6 | } 7 | vector child{}; 8 | return new Node(node->valueint, child); 9 | } 10 | 11 | Node *parseNode(const cJSON *node) 12 | { 13 | if (node->type != cJSON_Array) 14 | { 15 | throw "Parse parameter error, expect NumberArray"; 16 | } 17 | 18 | int size = cJSON_GetArraySize(node); 19 | 20 | if (size == 0) 21 | { 22 | return nullptr; 23 | } 24 | 25 | int i = 0; 26 | int isLeft = true; 27 | Node *first = parseNodeElement(cJSON_GetArrayItem(node, i)); 28 | queue q; 29 | q.push(first); 30 | 31 | for (i = 2; i < size; i++) 32 | { 33 | Node *top = q.front(); 34 | Node *child = parseNodeElement(cJSON_GetArrayItem(node, i)); 35 | if (child == nullptr) 36 | { 37 | q.pop(); 38 | } 39 | else 40 | { 41 | top->children.push_back(child); 42 | q.push(child); 43 | } 44 | } 45 | 46 | return first; 47 | } 48 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/429.h: -------------------------------------------------------------------------------- 1 | class Node 2 | { 3 | public: 4 | int val; 5 | vector children; 6 | 7 | Node() {} 8 | 9 | Node(int _val) 10 | { 11 | val = _val; 12 | } 13 | 14 | Node(int _val, vector _children) 15 | { 16 | val = _val; 17 | children = _children; 18 | } 19 | }; 20 | 21 | Node *parseNode(const cJSON *node); 22 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/843.cpp: -------------------------------------------------------------------------------- 1 | string secret = ""; 2 | 3 | int Master::guess(string word) 4 | { 5 | int match = 0; 6 | for (int i = 0; i < word.length(); i++) 7 | { 8 | if (word[i] == secret[i]) 9 | { 10 | match += 1; 11 | } 12 | } 13 | if (match == 0) 14 | { 15 | return -1; 16 | } 17 | return match; 18 | } 19 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/843.h: -------------------------------------------------------------------------------- 1 | extern string secret; 2 | 3 | class Master 4 | { 5 | public: 6 | int guess(string word); 7 | }; 8 | -------------------------------------------------------------------------------- /resources/debug/entry/cpp/problems/common.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef COMMON_DEFINE 3 | #define COMMON_DEFINE 4 | 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | struct ListNode { 11 | int val; 12 | ListNode *next; 13 | ListNode() : val(0), next(nullptr) {} 14 | ListNode(int x) : val(x), next(nullptr) {} 15 | ListNode(int x, ListNode *next) : val(x), next(next) {} 16 | }; 17 | 18 | int parseNumber(cJSON *node); 19 | vector parseNumberArray(cJSON *node); 20 | vector> parseNumberArrayArray(cJSON *node); 21 | string parseString(cJSON *node); 22 | vector parseStringArray(cJSON *node); 23 | vector> parseStringArrayArray(cJSON *node); 24 | char parseCharacter(cJSON *node); 25 | vector parseCharacterArray(cJSON *node); 26 | vector> parseCharacterArrayArray(cJSON *node); 27 | ListNode *parseListNode(const vector &vec); 28 | vector parseListNodeArray(const vector> &vec); 29 | 30 | class NestedInteger 31 | { 32 | private: 33 | vector list{}; 34 | int value; 35 | 36 | public: 37 | NestedInteger(cJSON *node); 38 | 39 | // Return true if this NestedInteger holds a single integer, rather than a nested list. 40 | bool isInteger() const; 41 | 42 | // Return the single integer that this NestedInteger holds, if it holds a single integer 43 | // The result is undefined if this NestedInteger holds a nested list 44 | int getInteger() const; 45 | 46 | // Return the nested list that this NestedInteger holds, if it holds a nested list 47 | // The result is undefined if this NestedInteger holds a single integer 48 | const vector &getList() const; 49 | }; 50 | 51 | vector parseNestedIntegerArray(cJSON *node); 52 | 53 | class MountainArray 54 | { 55 | private: 56 | vector value{}; 57 | 58 | public: 59 | MountainArray(const vector &ve); 60 | int get(int index); 61 | int length(); 62 | }; 63 | 64 | struct TreeNode 65 | { 66 | int val; 67 | TreeNode *left; 68 | TreeNode *right; 69 | TreeNode(int x) : val(x), left(NULL), right(NULL) 70 | { 71 | } 72 | }; 73 | 74 | TreeNode *parseTreeNodeElement(cJSON *node); 75 | TreeNode *parseTreeNode(const cJSON *node); 76 | 77 | vector parsecJSONArray(const cJSON *node); 78 | 79 | // @@stub-for-problem-define-code@@ 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/1095.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== 2) { 3 | onParameterError(); 4 | } 5 | userFunm.apply(null, [parseParameter(0, 'number', params[1]), parseParameter(1, 'MountainArray', params[0])]); 6 | } 7 | 8 | 9 | function parseSpecialParameter(index, type, param) { 10 | return null; 11 | } 12 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/116.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | userFunm.apply(null, parsedParams); 11 | } 12 | 13 | function Node(val, left, right, next) { 14 | this.val = val; 15 | this.left = left; 16 | this.right = right; 17 | this.next = next; 18 | } 19 | 20 | /** 21 | * @param {number[]} param 22 | */ 23 | function parseNode(nums) { 24 | const arr = []; 25 | nums.map((n, i) => { 26 | const node = new Node(n); 27 | arr.push(node); 28 | if (i !== 0) { 29 | if (i % 2 === 1) { 30 | arr[(i - 1) / 2].left = node; 31 | } else { 32 | arr[(i - 2) / 2].right = node; 33 | } 34 | } 35 | }); 36 | return arr[0]; 37 | } 38 | 39 | function parseSpecialParameter(index, type, param) { 40 | switch (type) { 41 | case "Node": 42 | return parseNode(param); 43 | } 44 | return null; 45 | } 46 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/117.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | userFunm.apply(null, parsedParams); 11 | } 12 | 13 | function Node(val, left, right, next) { 14 | this.val = val === undefined ? null : val; 15 | this.left = left === undefined ? null : left; 16 | this.right = right === undefined ? null : right; 17 | this.next = next === undefined ? null : next; 18 | } 19 | 20 | /** 21 | * @param {number[]} param 22 | */ 23 | function parseNode(param) { 24 | if (param.length == 0) { 25 | return null; 26 | } 27 | const first = new Node(param[0]); 28 | const queue = [[first, 0]]; 29 | for (let j = 1; j < param.length; j++) { 30 | const top = queue[0]; 31 | const val = param[j] === null ? null : new Node(param[j]); 32 | if (top[1] === 0) { 33 | top[0].left = val; 34 | top[1] = 1; 35 | } else { 36 | top[0].right = val; 37 | queue.shift(); 38 | } 39 | if (val !== null) { 40 | queue.push([val, 0]); 41 | } 42 | } 43 | return first; 44 | } 45 | 46 | function parseSpecialParameter(index, type, param) { 47 | switch (type) { 48 | case "Node": 49 | return parseNode(param); 50 | } 51 | return null; 52 | } 53 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/133.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | userFunm.apply(null, parsedParams); 11 | } 12 | 13 | function Node(val, neighbors) { 14 | this.val = val; 15 | this.neighbors = neighbors; 16 | } 17 | 18 | /** 19 | * @param {number[][]} param 20 | */ 21 | function parseNode(nums) { 22 | const arr = []; 23 | nums.map((n, i) => { 24 | arr.push(new Node(i + 1, [])); 25 | }); 26 | nums.map((nei, i) => { 27 | nei.map(k => { 28 | arr[i].neighbors.push(arr[k - 1]); 29 | }); 30 | }); 31 | return arr[0]; 32 | } 33 | 34 | function parseSpecialParameter(index, type, param) { 35 | switch (type) { 36 | case "Node": 37 | return parseNode(param); 38 | } 39 | return null; 40 | } 41 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/138.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | userFunm.apply(null, parsedParams); 11 | } 12 | 13 | function Node(val, next, random) { 14 | this.val = val; 15 | this.next = next; 16 | this.random = random; 17 | } 18 | 19 | /** 20 | * @param {number[][]} param 21 | */ 22 | function parseNode(nums) { 23 | const arr = []; 24 | nums.map((n, i) => { 25 | arr.push(new Node(n[0], null, null)); 26 | }); 27 | nums.map((n, i) => { 28 | if (i !== nums.length - 1) { 29 | arr[i].next = arr[i + 1]; 30 | } 31 | if (n[1] !== null) { 32 | arr[i].random = arr[n[1]]; 33 | } 34 | }); 35 | return arr[0]; 36 | } 37 | 38 | function parseSpecialParameter(index, type, param) { 39 | switch (type) { 40 | case "Node": 41 | return parseNode(param); 42 | } 43 | return null; 44 | } 45 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/278.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== 2) { 3 | onParameterError(); 4 | } 5 | const version = params[1]; 6 | const n = params[0]; 7 | const isBadVersion = function (k) { 8 | if (k >= version) { 9 | return true; 10 | } 11 | return false; 12 | } 13 | userFunm(isBadVersion)(n); 14 | } 15 | 16 | 17 | function parseSpecialParameter(index, type, param) { 18 | return null; 19 | } 20 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/341.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | 11 | const i = new userFunm(parsedParams[0]); 12 | const a = []; 13 | while (i.hasNext()) a.push(i.next()); 14 | } 15 | 16 | function parseSpecialParameter(index, type, param) { 17 | return null; 18 | } 19 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/429.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | userFunm.apply(null, parsedParams); 11 | } 12 | 13 | function Node(val, children) { 14 | this.val = val; 15 | this.children = children; 16 | } 17 | 18 | /** 19 | * @param {number[]} param 20 | */ 21 | function parseNode(param) { 22 | if (param.length === 0) { 23 | return null; 24 | } 25 | const first = new Node(param[0], []); 26 | const queue = [first]; 27 | for (let j = 2; j < param.length; j++) { 28 | const top = queue[0]; 29 | if (param[j] === null) { 30 | queue.shift(); 31 | } else { 32 | const child = new Node(param[j], []); 33 | top.children.push(child); 34 | queue.push(child); 35 | } 36 | } 37 | return first; 38 | } 39 | 40 | function parseSpecialParameter(index, type, param) { 41 | switch (type) { 42 | case "Node": 43 | return parseNode(param); 44 | } 45 | return null; 46 | } 47 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/559.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | userFunm.apply(null, parsedParams); 11 | } 12 | 13 | function Node(val, children) { 14 | this.val = val; 15 | this.children = children; 16 | } 17 | 18 | /** 19 | * @param {number[]} param 20 | */ 21 | function parseNode(param) { 22 | if (param.length === 0) { 23 | return null; 24 | } 25 | const first = new Node(param[0], []); 26 | const queue = [first]; 27 | for (let j = 2; j < param.length; j++) { 28 | const top = queue[0]; 29 | if (param[j] === null) { 30 | queue.shift(); 31 | } else { 32 | const child = new Node(param[j], []); 33 | top.children.push(child); 34 | queue.push(child); 35 | } 36 | } 37 | return first; 38 | } 39 | 40 | function parseSpecialParameter(index, type, param) { 41 | switch (type) { 42 | case "Node": 43 | return parseNode(param); 44 | } 45 | return null; 46 | } 47 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/589.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | userFunm.apply(null, parsedParams); 11 | } 12 | 13 | function Node(val, children) { 14 | this.val = val; 15 | this.children = children; 16 | } 17 | 18 | /** 19 | * @param {number[]} param 20 | */ 21 | function parseNode(param) { 22 | if (param.length === 0) { 23 | return null; 24 | } 25 | const first = new Node(param[0], []); 26 | const queue = [first]; 27 | for (let j = 2; j < param.length; j++) { 28 | const top = queue[0]; 29 | if (param[j] === null) { 30 | queue.shift(); 31 | } else { 32 | const child = new Node(param[j], []); 33 | top.children.push(child); 34 | queue.push(child); 35 | } 36 | } 37 | return first; 38 | } 39 | 40 | function parseSpecialParameter(index, type, param) { 41 | switch (type) { 42 | case "Node": 43 | return parseNode(param); 44 | } 45 | return null; 46 | } 47 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/590.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | userFunm.apply(null, parsedParams); 11 | } 12 | 13 | function Node(val, children) { 14 | this.val = val; 15 | this.children = children; 16 | } 17 | 18 | /** 19 | * @param {number[]} param 20 | */ 21 | function parseNode(param) { 22 | if (param.length === 0) { 23 | return null; 24 | } 25 | const first = new Node(param[0], []); 26 | const queue = [first]; 27 | for (let j = 2; j < param.length; j++) { 28 | const top = queue[0]; 29 | if (param[j] === null) { 30 | queue.shift(); 31 | } else { 32 | const child = new Node(param[j], []); 33 | top.children.push(child); 34 | queue.push(child); 35 | } 36 | } 37 | return first; 38 | } 39 | 40 | function parseSpecialParameter(index, type, param) { 41 | switch (type) { 42 | case "Node": 43 | return parseNode(param); 44 | } 45 | return null; 46 | } 47 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/843.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== 3) { 3 | onParameterError(); 4 | } 5 | function Master(secret, wordlist) { 6 | this.guess = function(word) { 7 | if (!wordlist.includes(word)) { 8 | return -1; 9 | } 10 | 11 | let match = 0; 12 | for (let i = 0; i < word.length; i++) { 13 | if (word[i] === secret[i]) { 14 | match += 1; 15 | } 16 | } 17 | return match; 18 | }; 19 | } 20 | userFunm(params[1], new Master(params[0], params[1])); 21 | } 22 | 23 | function parseSpecialParameter(index, type, param) { 24 | return null; 25 | } 26 | -------------------------------------------------------------------------------- /resources/debug/entry/javascript/problems/common.js: -------------------------------------------------------------------------------- 1 | function runUserScript(userFunm, params, paramTypes) { 2 | if (params.length !== paramTypes.length) { 3 | onParameterError(); 4 | } 5 | 6 | const parsedParams = params.map((param, index) => { 7 | const type = paramTypes[index]; 8 | return parseParameter(index, type, param); 9 | }); 10 | userFunm.apply(null, parsedParams); 11 | } 12 | 13 | 14 | function parseSpecialParameter(index, type, param) { 15 | return null; 16 | } 17 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/1095.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | func(parseParameter(0, 'number', params[1]), parseParameter( 6 | 1, 'MountainArray', params[0])) 7 | 8 | 9 | def parseSpecialParameter(index, paramType, param): 10 | return None 11 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/116.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | newParams = [] 6 | for i, val in enumerate(params): 7 | newParams.append(parseParameter(i, paramTypes[i], val)) 8 | func(*newParams) 9 | 10 | 11 | class Node: 12 | def __init__(self, val, left, right, next): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | self.next = next 17 | 18 | 19 | def parseNode(param): 20 | arr = [] 21 | 22 | for i, val in enumerate(param): 23 | node = Node(val, None, None, None) 24 | arr.append(node) 25 | if i is not 0: 26 | if i % 2 is 1: 27 | arr[int((i - 1) / 2)].left = node 28 | else: 29 | arr[int((i - 2) / 2)].right = node 30 | 31 | return arr[0] 32 | 33 | 34 | def parseSpecialParameter(index, paramType, param): 35 | if paramType == "Node": 36 | return parseNode(param) 37 | return None 38 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/117.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | newParams = [] 6 | for i, val in enumerate(params): 7 | newParams.append(parseParameter(i, paramTypes[i], val)) 8 | func(*newParams) 9 | 10 | 11 | class Node: 12 | def __init__(self, val, left, right, next): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | self.next = next 17 | 18 | 19 | def parseNode(param): 20 | first = Node(param[0], None, None, None) 21 | arr = [] 22 | arr.append([first, 0]) 23 | 24 | for i, val in enumerate(param): 25 | if i is 0: 26 | continue 27 | 28 | top = arr[0] 29 | val = None if param[i] is None else Node(param[i], None, None, None) 30 | if top[1] is 0: 31 | top[0].left = val 32 | top[1] = 1 33 | else: 34 | top[0].right = val 35 | arr.pop(0) 36 | 37 | if val is not None: 38 | arr.append([val, 0]) 39 | 40 | return first 41 | 42 | 43 | def parseSpecialParameter(index, paramType, param): 44 | if paramType == "Node": 45 | return parseNode(param) 46 | return None 47 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/133.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | newParams = [] 6 | for i, val in enumerate(params): 7 | newParams.append(parseParameter(i, paramTypes[i], val)) 8 | func(*newParams) 9 | 10 | 11 | class Node: 12 | def __init__(self, val, neighbors): 13 | self.val = val 14 | self.neighbors = neighbors 15 | 16 | 17 | def parseNode(param, nodeMap): 18 | arr = [] 19 | for i, val in enumerate(param): 20 | arr.append(Node(i + 1, [])) 21 | 22 | for i, val in enumerate(param): 23 | for j, k in enumerate(val): 24 | arr[i].neighbors.append(arr[k - 1]) 25 | 26 | return arr[0] 27 | 28 | 29 | def parseSpecialParameter(index, paramType, param): 30 | if paramType == "Node": 31 | return parseNode(param, None) 32 | return None 33 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/138.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | newParams = [] 6 | for i, val in enumerate(params): 7 | newParams.append(parseParameter(i, paramTypes[i], val)) 8 | func(*newParams) 9 | 10 | 11 | class Node: 12 | def __init__(self, val, next, random): 13 | self.val = val 14 | self.next = next 15 | self.random = random 16 | 17 | 18 | def parseNode(param, nodeMap): 19 | arr = [] 20 | for i, val in enumerate(param): 21 | arr.append(Node(val[0], None, None)) 22 | 23 | for i, val in enumerate(param): 24 | if i is not len(param) - 1: 25 | arr[i].next = arr[i + 1] 26 | 27 | if val[1] is not None: 28 | arr[i].random = arr[val[1]] 29 | 30 | return arr[0] 31 | 32 | 33 | def parseSpecialParameter(index, paramType, param): 34 | if paramType == "Node": 35 | return parseNode(param, None) 36 | return None 37 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/278.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != 2): 3 | onParameterError() 4 | 5 | version = params[1] 6 | n = params[0] 7 | 8 | def isBadVersion(k): 9 | if k > version: 10 | return True 11 | return False 12 | 13 | newParams = [n] 14 | func(*newParams) 15 | 16 | 17 | def parseSpecialParameter(index, paramType, param): 18 | return None 19 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/429.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | newParams = [] 6 | for i, val in enumerate(params): 7 | newParams.append(parseParameter(i, paramTypes[i], val)) 8 | func(*newParams) 9 | 10 | 11 | class Node: 12 | def __init__(self, val, children): 13 | self.val = val 14 | self.children = children 15 | 16 | 17 | def parseNode(param): 18 | if len(param) is 0: 19 | return 20 | 21 | first = Node(param[0], []) 22 | arr = [first] 23 | 24 | for i, val in enumerate(param): 25 | if i is 0: 26 | continue 27 | 28 | if i is 1: 29 | continue 30 | 31 | top = arr[0] 32 | if val is None: 33 | arr.pop(0) 34 | else: 35 | child = Node(val, []) 36 | top.children.append(child) 37 | arr.append(child) 38 | 39 | return first 40 | 41 | 42 | def parseSpecialParameter(index, paramType, param): 43 | if paramType == "Node": 44 | return parseNode(param) 45 | return None 46 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/559.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | newParams = [] 6 | for i, val in enumerate(params): 7 | newParams.append(parseParameter(i, paramTypes[i], val)) 8 | func(*newParams) 9 | 10 | 11 | class Node: 12 | def __init__(self, val, children): 13 | self.val = val 14 | self.children = children 15 | 16 | 17 | def parseNode(param): 18 | if len(param) is 0: 19 | return 20 | 21 | first = Node(param[0], []) 22 | arr = [first] 23 | 24 | for i, val in enumerate(param): 25 | if i is 0: 26 | continue 27 | 28 | if i is 1: 29 | continue 30 | 31 | top = arr[0] 32 | if val is None: 33 | arr.pop(0) 34 | else: 35 | child = Node(val, []) 36 | top.children.append(child) 37 | arr.append(child) 38 | 39 | return first 40 | 41 | 42 | def parseSpecialParameter(index, paramType, param): 43 | if paramType == "Node": 44 | return parseNode(param) 45 | return None 46 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/589.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | newParams = [] 6 | for i, val in enumerate(params): 7 | newParams.append(parseParameter(i, paramTypes[i], val)) 8 | func(*newParams) 9 | 10 | 11 | class Node: 12 | def __init__(self, val, children): 13 | self.val = val 14 | self.children = children 15 | 16 | 17 | def parseNode(param): 18 | if len(param) is 0: 19 | return 20 | 21 | first = Node(param[0], []) 22 | arr = [first] 23 | 24 | for i, val in enumerate(param): 25 | if i is 0: 26 | continue 27 | 28 | if i is 1: 29 | continue 30 | 31 | top = arr[0] 32 | if val is None: 33 | arr.pop(0) 34 | else: 35 | child = Node(val, []) 36 | top.children.append(child) 37 | arr.append(child) 38 | 39 | return first 40 | 41 | 42 | def parseSpecialParameter(index, paramType, param): 43 | if paramType == "Node": 44 | return parseNode(param) 45 | return None 46 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/590.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | newParams = [] 6 | for i, val in enumerate(params): 7 | newParams.append(parseParameter(i, paramTypes[i], val)) 8 | func(*newParams) 9 | 10 | 11 | class Node: 12 | def __init__(self, val, children): 13 | self.val = val 14 | self.children = children 15 | 16 | 17 | def parseNode(param): 18 | if len(param) is 0: 19 | return 20 | 21 | first = Node(param[0], []) 22 | arr = [first] 23 | 24 | for i, val in enumerate(param): 25 | if i is 0: 26 | continue 27 | 28 | if i is 1: 29 | continue 30 | 31 | top = arr[0] 32 | if val is None: 33 | arr.pop(0) 34 | else: 35 | child = Node(val, []) 36 | top.children.append(child) 37 | arr.append(child) 38 | 39 | return first 40 | 41 | 42 | def parseSpecialParameter(index, paramType, param): 43 | if paramType == "Node": 44 | return parseNode(param) 45 | return None 46 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/843.py: -------------------------------------------------------------------------------- 1 | class Master: 2 | def __init__(self, secret, wordlist): 3 | self.secret = secret 4 | self.wordlist = wordlist 5 | 6 | def guess(self, word: str) -> int: 7 | match = 0 8 | if word not in self.wordlist: 9 | return -1 10 | for i, val in enumerate(word): 11 | if (val == self.secret[i]): 12 | match += 1 13 | 14 | return match 15 | 16 | 17 | def runUserScript(func, params, paramTypes): 18 | if (len(params) != len(paramTypes)): 19 | onParameterError() 20 | 21 | newParams = [] 22 | for i, val in enumerate(params): 23 | newParams.append(parseParameter(i, paramTypes[i], val)) 24 | 25 | func(newParams[1], Master(newParams[0], newParams[1])) 26 | 27 | 28 | def parseSpecialParameter(index, paramType, param): 29 | return None 30 | -------------------------------------------------------------------------------- /resources/debug/entry/python3/problems/common.py: -------------------------------------------------------------------------------- 1 | def runUserScript(func, params, paramTypes): 2 | if (len(params) != len(paramTypes)): 3 | onParameterError() 4 | 5 | newParams = [] 6 | for i, val in enumerate(params): 7 | newParams.append(parseParameter(i, paramTypes[i], val)) 8 | func(*newParams) 9 | 10 | 11 | def parseSpecialParameter(index, type, param): 12 | return None 13 | -------------------------------------------------------------------------------- /resources/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/edit_inverse.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/leetcode-extension-recentcontests.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/leetcode-extension-recentcontests.gif -------------------------------------------------------------------------------- /resources/light/dislike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/light/dislike.png -------------------------------------------------------------------------------- /resources/light/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/light/like.png -------------------------------------------------------------------------------- /resources/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/lock.png -------------------------------------------------------------------------------- /resources/mygif/quickstart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/mygif/quickstart.gif -------------------------------------------------------------------------------- /resources/mygif/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/mygif/search.gif -------------------------------------------------------------------------------- /resources/mygif/tag.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/mygif/tag.gif -------------------------------------------------------------------------------- /resources/templates/codeonly.tpl: -------------------------------------------------------------------------------- 1 | <%=comment.start%> 2 | <%=comment.line%> @lc app=<%=app%> id=<%=fid%> lang=<%=lang%> 3 | <%=comment.line%> @lcpr version=<%=LCPTCTX.version%> 4 | <%=comment.line%> 5 | <%=comment.line%> [<%=fid%>] <%=name%> 6 | <%=comment.end%> 7 | 8 | <%=comment.singleLine%> @lc code=start 9 | <%=code%> 10 | <%=comment.singleLine%> @lc code=end 11 | 12 | 13 | <% if(allCaseList && allCaseList.length > 0){ %> 14 | <%=comment.start%><% allCaseList.forEach(function(acase) { %> 15 | <%=comment.singleLine%> @lcpr case=start 16 | <%=comment.singleLine%> <% acase.forEach(function(a_caseitem) { %><%=a_caseitem%>\n<% }) %> 17 | <%=comment.singleLine%> @lcpr case=end 18 | <% }) %> 19 | <%=comment.end%> 20 | <% } %> 21 | -------------------------------------------------------------------------------- /resources/templates/detailed.tpl: -------------------------------------------------------------------------------- 1 | <%=comment.start%> 2 | <%=comment.line%> @lc app=<%=app%> id=<%=fid%> lang=<%=lang%> 3 | <%=comment.line%> @lcpr version=<%=LCPTCTX.version%> 4 | <%=comment.line%> 5 | <%=comment.line%> [<%=fid%>] <%=name%> 6 | <%=comment.line%> 7 | <%=comment.line%> <%=link%> 8 | <%=comment.line%> 9 | <%=comment.line%> <%=category%> 10 | <%=comment.line%> <%=level%> (<%=percent%>%) 11 | <%=comment.line%> Likes: <%=likes%> 12 | <%=comment.line%> Dislikes: <%=dislikes%> 13 | <%=comment.line%> Total Accepted: <%=totalAC%> 14 | <%=comment.line%> Total Submissions: <%=totalSubmit%> 15 | <%=comment.line%> Testcase Example: <%=testcase%> 16 | <%=comment.line%> 17 | <% desc.forEach(function(x) { %><%=comment.line%> <%=x%> 18 | <% }) %><%=comment.end%> 19 | 20 | <%=comment.singleLine%> @lc code=start 21 | <%=code%> 22 | <%=comment.singleLine%> @lc code=end 23 | 24 | 25 | <% if(allCaseList && allCaseList.length > 0){ %> 26 | <%=comment.start%><% allCaseList.forEach(function(acase) { %> 27 | <%=comment.singleLine%> @lcpr case=start 28 | <%=comment.singleLine%> <% acase.forEach(function(a_caseitem) { %><%=a_caseitem%>\n<% }) %> 29 | <%=comment.singleLine%> @lcpr case=end 30 | <% }) %> 31 | <%=comment.end%> 32 | <% } %> 33 | -------------------------------------------------------------------------------- /resources/x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccagml/leetcode-extension/e7f78d3b657b74e512d9fd6487fafd0f3d59f6d3/resources/x.png -------------------------------------------------------------------------------- /src/dao/groupDao.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * https://github.com/ccagml/leetcode-extension/src/dao/groupDao.ts 3 | * Path: https://github.com/ccagml/leetcode-extension 4 | * Created Date: Wednesday, November 30th 2022, 9:47:36 am 5 | * Author: ccagml 6 | * 7 | * Copyright (c) 2022 ccagml . All rights reserved. 8 | */ 9 | 10 | import { selectWorkspaceFolder } from "../utils/ConfigUtils"; 11 | import { useWsl, toWinPath, getDayNowM } from "../utils/SystemUtils"; 12 | import * as path from "path"; 13 | import * as fse from "fs-extra"; 14 | 15 | // let group_json = { 16 | // version: 1, 17 | // all_group: [{ name: "aaa", time: "qqq", qid_list: [] }, {}, {}], 18 | // }; 19 | 20 | class GroupDao { 21 | version = 1; 22 | public async group_data_path() { 23 | // const language: string | undefined = await fetchProblemLanguage(); 24 | // if (!language) { 25 | // return; 26 | // } 27 | const workspaceFolder: string = await selectWorkspaceFolder(false); 28 | if (!workspaceFolder) { 29 | return; 30 | } 31 | let lcpr_data_path: string = path.join(workspaceFolder, ".lcpr_data"); 32 | await fse.ensureDir(lcpr_data_path); 33 | 34 | let finalPath = path.join(lcpr_data_path, "group.json"); 35 | finalPath = useWsl() ? await toWinPath(finalPath) : finalPath; 36 | 37 | if (!(await fse.pathExists(finalPath))) { 38 | await fse.createFile(finalPath); 39 | await fse.writeFile(finalPath, JSON.stringify({ version: this.version })); 40 | } 41 | return finalPath; 42 | } 43 | public async init() { 44 | let lcpr_data_path = await this.group_data_path(); 45 | if (!lcpr_data_path) { 46 | return; 47 | } 48 | } 49 | 50 | private async _write_data(data: object) { 51 | let lcpr_data_path = await this.group_data_path(); 52 | if (!lcpr_data_path) { 53 | return; 54 | } 55 | return await fse.writeFile(lcpr_data_path, JSON.stringify(data)); 56 | } 57 | 58 | private async _read_data() { 59 | let lcpr_data_path = await this.group_data_path(); 60 | if (!lcpr_data_path) { 61 | return {}; 62 | } 63 | let temp_data = await fse.readFile(lcpr_data_path, "utf8"); 64 | return JSON.parse(temp_data) || {}; 65 | } 66 | 67 | // 获取所有分组 68 | public async getAllGroup() { 69 | let old_data = await this._read_data(); 70 | let all_group = old_data.all_group || []; 71 | return all_group; 72 | } 73 | 74 | // 新的分组 75 | public async newBrickGroup(name: string) { 76 | let old_data = await this._read_data(); 77 | let all_group = old_data.all_group || []; 78 | let newGroup = {}; 79 | newGroup["name"] = name; 80 | newGroup["time"] = getDayNowM(); 81 | newGroup["qid_list"] = []; 82 | all_group.push(newGroup); 83 | old_data.all_group = all_group; 84 | this._write_data(old_data); 85 | } 86 | 87 | public async removeBrickGroupByTime(time) { 88 | let old_data = await this._read_data(); 89 | let all_group = old_data.all_group || []; 90 | old_data.all_group = all_group.filter((gob) => gob.time !== time); 91 | this._write_data(old_data); 92 | } 93 | 94 | public async getQidByTime(time) { 95 | let old_data = await this._read_data(); 96 | let all_group = old_data.all_group || []; 97 | let result = []; 98 | all_group.forEach((element) => { 99 | if (element.time == time) { 100 | result = element.qid_list || []; 101 | return; 102 | } 103 | }); 104 | return result; 105 | } 106 | 107 | public async addQidToTimeList(qid, time_list) { 108 | let new_qid = qid.toString(); 109 | let time_map: Map = new Map(); 110 | time_list.forEach((element) => { 111 | time_map.set(element, 1); 112 | }); 113 | 114 | let old_data = await this._read_data(); 115 | let all_group = old_data.all_group || []; 116 | all_group.forEach((element) => { 117 | if (time_map.get(element.time)) { 118 | element.qid_list = element.qid_list.filter((eqid) => eqid !== new_qid); 119 | element.qid_list.push(new_qid); 120 | } 121 | }); 122 | old_data.all_group = all_group; 123 | this._write_data(old_data); 124 | } 125 | 126 | public async removeQidFromTime(qid, time) { 127 | let new_qid = qid.toString(); 128 | let old_data = await this._read_data(); 129 | let all_group = old_data.all_group || []; 130 | all_group.forEach((element) => { 131 | if (element.time == time) { 132 | element.qid_list = element.qid_list.filter((eqid) => eqid !== new_qid); 133 | } 134 | }); 135 | old_data.all_group = all_group; 136 | this._write_data(old_data); 137 | } 138 | 139 | public async getPickOneTags() { 140 | let old_data = await this._read_data(); 141 | let pick_one_tags = old_data.pick_one_tags || []; 142 | return pick_one_tags; 143 | } 144 | public async setPickOneTags(new_pick_one_tags) { 145 | let old_data = await this._read_data(); 146 | old_data.pick_one_tags = new_pick_one_tags; 147 | this._write_data(old_data); 148 | } 149 | } 150 | 151 | export const groupDao: GroupDao = new GroupDao(); 152 | -------------------------------------------------------------------------------- /src/dao/remarkDao.ts: -------------------------------------------------------------------------------- 1 | // > workspace/ 工作目录 2 | // > 3 | // > > .lcpr_data/ 存数据 4 | // > > 5 | // > > > remake/ 备注 6 | // > > > 7 | // > > > > 题目内部编号.json 根据 qid 备注的信息 8 | // > > 9 | 10 | import { selectWorkspaceFolder } from "../utils/ConfigUtils"; 11 | import { useWsl, toWinPath } from "../utils/SystemUtils"; 12 | import * as path from "path"; 13 | import * as fse from "fs-extra"; 14 | 15 | class RemarkDao { 16 | version = 1; 17 | public async get_remark_dir_path() { 18 | // const language: string | undefined = await fetchProblemLanguage(); 19 | // if (!language) { 20 | // return; 21 | // } 22 | const workspaceFolder: string = await selectWorkspaceFolder(false); 23 | if (!workspaceFolder) { 24 | return; 25 | } 26 | let lcpr_data_path: string = path.join(workspaceFolder, ".lcpr_data"); 27 | await fse.ensureDir(lcpr_data_path); 28 | 29 | let remark_dir: string = path.join(lcpr_data_path, "remark"); 30 | await fse.ensureDir(remark_dir); 31 | 32 | remark_dir = useWsl() ? await toWinPath(remark_dir) : remark_dir; 33 | return remark_dir; 34 | } 35 | public async init() { 36 | let lcpr_data_path = await this.get_remark_dir_path(); 37 | if (!lcpr_data_path) { 38 | return; 39 | } 40 | } 41 | 42 | private async getQidPath(qid: string) { 43 | let remark_dir = await this.get_remark_dir_path(); 44 | if (!remark_dir) { 45 | return; 46 | } 47 | if (!qid) { 48 | return; 49 | } 50 | let qid_path: string = path.join(remark_dir, qid); 51 | qid_path = useWsl() ? await toWinPath(qid_path) : qid_path; 52 | return qid_path; 53 | } 54 | 55 | private async _write_data(qid: string, data: object) { 56 | let qid_data_path = await this.getQidPath(qid); 57 | if (!qid_data_path) { 58 | return; 59 | } 60 | return await fse.writeFile(qid_data_path, JSON.stringify(data)); 61 | } 62 | 63 | private async _read_data(qid: string) { 64 | let qid_data_path = await this.getQidPath(qid); 65 | if (!qid_data_path) { 66 | return; 67 | } 68 | 69 | if (!(await fse.pathExists(qid_data_path))) { 70 | return {}; 71 | } 72 | let temp_data = await fse.readFile(qid_data_path, "utf8"); 73 | return JSON.parse(temp_data) || {}; 74 | } 75 | 76 | public async getInfoByQid(qid: string) { 77 | let all_remark = await this._read_data(qid); 78 | return all_remark || {}; 79 | } 80 | public async setInfoByQid(qid: string, info) { 81 | await this._write_data(qid, info); 82 | } 83 | } 84 | 85 | export const remarkDao: RemarkDao = new RemarkDao(); 86 | -------------------------------------------------------------------------------- /src/debug/DoJs.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as fse from "fs-extra"; 3 | import { fileMeta, getEntryFile, IProblemType } from "../utils/problemUtils"; 4 | 5 | import { ShowMessage } from "../utils/OutputUtils"; 6 | import { OutPutType } from "../model/ConstDefind"; 7 | import { debugArgDao } from "../dao/debugArgDao"; 8 | 9 | export class DebugJs { 10 | static DEBUG_LANG = "javascript"; 11 | private getProblemFunName(language: string, problemType: IProblemType): string { 12 | if (problemType.specialFunName && problemType.specialFunName[language]) { 13 | return problemType.specialFunName[language]; 14 | } 15 | return problemType.funName; 16 | } 17 | 18 | public async execute( 19 | document: vscode.TextDocument, 20 | filePath: string, 21 | testString: string, 22 | language: string, 23 | port: number 24 | ): Promise { 25 | if (language != DebugJs.DEBUG_LANG) { 26 | return; 27 | } 28 | 29 | let debugConfig = { 30 | type: "node", 31 | program: "", 32 | }; 33 | const fileContent: Buffer = await fse.readFile(filePath); 34 | const meta: { id: string; lang: string } | null = fileMeta(fileContent.toString()); 35 | if (meta == null) { 36 | ShowMessage( 37 | "File meta info has been changed, please check the content: '@lc app=leetcode.cn id=xx lang=xx'.", 38 | OutPutType.error 39 | ); 40 | return; 41 | } 42 | const problemType: IProblemType | undefined = debugArgDao.getDebugArgData(meta.id, document); 43 | if (problemType == undefined) { 44 | ShowMessage(`Notsupported problem: ${meta.id}.`, OutPutType.error); 45 | return; 46 | } 47 | 48 | debugConfig.program = await getEntryFile(meta.lang, meta.id); 49 | 50 | const funName: string = this.getProblemFunName(language, problemType); 51 | 52 | // check whether module.exports is exist or not 53 | const moduleExportsReg: RegExp = new RegExp(`module.exports = ${problemType.funName};`); 54 | if (!moduleExportsReg.test(fileContent.toString())) { 55 | fse.writeFile( 56 | filePath, 57 | fileContent.toString() + `\n// @lcpr-after-debug-begin\nmodule.exports = ${funName};\n// @lcpr-after-debug-end` 58 | ); 59 | } 60 | 61 | const args: string[] = [ 62 | filePath, 63 | testString, 64 | problemType.funName, 65 | problemType.paramTypes.join(","), 66 | problemType.returnType || "returnType", 67 | meta.id, 68 | port.toString(), 69 | ]; 70 | if (vscode.debug.activeDebugSession) { 71 | return; 72 | } 73 | vscode.debug.startDebugging( 74 | undefined, 75 | Object.assign({}, debugConfig, { 76 | request: "launch", 77 | name: "Launch Program", 78 | args, 79 | }) 80 | ); 81 | return; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/debug/DoPy3.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as fse from "fs-extra"; 3 | 4 | import { fileMeta, getEntryFile, IProblemType } from "../utils/problemUtils"; 5 | 6 | import { ShowMessage } from "../utils/OutputUtils"; 7 | import { OutPutType } from "../model/ConstDefind"; 8 | import { debugArgDao } from "../dao/debugArgDao"; 9 | 10 | export class DebugPy3 { 11 | static DEBUG_LANG = "python3"; 12 | 13 | public async execute( 14 | document: vscode.TextDocument, 15 | filePath: string, 16 | testString: string, 17 | language: string, 18 | port: number 19 | ): Promise { 20 | if (language != DebugPy3.DEBUG_LANG) { 21 | return; 22 | } 23 | let debugConfig = { 24 | type: "python", 25 | env: { 26 | PYTHONPATH: "", 27 | }, 28 | program: "", 29 | }; 30 | const fileContent: Buffer = await fse.readFile(filePath); 31 | const meta: { id: string; lang: string } | null = fileMeta(fileContent.toString()); 32 | if (meta == null) { 33 | ShowMessage( 34 | "File meta info has been changed, please check the content: '@lc app=leetcode.cn id=xx lang=xx'.", 35 | OutPutType.error 36 | ); 37 | return; 38 | } 39 | const problemType: IProblemType | undefined = debugArgDao.getDebugArgData(meta.id, document); 40 | if (problemType == undefined) { 41 | ShowMessage(`Notsupported problem: ${meta.id}.`, OutPutType.error); 42 | return; 43 | } 44 | 45 | const numericStr = meta.id.replace(/\D/g, ""); // 去除非数字字符 46 | const temp_meta_id = parseInt(numericStr, 10); 47 | 48 | debugConfig.program = await getEntryFile(meta.lang, temp_meta_id.toString()); 49 | 50 | // check whether module.exports is exist or not 51 | const moduleExportsReg: RegExp = /# @lcpr-before-debug-begin/; 52 | if (!moduleExportsReg.test(fileContent.toString())) { 53 | await fse.writeFile( 54 | filePath, 55 | `# @lcpr-before-debug-begin\nfrom python3problem${temp_meta_id.toString()} import * # type: ignore \nfrom typing import *\n# @lcpr-before-debug-end\n\n` + 56 | fileContent.toString() 57 | ); 58 | } 59 | debugConfig.env!.PYTHONPATH = debugConfig.program; 60 | const args: string[] = [ 61 | filePath, 62 | testString, 63 | problemType.funName, 64 | problemType.paramTypes.join(","), 65 | problemType.returnType || "returnType", 66 | temp_meta_id.toString(), 67 | port.toString(), 68 | ]; 69 | if (vscode.debug.activeDebugSession) { 70 | return; 71 | } 72 | vscode.debug.startDebugging( 73 | undefined, 74 | Object.assign({}, debugConfig, { 75 | request: "launch", 76 | name: "Launch Program", 77 | args, 78 | }) 79 | ); 80 | 81 | return; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/logOutput/logOutputModule.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Filename: https://github.com/ccagml/leetcode-extension/src/logOutput/logOutputModule.ts 3 | * Path: https://github.com/ccagml/leetcode-extension 4 | * Created Date: Saturday, October 14th 2023, 1:57:10 pm 5 | * Author: ccagml 6 | * 7 | * Copyright (c) 2023 ccagml . All rights reserved 8 | */ 9 | 10 | import * as vscode from "vscode"; 11 | import { BABAMediator, BABAProxy, BabaStr, BaseCC } from "../BABA"; 12 | 13 | class LogOutput implements vscode.Disposable { 14 | private readonly channel: vscode.OutputChannel = vscode.window.createOutputChannel("LeetCodeProblemRating"); 15 | 16 | private LCPTCTX = {}; 17 | public appendLine(message: string): void { 18 | this.channel.appendLine(message); 19 | } 20 | 21 | public append(message: string): void { 22 | this.channel.append(message); 23 | } 24 | 25 | public show(): void { 26 | this.channel.show(); 27 | } 28 | 29 | public dispose(): void { 30 | this.channel.dispose(); 31 | } 32 | 33 | public setLCPTCTX(k, v) { 34 | this.LCPTCTX[k] = v; 35 | } 36 | 37 | public getLCPTCTX(k) { 38 | return this.LCPTCTX[k]; 39 | } 40 | 41 | public getLCPTCTXAll() { 42 | return this.LCPTCTX; 43 | } 44 | } 45 | 46 | export const logOutput: LogOutput = new LogOutput(); 47 | 48 | export class LogOutputProxy extends BABAProxy { 49 | static NAME = BabaStr.LogOutputProxy; 50 | constructor() { 51 | super(LogOutputProxy.NAME); 52 | } 53 | 54 | /** 55 | * get_log 56 | */ 57 | public get_log() { 58 | return logOutput; 59 | } 60 | } 61 | 62 | export class LogOutputMediator extends BABAMediator { 63 | static NAME = BabaStr.LogOutputMediator; 64 | constructor() { 65 | super(LogOutputMediator.NAME); 66 | } 67 | 68 | listNotificationInterests(): string[] { 69 | return [BabaStr.VSCODE_DISPOST, BabaStr.InitFile]; 70 | } 71 | async handleNotification(_notification: BaseCC.BaseCC.INotification) { 72 | switch (_notification.getName()) { 73 | case BabaStr.VSCODE_DISPOST: 74 | logOutput.dispose(); 75 | break; 76 | case BabaStr.InitFile: 77 | let context = _notification.getBody(); 78 | let cur_version: string = context.extension.packageJSON.version || "1.0.0"; 79 | let cur_version_arr: Array = cur_version.split("."); 80 | let cur_version_num = 0; 81 | cur_version_arr.forEach((e) => { 82 | cur_version_num *= 100; 83 | cur_version_num += Number(e); 84 | }); 85 | logOutput.setLCPTCTX("version", cur_version_num); 86 | break; 87 | default: 88 | break; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/questionData/QuestionDataModule.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Filename: /home/cc/leetcode-extension/src/questionData/questionDataModule.ts 3 | * Path: /home/cc/leetcode-extension 4 | * Created Date: Monday, October 16th 2023, 10:00:51 am 5 | * Author: ccagml 6 | * 7 | * Copyright (c) 2023 ccagml . All rights reserved 8 | */ 9 | 10 | import { BABA, BABAMediator, BABAProxy, BabaStr, BaseCC } from "../BABA"; 11 | import { ISubmitEvent, OutPutType, ProblemState, UserStatus } from "../model/ConstDefind"; 12 | import { CreateTreeNodeModel, IQuestionData, TreeNodeModel, TreeNodeType } from "../model/TreeNodeModel"; 13 | 14 | import { isShowLocked, isUseEndpointTranslation } from "../utils/ConfigUtils"; 15 | import { ShowMessage } from "../utils/OutputUtils"; 16 | 17 | class QuestionData { 18 | private fidMapQuestionData: Map = new Map(); 19 | private fidToQid: Map = new Map(); 20 | private qidToFid: Map = new Map(); 21 | private companySet: Set = new Set(); 22 | private tagSet: Set = new Set(); 23 | 24 | public clearCache(): void { 25 | this.fidMapQuestionData.clear(); 26 | this.companySet.clear(); 27 | this.tagSet.clear(); 28 | this.fidToQid.clear(); 29 | this.qidToFid.clear(); 30 | } 31 | 32 | public async ReBuildQuestionData() { 33 | let all_data = await BABA.getProxy(BabaStr.QuestionDataProxy).getAllQuestionData(); 34 | for (const problem of all_data) { 35 | let TreeNodeObj = CreateTreeNodeModel(problem, TreeNodeType.TreeQuestionData); 36 | this.fidMapQuestionData.set(TreeNodeObj.id, TreeNodeObj); 37 | this.fidToQid.set(TreeNodeObj.id, TreeNodeObj.qid.toString()); 38 | this.qidToFid.set(TreeNodeObj.qid.toString(), TreeNodeObj.id); 39 | 40 | for (const company of TreeNodeObj.companies) { 41 | this.companySet.add(company); 42 | } 43 | for (const tag of TreeNodeObj.tags) { 44 | this.tagSet.add(tag); 45 | } 46 | } 47 | 48 | BABA.sendNotification(BabaStr.QuestionData_ReBuildQuestionDataFinish); 49 | } 50 | public getfidMapQuestionData(): Map { 51 | return this.fidMapQuestionData; 52 | } 53 | 54 | public getCompanySet() { 55 | return this.companySet; 56 | } 57 | public getTagSet() { 58 | return this.tagSet; 59 | } 60 | public getFidToQid() { 61 | return this.fidToQid; 62 | } 63 | public getQidToFid() { 64 | return this.qidToFid; 65 | } 66 | public checkSubmit(e: ISubmitEvent) { 67 | if (e.sub_type == "submit" && e.accepted) { 68 | if (this.fidMapQuestionData.get(e.fid)?.state != ProblemState.AC) { 69 | BABA.sendNotification(BabaStr.QuestionData_submitNewAccept); 70 | } 71 | } 72 | } 73 | } 74 | 75 | const questionData: QuestionData = new QuestionData(); 76 | 77 | export class QuestionDataProxy extends BABAProxy { 78 | static NAME = BabaStr.QuestionDataProxy; 79 | constructor() { 80 | super(QuestionDataProxy.NAME); 81 | } 82 | 83 | public getfidMapQuestionData(): Map { 84 | return questionData.getfidMapQuestionData(); 85 | } 86 | 87 | public getCompanySet() { 88 | return questionData.getCompanySet(); 89 | } 90 | public getTagSet() { 91 | return questionData.getTagSet(); 92 | } 93 | 94 | public getNodeById(id: string): TreeNodeModel | undefined { 95 | let map = this.getfidMapQuestionData() as Map; 96 | if (map.has(id)) { 97 | return map.get(id); 98 | } else if (map.has(parseInt(id))) { 99 | return map.get(parseInt(id)); 100 | } else { 101 | return undefined; 102 | } 103 | } 104 | 105 | public getNodeByQid(qid: string): TreeNodeModel | undefined { 106 | let new_qid = qid.toString(); 107 | return this.getNodeById(questionData.getQidToFid().get(new_qid) || ""); 108 | } 109 | 110 | public getQidByFid(id: string) { 111 | return questionData.getFidToQid().get(id); 112 | } 113 | 114 | public async getAllQuestionData(): Promise { 115 | try { 116 | let sbp = BABA.getProxy(BabaStr.StatusBarProxy); 117 | if (sbp.getStatus() === UserStatus.SignedOut) { 118 | return []; 119 | } 120 | 121 | const showLockedFlag: boolean = isShowLocked(); 122 | const useEndpointTranslation: boolean = isUseEndpointTranslation(); 123 | const result: string = await BABA.getProxy(BabaStr.ChildCallProxy) 124 | .get_instance() 125 | .getAllProblems(showLockedFlag, useEndpointTranslation); 126 | let all_problem_info = JSON.parse(result); 127 | if (!showLockedFlag) { 128 | all_problem_info = all_problem_info.filter((p) => !p.locked); 129 | } 130 | const problems: IQuestionData[] = []; 131 | // const AllScoreData = BABA.getProxy(BabaStr.TreeDataProxy).getScoreData(); 132 | // // 增加直接在线获取分数数据 133 | // const AllScoreDataOnline = await BABA.getProxy(BabaStr.TreeDataProxy).getScoreDataOnline(); 134 | for (const p of all_problem_info) { 135 | problems.push({ 136 | id: p.fid, 137 | qid: p.id, 138 | isFavorite: p.starred, 139 | locked: p.locked, 140 | state: this.parseProblemState(p.state), 141 | name: p.name, 142 | cn_name: p.cn_name, 143 | en_name: p.en_name, 144 | difficulty: p.level, 145 | passRate: p.percent, 146 | companies: p.companies || [], 147 | }); 148 | } 149 | return problems.reverse(); 150 | } catch (error) { 151 | await ShowMessage("获取题目失败. 请查看控制台信息~", OutPutType.error); 152 | return []; 153 | } 154 | } 155 | public parseProblemState(stateOutput: string): ProblemState { 156 | if (!stateOutput) { 157 | return ProblemState.Unknown; 158 | } 159 | switch (stateOutput.trim()) { 160 | case "v": 161 | case "✔": 162 | case "√": 163 | case "ac": 164 | return ProblemState.AC; 165 | case "X": 166 | case "✘": 167 | case "×": 168 | case "notac": 169 | return ProblemState.NotAC; 170 | default: 171 | return ProblemState.Unknown; 172 | } 173 | } 174 | } 175 | 176 | export class QuestionDataMediator extends BABAMediator { 177 | static NAME = BabaStr.QuestionDataMediator; 178 | constructor() { 179 | super(QuestionDataMediator.NAME); 180 | } 181 | 182 | listNotificationInterests(): string[] { 183 | return [ 184 | BabaStr.VSCODE_DISPOST, 185 | BabaStr.QuestionData_clearCache, 186 | BabaStr.QuestionData_ReBuildQuestionData, 187 | BabaStr.CommitResult_showFinish, 188 | BabaStr.StartReadData, 189 | ]; 190 | } 191 | async handleNotification(_notification: BaseCC.BaseCC.INotification) { 192 | switch (_notification.getName()) { 193 | case BabaStr.VSCODE_DISPOST: 194 | break; 195 | case BabaStr.QuestionData_clearCache: 196 | questionData.clearCache(); 197 | break; 198 | case BabaStr.QuestionData_ReBuildQuestionData: 199 | await questionData.ReBuildQuestionData(); 200 | break; 201 | case BabaStr.CommitResult_showFinish: 202 | questionData.checkSubmit(_notification.getBody()); 203 | break; 204 | case BabaStr.StartReadData: 205 | questionData.clearCache(); 206 | await questionData.ReBuildQuestionData(); 207 | break; 208 | default: 209 | break; 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/rankScore/RankScoreDataModule.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Filename: /home/cc/leetcode-extension/src/rankScore/RankScoreDataModule.ts 3 | * Path: /home/cc/leetcode-extension 4 | * Created Date: Monday, October 23rd 2023, 8:41:31 pm 5 | * Author: ccagml 6 | * 7 | * Copyright (c) 2023 ccagml . All rights reserved 8 | */ 9 | 10 | import { BABAMediator, BABAProxy, BabaStr, BaseCC, BABA } from "../BABA"; 11 | import { IScoreData, OutPutType } from "../model/ConstDefind"; 12 | import { ShowMessage } from "../utils/OutputUtils"; 13 | 14 | class RankScoreData { 15 | private scoreBase = require("../../../resources/data.json"); 16 | public scoreData; 17 | public nameSiteMapping = new Map(); 18 | constructor() { 19 | let scoreData = this.scoreBase as IScoreData[]; 20 | 21 | scoreData.forEach((element) => { 22 | element.score = "" + Math.floor(element.Rating || 0); 23 | this.nameSiteMapping.set("" + element.ID, element); 24 | }); 25 | } 26 | public async readData() { 27 | // 增加直接在线获取分数数据 28 | const AllScoreDataOnline = await this.getScoreDataOnline(); 29 | if (AllScoreDataOnline != undefined) { 30 | AllScoreDataOnline.forEach((element) => { 31 | element.score = "" + Math.floor(element.Rating || 0); 32 | this.nameSiteMapping.set("" + element.ID, element); 33 | }); 34 | } 35 | } 36 | 37 | // 在线获取题目数据 38 | public async getScoreDataOnline() { 39 | let stringData = await BABA.getProxy(BabaStr.ChildCallProxy).get_instance().getScoreDataOnline(); 40 | let objData; 41 | try { 42 | objData = JSON.parse(stringData); 43 | } catch (error) { 44 | objData = {}; 45 | } 46 | if (objData.code == 101) { 47 | ShowMessage( 48 | `从 https://zerotrac.github.io/leetcode_problem_rating/data.json 获取数据出错 ${stringData}`, 49 | OutPutType.info 50 | ); 51 | objData = {}; 52 | } else if (objData.code == 102) { 53 | objData = {}; 54 | // 请求超时 不处理 55 | } 56 | return objData.data; 57 | } 58 | } 59 | 60 | const rankScoreData: RankScoreData = new RankScoreData(); 61 | 62 | export class RankScoreDataProxy extends BABAProxy { 63 | static NAME = BabaStr.RankScoreDataProxy; 64 | constructor() { 65 | super(RankScoreDataProxy.NAME); 66 | } 67 | 68 | public getDataByFid(fid: string): IScoreData | undefined { 69 | return rankScoreData.nameSiteMapping.get(fid); 70 | } 71 | } 72 | 73 | export class RankScoreDataMediator extends BABAMediator { 74 | static NAME = BabaStr.RankScoreDataMediator; 75 | constructor() { 76 | super(RankScoreDataMediator.NAME); 77 | } 78 | 79 | listNotificationInterests(): string[] { 80 | return [BabaStr.VSCODE_DISPOST, BabaStr.StartReadData]; 81 | } 82 | async handleNotification(_notification: BaseCC.BaseCC.INotification) { 83 | switch (_notification.getName()) { 84 | case BabaStr.VSCODE_DISPOST: 85 | break; 86 | case BabaStr.StartReadData: 87 | await rankScoreData.readData(); 88 | break; 89 | default: 90 | break; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/recentContestData/ContestQuestionDataModule.ts: -------------------------------------------------------------------------------- 1 | import { BABAMediator, BABAProxy, BabaStr, BaseCC, BABA } from "../BABA"; 2 | import { OutPutType } from "../model/ConstDefind"; 3 | import { promptForSignIn, ShowMessage } from "../utils/OutputUtils"; 4 | 5 | class ContestQuestionData { 6 | fidInfo: Map = new Map(); 7 | 8 | setFidInfo(fid, data) { 9 | this.fidInfo.set(fid, data); 10 | } 11 | 12 | getFidInfo(fid) { 13 | return this.fidInfo.get(fid); 14 | } 15 | } 16 | 17 | const contestQuestionData: ContestQuestionData = new ContestQuestionData(); 18 | 19 | export class ContestQuestionProxy extends BABAProxy { 20 | static NAME = BabaStr.ContestQuestionProxy; 21 | constructor() { 22 | super(ContestQuestionProxy.NAME); 23 | } 24 | 25 | public getAllRecentContestQuestionData() { 26 | return contestQuestionData.fidInfo; 27 | } 28 | 29 | public getContestQuestionData(contestName) { 30 | return contestQuestionData.getFidInfo(contestName); 31 | } 32 | 33 | public async searchContestQuestionData(contestName): Promise { 34 | let sbp = BABA.getProxy(BabaStr.StatusBarProxy); 35 | if (!sbp.getUser()) { 36 | promptForSignIn(); 37 | return; 38 | } 39 | try { 40 | const solution: string = await BABA.getProxy(BabaStr.ChildCallProxy) 41 | .get_instance() 42 | .getContestQuestion(contestName); 43 | const query_result = JSON.parse(solution); 44 | 45 | for (let i = 0; i < query_result.length; i++) { 46 | let data = query_result[i].questions.map((item) => item.question_id); 47 | contestQuestionData.setFidInfo(query_result[i].contest, data); 48 | } 49 | BABA.sendNotification(BabaStr.TreeData_searchContestQuestionFinish); 50 | } catch (error) { 51 | BABA.getProxy(BabaStr.LogOutputProxy).get_log().appendLine(error.toString()); 52 | await ShowMessage("Failed to fetch question of" + contestName + ". 请查看控制台信息~", OutPutType.error); 53 | } 54 | } 55 | } 56 | 57 | export class ContestQuestionMediator extends BABAMediator { 58 | static NAME = BabaStr.ContestQuestionMediator; 59 | constructor() { 60 | super(ContestQuestionMediator.NAME); 61 | } 62 | 63 | listNotificationInterests(): string[] { 64 | return [ 65 | BabaStr.TreeData_searchRecentContestFinish, 66 | ]; 67 | } 68 | async handleNotification(_notification: BaseCC.BaseCC.INotification) { 69 | switch (_notification.getName()) { 70 | case BabaStr.TreeData_searchRecentContestFinish: 71 | // let ContestList = BABA.getProxy(BabaStr.RecentContestProxy).getAllRecentContestData(); 72 | // let contestName = Array.from(ContestList.keys()).join(","); 73 | // await BABA.getProxy(BabaStr.ContestQuestionProxy).searchContestQuestionData(contestName); 74 | break; 75 | default: 76 | break; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/recentContestData/RecentContestDataModule.ts: -------------------------------------------------------------------------------- 1 | import { BABAMediator, BABAProxy, BabaStr, BaseCC, BABA } from "../BABA"; 2 | import { OutPutType } from "../model/ConstDefind"; 3 | import { IContestData, ITreeDataNormal } from "../model/TreeNodeModel"; 4 | import { isUseEndpointTranslation } from "../utils/ConfigUtils"; 5 | import { promptForSignIn, ShowMessage } from "../utils/OutputUtils"; 6 | 7 | class RecentContestData { 8 | slugInfo: Map = new Map(); 9 | 10 | setSlugInfo(data) { 11 | this.slugInfo.set(data.titleSlug, data); 12 | } 13 | 14 | getSlugInfo(slug) { 15 | return this.slugInfo.get(slug); 16 | } 17 | } 18 | 19 | const recentContestData: RecentContestData = new RecentContestData(); 20 | 21 | export class RecentContestProxy extends BABAProxy { 22 | static NAME = BabaStr.RecentContestProxy; 23 | constructor() { 24 | super(RecentContestProxy.NAME); 25 | } 26 | 27 | public getAllRecentContestData() { 28 | return recentContestData.slugInfo; 29 | } 30 | 31 | public getRecentContestData(slug) { 32 | return recentContestData.getSlugInfo(slug); 33 | } 34 | 35 | public getAllContestTreeNode(): ITreeDataNormal[] { 36 | let contestData: ITreeDataNormal[] = []; 37 | this.getAllRecentContestData().forEach((value) => { 38 | let data: ITreeDataNormal = { 39 | id: value.titleSlug, 40 | name: value.title, 41 | rootNodeSortId: value.startTime, 42 | }; 43 | contestData.push(data); 44 | }); 45 | return contestData; 46 | } 47 | 48 | public async searchRecentContest(): Promise { 49 | let sbp = BABA.getProxy(BabaStr.StatusBarProxy); 50 | if (!sbp.getUser()) { 51 | promptForSignIn(); 52 | return; 53 | } 54 | try { 55 | const needTranslation: boolean = isUseEndpointTranslation(); 56 | const solution: string = await BABA.getProxy(BabaStr.ChildCallProxy) 57 | .get_instance() 58 | .getRecentContest(needTranslation); 59 | const query_result = JSON.parse(solution); 60 | // "{\"titleSlug\":\"number-of-dice-rolls-with-target-sum\",\"questionId\":\"1263\",\"fid\":\"1155\",\"userStatus\":\"NOT_START\"}\n" 61 | 62 | // const titleSlug: string = query_result.titleSlug 63 | // const questionId: string = query_result.questionId 64 | const contestLen = query_result.contests.length; 65 | for (let i = 0; i < contestLen; i++) { 66 | const contest = query_result.contests[i]; 67 | if (this.getRecentContestData(contest.titleSlug)) { 68 | continue; 69 | } 70 | let data: any = {}; 71 | data.index = i; 72 | data.titleSlug = contest.titleSlug; 73 | data.title = contest.title; 74 | data.startTime = contest.startTime; 75 | data.duration = contest.duration; 76 | recentContestData.setSlugInfo(data); 77 | } 78 | if (contestLen > 0) { 79 | BABA.sendNotification(BabaStr.TreeData_searchRecentContestFinish); 80 | } 81 | } catch (error) { 82 | BABA.getProxy(BabaStr.LogOutputProxy).get_log().appendLine(error.toString()); 83 | await ShowMessage("Failed to fetch recent contest list. 请查看控制台信息~", OutPutType.error); 84 | } 85 | } 86 | 87 | } 88 | 89 | export class RecentContestMediator extends BABAMediator { 90 | static NAME = BabaStr.RecentContestMediator; 91 | constructor() { 92 | super(RecentContestMediator.NAME); 93 | } 94 | 95 | listNotificationInterests(): string[] { 96 | return [ 97 | BabaStr.VSCODE_DISPOST, 98 | BabaStr.StartReadData, 99 | BabaStr.BABACMD_refresh, 100 | // BabaStr.every_minute, 101 | ]; 102 | } 103 | async handleNotification(_notification: BaseCC.BaseCC.INotification) { 104 | switch (_notification.getName()) { 105 | case BabaStr.VSCODE_DISPOST: 106 | break; 107 | case BabaStr.StartReadData: 108 | // await BABA.getProxy(BabaStr.RecentContestProxy).searchRecentContest(); 109 | break; 110 | case BabaStr.BABACMD_refresh: 111 | // BABA.getProxy(BabaStr.RecentContestProxy).searchRecentContest(); 112 | break; 113 | // case BabaStr.every_minute: 114 | // await todayData.checkNeedReadNew(); 115 | // break; 116 | default: 117 | break; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/rpc/actionChain/chainManager.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * https://github.com/ccagml/leetcode-extension/src/rpc/actionChain/chainMgr.ts 3 | * Path: https://github.com/ccagml/leetcode-extension 4 | * Created Date: Monday, November 14th 2022, 4:04:31 pm 5 | * Author: ccagml 6 | * 7 | * Copyright (c) 2022 ccagml . All rights reserved. 8 | */ 9 | 10 | let underscore = require("underscore"); 11 | 12 | import { storageUtils } from "../utils/storageUtils"; 13 | import { commUtils } from "../utils/commUtils"; 14 | import { ChainNodeBase } from "./chainNodeBase"; 15 | 16 | /* It's a class that manages a chain of plugins */ 17 | export class ChainManager { 18 | id; 19 | name; 20 | ver; 21 | desc; 22 | 23 | plugins: Array = []; 24 | installed: Array = []; 25 | head: ChainNodeBase; // 插件头 是core 26 | 27 | constructor() {} 28 | 29 | /** 30 | * Return the head of the chain. 31 | * @returns The head of the chain. 32 | */ 33 | public getChainHead(): ChainNodeBase { 34 | return this.head; 35 | } 36 | 37 | /** 38 | * It loads all the plugins in the directory and initializes them. 39 | * @param {ChainNodeBase | undefined} head - The first node in the chain of responsibility. 40 | * @returns The return value is a boolean. 41 | */ 42 | public init(head: ChainNodeBase | undefined): Object | undefined { 43 | if (head) { 44 | this.head = head; 45 | } 46 | const stats = storageUtils.getCache(commUtils.KEYS.plugins) || {}; 47 | let fileChainNode: Array = storageUtils.listCodeDir("../actionChain/chainNode"); 48 | this.installed = []; 49 | for (let f of fileChainNode) { 50 | const p = f.data; 51 | if (!p) continue; 52 | p.file = f.file; 53 | p.enabled = stats[p.name]; 54 | if (!(p.name in stats)) { 55 | if (p.builtin) { 56 | p.enabled = true; 57 | } else { 58 | p.enabled = false; 59 | } 60 | } 61 | this.installed.push(p); 62 | } 63 | // 根据id大小排序, 大的前面 64 | this.installed = underscore.sortBy(this.installed, (x) => -x.id); 65 | // 从小的开始init 66 | for (let i = this.installed.length - 1; i >= 0; --i) { 67 | const p = this.installed[i]; 68 | if (p.enabled) { 69 | p.init(); 70 | } 71 | } 72 | // 连成链表状 73 | this.plugins = this.installed.filter((x) => x.enabled); 74 | let last = head; 75 | if (last) { 76 | for (let p of this.plugins) { 77 | last.setNext(p); 78 | last = p; 79 | } 80 | } 81 | 82 | return true; 83 | } 84 | 85 | /** 86 | * For each plugin in the plugins array, call the save function. 87 | */ 88 | public save_all(): void { 89 | for (let p of this.plugins) { 90 | p.save(); 91 | } 92 | } 93 | } 94 | 95 | export const chainMgr: ChainManager = new ChainManager(); 96 | -------------------------------------------------------------------------------- /src/rpc/actionChain/chainNode/cache.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * https://github.com/ccagml/leetcode-extension/src/rpc/actionChain/cache.ts 3 | * Path: https://github.com/ccagml/leetcode-extension 4 | * Created Date: Monday, November 14th 2022, 4:04:31 pm 5 | * Author: ccagml 6 | * 7 | * Copyright (c) 2022 ccagml . All rights reserved. 8 | */ 9 | 10 | import { ChainNodeBase } from "../chainNodeBase"; 11 | 12 | let underscore = require("underscore"); 13 | 14 | import { storageUtils } from "../../utils/storageUtils"; 15 | import { commUtils } from "../../utils/commUtils"; 16 | import { sessionUtils } from "../../utils/sessionUtils"; 17 | 18 | /* It's a plugin that caches the data it gets from the next plugin in the chain */ 19 | class CachePlugin extends ChainNodeBase { 20 | id = 50; 21 | name = "cache"; 22 | builtin = true; 23 | constructor() { 24 | super(); 25 | } 26 | 27 | /* Checking if the translation config has changed. If it has, it clears the cache. */ 28 | clearCacheIfTchanged = (needTranslation) => { 29 | const translationConfig = storageUtils.getCache(commUtils.KEYS.translation); 30 | if (!translationConfig || translationConfig["useEndpointTranslation"] != needTranslation) { 31 | storageUtils.deleteAllCache(); 32 | storageUtils.setCache(commUtils.KEYS.translation, { 33 | useEndpointTranslation: needTranslation, 34 | }); 35 | } 36 | }; 37 | 38 | /* A method that is used to get problems from the cache. If the cache is empty, it will get the 39 | problems from the next layer. */ 40 | public getProblems = (needTranslation, cb) => { 41 | this.clearCacheIfTchanged(needTranslation); 42 | const problems = storageUtils.getCache(commUtils.KEYS.problems); 43 | if (problems) { 44 | return cb(null, problems); 45 | } 46 | this.next.getProblems(needTranslation, function (e, problems) { 47 | if (e) return cb(e); 48 | storageUtils.setCache(commUtils.KEYS.problems, problems); 49 | return cb(null, problems); 50 | }); 51 | }; 52 | 53 | /* A method that is used to get problems from the cache. If the cache is empty, it will get the 54 | problems from the next layer. */ 55 | 56 | public getRatingOnline = (cb) => { 57 | const cacheRantingData = storageUtils.getCache(commUtils.KEYS.ranting_path); 58 | if (cacheRantingData) { 59 | return cb(null, cacheRantingData); 60 | } 61 | this.next.getRatingOnline(function (e, ratingData) { 62 | if (e) return cb(e); 63 | let ratingObj; 64 | try { 65 | ratingObj = JSON.parse(ratingData); 66 | } catch (error) { 67 | return cb("JSON.parse(ratingData) error"); 68 | } 69 | storageUtils.setCache(commUtils.KEYS.ranting_path, ratingObj); 70 | return cb(null, ratingObj); 71 | }); 72 | }; 73 | 74 | /* A cache layer for the getProblem function. */ 75 | public getProblem = (problem, needTranslation, cb) => { 76 | this.clearCacheIfTchanged(needTranslation); 77 | const k = commUtils.KEYS.problem(problem); 78 | const _problem = storageUtils.getCache(k); 79 | let that = this; 80 | if (_problem) { 81 | if (!_problem.desc.includes("
")) {
 82 |         //
 83 |       } else if (!["likes", "dislikes"].every((p) => p in _problem)) {
 84 |         //
 85 |       } else {
 86 |         underscore.extendOwn(problem, _problem);
 87 |         return cb(null, problem);
 88 |       }
 89 |     }
 90 |     this.next.getProblem(problem, needTranslation, function (e, _problem) {
 91 |       if (e) return cb(e);
 92 | 
 93 |       that.saveProblem(_problem);
 94 |       return cb(null, _problem);
 95 |     });
 96 |   };
 97 | 
 98 |   saveProblem = (problem) => {
 99 |     const _problem = underscore.omit(problem, ["locked", "state", "starred"]);
100 |     return storageUtils.setCache(commUtils.KEYS.problem(problem), _problem);
101 |   };
102 | 
103 |   /* Updating the problem in the cache. */
104 |   updateProblem = (problem, kv) => {
105 |     const problems = storageUtils.getCache(commUtils.KEYS.problems);
106 |     if (!problems) return false;
107 | 
108 |     const _problem = problems.find((x) => x.id === problem.id);
109 |     if (!_problem) return false;
110 | 
111 |     underscore.extend(_problem, kv);
112 |     return storageUtils.setCache(commUtils.KEYS.problems, problems);
113 |   };
114 | 
115 |   /* Logging out the user and then logging in the user. */
116 |   login = (user, cb) => {
117 |     this.logout(user, false);
118 |     this.next.login(user, function (e, user) {
119 |       if (e) return cb(e);
120 |       sessionUtils.saveUser(user);
121 |       return cb(null, user);
122 |     });
123 |   };
124 | 
125 |   /* Logging out the user and then logging in the user. */
126 |   logout = (user, purge) => {
127 |     if (!user) user = sessionUtils.getUser();
128 |     if (purge) sessionUtils.deleteUser();
129 |     sessionUtils.deleteCodingSession();
130 |     return user;
131 |   };
132 | 
133 |   public getHintsOnline = (problem, cb) => {
134 |     const hints = storageUtils.getCache(commUtils.KEYS.hints) || {};
135 |     if (hints && hints[problem.id]) {
136 |       return cb(null, hints[problem.id]);
137 |     }
138 |     this.next.getHintsOnline(problem, function (e, hints_result) {
139 |       if (e) return cb(e);
140 | 
141 |       const hints = storageUtils.getCache(commUtils.KEYS.hints) || {};
142 |       hints[problem.id] = hints_result;
143 |       storageUtils.setCache(commUtils.KEYS.hints, hints);
144 | 
145 |       return cb(null, hints_result);
146 |     });
147 |   };
148 | }
149 | 
150 | export const pluginObj: CachePlugin = new CachePlugin();
151 | 


--------------------------------------------------------------------------------
/src/rpc/actionChain/chainNode/core.ts:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/actionChain/core.ts
  3 |  * Path: https://github.com/ccagml/leetcode-extension
  4 |  * Created Date: Monday, November 14th 2022, 4:04:31 pm
  5 |  * Author: ccagml
  6 |  *
  7 |  * Copyright (c) 2022 ccagml . All rights reserved.
  8 |  */
  9 | 
 10 | let util = require("util");
 11 | 
 12 | let _ = require("underscore");
 13 | let cheerio = require("cheerio");
 14 | 
 15 | import { storageUtils } from "../../utils/storageUtils";
 16 | import { configUtils } from "../../utils/configUtils";
 17 | 
 18 | import { ChainNodeBase } from "../chainNodeBase";
 19 | 
 20 | // function hasTag(o, tag) {
 21 | //   return Array.isArray(o) && o.some((x) => x.indexOf(tag.toLowerCase()) >= 0);
 22 | // }
 23 | 
 24 | /* It's a class that extends the ChainNodeBase class, and it has a bunch of methods that are called by
 25 | the LeetCode CLI */
 26 | class CorePlugin extends ChainNodeBase {
 27 |   id = 99999999;
 28 |   name = "core";
 29 |   builtin = true;
 30 |   constructor() {
 31 |     super();
 32 |   }
 33 | 
 34 |   /* It's a method that filters the problems. */
 35 |   filterProblems = (opts, cb) => {
 36 |     this.getProblems(!opts.dontTranslate, function (e, problems) {
 37 |       if (e) return cb(e);
 38 | 
 39 |       // for (let q of (opts.query || "").split("")) {
 40 |       //   const f = QUERY_HANDLERS[q];
 41 |       //   if (!f) continue;
 42 |       //   problems = problems.filter((x) => f(x, q));
 43 |       // }
 44 | 
 45 |       // for (let t of opts.tag || []) {
 46 |       //   problems = problems.filter(function (x) {
 47 |       //     return x.category === t || hasTag(x.companies, t) || hasTag(x.tags, t);
 48 |       //   });
 49 |       // }
 50 | 
 51 |       return cb(null, problems);
 52 |     });
 53 |   };
 54 |   /* It's a method that gets the problem. */
 55 |   public getProblem = (keyword, needTranslation, cb) => {
 56 |     let that = this;
 57 |     this.getProblems(needTranslation, function (e, problems) {
 58 |       if (e) return cb(e);
 59 |       keyword = Number(keyword) || keyword;
 60 |       const metaFid = storageUtils.exist(keyword) ? storageUtils.meta(keyword).id : NaN;
 61 |       const problem = problems.find(function (x) {
 62 |         if (keyword?.fid) {
 63 |           return x.fid + "" === keyword.fid + "";
 64 |         } else if (keyword?.qid) {
 65 |           return x.id + "" === keyword.qid + "";
 66 |         } else {
 67 |           return x.id + "" === keyword + "" || x.fid + "" === metaFid + "" || x.name === keyword || x.slug === keyword;
 68 |         }
 69 |       });
 70 |       if (!problem) return cb("Problem not found!");
 71 |       that.next.getProblem(problem, needTranslation, cb);
 72 |     });
 73 |   };
 74 | 
 75 |   /* It's a method that stars the problem. */
 76 |   starProblem = (problem, starred, cb) => {
 77 |     if (problem.starred === starred) {
 78 |       return cb(null, starred);
 79 |     }
 80 | 
 81 |     this.next.starProblem(problem, starred, cb);
 82 |   };
 83 | 
 84 |   /* It's a method that exports the problem. */
 85 |   exportProblem = (problem, opts) => {
 86 |     const data = _.extend({}, problem);
 87 | 
 88 |     // 增加版本信息
 89 |     data.LCPTCTX = configUtils.LCPTCTX;
 90 |     data.allCaseList = storageUtils.getAllCase(problem.desc);
 91 |     // unify format before rendering
 92 | 
 93 |     data.app = configUtils.app || "leetcode";
 94 |     if (!data.fid) data.fid = data.id;
 95 |     if (!data.lang) data.lang = opts.lang;
 96 |     data.code = (opts.code || data.code || "").replace(/\r\n/g, "\n");
 97 |     data.comment = storageUtils.getCommentStyleByLanguage(data.lang);
 98 |     data.percent = data.percent.toFixed(2);
 99 |     data.testcase = util.inspect(data.testcase || "");
100 | 
101 |     if (opts.tpl === "detailed") {
102 |       let desc = data.desc;
103 |       // Replace  with '^' as the power operator
104 |       desc = desc.replace(/<\/sup>/gm, "").replace(//gm, "^");
105 |       desc = require("he").decode(cheerio.load(desc).root().text());
106 |       // NOTE: wordwrap internally uses '\n' as EOL, so here we have to
107 |       // remove all '\r' in the raw string.
108 |       desc = desc.replace(/\r\n/g, "\n").replace(/^ /gm, "⁠");
109 |       const wrap = require("wordwrap")(79 - data.comment.line.length);
110 |       data.desc = wrap(desc).split("\n");
111 |     }
112 |     return storageUtils.render(opts.tpl, data);
113 |   };
114 | 
115 |   getTodayQuestion = (cb) => {
116 |     this.getQuestionOfToday(function (e, result) {
117 |       if (e) return cb(e);
118 |       return cb(null, result);
119 |     });
120 |   };
121 | 
122 |   getRecentContest = (cb) => {
123 |     this.getRecentContestList(function (e, result) {
124 |       if (e) return cb(e);
125 |       return cb(null, result);
126 |     });
127 |   };
128 | 
129 |   getContestQuestion = (contestName, cb) => {
130 |     this.getContestQuestionList(contestName, function (e, result) {
131 |       if (e) return cb(e);
132 |       return cb(null, result);
133 |     });
134 |   };
135 |   getRating = (cb) => {
136 |     this.getRatingOnline(function (e, result) {
137 |       if (e) return cb(e);
138 |       return cb(null, result);
139 |     });
140 |   };
141 | 
142 |   getQueryZ = (username, cb) => {
143 |     this.getTestApi(username, function (e, result) {
144 |       if (e) return cb(e);
145 |       return cb(null, result);
146 |     });
147 |   };
148 | 
149 |   getUserContest = (username, cb) => {
150 |     this.getUserContestP(username, function (e, result) {
151 |       if (e) return cb(e);
152 |       return cb(null, result);
153 |     });
154 |   };
155 |   getHelp = (problem, cn_flag, lang) => {
156 |     this.getHelpOnline(problem, cn_flag, lang);
157 |   };
158 | }
159 | 
160 | // const isLevel = (x, q) => x.level[0].toLowerCase() === q.toLowerCase();
161 | // const isACed = (x) => x.state === "ac";
162 | // const isLocked = (x) => x.locked;
163 | // const isStarred = (x) => x.starred;
164 | 
165 | /* It's a dictionary that maps the query to the function that filters the problems. */
166 | // const QUERY_HANDLERS = {
167 | //   e: isLevel,
168 | //   E: _.negate(isLevel),
169 | //   m: isLevel,
170 | //   M: _.negate(isLevel),
171 | //   h: isLevel,
172 | //   H: _.negate(isLevel),
173 | //   l: isLocked,
174 | //   L: _.negate(isLocked),
175 | //   d: isACed,
176 | //   D: _.negate(isACed),
177 | //   s: isStarred,
178 | //   S: _.negate(isStarred),
179 | // };
180 | 
181 | export const corePlugin: CorePlugin = new CorePlugin();
182 | 


--------------------------------------------------------------------------------
/src/rpc/actionChain/chainNode/retry.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/actionChain/retry.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Monday, November 14th 2022, 4:04:31 pm
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | import { ChainNodeBase } from "../chainNodeBase";
11 | import { configUtils } from "../../utils/configUtils";
12 | import { sessionUtils } from "../../utils/sessionUtils";
13 | 
14 | class RetryPlugin extends ChainNodeBase {
15 |   id = 30;
16 |   name = "retry";
17 |   builtin = true;
18 |   count = {};
19 | 
20 |   canRetry = (e, name) => {
21 |     return (
22 |       configUtils.autologin.enable &&
23 |       e === sessionUtils.errors.EXPIRED &&
24 |       (this.count[name] || 0) < configUtils.autologin.retry
25 |     );
26 |   };
27 |   /* A wrapper for the API. */
28 |   init = () => {
29 |     const names = [
30 |       "activateSession",
31 |       "createSession",
32 |       "deleteSession",
33 |       "getProblems",
34 |       "getProblem",
35 |       "getSessions",
36 |       "getSubmissions",
37 |       "getSubmission",
38 |       "getFavorites",
39 |       "testProblem",
40 |       "submitProblem",
41 |       "starProblem",
42 |     ];
43 |     let that = this;
44 |     for (let name of names) {
45 |       that.count[name] = 0;
46 |       this[name] = function () {
47 |         const args = Array.from(arguments);
48 |         const cb = args.pop();
49 | 
50 |         const _cb = function () {
51 |           const results = Array.from(arguments);
52 |           const e = results[0];
53 |           if (!that.canRetry(e, name)) {
54 |             that.count[name] = 0;
55 |             return cb.apply(null, results);
56 |           }
57 | 
58 |           ++that.count[name];
59 |           that.relogin(function () {
60 |             // for now we don't care result, just blindly retry
61 |             that[name].apply(that, args.concat(cb));
62 |           });
63 |         };
64 | 
65 |         const next = this.next;
66 |         next[name].apply(next, args.concat(_cb));
67 |       };
68 |     }
69 |   };
70 | 
71 |   /* The above code is checking if the user is logged in. If the user is logged in, it will call the next
72 | login function. */
73 |   // leetcode.com is limiting one session alive in the same time,
74 |   // which means once you login on web, your cli session will get
75 |   // expired immediately. In that case we will try to re-login in
76 |   // the backend to give a seamless user experience.
77 |   relogin = (cb) => {
78 |     const user = sessionUtils.getUser();
79 |     if (!user) {
80 |       return cb();
81 |     }
82 | 
83 |     this.next.login(user, function (e) {
84 |       if (e) {
85 |         //
86 |       } else {
87 |         //
88 |       }
89 |       return cb();
90 |     });
91 |   };
92 | }
93 | 
94 | export const pluginObj: RetryPlugin = new RetryPlugin();
95 | 


--------------------------------------------------------------------------------
/src/rpc/actionChain/chainNodeBase.ts:
--------------------------------------------------------------------------------
  1 | import { commUtils } from "../utils/commUtils";
  2 | import { storageUtils } from "../utils/storageUtils";
  3 | 
  4 | /* It's a chain of responsibility pattern */
  5 | export class ChainNodeBase {
  6 |   next: ChainNodeBase; // 下一个点
  7 |   enabled: boolean;
  8 |   builtin: boolean = true;
  9 |   name: string; // 点的名称
 10 |   deleted: boolean;
 11 | 
 12 |   public init() { }
 13 | 
 14 |   public save(): void {
 15 |     const stats = storageUtils.getCache(commUtils.KEYS.plugins) || {};
 16 | 
 17 |     if (this.deleted) delete stats[this.name];
 18 |     else stats[this.name] = this.enabled;
 19 | 
 20 |     storageUtils.setCache(commUtils.KEYS.plugins, stats);
 21 |   }
 22 | 
 23 |   public setNext(next: ChainNodeBase): void {
 24 |     Object.setPrototypeOf(this, next);
 25 |     this.next = next;
 26 |   }
 27 |   public getProblems(Translate: boolean, cb: Function): void {
 28 |     this.next.getProblems(Translate, cb);
 29 |   }
 30 |   public getQuestionOfToday(cb: Function): void {
 31 |     this.next.getQuestionOfToday(cb);
 32 |   }
 33 | 
 34 |   public getRecentContestList(cb: Function): void {
 35 |     this.next.getRecentContestList(cb);
 36 |   }
 37 | 
 38 |   public getContestQuestionList(contestName:string, cb: Function): void {
 39 |     this.next.getContestQuestionList(contestName,cb);
 40 |   }
 41 | 
 42 |   public getRatingOnline(cb: Function): void {
 43 |     this.next.getRatingOnline(cb);
 44 |   }
 45 | 
 46 |   public getTestApi(username, cb: Function): void {
 47 |     this.next.getTestApi(username, cb);
 48 |   }
 49 |   public getUserContestP(username, cb: Function): void {
 50 |     this.next.getUserContestP(username, cb);
 51 |   }
 52 |   public getProblemsTitle(cb: Function): void {
 53 |     this.next.getProblemsTitle(cb);
 54 |   }
 55 |   public createSession(a, cb: Function): void {
 56 |     this.next.createSession(a, cb);
 57 |   }
 58 |   public getSessions(cb: Function): void {
 59 |     this.next.getSessions(cb);
 60 |   }
 61 |   public activateSession(s, cb: Function): void {
 62 |     this.next.activateSession(s, cb);
 63 |   }
 64 |   public deleteSession(s, cb: Function): void {
 65 |     this.next.deleteSession(s, cb);
 66 |   }
 67 |   public updateProblem(a, b): void {
 68 |     this.next.updateProblem(a, b);
 69 |   }
 70 |   public getSubmissions(s, cb: Function): void {
 71 |     this.next.getSubmissions(s, cb);
 72 |   }
 73 |   public getSubmission(s, cb: Function): void {
 74 |     this.next.getSubmission(s, cb);
 75 |   }
 76 |   public submitProblem(s, cb: Function): void {
 77 |     this.next.submitProblem(s, cb);
 78 |   }
 79 |   public testProblem(s, cb: Function): void {
 80 |     this.next.testProblem(s, cb);
 81 |   }
 82 |   public login(user, cb: Function): void {
 83 |     this.next.login(user, cb);
 84 |   }
 85 |   public logout(user, cb): void {
 86 |     this.next.logout(user, cb);
 87 |   }
 88 |   public githubLogin(user, cb: Function): void {
 89 |     this.next.githubLogin(user, cb);
 90 |   }
 91 |   public linkedinLogin(user, cb: Function): void {
 92 |     this.next.linkedinLogin(user, cb);
 93 |   }
 94 |   public cookieLogin(user, cb: Function): void {
 95 |     this.next.cookieLogin(user, cb);
 96 |   }
 97 |   public curlcookieLogin(user, cb: Function): void {
 98 |     this.next.curlcookieLogin(user, cb);
 99 |   }
100 |   public filterProblems(opts, cb: Function): void {
101 |     this.next.filterProblems(opts, cb);
102 |   }
103 | 
104 |   public getProblem(keyword, needTranslation, cb: Function): void {
105 |     this.next.getProblem(keyword, needTranslation, cb);
106 |   }
107 | 
108 |   public starProblem(problem, starred, cb: Function): void {
109 |     this.next.starProblem(problem, starred, cb);
110 |   }
111 |   public exportProblem(problem, opts): void {
112 |     this.next.exportProblem(problem, opts);
113 |   }
114 | 
115 |   public getTodayQuestion(cb: Function): void {
116 |     this.next.getTodayQuestion(cb);
117 |   }
118 | 
119 |   public getRecentContest(cb: Function): void {
120 |     this.next.getRecentContest(cb);
121 |   }
122 | 
123 |   public getContestQuestion(contestName:string, cb: Function): void {
124 |     this.next.getContestQuestion(contestName,cb);
125 |   }
126 | 
127 |   public getQueryZ(username, cb: Function): void {
128 |     this.next.getQueryZ(username, cb);
129 |   }
130 | 
131 |   public getUserContest(username, cb: Function): void {
132 |     this.next.getUserContest(username, cb);
133 |   }
134 |   public getRating(cb: Function): void {
135 |     this.next.getRating(cb);
136 |   }
137 |   public getHelpOnline(problem, cn_flag, lang): void {
138 |     this.next.getHelpOnline(problem, cn_flag, lang);
139 |   }
140 |   public getHintsOnline(problem, cb: Function): void {
141 |     this.next.getHintsOnline(problem, cb);
142 |   }
143 | }
144 | 


--------------------------------------------------------------------------------
/src/rpc/childMain.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * Filename: https://github.com/ccagml/leetcode-extension/src/rpc/cli.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Thursday, October 27th 2022, 7:43:29 pm
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | import { chainMgr } from "./actionChain/chainManager";
11 | import { corePlugin } from "./actionChain/chainNode/core";
12 | import { configUtils } from "./utils/configUtils";
13 | import { reply } from "./utils/ReplyUtils";
14 | import { storageUtils } from "./utils/storageUtils";
15 | import { apiFactory } from "./factory/apiFactory";
16 | import { IApi } from "./factory/apiBase";
17 | 
18 | class Main {
19 |   constructor() {
20 |     this.exec();
21 |   }
22 |   public exec() {
23 |     process.stdout.on("error", function (e) {
24 |       if (e.code === "EPIPE") process.exit();
25 |     });
26 |     configUtils.init(JSON.parse(process.env.ccagml || "{}"));
27 |     reply.init();
28 |     storageUtils.init();
29 |     chainMgr.init(corePlugin);
30 | 
31 |     let com_str: string = process.argv[2];
32 |     let curApi: IApi | undefined = apiFactory.getApi(com_str);
33 |     curApi?.call(curApi?.callArg(process.argv));
34 |   }
35 | }
36 | 
37 | export const main: Main = new Main();
38 | 


--------------------------------------------------------------------------------
/src/rpc/factory/api/cacheApi.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/factory/api/cacheApi.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Thursday, November 17th 2022, 11:44:14 am
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | import { storageUtils } from "../../utils/storageUtils";
11 | import { sessionUtils } from "../../utils/sessionUtils";
12 | import { ApiBase } from "../apiBase";
13 | import { reply } from "../../utils/ReplyUtils";
14 | 
15 | class CacheApi extends ApiBase {
16 |   constructor() {
17 |     super();
18 |   }
19 | 
20 |   callArg(argv) {
21 |     let argv_config = this.api_argv()
22 |       .option("d", {
23 |         alias: "delete",
24 |         type: "boolean",
25 |         describe: "Delete cache by keyword",
26 |         default: false,
27 |       })
28 |       .option("t", {
29 |         alias: "lastmodify",
30 |         type: "string",
31 |         default: "",
32 |         describe: "",
33 |       })
34 |       .positional("keyword", {
35 |         type: "string",
36 |         default: "",
37 |         describe: "帮助的参数?",
38 |       });
39 |     argv_config.parseArgFromCmd(argv);
40 | 
41 |     return argv_config.get_result();
42 |   }
43 | 
44 |   call(argv) {
45 |     sessionUtils.argv = argv;
46 | 
47 |     const name = argv.keyword || "";
48 |     const isInteger = Number.isInteger(Number(name));
49 | 
50 |     let option_t = Number(argv.t);
51 |     const need_last_modify_time = Number.isInteger(option_t);
52 |     if (need_last_modify_time) {
53 |       option_t *= 1000;
54 |     }
55 |     const all_data_file = storageUtils.listCache().filter(function (f) {
56 |       return name.length === 0 || (isInteger ? f.name.startsWith(name + ".") : f.name === name);
57 |     });
58 | 
59 |     if (argv.delete) {
60 |       const cur_time = new Date().getTime();
61 |       for (let f of all_data_file) {
62 |         if (!need_last_modify_time || (f.mtimeMs || 0) + option_t < cur_time) {
63 |           storageUtils.delCache(f.name);
64 |         }
65 |       }
66 |     }
67 |     reply.info(JSON.stringify({ code: 100 }));
68 |   }
69 | }
70 | 
71 | export const cacheApi: CacheApi = new CacheApi();
72 | 


--------------------------------------------------------------------------------
/src/rpc/factory/api/pluginApi.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/factory/api/pluginApi.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Thursday, November 17th 2022, 11:44:14 am
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | import { configUtils } from "../../utils/configUtils";
11 | import { reply } from "../../utils/ReplyUtils";
12 | import { chainMgr } from "../../actionChain/chainManager";
13 | import { sessionUtils } from "../../utils/sessionUtils";
14 | import { ApiBase } from "../apiBase";
15 | 
16 | class PluginApi extends ApiBase {
17 |   constructor() {
18 |     super();
19 |   }
20 | 
21 |   callArg(argv) {
22 |     let argv_config = this.api_argv()
23 |       .option("d", {
24 |         alias: "disable",
25 |         type: "boolean",
26 |         describe: "Disable plugin",
27 |         default: false,
28 |       })
29 |       .option("e", {
30 |         alias: "enable",
31 |         type: "boolean",
32 |         describe: "Enable plugin",
33 |         default: false,
34 |       })
35 |       .option("i", {
36 |         alias: "install",
37 |         type: "boolean",
38 |         describe: "Install plugin",
39 |         default: false,
40 |       })
41 |       .positional("name", {
42 |         type: "string",
43 |         describe: "Filter plugin by name",
44 |         default: "",
45 |       });
46 | 
47 |     argv_config.parseArgFromCmd(argv);
48 | 
49 |     return argv_config.get_result();
50 |   }
51 | 
52 |   call(argv) {
53 |     sessionUtils.argv = argv;
54 | 
55 |     let all_plugin = chainMgr.installed;
56 |     const name = argv.name;
57 | 
58 |     if (name) {
59 |       all_plugin = all_plugin.filter((x) => x.name === name);
60 |     }
61 |     if (all_plugin.length === 0) {
62 |       return reply.fatal("Plugin not found!");
63 |     }
64 | 
65 |     const p = all_plugin[0];
66 |     if (argv.enable) {
67 |       p.enabled = true;
68 |       p.save();
69 |     } else if (argv.disable) {
70 |       p.enabled = false;
71 |       p.save();
72 |     } else if (argv.delete) {
73 |       // p.delete();
74 |       p.save();
75 |       chainMgr.init(undefined);
76 |     } else if (argv.config) {
77 |       reply.info(JSON.stringify(configUtils.plugins[name] || {}, null, 2));
78 |     }
79 |   }
80 | }
81 | 
82 | export const pluginApi: PluginApi = new PluginApi();
83 | 


--------------------------------------------------------------------------------
/src/rpc/factory/api/queryApi.ts:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/factory/api/queryApi.ts
  3 |  * Path: https://github.com/ccagml/leetcode-extension
  4 |  * Created Date: Thursday, November 17th 2022, 11:44:14 am
  5 |  * Author: ccagml
  6 |  *
  7 |  * Copyright (c) 2022 ccagml . All rights reserved.
  8 |  */
  9 | 
 10 | import { reply } from "../../utils/ReplyUtils";
 11 | import { sessionUtils } from "../../utils/sessionUtils";
 12 | import { ApiBase } from "../apiBase";
 13 | 
 14 | import { chainMgr } from "../../actionChain/chainManager";
 15 | import { configUtils } from "../../utils/configUtils";
 16 | 
 17 | class QueryApi extends ApiBase {
 18 |   constructor() {
 19 |     super();
 20 |   }
 21 |   callArg(argv) {
 22 |     let argv_config = this.api_argv()
 23 |       .option("T", {
 24 |         alias: "dontTranslate",
 25 |         type: "boolean",
 26 |         default: false,
 27 |         describe: "Set to true to disable endpoint's translation",
 28 |       })
 29 |       .option("a", {
 30 |         alias: "getTodayQuestion",
 31 |         type: "boolean",
 32 |         default: false,
 33 |         describe: "getTodayQuestion",
 34 |       })
 35 |       .option("b", {
 36 |         alias: "username",
 37 |         type: "string",
 38 |         default: "",
 39 |         describe: "user name",
 40 |       })
 41 |       .option("c", {
 42 |         alias: "getRating",
 43 |         type: "boolean",
 44 |         default: false,
 45 |         describe: "ranking",
 46 |       })
 47 |       .option("d", {
 48 |         alias: "getAllProblems",
 49 |         type: "boolean",
 50 |         default: false,
 51 |         describe: "getAllProblems",
 52 |       })
 53 |       .option("e", {
 54 |         alias: "getHelp",
 55 |         type: "boolean",
 56 |         default: false,
 57 |         describe: "getHelp",
 58 |       })
 59 |       .option("f", {
 60 |         alias: "help_cn",
 61 |         type: "boolean",
 62 |         default: false,
 63 |         describe: "getHelp help_cn",
 64 |       })
 65 |       .option("g", {
 66 |         alias: "lang",
 67 |         type: "string",
 68 |         default: configUtils.code.lang,
 69 |         describe: "getHelp Programming language of the source code",
 70 |       })
 71 |       .option("h", {
 72 |         alias: "hints",
 73 |         type: "boolean",
 74 |         default: false,
 75 |         describe: "get Hints Programming language of the source code",
 76 |       })
 77 |       .option("i", {
 78 |         alias: "getRecentContest",
 79 |         type: "boolean",
 80 |         default: false,
 81 |         describe: "getRecentContestList",
 82 |       })
 83 |       .option("j", {
 84 |         alias: "getContestQuestion",
 85 |         type: "string",
 86 |         default: false,
 87 |         describe: "get Question list of a contest",
 88 |       })
 89 |       .option("z", {
 90 |         alias: "test",
 91 |         type: "string",
 92 |         default: "",
 93 |         describe: "test",
 94 |       })
 95 |       .positional("keyword", {
 96 |         type: "string",
 97 |         default: "",
 98 |         describe: "帮助的参数?",
 99 |       });
100 |     argv_config.parseArgFromCmd(argv);
101 |     return argv_config.get_result();
102 |   }
103 | 
104 |   call(argv) {
105 |     sessionUtils.argv = argv;
106 |     if (argv.a) {
107 |       chainMgr.getChainHead().getTodayQuestion(function (e, result) {
108 |         if (e) return;
109 |         reply.info(JSON.stringify(result));
110 |       });
111 |     } else if (argv.b) {
112 |       chainMgr.getChainHead().getUserContest(argv.b, function (e, result) {
113 |         if (e) return;
114 |         reply.info(JSON.stringify(result));
115 |       });
116 |     } else if (argv.c) {
117 |       chainMgr.getChainHead().getRating(function (e, result) {
118 |         if (e) {
119 |           let log_data = {
120 |             code: 101,
121 |             data: {},
122 |           };
123 |           if (e.code == "ETIMEDOUT") {
124 |             log_data.code = 102;
125 |           }
126 | 
127 |           reply.info(JSON.stringify(log_data));
128 |           return;
129 |         }
130 |         let log_data = {
131 |           code: 100,
132 |           data: result,
133 |         };
134 |         reply.info(JSON.stringify(log_data));
135 |       });
136 |     } else if (argv.d) {
137 |       chainMgr.getChainHead().filterProblems(argv, function (e, problems) {
138 |         if (e) return reply.info(e);
139 |         let new_objcet: Array = [];
140 |         problems.forEach((element) => {
141 |           let temp_ele: any = {};
142 |           for (const key in element) {
143 |             if (key != "link") {
144 |               temp_ele[key] = element[key];
145 |             }
146 |           }
147 |           new_objcet.push(temp_ele);
148 |         });
149 |         reply.info(JSON.stringify(new_objcet));
150 |       });
151 |     } else if (argv.e) {
152 |       if (argv.keyword.length > 0) {
153 |         // show specific one
154 |         chainMgr.getChainHead().getProblem(argv.keyword, !argv.dontTranslate, function (e, problem) {
155 |           if (e) return reply.info(e);
156 |           chainMgr.getChainHead().getHelpOnline(problem, argv.f, argv.g);
157 |         });
158 |       }
159 |     } else if (argv.h) {
160 |       if (argv.keyword.length > 0) {
161 |         // show specific one
162 |         chainMgr.getChainHead().getProblem(argv.keyword, !argv.dontTranslate, function (e, problem) {
163 |           if (e) return reply.info(e);
164 |           chainMgr.getChainHead().getHintsOnline(problem, function (e, result) {
165 |             if (e) return;
166 |             reply.info(JSON.stringify({ code: 100, hints: result }));
167 |           });
168 |         });
169 |       }
170 |     } if (argv.i) {
171 |       chainMgr.getChainHead().getRecentContest(function (e, result) {
172 |         if (e) return;
173 |         reply.info(JSON.stringify(result));
174 |       });
175 |     } if (argv.j) {
176 |       chainMgr.getChainHead().getContestQuestion(argv.j, function (e, result) {
177 |         if (e) return;
178 |         reply.info(JSON.stringify(result));
179 |       });
180 |     } else if (argv.z) {
181 |       chainMgr.getChainHead().getQueryZ(argv.z, function (e, result) {
182 |         if (e) return;
183 |         reply.info(JSON.stringify(result));
184 |       });
185 |     }
186 |   }
187 | }
188 | 
189 | export const queryApi: QueryApi = new QueryApi();
190 | 


--------------------------------------------------------------------------------
/src/rpc/factory/api/showApi.ts:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/factory/api/showApi.ts
  3 |  * Path: https://github.com/ccagml/leetcode-extension
  4 |  * Created Date: Thursday, November 17th 2022, 11:44:14 am
  5 |  * Author: ccagml
  6 |  *
  7 |  * Copyright (c) 2022 ccagml . All rights reserved.
  8 |  */
  9 | 
 10 | // let util = require("util");
 11 | 
 12 | import { storageUtils } from "../../utils/storageUtils";
 13 | 
 14 | import { reply } from "../../utils/ReplyUtils";
 15 | import { configUtils } from "../../utils/configUtils";
 16 | 
 17 | import { sessionUtils } from "../../utils/sessionUtils";
 18 | import { ApiBase } from "../apiBase";
 19 | import { chainMgr } from "../../actionChain/chainManager";
 20 | 
 21 | class ShowApi extends ApiBase {
 22 |   constructor() {
 23 |     super();
 24 |   }
 25 | 
 26 |   callArg(argv) {
 27 |     let argv_config = this.api_argv()
 28 |       .option("c", {
 29 |         alias: "codeonly",
 30 |         type: "boolean",
 31 |         default: false,
 32 |         describe: "Only show code template",
 33 |       })
 34 |       .option("l", {
 35 |         alias: "lang",
 36 |         type: "string",
 37 |         default: configUtils.code.lang,
 38 |         describe: "Programming language of the source code",
 39 |         choices: configUtils.sys.langs,
 40 |       })
 41 |       .option("q", {
 42 |         alias: "query",
 43 |         type: "string",
 44 |         default: "",
 45 |         describe: [
 46 |           "Filter questions by condition:",
 47 |           "Uppercase means negative",
 48 |           "e = easy     E = m+h",
 49 |           "m = medium   M = e+h",
 50 |           "h = hard     H = e+m",
 51 |           "d = done     D = not done",
 52 |           "l = locked   L = non locked",
 53 |           "s = starred  S = not starred",
 54 |         ].join("\n"),
 55 |       })
 56 |       .option("t", {
 57 |         alias: "tag",
 58 |         type: "array",
 59 |         default: [],
 60 |         describe: "Filter questions by tag",
 61 |       })
 62 |       .option("x", {
 63 |         alias: "extra",
 64 |         type: "boolean",
 65 |         default: false,
 66 |         describe: "Show extra question details in source code",
 67 |       })
 68 |       .option("T", {
 69 |         alias: "dontTranslate",
 70 |         type: "boolean",
 71 |         default: false,
 72 |         describe: "Set to true to disable endpoint's translation",
 73 |       })
 74 |       .positional("keyword", {
 75 |         type: "string",
 76 |         default: "",
 77 |         describe: "Show question by name or id",
 78 |       });
 79 |     argv_config.parseArgFromCmd(argv);
 80 |     return argv_config.get_result();
 81 |   }
 82 |   genFileName(problem, opts) {
 83 |     const path = require("path");
 84 |     const params = [storageUtils.fmt(configUtils.file.show, problem), "", storageUtils.getFileExtByLanguage(opts.lang)];
 85 | 
 86 |     // try new name to avoid overwrite by mistake
 87 |     for (let i = 0; ; ++i) {
 88 |       const name = path.join(opts.outdir, params.join(".").replace(/\.+/g, "."));
 89 |       if (!storageUtils.exist(name)) return name;
 90 |       params[1] = i;
 91 |     }
 92 |   }
 93 | 
 94 |   showProblem(problem, argv) {
 95 |     let code;
 96 |     const needcode = argv.gen || argv.codeonly;
 97 |     if (needcode) {
 98 |       const template = problem.templates.find((x) => x.value === argv.lang);
 99 |       if (!template) {
100 |         reply.info(JSON.stringify({ code: 101, error: `Not supported language ${argv.lang} ` }));
101 |         return;
102 |       }
103 | 
104 |       const opts = {
105 |         lang: argv.lang,
106 |         code: template.defaultCode,
107 |         tpl: argv.extra ? "detailed" : "codeonly",
108 |       };
109 |       code = chainMgr.getChainHead().exportProblem(problem, opts);
110 |     }
111 | 
112 |     if (argv.codeonly) {
113 |       reply.info(JSON.stringify({ code: 100, msg: code }));
114 |       return;
115 |     }
116 | 
117 |     let preview_data: any = {};
118 |     preview_data.url = problem.link;
119 |     preview_data.category = `${problem.category}`;
120 |     preview_data.difficulty = `${problem.level} (${problem.percent.toFixed(2)}%)`;
121 |     preview_data.likes = `${problem.likes}`;
122 |     preview_data.dislikes = `${problem.dislikes}`;
123 |     preview_data.desc = problem.desc;
124 |     reply.info(JSON.stringify({ code: 100, msg: preview_data }));
125 |   }
126 | 
127 |   call(argv) {
128 |     let that = this;
129 |     sessionUtils.argv = argv;
130 |     if (argv.keyword.length > 0) {
131 |       // show specific one
132 |       chainMgr.getChainHead().getProblem(argv.keyword, !argv.dontTranslate, function (e, problem) {
133 |         if (e) return reply.info(JSON.stringify({ code: 102, error: e }));
134 |         that.showProblem(problem, argv);
135 |       });
136 |     } else {
137 |       //
138 |     }
139 |   }
140 | }
141 | 
142 | export const showApi: ShowApi = new ShowApi();
143 | 


--------------------------------------------------------------------------------
/src/rpc/factory/api/starApi.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/factory/api/starApi.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Thursday, November 17th 2022, 11:44:14 am
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | import { reply } from "../../utils/ReplyUtils";
11 | 
12 | import { sessionUtils } from "../../utils/sessionUtils";
13 | import { ApiBase } from "../apiBase";
14 | import { chainMgr } from "../../actionChain/chainManager";
15 | 
16 | class StarApi extends ApiBase {
17 |   constructor() {
18 |     super();
19 |   }
20 | 
21 |   callArg(argv) {
22 |     let argv_config = this.api_argv()
23 |       .option("d", {
24 |         alias: "delete",
25 |         type: "boolean",
26 |         describe: "Unstar question",
27 |         default: false,
28 |       })
29 |       .positional("keyword", {
30 |         type: "string",
31 |         describe: "Question name or id",
32 |         default: "",
33 |       });
34 | 
35 |     argv_config.parseArgFromCmd(argv);
36 | 
37 |     return argv_config.get_result();
38 |   }
39 | 
40 |   call(argv) {
41 |     sessionUtils.argv = argv;
42 |     // translation doesn't affect question lookup
43 |     chainMgr.getChainHead().getProblem(argv.keyword, true, function (e, problem) {
44 |       if (e) return reply.info(e);
45 | 
46 |       chainMgr.getChainHead().starProblem(problem, !argv.delete, function (e, flag) {
47 |         if (e) return reply.info(e);
48 |         chainMgr.getChainHead().updateProblem(problem, { starred: flag });
49 |         reply.info(`[${problem.fid}] ${problem.name} ${flag ? "icon.like" : "icon.unlike"}`);
50 |       });
51 |     });
52 |   }
53 | }
54 | 
55 | export const starApi: StarApi = new StarApi();
56 | 


--------------------------------------------------------------------------------
/src/rpc/factory/api/submitApi.ts:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/factory/api/submitApi.ts
  3 |  * Path: https://github.com/ccagml/leetcode-extension
  4 |  * Created Date: Thursday, November 17th 2022, 11:44:14 am
  5 |  * Author: ccagml
  6 |  *
  7 |  * Copyright (c) 2022 ccagml . All rights reserved.
  8 |  */
  9 | 
 10 | let util = require("util");
 11 | let lodash = require("lodash");
 12 | 
 13 | import { storageUtils } from "../../utils/storageUtils";
 14 | import { reply } from "../../utils/ReplyUtils";
 15 | 
 16 | import { sessionUtils } from "../../utils/sessionUtils";
 17 | import { ApiBase } from "../apiBase";
 18 | import { chainMgr } from "../../actionChain/chainManager";
 19 | 
 20 | class SubmitApi extends ApiBase {
 21 |   constructor() {
 22 |     super();
 23 |   }
 24 | 
 25 |   callArg(argv) {
 26 |     let argv_config = this.api_argv().positional("filename", {
 27 |       type: "string",
 28 |       describe: "Code file to submit",
 29 |       default: "",
 30 |     });
 31 |     argv_config.parseArgFromCmd(argv);
 32 | 
 33 |     return argv_config.get_result();
 34 |   }
 35 | 
 36 |   printResult(actual, k, log_obj) {
 37 |     if (!actual.hasOwnProperty(k)) return;
 38 | 
 39 |     const v = actual[k] || "";
 40 |     const lines = Array.isArray(v) ? v : [v];
 41 |     for (let line of lines) {
 42 |       if (k !== "state") {
 43 |         if (!log_obj.hasOwnProperty(lodash.startCase(k))) {
 44 |           log_obj[lodash.startCase(k)] = [line];
 45 |         } else {
 46 |           log_obj[lodash.startCase(k)].push(line);
 47 |         }
 48 |       } else {
 49 |         log_obj.messages.push(line);
 50 |       }
 51 |     }
 52 |   }
 53 | 
 54 |   printLine(log_obj, ...ret: any[]) {
 55 |     const args = ret.slice(1);
 56 |     const line = util.format.apply(util, args);
 57 |     log_obj.messages.push(line);
 58 |   }
 59 | 
 60 |   call(argv) {
 61 |     sessionUtils.argv = argv;
 62 |     if (!storageUtils.exist(argv.filename)) return reply.fatal("File " + argv.filename + " not exist!");
 63 | 
 64 |     const meta = storageUtils.meta(argv.filename);
 65 |     let that = this;
 66 |     // translation doesn't affect problem lookup
 67 |     chainMgr.getChainHead().getProblem(meta, true, function (e, problem) {
 68 |       if (e) return reply.info(e);
 69 | 
 70 |       problem.file = argv.filename;
 71 |       problem.lang = meta.lang;
 72 | 
 73 |       chainMgr.getChainHead().submitProblem(problem, function (e, results) {
 74 |         if (e) return reply.info(e);
 75 | 
 76 |         const result = results[0];
 77 | 
 78 |         let log_obj: any = {};
 79 |         log_obj.messages = [];
 80 |         log_obj.system_message = {};
 81 |         log_obj.system_message.fid = problem.fid;
 82 |         log_obj.system_message.id = problem.id;
 83 |         log_obj.system_message.qid = problem.id;
 84 |         log_obj.system_message.sub_type = "submit";
 85 |         log_obj.system_message.accepted = false;
 86 | 
 87 |         that.printResult(result, "state", log_obj);
 88 |         that.printLine(log_obj, result, "%d/%d cases passed (%s)", result.passed, result.total, result.runtime);
 89 | 
 90 |         if (result.ok) {
 91 |           sessionUtils.updateStat("ac", 1);
 92 |           sessionUtils.updateStat("ac.set", problem.fid);
 93 |           log_obj.system_message.accepted = true;
 94 | 
 95 |           (function () {
 96 |             if (result.runtime_percentile)
 97 |               that.printLine(
 98 |                 log_obj,
 99 |                 result,
100 |                 "Your runtime beats %d %% of %s submissions",
101 |                 result.runtime_percentile.toFixed(2),
102 |                 result.lang
103 |               );
104 | 
105 |             if (result.memory && result.memory_percentile)
106 |               that.printLine(
107 |                 log_obj,
108 |                 result,
109 |                 "Your memory usage beats %d %% of %s submissions (%s)",
110 |                 result.memory_percentile.toFixed(2),
111 |                 result.lang,
112 |                 result.memory
113 |               );
114 |           })();
115 |         } else {
116 |           result.testcase = result.testcase.slice(1, -1).replace(/\\n/g, "\n");
117 |           that.printResult(result, "error", log_obj);
118 |           that.printResult(result, "testcase", log_obj);
119 |           that.printResult(result, "answer", log_obj);
120 |           that.printResult(result, "expected_answer", log_obj);
121 |           that.printResult(result, "stdout", log_obj);
122 |         }
123 |         reply.info(JSON.stringify(log_obj));
124 |         chainMgr.getChainHead().updateProblem(problem, {
125 |           state: result.ok ? "ac" : "notac",
126 |         });
127 |       });
128 |     });
129 |   }
130 | }
131 | 
132 | export const submitApi: SubmitApi = new SubmitApi();
133 | 


--------------------------------------------------------------------------------
/src/rpc/factory/api/testApi.ts:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/factory/api/testApi.ts
  3 |  * Path: https://github.com/ccagml/leetcode-extension
  4 |  * Created Date: Thursday, November 17th 2022, 11:44:14 am
  5 |  * Author: ccagml
  6 |  *
  7 |  * Copyright (c) 2022 ccagml . All rights reserved.
  8 |  */
  9 | 
 10 | let _ = require("underscore");
 11 | let lodash = require("lodash");
 12 | 
 13 | import { storageUtils } from "../../utils/storageUtils";
 14 | import { reply } from "../../utils/ReplyUtils";
 15 | 
 16 | import { sessionUtils } from "../../utils/sessionUtils";
 17 | import { ApiBase } from "../apiBase";
 18 | // import { commUtils } from "../../utils/commUtils";
 19 | import { chainMgr } from "../../actionChain/chainManager";
 20 | 
 21 | class TestApi extends ApiBase {
 22 |   constructor() {
 23 |     super();
 24 |   }
 25 | 
 26 |   callArg(argv) {
 27 |     let argv_config = this.api_argv()
 28 |       .option("i", {
 29 |         alias: "interactive",
 30 |         type: "boolean",
 31 |         default: false,
 32 |         describe: "Provide test case interactively",
 33 |       })
 34 |       .option("t", {
 35 |         alias: "testcase",
 36 |         type: "string",
 37 |         default: "",
 38 |         describe: "Provide test case",
 39 |       })
 40 |       .option("a", {
 41 |         alias: "allcase",
 42 |         type: "boolean",
 43 |         default: false,
 44 |         describe: "Provide all test case",
 45 |       })
 46 |       .positional("filename", {
 47 |         type: "string",
 48 |         default: "",
 49 |         describe: "Code file to test",
 50 |       });
 51 | 
 52 |     argv_config.parseArgFromCmd(argv);
 53 | 
 54 |     return argv_config.get_result();
 55 |   }
 56 | 
 57 |   printResult(actual, extra, k, log_obj) {
 58 |     if (!actual.hasOwnProperty(k)) return;
 59 |     // HACk: leetcode still return 'Accepted' even the answer is wrong!!
 60 |     const v = actual[k] || "";
 61 |     if (k === "state" && v === "Accepted") return;
 62 | 
 63 |     // let ok = actual.ok;
 64 | 
 65 |     const lines = Array.isArray(v) ? v : [v];
 66 |     for (let line of lines) {
 67 |       const extraInfo = extra ? ` (${extra})` : "";
 68 |       if (k !== "state") {
 69 |         let new_kk = lodash.startCase(k) + extraInfo;
 70 |         if (!log_obj.hasOwnProperty(new_kk)) {
 71 |           log_obj[new_kk] = [line];
 72 |         } else {
 73 |           log_obj[new_kk].push(line);
 74 |         }
 75 |       } else {
 76 |         log_obj.messages.push(line);
 77 |       }
 78 |     }
 79 |   }
 80 | 
 81 |   runTest(argv) {
 82 |     let that = this;
 83 |     if (!storageUtils.exist(argv.filename)) return reply.fatal("File " + argv.filename + " not exist!");
 84 | 
 85 |     const meta = storageUtils.meta(argv.filename);
 86 | 
 87 |     // [key: string]: string[];
 88 |     // messages: string[];
 89 | 
 90 |     chainMgr.getChainHead().getProblem(meta, true, function (e, problem) {
 91 |       if (e)
 92 |         return reply.info(
 93 |           JSON.stringify({
 94 |             messages: ["error"],
 95 |             code: [-1],
 96 |             error: [e.msg || e],
 97 |           })
 98 |         );
 99 | 
100 |       if (!problem.testable)
101 |         return reply.info(
102 |           JSON.stringify({
103 |             messages: ["error"],
104 |             code: [-2],
105 |             error: ["not testable? please submit directly!"],
106 |           })
107 |         );
108 | 
109 |       if (argv.testcase) {
110 |         problem.testcase = argv.testcase.replace(/\\n/g, "\n");
111 |       }
112 | 
113 |       if (argv.allcase) {
114 |         let temp_test_set: Set = new Set();
115 | 
116 |         let new_desc = problem.desc;
117 |         let calcCaseList = storageUtils.getAllCase(new_desc);
118 |         calcCaseList.forEach((x) => {
119 |           let xxx = x.join("\n");
120 |           temp_test_set.add(xxx);
121 |         });
122 |         if (meta.writeCase) {
123 |           meta.writeCase.forEach((xxx) => {
124 |             temp_test_set.add(xxx);
125 |           });
126 |         }
127 | 
128 |         let temp_test: Array = [];
129 |         temp_test_set.forEach((x) => {
130 |           temp_test.push(x);
131 |         });
132 | 
133 |         let all_case = temp_test.join("\n");
134 |         problem.testcase = all_case;
135 |       }
136 | 
137 |       if (!problem.testcase)
138 |         return reply.info(
139 |           JSON.stringify({
140 |             messages: ["error"],
141 |             code: [-3],
142 |             error: ["missing testcase?"],
143 |           })
144 |         );
145 | 
146 |       problem.file = argv.filename;
147 |       problem.lang = meta.lang;
148 | 
149 |       chainMgr.getChainHead().testProblem(problem, function (e, results) {
150 |         if (e) return reply.info(JSON.stringify(e));
151 | 
152 |         results = _.sortBy(results, (x) => x.type);
153 | 
154 |         let log_obj: any = {};
155 |         log_obj.messages = [];
156 |         log_obj.system_message = {};
157 |         log_obj.system_message.fid = problem.fid;
158 |         log_obj.system_message.id = problem.id;
159 |         log_obj.system_message.qid = problem.id;
160 |         log_obj.system_message.sub_type = "test";
161 |         log_obj.system_message.accepted = false;
162 | 
163 |         if (results[0].state === "Accepted") {
164 |           results[0].state = "Finished";
165 |           log_obj.system_message.compare_result = results[0].compare_result;
166 |         }
167 |         if (results[0].ok) {
168 |           log_obj.system_message.accepted = true;
169 |         }
170 |         that.printResult(results[0], null, "state", log_obj);
171 |         that.printResult(results[0], null, "error", log_obj);
172 | 
173 |         results[0].your_input = problem.testcase;
174 |         results[0].output = results[0].answer;
175 |         // LeetCode-CN returns the actual and expected answer into two separate responses
176 |         if (results[1]) {
177 |           results[0].expected_answer = results[1].answer;
178 |         }
179 |         results[0].stdout = results[0].stdout.slice(1, -1).replace(/\\n/g, "\n");
180 |         that.printResult(results[0], null, "your_input", log_obj);
181 |         that.printResult(results[0], results[0].runtime, "output", log_obj);
182 |         that.printResult(results[0], null, "expected_answer", log_obj);
183 |         that.printResult(results[0], null, "stdout", log_obj);
184 |         reply.info(JSON.stringify(log_obj));
185 |       });
186 |     });
187 |   }
188 | 
189 |   call(argv) {
190 |     let that = this;
191 |     sessionUtils.argv = argv;
192 |     if (!argv.i) return that.runTest(argv);
193 | 
194 |     // commUtils.readStdin(function (e, data) {
195 |     //   if (e) return reply.info(e);
196 | 
197 |     //   argv.testcase = data;
198 |     //   return that.runTest(argv);
199 |     // });
200 |   }
201 | }
202 | 
203 | export const testApi: TestApi = new TestApi();
204 | 


--------------------------------------------------------------------------------
/src/rpc/factory/api/userApi.ts:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/factory/api/userApi.ts
  3 |  * Path: https://github.com/ccagml/leetcode-extension
  4 |  * Created Date: Thursday, November 17th 2022, 11:44:14 am
  5 |  * Author: ccagml
  6 |  *
  7 |  * Copyright (c) 2022 ccagml . All rights reserved.
  8 |  */
  9 | 
 10 | let prompt_out = require("prompt");
 11 | 
 12 | import { reply } from "../../utils/ReplyUtils";
 13 | 
 14 | import { sessionUtils } from "../../utils/sessionUtils";
 15 | import { ApiBase } from "../apiBase";
 16 | 
 17 | import { chainMgr } from "../../actionChain/chainManager";
 18 | 
 19 | class UserApi extends ApiBase {
 20 |   constructor() {
 21 |     super();
 22 |   }
 23 | 
 24 |   callArg(argv) {
 25 |     let argv_config = this.api_argv()
 26 |       .option("l", {
 27 |         alias: "login",
 28 |         type: "boolean",
 29 |         default: false,
 30 |         describe: "Login",
 31 |       })
 32 |       .option("c", {
 33 |         alias: "cookie",
 34 |         type: "boolean",
 35 |         default: false,
 36 |         describe: "cookieLogin",
 37 |       })
 38 |       .option("r", {
 39 |         alias: "curl",
 40 |         type: "boolean",
 41 |         default: false,
 42 |         describe: "curl",
 43 |       })
 44 |       .option("g", {
 45 |         alias: "github",
 46 |         type: "boolean",
 47 |         default: false,
 48 |         describe: "githubLogin",
 49 |       })
 50 |       .option("i", {
 51 |         alias: "linkedin",
 52 |         type: "boolean",
 53 |         default: false,
 54 |         describe: "linkedinLogin",
 55 |       })
 56 |       .option("L", {
 57 |         alias: "logout",
 58 |         type: "boolean",
 59 |         default: false,
 60 |         describe: "Logout",
 61 |       });
 62 | 
 63 |     argv_config.parseArgFromCmd(argv);
 64 | 
 65 |     return argv_config.get_result();
 66 |   }
 67 | 
 68 |   call(argv) {
 69 |     sessionUtils.argv = argv;
 70 |     let user: any = null;
 71 |     if (argv.login) {
 72 |       // login
 73 |       prompt_out.colors = false;
 74 |       prompt_out.message = "";
 75 |       prompt_out.start();
 76 |       prompt_out.get(
 77 |         [
 78 |           { name: "login", required: true },
 79 |           { name: "pass", required: true, hidden: true },
 80 |         ],
 81 |         function (e, user) {
 82 |           if (e) {
 83 |             return reply.info(JSON.stringify({ code: -1, msg: e.msg || e }));
 84 |           }
 85 | 
 86 |           chainMgr.getChainHead().login(user, function (e, user) {
 87 |             if (e) {
 88 |               return reply.info(JSON.stringify({ code: -2, msg: e.msg || e }));
 89 |             }
 90 |             reply.info(JSON.stringify({ code: 100, user_name: user.name || user.login || "username" }));
 91 |           });
 92 |         }
 93 |       );
 94 |     } else if (argv.logout) {
 95 |       // logout
 96 |       user = chainMgr.getChainHead().logout(user, true);
 97 |       if (user) reply.info(JSON.stringify({ code: 100, user_name: user.name || user.login || "username" }));
 98 |       else reply.info(JSON.stringify({ code: -3, msg: "You are not login yet?" }));
 99 |       // third parties
100 |     } else if (argv.github || argv.linkedin) {
101 |       // add future third parties here
102 |       const functionMap = new Map([
103 |         ["g", chainMgr.getChainHead().githubLogin],
104 |         ["github", chainMgr.getChainHead().githubLogin],
105 |         ["i", chainMgr.getChainHead().linkedinLogin],
106 |         ["linkedin", chainMgr.getChainHead().linkedinLogin],
107 |       ]);
108 |       const keyword = Object.entries(argv).filter((i) => i[1] === true)[0][0];
109 |       const coreFunction = functionMap.get(keyword);
110 |       if (coreFunction) {
111 |         prompt_out.colors = false;
112 |         prompt_out.message = "";
113 |         prompt_out.start();
114 |         prompt_out.get(
115 |           [
116 |             { name: "login", required: true },
117 |             { name: "pass", required: true, hidden: true },
118 |           ],
119 |           function (e, user) {
120 |             if (e) return reply.info(JSON.stringify({ code: -4, msg: e.msg || e }));
121 |             coreFunction(user, function (e, user) {
122 |               if (e) return reply.info(JSON.stringify({ code: -5, msg: e.msg || e }));
123 |               reply.info(JSON.stringify({ code: 100, user_name: user.name || user.login || "username" }));
124 |             });
125 |           }
126 |         );
127 |       }
128 |     } else if (argv.cookie) {
129 |       // session
130 |       prompt_out.colors = false;
131 |       prompt_out.message = "";
132 |       prompt_out.start();
133 |       prompt_out.get(
134 |         [
135 |           { name: "login", required: true },
136 |           { name: "cookie", required: true },
137 |         ],
138 |         function (e, user) {
139 |           if (e) return reply.info(e);
140 |           chainMgr.getChainHead().cookieLogin(user, function (e, user) {
141 |             if (e) return reply.info(JSON.stringify({ code: -6, msg: e.msg || e }));
142 |             reply.info(JSON.stringify({ code: 100, user_name: user.name || user.login || "username" }));
143 |           });
144 |         }
145 |       );
146 |     } else if (argv.curl) {
147 |       // session
148 |       prompt_out.colors = false;
149 |       prompt_out.message = "";
150 |       prompt_out.start();
151 |       prompt_out.get(
152 |         [
153 |           { name: "login", required: true },
154 |           { name: "curl_data", required: true },
155 |         ],
156 |         function (e, user) {
157 |           if (e) return reply.info(e);
158 |           chainMgr.getChainHead().curlcookieLogin(user, function (e, user) {
159 |             if (e) return reply.info(JSON.stringify({ code: -6, msg: e.msg || e }));
160 |             reply.info(JSON.stringify({ code: 100, user_name: user.name || user.login || "username" }));
161 |           });
162 |         }
163 |       );
164 |     } else {
165 |       // show current user
166 |       user = sessionUtils.getUser();
167 |       if (user) {
168 |         reply.info(JSON.stringify({ code: 100, user_name: user.name || user.login || "username" }));
169 |       } else return reply.info(JSON.stringify({ code: -7, msg: "You are not login yet?" }));
170 |     }
171 |   }
172 | }
173 | 
174 | export const userApi: UserApi = new UserApi();
175 | 


--------------------------------------------------------------------------------
/src/rpc/factory/apiBase.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/factory/apiBase.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Thursday, November 17th 2022, 11:44:14 am
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | export interface IApi {
11 |   callArg(arg);
12 |   call(arg): void;
13 | }
14 | 
15 | export class ApiBase implements IApi {
16 |   constructor() {}
17 |   callArg(arg: any) {
18 |     console.log("未实现callArg", arg);
19 |   }
20 |   call(arg: any) {
21 |     console.log("未实现call", arg);
22 |   }
23 |   api_argv() {
24 |     let base = {
25 |       all_base_data: {},
26 |       positional_index: 0,
27 |       positional_key: {},
28 |       option: function (key, value) {
29 |         this.all_base_data[key] = value.default;
30 |         this.all_base_data[value.alias] = value.default;
31 |         this[key] = value;
32 |         return this;
33 |       },
34 |       positional: function (key, value) {
35 |         this.positional_key[this.positional_index] = key;
36 |         this.positional_index = this.positional_index + 1;
37 |         this.all_base_data[key] = value.default;
38 |         this.all_base_data[value.alias] = value.default;
39 |         this[key] = value;
40 |         return this;
41 |       },
42 |       set_opt(key, temp_val?) {
43 |         let cfg = this[key];
44 |         if (cfg) {
45 |           if (cfg.type == "boolean") {
46 |             this.all_base_data[key] = true;
47 |             if (cfg.alias) {
48 |               this.all_base_data[cfg.alias] = true;
49 |             }
50 |             return false;
51 |           } else {
52 |             this.all_base_data[key] = temp_val;
53 |             if (cfg.alias) {
54 |               this.all_base_data[cfg.alias] = temp_val;
55 |             }
56 |             return true;
57 |           }
58 |         } else {
59 |           this.all_base_data[key] = true;
60 |         }
61 |         return false;
62 |       },
63 |       set_posi(value, index) {
64 |         let cfg_key = this.positional_key[index];
65 |         let cfg = this[cfg_key];
66 |         if (cfg) {
67 |           this.all_base_data[cfg_key] = value;
68 |           if (cfg.alias) {
69 |             this.all_base_data[cfg.alias] = value;
70 |           }
71 |         }
72 |       },
73 |       parseArgFromCmd(argv) {
74 |         let all_posi = 0;
75 |         for (let index = 3; index < argv.length; index++) {
76 |           let con = argv[index];
77 |           if (con[0] == "-" && con[1] == "-") {
78 |             this.set_opt(con.substring(2));
79 |           } else if (con[0] == "-") {
80 |             for (let con_index = 1; con_index < con.length; con_index++) {
81 |               if (this.set_opt(con[con_index], argv[index + 1])) {
82 |                 con_index++;
83 |               }
84 |             }
85 |           } else {
86 |             this.set_posi(con, all_posi);
87 |             all_posi = all_posi + 1;
88 |           }
89 |         }
90 |       },
91 |       get_result: function () {
92 |         return this.all_base_data;
93 |       },
94 |     };
95 |     return base;
96 |   }
97 | }
98 | 


--------------------------------------------------------------------------------
/src/rpc/factory/apiFactory.ts:
--------------------------------------------------------------------------------
 1 | import { cacheApi } from "./api/cacheApi";
 2 | 
 3 | import { pluginApi } from "./api/pluginApi";
 4 | import { queryApi } from "./api/queryApi";
 5 | import { showApi } from "./api/showApi";
 6 | import { starApi } from "./api/starApi";
 7 | import { submitApi } from "./api/submitApi";
 8 | import { testApi } from "./api/testApi";
 9 | import { userApi } from "./api/userApi";
10 | import { IApi } from "./apiBase";
11 | 
12 | class ApiFactory {
13 |   constructor() {}
14 |   getApi(api: string): IApi | undefined {
15 |     if (api == "cache") {
16 |       return cacheApi;
17 |     } else if (api == "plugin") {
18 |       return pluginApi;
19 |     } else if (api == "query") {
20 |       return queryApi;
21 |     } else if (api == "show") {
22 |       return showApi;
23 |     } else if (api == "star") {
24 |       return starApi;
25 |     } else if (api == "submit") {
26 |       return submitApi;
27 |     } else if (api == "test") {
28 |       return testApi;
29 |     } else if (api == "user") {
30 |       return userApi;
31 |     }
32 |     return undefined;
33 |   }
34 | }
35 | export const apiFactory: ApiFactory = new ApiFactory();
36 | 


--------------------------------------------------------------------------------
/src/rpc/utils/ReplyUtils.ts:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/Response.ts
  3 |  * Path: https://github.com/ccagml/leetcode-extension
  4 |  * Created Date: Monday, November 14th 2022, 4:04:31 pm
  5 |  * Author: ccagml
  6 |  *
  7 |  * Copyright (c) 2022 ccagml . All rights reserved.
  8 |  */
  9 | 
 10 | let _ = require("underscore");
 11 | 
 12 | class Reply {
 13 |   init_recv_flag = false;
 14 |   output = _.bind(console.log, console);
 15 |   level: any;
 16 |   levels = new Map([
 17 |     ["INFO", { value: 2 }],
 18 |     ["WARN", { value: 3 }],
 19 |     ["ERROR", { value: 4 }],
 20 |   ]);
 21 | 
 22 |   operCookie = 0;
 23 | 
 24 |   operWaitMap: Map = new Map();
 25 | 
 26 |   setLevel(name: any) {
 27 |     this.level = this.levels.get(name) || this.levels.get("INFO");
 28 |   }
 29 | 
 30 |   fail(e: any) {
 31 |     let msg = e.msg || e;
 32 |     if (e.statusCode) {
 33 |       msg += " [code=" + e.statusCode + "]";
 34 |     }
 35 |     this.error(msg);
 36 |   }
 37 | 
 38 |   fatal(e: any) {
 39 |     this.error(e);
 40 |     process.exit(1);
 41 |   }
 42 | 
 43 |   init() {
 44 |     this.setLevel("INFO");
 45 |   }
 46 | 
 47 |   info(...rest: any[]) {
 48 |     const args = rest; //Array.from(arguments);
 49 |     let s = args.map((x) => x.toString()).join(" ");
 50 |     this.output(s);
 51 |   }
 52 |   warn(...rest: any[]) {
 53 |     const args = rest; //Array.from(arguments);
 54 |     args.unshift("[" + "warn" + "]");
 55 | 
 56 |     let s = args.map((x) => x.toString()).join(" ");
 57 |     this.output(s);
 58 |   }
 59 |   error(...rest: any[]) {
 60 |     const args = rest; //Array.from(arguments);
 61 |     args.unshift("[" + "error" + "]");
 62 | 
 63 |     let s = args.map((x) => x.toString()).join(" ");
 64 |     this.output(s);
 65 |   }
 66 | 
 67 |   initRecv() {
 68 |     // 监听父进程的输入
 69 |     this.init_recv_flag = true;
 70 |     process.stdin.on("data", (data) => {
 71 |       reply.recvCallback(data.toString());
 72 |     });
 73 |   }
 74 | 
 75 |   recvCallback(data) {
 76 |     let data_ob = JSON.parse(data);
 77 |     let c = data_ob.c;
 78 |     let need_call = this.operWaitMap.get(c);
 79 |     if (need_call) {
 80 |       need_call.callback(need_call.arg, data_ob);
 81 |     }
 82 |   }
 83 | 
 84 |   getOperCookie() {
 85 |     this.operCookie = this.operCookie + 1;
 86 |     return this.operCookie;
 87 |   }
 88 | 
 89 |   remote_post(oper_data, cb) {
 90 |     if (!this.init_recv_flag) {
 91 |       this.initRecv();
 92 |     }
 93 |     let c = this.getOperCookie();
 94 |     this.operWaitMap.set(c, { arg: oper_data, callback: cb });
 95 | 
 96 |     let msg = { oper: "requireOper", cookie: c, arg: oper_data };
 97 |     this.output(JSON.stringify(msg));
 98 |   }
 99 | }
100 | 
101 | export const reply: Reply = new Reply();
102 | 


--------------------------------------------------------------------------------
/src/rpc/utils/commUtils.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * https://github.com/ccagml/leetcode-extension/src/rpc/commUtils.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Wednesday, November 16th 2022, 4:50:55 pm
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | // import { storageUtils } from "./storageUtils";
11 | 
12 | class CommUtils {
13 |   KEYS;
14 |   constructor() {
15 |     this.KEYS = {
16 |       user: "../user",
17 |       stat: "../stat",
18 |       plugins: "../../plugins",
19 |       problems: "problems",
20 |       translation: "translationConfig",
21 |       ranting_path: "../../rating",
22 |       hints_path: "../../hints",
23 |       problem: (p) => p.fid + "." + p.slug + "." + p.category,
24 |     };
25 |   }
26 | 
27 |   getNameByLevel(level: any) {
28 |     switch (level) {
29 |       case 1:
30 |         return "Easy";
31 |       case 2:
32 |         return "Medium";
33 |       case 3:
34 |         return "Hard";
35 |       default:
36 |         return " ";
37 |     }
38 |   }
39 | 
40 |   // readStdin(cb) {
41 |   //   const stdin = process.stdin;
42 |   //   let bufs: Array = [];
43 | 
44 |   //   console.log(
45 |   //     "NOTE: to finish the input, press " + (storageUtils.isWindows() ? " and " : "")
46 |   //   );
47 | 
48 |   //   stdin.on("readable", function () {
49 |   //     const data = stdin.read();
50 |   //     if (data) {
51 |   //       // windows doesn't treat ctrl-D as EOF
52 |   //       if (storageUtils.isWindows() && data.toString() === "\x04\r\n") {
53 |   //         stdin.emit("end");
54 |   //       } else {
55 |   //         bufs.push(data);
56 |   //       }
57 |   //     }
58 |   //   });
59 |   //   stdin.on("end", function () {
60 |   //     cb(null, Buffer.concat(bufs).toString());
61 |   //   });
62 |   //   stdin.on("error", cb);
63 |   // }
64 | 
65 |   getSetCookieValue(resp: any, key: any) {
66 |     const cookies = resp.headers["set-cookie"];
67 |     if (!cookies) return null;
68 | 
69 |     for (let i = 0; i < cookies.length; ++i) {
70 |       const sections = cookies[i].split(";");
71 |       for (let j = 0; j < sections.length; ++j) {
72 |         const kv = sections[j].trim().split("=");
73 |         if (kv[0] === key) return kv[1];
74 |       }
75 |     }
76 |     return null;
77 |   }
78 | 
79 |   printSafeHTTP(msg: any) {
80 |     return msg
81 |       .replace(/(Cookie\s*:\s*)'.*?'/, "$1")
82 |       .replace(/('X-CSRFToken'\s*:\s*)'.*?'/, "$1")
83 |       .replace(/('set-cookie'\s*:\s*)\[.*?\]/, "$1");
84 |   }
85 | }
86 | 
87 | export const commUtils: CommUtils = new CommUtils();
88 | 


--------------------------------------------------------------------------------
/src/rpc/utils/configUtils.ts:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * Filename: https://github.com/ccagml/leetcode-extension/src/rpc/config.ts
  3 |  * Path: https://github.com/ccagml/leetcode-extension
  4 |  * Created Date: Thursday, October 27th 2022, 7:43:29 pm
  5 |  * Author: ccagml
  6 |  *
  7 |  * Copyright (c) 2022 ccagml . All rights reserved.
  8 |  */
  9 | 
 10 | let underscore = require("underscore");
 11 | 
 12 | class Config {
 13 |   LCPTCTX: any;
 14 |   app: any;
 15 |   sys: any;
 16 |   autologin: any;
 17 |   code: any;
 18 |   file: any;
 19 |   color: any;
 20 |   icon: any;
 21 |   network: any;
 22 |   plugins: any;
 23 |   constructor() {
 24 |     this.sys = {
 25 |       categories: ["algorithms", "LCCI", "LCOF", "LCOF2"],
 26 |       langs: [
 27 |         "bash",
 28 |         "c",
 29 |         "cpp",
 30 |         "csharp",
 31 |         "golang",
 32 |         "java",
 33 |         "javascript",
 34 |         "kotlin",
 35 |         "mysql",
 36 |         "php",
 37 |         "python",
 38 |         "python3",
 39 |         "ruby",
 40 |         "rust",
 41 |         "scala",
 42 |         "swift",
 43 |         "typescript",
 44 |       ],
 45 |       my_headers: {
 46 |         User_Agent: 'Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0',
 47 |         Referer: 'https://leetcode.com',
 48 |         Origin: 'https://leetcode.com/',
 49 |         Host: 'leetcode.com',
 50 |         Content_Type: 'application/json',
 51 |         Accept: 'application/json',
 52 |       },
 53 | 
 54 |       urls: {
 55 |         // base urls
 56 |         base: "https://leetcode.com",
 57 |         graphql: "https://leetcode.com/graphql",
 58 |         login: "https://leetcode.com/accounts/login/",
 59 |         // third part login base urls. TODO facebook google
 60 |         github_login: "https://leetcode.com/accounts/github/login/?next=%2F",
 61 |         facebook_login: "https://leetcode.com/accounts/facebook/login/?next=%2F",
 62 |         linkedin_login: "https://leetcode.com/accounts/linkedin_oauth2/login/?next=%2F",
 63 |         // redirect urls
 64 |         leetcode_redirect: "https://leetcode.com/",
 65 |         github_tf_redirect: "https://github.com/sessions/two-factor",
 66 |         // simulate login urls
 67 |         github_login_request: "https://github.com/login",
 68 |         github_session_request: "https://github.com/session",
 69 |         github_tf_session_request: "https://github.com/sessions/two-factor",
 70 |         linkedin_login_request: "https://www.linkedin.com/login",
 71 |         linkedin_session_request: "https://www.linkedin.com/checkpoint/lg/login-submit",
 72 |         // questions urls
 73 |         problems: "https://leetcode.com/api/problems/$category/",
 74 |         problem: "https://leetcode.com/problems/$slug/description/",
 75 |         test: "https://leetcode.com/problems/$slug/interpret_solution/",
 76 |         session: "https://leetcode.com/session/",
 77 |         submit: "https://leetcode.com/problems/$slug/submit/",
 78 |         submissions: "https://leetcode.com/api/submissions/$slug",
 79 |         submission: "https://leetcode.com/submissions/detail/$id/",
 80 |         verify: "https://leetcode.com/submissions/detail/$id/check/",
 81 |         favorites: "https://leetcode.com/list/api/questions",
 82 |         favorite_delete: "https://leetcode.com/list/api/questions/$hash/$id",
 83 |         problem_detail: "",
 84 |         noj_go: "",
 85 |         u: "",
 86 |       },
 87 |     };
 88 | 
 89 |     this.autologin = {
 90 |       enable: false,
 91 |       retry: 2,
 92 |     };
 93 |     this.code = {
 94 |       editor: "vim",
 95 |       lang: "cpp",
 96 |     };
 97 |     this.file = {
 98 |       show: "${fid}.${slug}",
 99 |       submission: "${fid}.${slug}.${sid}.${ac}",
100 |     };
101 |     this.color = {
102 |       enable: true,
103 |       theme: "default",
104 |     };
105 |     this.icon = {
106 |       theme: "",
107 |     };
108 |     this.network = {
109 |       concurrency: 10,
110 |       delay: 1,
111 |     };
112 |     this.plugins = {};
113 |   }
114 | 
115 |   init(ctx) {
116 |     this.LCPTCTX = ctx;
117 |   }
118 | 
119 |   getAll(useronly) {
120 |     const cfg = underscore.extendOwn({}, this);
121 |     if (useronly) delete cfg.sys;
122 |     return cfg;
123 |   }
124 | 
125 |   isCN() {
126 |     return this.app == "leetcode.cn"
127 |   }
128 | 
129 |   fix_cn() {
130 |     this.app = "leetcode.cn";
131 |     this.sys.urls.base = "https://leetcode.cn";
132 |     this.sys.urls.login = "https://leetcode.cn/accounts/login/";
133 |     this.sys.urls.problems = "https://leetcode.cn/api/problems/$category/";
134 |     this.sys.urls.problem = "https://leetcode.cn/problems/$slug/description/";
135 |     this.sys.urls.graphql = "https://leetcode.cn/graphql";
136 |     this.sys.urls.problem_detail = "https://leetcode.cn/graphql";
137 |     this.sys.urls.test = "https://leetcode.cn/problems/$slug/interpret_solution/";
138 |     this.sys.urls.session = "https://leetcode.cn/session/";
139 |     this.sys.urls.submit = "https://leetcode.cn/problems/$slug/submit/";
140 |     this.sys.urls.submissions = "https://leetcode.cn/api/submissions/$slug";
141 |     this.sys.urls.submission = "https://leetcode.cn/submissions/detail/$id/";
142 |     this.sys.urls.verify = "https://leetcode.cn/submissions/detail/$id/check/";
143 |     this.sys.urls.favorites = "https://leetcode.cn/list/api/questions";
144 |     this.sys.urls.favorite_delete = "https://leetcode.cn/list/api/questions/$hash/$id";
145 |     this.sys.urls.noj_go = "https://leetcode.cn/graphql/noj-go/";
146 |     this.sys.urls.u = "https://leetcode.cn/u/$username/";
147 |     this.sys.urls.github_login = "https://leetcode.cn/accounts/github/login/?next=%2F";
148 |     this.sys.urls.linkedin_login = "https://leetcode.cn/accounts/linkedin_oauth2/login/?next=%2F";
149 |     this.sys.urls.leetcode_redirect = "https://leetcode.cn/";
150 |     this.sys.my_headers = {
151 |       User_Agent: 'Mozilla/5.0 (X11; Linux x86_64; rv:123.0) Gecko/20100101 Firefox/123.0',
152 |       Referer: 'https://leetcode.cn',
153 |       Origin: 'https://leetcode.cn/',
154 |       Host: 'leetcode.cn',
155 |       Content_Type: 'application/json',
156 |       Accept: 'application/json',
157 |     }
158 |   }
159 | }
160 | 
161 | export const configUtils: Config = new Config();
162 | 


--------------------------------------------------------------------------------
/src/rpc/utils/queueUtils.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * Filename: https://github.com/ccagml/leetcode-extension/src/rpc/queue.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Thursday, October 27th 2022, 7:43:29 pm
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | let underscore = require("underscore");
11 | 
12 | import { configUtils } from "./configUtils";
13 | 
14 | export class Queue {
15 |   tasks;
16 |   ctx;
17 |   onTask;
18 |   error;
19 |   concurrency;
20 |   onDone;
21 |   constructor(tasks, ctx, onTask) {
22 |     this.tasks = underscore.clone(tasks) || [];
23 |     this.ctx = ctx || {};
24 |     this.onTask = onTask;
25 |     this.error = null;
26 |   }
27 | 
28 |   addTask(task) {
29 |     this.tasks.push(task);
30 |     return this;
31 |   }
32 | 
33 |   addTasks(tasks) {
34 |     this.tasks = this.tasks.concat(tasks);
35 |     return this;
36 |   }
37 | 
38 |   run(concurrency?, onDone?) {
39 |     this.concurrency = concurrency || configUtils.network.concurrency || 1;
40 |     this.onDone = onDone;
41 | 
42 |     const self = this;
43 |     for (let i = 0; i < this.concurrency; ++i) {
44 |       setImmediate(function () {
45 |         self.workerRun();
46 |       });
47 |     }
48 |   }
49 | 
50 |   workerRun() {
51 |     // no more tasks, quit now
52 |     if (this.tasks.length === 0) {
53 |       if (--this.concurrency === 0 && this.onDone) this.onDone(this.error, this.ctx);
54 |       return;
55 |     }
56 | 
57 |     const task = this.tasks.shift();
58 |     const self = this;
59 |     this.onTask(task, self, function (e) {
60 |       if (e) self.error = e;
61 | 
62 |       // TODO: could retry failed task here.
63 |       setImmediate(function () {
64 |         self.workerRun();
65 |       });
66 |     });
67 |   }
68 | }
69 | 


--------------------------------------------------------------------------------
/src/rpc/utils/sessionUtils.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * Filename: https://github.com/ccagml/leetcode-extension/src/rpc/session.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Thursday, October 27th 2022, 7:43:29 pm
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | let moment_out = require("moment");
11 | let underscore = require("underscore");
12 | 
13 | import { storageUtils } from "./storageUtils";
14 | import { configUtils } from "./configUtils";
15 | import { commUtils } from "./commUtils";
16 | 
17 | class Session {
18 |   errors = {
19 |     EXPIRED: {
20 |       msg: "session expired, please login again",
21 |       statusCode: -1,
22 |       code: -1000,
23 |     },
24 |   };
25 |   argv: any = {};
26 |   constructor() {}
27 |   public getUser = function () {
28 |     return storageUtils.getCache(commUtils.KEYS.user);
29 |   };
30 | 
31 |   public saveUser = function (user) {
32 |     // when auto login enabled, have to save password to re-login later
33 |     // otherwise don't dump password for the sake of security.
34 |     const _user = underscore.omit(user, configUtils.autologin.enable ? [] : ["pass"]);
35 |     storageUtils.setCache(commUtils.KEYS.user, _user);
36 |   };
37 | 
38 |   public deleteUser = function () {
39 |     storageUtils.delCache(commUtils.KEYS.user);
40 |   };
41 | 
42 |   public deleteCodingSession = function () {
43 |     storageUtils.delCache(commUtils.KEYS.problems);
44 |   };
45 | 
46 |   public isLogin() {
47 |     return this.getUser() !== null;
48 |   }
49 | 
50 |   public updateStat = function (k, v) {
51 |     // TODO: use other storage if too many stat data
52 |     const today = moment_out().format("YYYY-MM-DD");
53 |     const stats = storageUtils.getCache(commUtils.KEYS.stat) || {};
54 |     const stat = (stats[today] = stats[today] || {});
55 | 
56 |     if (k.endsWith(".set")) {
57 |       const s = new Set(stat[k] || []);
58 |       s.add(v);
59 |       stat[k] = Array.from(s);
60 |     } else {
61 |       stat[k] = (stat[k] || 0) + v;
62 |     }
63 |     storageUtils.setCache(commUtils.KEYS.stat, stats);
64 |   };
65 | }
66 | export const sessionUtils: Session = new Session();
67 | 


--------------------------------------------------------------------------------
/src/service/BaseWebviewService.ts:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * Filename: https://github.com/ccagml/leetcode-extension/src/webview/BaseWebViewService.ts
 3 |  * Path: https://github.com/ccagml/leetcode-extension
 4 |  * Created Date: Thursday, October 27th 2022, 7:43:29 pm
 5 |  * Author: ccagml
 6 |  *
 7 |  * Copyright (c) 2022 ccagml . All rights reserved.
 8 |  */
 9 | 
10 | import { commands, ConfigurationChangeEvent, Disposable, ViewColumn, WebviewPanel, window, workspace } from "vscode";
11 | import { markdownService } from "./MarkdownService";
12 | import { IWebViewOption } from "../model/ConstDefind";
13 | import { openSettingsEditor } from "../utils/ConfigUtils";
14 | import { promptHintMessage } from "../utils/OutputUtils";
15 | 
16 | export abstract class BaseWebViewService implements Disposable {
17 |   protected readonly viewType: string = "leetcode.webview";
18 |   protected panel: WebviewPanel | undefined;
19 |   private listeners: Disposable[] = [];
20 | 
21 |   public dispose(): void {
22 |     if (this.panel) {
23 |       this.panel.dispose();
24 |     }
25 |   }
26 | 
27 |   protected showWebviewInternal(): void {
28 |     const { title, viewColumn, preserveFocus } = this.getWebviewOption();
29 |     if (!this.panel) {
30 |       this.panel = window.createWebviewPanel(
31 |         this.viewType,
32 |         title,
33 |         { viewColumn, preserveFocus },
34 |         {
35 |           enableScripts: true,
36 |           enableCommandUris: true,
37 |           enableFindWidget: true,
38 |           retainContextWhenHidden: true,
39 |           localResourceRoots: markdownService.localResourceRoots,
40 |         }
41 |       );
42 |       this.panel.onDidDispose(this.onDidDisposeWebview, this, this.listeners);
43 |       this.panel.webview.onDidReceiveMessage(this.onDidReceiveMessage, this, this.listeners);
44 |       workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.listeners);
45 |     } else {
46 |       this.panel.title = title;
47 |       if (viewColumn === ViewColumn.Two) {
48 |         // Make sure second group exists. See vscode#71608 issue
49 |         commands.executeCommand("workbench.action.focusSecondEditorGroup").then(() => {
50 |           this.panel!.reveal(viewColumn, preserveFocus);
51 |         });
52 |       } else {
53 |         this.panel.reveal(viewColumn, preserveFocus);
54 |       }
55 |     }
56 |     this.panel.webview.html = this.getWebviewContent();
57 |     this.showMarkdownConfigHint();
58 |   }
59 | 
60 |   protected onDidDisposeWebview(): void {
61 |     this.panel = undefined;
62 |     for (const listener of this.listeners) {
63 |       listener.dispose();
64 |     }
65 |     this.listeners = [];
66 |   }
67 | 
68 |   protected async onDidChangeConfiguration(event: ConfigurationChangeEvent): Promise {
69 |     if (this.panel && event.affectsConfiguration("markdown")) {
70 |       this.panel.webview.html = this.getWebviewContent();
71 |     }
72 |   }
73 | 
74 |   protected async onDidReceiveMessage(_message: any): Promise {
75 |     /* no special rule */
76 |   }
77 | 
78 |   protected abstract getWebviewOption(): IWebViewOption;
79 | 
80 |   protected abstract getWebviewContent(): string;
81 | 
82 |   private async showMarkdownConfigHint(): Promise {
83 |     await promptHintMessage(
84 |       "hint.configWebviewMarkdown",
85 |       'You can change the webview appearance ("fontSize", "lineWidth" & "fontFamily") in "markdown.preview" configuration.',
86 |       "Open settings",
87 |       (): Promise => openSettingsEditor("markdown.preview")
88 |     );
89 |   }
90 | }
91 | 


--------------------------------------------------------------------------------
/src/service/MarkdownService.ts:
--------------------------------------------------------------------------------
  1 | /*
  2 |  * Filename: https://github.com/ccagml/leetcode-extension/src/service/markdownService.ts
  3 |  * Path: https://github.com/ccagml/leetcode-extension
  4 |  * Created Date: Thursday, October 27th 2022, 7:43:29 pm
  5 |  * Author: ccagml
  6 |  *
  7 |  * Copyright (c) 2022 ccagml . All rights reserved.
  8 |  */
  9 | 
 10 | import * as hljs from "highlight.js";
 11 | import * as MarkdownIt from "markdown-it";
 12 | import * as MarkDownItKatex from "markdown-it-katex";
 13 | import * as os from "os";
 14 | import * as path from "path";
 15 | import * as vscode from "vscode";
 16 | import { BABA, BabaStr } from "../BABA";
 17 | import { isWindows } from "../utils/SystemUtils";
 18 | 
 19 | class MarkdownService implements vscode.Disposable {
 20 |   private engine: MarkdownIt;
 21 |   private config: MarkdownConfiguration;
 22 |   private listener: vscode.Disposable;
 23 | 
 24 |   public constructor() {
 25 |     this.reload();
 26 |     this.listener = vscode.workspace.onDidChangeConfiguration((event: vscode.ConfigurationChangeEvent) => {
 27 |       if (event.affectsConfiguration("markdown")) {
 28 |         this.reload();
 29 |       }
 30 |     }, this);
 31 |   }
 32 | 
 33 |   public get localResourceRoots(): vscode.Uri[] {
 34 |     return [
 35 |       vscode.Uri.file(path.join(this.config.extRoot, "media")),
 36 |       vscode.Uri.file(path.join(__dirname, "..", "..", "..", "resources", "katexcss")),
 37 |     ];
 38 |   }
 39 | 
 40 |   public dispose(): void {
 41 |     this.listener.dispose();
 42 |   }
 43 | 
 44 |   public reload(): void {
 45 |     this.engine = this.initEngine();
 46 |     this.config = new MarkdownConfiguration();
 47 |   }
 48 | 
 49 |   public render(md: string, env?: any): string {
 50 |     return this.engine.render(md, env);
 51 |   }
 52 | 
 53 |   public getStyles(panel: vscode.WebviewPanel | undefined): string {
 54 |     return [this.getBuiltinStyles(panel), this.getDefaultStyle()].join(os.EOL);
 55 |   }
 56 | 
 57 |   private getBuiltinStyles(panel: vscode.WebviewPanel | undefined): string {
 58 |     let styles: vscode.Uri[] = [];
 59 |     try {
 60 |       const stylePaths: string[] = require(path.join(this.config.extRoot, "package.json"))["contributes"][
 61 |         "markdown.previewStyles"
 62 |       ];
 63 |       styles = stylePaths.map((p: string) =>
 64 |         vscode.Uri.file(path.join(this.config.extRoot, p))
 65 |       );
 66 |     } catch (error) {
 67 |       BABA.getProxy(BabaStr.LogOutputProxy).get_log().appendLine("[Error] Fail to load built-in markdown style file.");
 68 |     }
 69 |     let bbb = styles
 70 |       .map((style: vscode.Uri) => ``)
 71 |       .join(os.EOL);
 72 |     return bbb
 73 |   }
 74 | 
 75 |   private getDefaultStyle(): string {
 76 |     return [
 77 |       ``,
 84 |     ].join(os.EOL);
 85 |   }
 86 | 
 87 |   private initEngine(): MarkdownIt {
 88 |     const md: MarkdownIt = new MarkdownIt({
 89 |       linkify: true,
 90 |       typographer: true,
 91 |       highlight: (code: string, lang?: string): string => {
 92 |         switch (lang && lang.toLowerCase()) {
 93 |           case "mysql":
 94 |             lang = "sql";
 95 |             break;
 96 |           case "json5":
 97 |             lang = "json";
 98 |             break;
 99 |           case "python3":
100 |             lang = "python";
101 |             break;
102 |         }
103 |         if (lang && hljs.getLanguage(lang)) {
104 |           try {
105 |             return hljs.highlight(lang, code, true).value;
106 |           } catch (error) {
107 |             /* do not highlight */
108 |           }
109 |         }
110 |         return ""; // use external default escaping
111 |       },
112 |     });
113 | 
114 |     md.use(MarkDownItKatex);
115 |     this.addCodeBlockHighlight(md);
116 |     this.addImageUrlCompletion(md);
117 |     this.addLinkValidator(md);
118 |     return md;
119 |   }
120 | 
121 |   private addCodeBlockHighlight(md: MarkdownIt): void {
122 |     const codeBlock: MarkdownIt.TokenRender = md.renderer.rules["code_block"];
123 |     // tslint:disable-next-line:typedef
124 |     md.renderer.rules["code_block"] = (tokens, idx, options, env, self) => {
125 |       // if any token uses lang-specified code fence, then do not highlight code block
126 |       if (tokens.some((token: any) => token.type === "fence")) {
127 |         return codeBlock(tokens, idx, options, env, self);
128 |       }
129 |       // otherwise, highlight with default lang in env object.
130 |       const highlighted: string = options.highlight(tokens[idx].content, env.lang);
131 |       return [
132 |         `
`,
133 |         highlighted || md.utils.escapeHtml(tokens[idx].content),
134 |         "
", 135 | ].join(os.EOL); 136 | }; 137 | } 138 | 139 | private addImageUrlCompletion(md: MarkdownIt): void { 140 | const image: MarkdownIt.TokenRender = md.renderer.rules["image"]; 141 | // tslint:disable-next-line:typedef 142 | md.renderer.rules["image"] = (tokens, idx, options, env, self) => { 143 | const imageSrc: string[] | undefined = tokens[idx].attrs.find((value: string[]) => value[0] === "src"); 144 | if (env.host && imageSrc && imageSrc[1].startsWith("/")) { 145 | imageSrc[1] = `${env.host}${imageSrc[1]}`; 146 | } 147 | return image(tokens, idx, options, env, self); 148 | }; 149 | } 150 | 151 | private addLinkValidator(md: MarkdownIt): void { 152 | const validateLink: (link: string) => boolean = md.validateLink; 153 | md.validateLink = (link: string): boolean => { 154 | // support file:// protocal link 155 | return validateLink(link) || link.startsWith("file:"); 156 | }; 157 | } 158 | } 159 | 160 | // tslint:disable-next-line: max-classes-per-file 161 | class MarkdownConfiguration { 162 | public readonly extRoot: string; // root path of vscode built-in markdown extension 163 | public readonly lineHeight: number; 164 | public readonly fontSize: number; 165 | public readonly fontFamily: string; 166 | 167 | public constructor() { 168 | const markdownConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("markdown", null); 169 | this.extRoot = path.join(vscode.env.appRoot, "extensions", "markdown-language-features"); 170 | this.lineHeight = Math.max(0.6, +markdownConfig.get("preview.lineHeight", NaN)); 171 | this.fontSize = Math.max(8, +markdownConfig.get("preview.fontSize", NaN)); 172 | this.fontFamily = this.resolveFontFamily(markdownConfig); 173 | } 174 | 175 | private resolveFontFamily(config: vscode.WorkspaceConfiguration): string { 176 | let fontFamily: string = config.get("preview.fontFamily", ""); 177 | if (isWindows() && fontFamily === config.inspect("preview.fontFamily")!.defaultValue) { 178 | fontFamily = `${fontFamily}, 'Microsoft Yahei UI'`; 179 | } 180 | return fontFamily; 181 | } 182 | } 183 | 184 | export const markdownService: MarkdownService = new MarkdownService(); 185 | -------------------------------------------------------------------------------- /src/todayData/TodayDataModule.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Filename: /home/cc/leetcode-extension/src/todayData/TodayDataModule.ts 3 | * Path: /home/cc/leetcode-extension 4 | * Created Date: Tuesday, October 24th 2023, 20:06:29 pm 5 | * Author: ccagml 6 | * 7 | * Copyright (c) 2023 ccagml . All rights reserved 8 | */ 9 | 10 | import { BABAMediator, BABAProxy, BabaStr, BaseCC, BABA } from "../BABA"; 11 | import { ISubmitEvent, OutPutType } from "../model/ConstDefind"; 12 | import { ITodayDataResponse } from "../model/TreeNodeModel"; 13 | import { isUseEndpointTranslation } from "../utils/ConfigUtils"; 14 | import { promptForSignIn, ShowMessage } from "../utils/OutputUtils"; 15 | import { getDayEnd, getDayNow, getDayStart } from "../utils/SystemUtils"; 16 | 17 | class TodayData { 18 | fidInfo: Map = new Map(); 19 | 20 | setFidInfo(data) { 21 | this.fidInfo.set(data.fid, data); 22 | } 23 | 24 | getFidInfo(fid) { 25 | return this.fidInfo.get(fid); 26 | } 27 | 28 | async checkNeedReadNew() { 29 | const day_start = getDayStart(); //获取当天零点的时间 30 | const day_end = getDayEnd(); //获取当天23:59:59的时间 31 | let need_get_today: boolean = true; 32 | 33 | let cur_time = getDayNow(); 34 | if (cur_time > day_start + 600 || cur_time < day_start + 300) { 35 | return; 36 | } 37 | 38 | this.fidInfo.forEach((value) => { 39 | if (day_start <= value.time && value.time <= day_end) { 40 | need_get_today = false; 41 | } 42 | }); 43 | if (need_get_today) { 44 | await BABA.getProxy(BabaStr.TodayDataProxy).searchToday(); 45 | } 46 | } 47 | public async checkSubmit(e: ISubmitEvent) { 48 | if (e.sub_type == "submit" && e.accepted) { 49 | if (this.getFidInfo(e.fid)) { 50 | let cur_info = this.getFidInfo(e.fid); 51 | if (cur_info?.userStatus != "FINISH") { 52 | setTimeout(() => { 53 | BABA.getProxy(BabaStr.TodayDataProxy).searchToday(); 54 | }, 5000); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | const todayData: TodayData = new TodayData(); 62 | 63 | export class TodayDataProxy extends BABAProxy { 64 | static NAME = BabaStr.TodayDataProxy; 65 | constructor() { 66 | super(TodayDataProxy.NAME); 67 | } 68 | 69 | public getAllTodayData() { 70 | return todayData.fidInfo; 71 | } 72 | 73 | public getTodayData(fid) { 74 | return todayData.getFidInfo(fid); 75 | } 76 | 77 | public async searchToday(): Promise { 78 | let sbp = BABA.getProxy(BabaStr.StatusBarProxy); 79 | if (!sbp.getUser()) { 80 | promptForSignIn(); 81 | return; 82 | } 83 | try { 84 | const needTranslation: boolean = isUseEndpointTranslation(); 85 | const solution: string = await BABA.getProxy(BabaStr.ChildCallProxy) 86 | .get_instance() 87 | .getTodayQuestion(needTranslation); 88 | const query_result = JSON.parse(solution); 89 | // "{\"titleSlug\":\"number-of-dice-rolls-with-target-sum\",\"questionId\":\"1263\",\"fid\":\"1155\",\"userStatus\":\"NOT_START\"}\n" 90 | 91 | // const titleSlug: string = query_result.titleSlug 92 | // const questionId: string = query_result.questionId 93 | const fid: string = query_result.fid; 94 | if (fid) { 95 | let data: any = {}; 96 | data.titleSlug = query_result.titleSlug; 97 | data.questionId = query_result.questionId; 98 | data.fid = query_result.fid; 99 | data.userStatus = query_result.userStatus; 100 | data.time = Math.floor(Date.now() / 1000); 101 | todayData.setFidInfo(data); 102 | 103 | BABA.sendNotification(BabaStr.TreeData_searchTodayFinish); 104 | } 105 | } catch (error) { 106 | BABA.getProxy(BabaStr.LogOutputProxy).get_log().appendLine(error.toString()); 107 | await ShowMessage("Failed to fetch today question. 请查看控制台信息~", OutPutType.error); 108 | } 109 | } 110 | } 111 | 112 | export class TodayDataMediator extends BABAMediator { 113 | static NAME = BabaStr.TodayDataMediator; 114 | constructor() { 115 | super(TodayDataMediator.NAME); 116 | } 117 | 118 | listNotificationInterests(): string[] { 119 | return [ 120 | BabaStr.VSCODE_DISPOST, 121 | BabaStr.StartReadData, 122 | BabaStr.CommitResult_showFinish, 123 | BabaStr.BABACMD_refresh, 124 | BabaStr.every_minute, 125 | ]; 126 | } 127 | async handleNotification(_notification: BaseCC.BaseCC.INotification) { 128 | switch (_notification.getName()) { 129 | case BabaStr.VSCODE_DISPOST: 130 | break; 131 | case BabaStr.StartReadData: 132 | await BABA.getProxy(BabaStr.TodayDataProxy).searchToday(); 133 | break; 134 | case BabaStr.CommitResult_showFinish: 135 | todayData.checkSubmit(_notification.getBody()); 136 | break; 137 | case BabaStr.BABACMD_refresh: 138 | BABA.getProxy(BabaStr.TodayDataProxy).searchToday(); 139 | break; 140 | case BabaStr.every_minute: 141 | await todayData.checkNeedReadNew(); 142 | break; 143 | default: 144 | break; 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/treeColor/TreeColorModule.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Filename: /home/cc/leetcode-extension/src/treeColor/TreeColorModule.ts 3 | * Path: /home/cc/leetcode-extension 4 | * Created Date: Thursday, October 19th 2023, 00:40:45 am 5 | * Author: ccagml 6 | * 7 | * Copyright (c) 2023 ccagml . All rights reserved 8 | */ 9 | 10 | import { URLSearchParams } from "url"; 11 | import { 12 | FileDecoration, 13 | FileDecorationProvider, 14 | ProviderResult, 15 | ThemeColor, 16 | Uri, 17 | workspace, 18 | WorkspaceConfiguration, 19 | } from "vscode"; 20 | import { BABA, BabaStr } from "../BABA"; 21 | import { is_problem_by_nodeType, TreeNodeType } from "../model/TreeNodeModel"; 22 | import { getDayNow, getYMD } from "../utils/SystemUtils"; 23 | 24 | export class TreeColor implements FileDecorationProvider { 25 | private readonly ITEM_COLOR: { [key: string]: ThemeColor } = { 26 | easy: new ThemeColor("charts.green"), 27 | medium: new ThemeColor("charts.yellow"), 28 | hard: new ThemeColor("charts.red"), 29 | 30 | green: new ThemeColor("charts.green"), // 图表中绿色元素的颜色。 // 低于玩家分数 200分 31 | blue: new ThemeColor("charts.blue"), // 图表中蓝色元素的颜色。 // 低于玩家分数 50 - 199分 32 | purple: new ThemeColor("charts.purple"), // 图表中紫色元素的颜色。// 高于玩家50 到低于49 33 | yellow: new ThemeColor("charts.yellow"), // 图表中黄色元素的颜色。 // 高于玩家50 - 199 34 | red: new ThemeColor("charts.red"), // 图表中红色元素的颜色。 // 高于200 35 | }; 36 | 37 | public provideFileDecoration(uri: Uri): ProviderResult { 38 | if (!this.isDifficultyBadgeEnabled()) { 39 | return; 40 | } 41 | 42 | // 不是插件的上色点 43 | if (uri.scheme !== "lcpr") { 44 | return 45 | } 46 | if (is_problem_by_nodeType(uri.authority)) { 47 | return this.leafColor(uri); 48 | } 49 | 50 | // 看是不是日期节点 51 | if (Number(uri.authority) == TreeNodeType.Bricks_NeedReview_Day) { 52 | return this.NeedReview_Day_Color(uri) 53 | } 54 | return; 55 | } 56 | // 复习过期颜色 57 | private NeedReview_Day_Color(uri: Uri): ProviderResult { 58 | const params: URLSearchParams = new URLSearchParams(uri.query); 59 | const groupTimeStr: string = params.get("groupTime") || "0"; 60 | const groupTime = Number(groupTimeStr) 61 | 62 | const file_color: FileDecoration = {}; 63 | if (groupTime > 0) { 64 | let cur_time = getDayNow() 65 | if (cur_time > (groupTime + 86400)) { 66 | file_color.color = this.ITEM_COLOR.red; 67 | file_color.tooltip = `已过期${getYMD(groupTime)}`; 68 | } 69 | } 70 | return file_color; 71 | } 72 | // 叶子的颜色既问题难度分的颜色 73 | private leafColor(uri: Uri): ProviderResult { 74 | const params: URLSearchParams = new URLSearchParams(uri.query); 75 | // const difficulty: string = params.get("difficulty")!.toLowerCase(); 76 | const score: string = params.get("score") || "0"; 77 | // const user_score: string = params.get("user_score") || "0"; 78 | const user_score = BABA.getProxy(BabaStr.StatusBarProxy).getUserContestScore(); 79 | 80 | const file_color: FileDecoration = {}; 81 | const score_num = Number(score); 82 | const user_score_num = Number(user_score); 83 | if (score_num > 0) { 84 | if (user_score_num > 0) { 85 | const diff_num = score_num - user_score_num; 86 | // green: new ThemeColor("charts.green"), // 图表中绿色元素的颜色。 // 低于玩家分数 200分 87 | // blue: new ThemeColor("charts.blue"), // 图表中蓝色元素的颜色。 // 低于玩家分数 50 - 199分 88 | // purple: new ThemeColor("charts.purple"), // 图表中紫色元素的颜色。// 高于玩家50 到低于49 89 | // yellow: new ThemeColor("charts.yellow"), // 图表中黄色元素的颜色。 // 高于玩家50 - 199 90 | // red: new ThemeColor("charts.red"), // 图表中红色元素的颜色。 // 高于200 91 | if (diff_num < -200) { 92 | file_color.color = this.ITEM_COLOR.green; 93 | file_color.tooltip = "秒杀难度"; 94 | } else if (diff_num < -50) { 95 | file_color.color = this.ITEM_COLOR.blue; 96 | file_color.tooltip = "热身难度"; 97 | } else if (diff_num < 50) { 98 | file_color.color = this.ITEM_COLOR.purple; 99 | file_color.tooltip = "普通难度"; 100 | } else if (diff_num < 199) { 101 | file_color.color = this.ITEM_COLOR.yellow; 102 | file_color.tooltip = "吃力难度"; 103 | } else { 104 | file_color.color = this.ITEM_COLOR.red; 105 | file_color.tooltip = "劝退难度"; 106 | } 107 | } else { 108 | file_color.tooltip = "还没有竞赛分"; 109 | } 110 | } else { 111 | const difficulty: string = params.get("difficulty") || "0"; 112 | if (difficulty == "Easy") { 113 | file_color.color = this.ITEM_COLOR.green; 114 | file_color.tooltip = "简单难度"; 115 | } else if (difficulty == "Medium") { 116 | file_color.color = this.ITEM_COLOR.yellow; 117 | file_color.tooltip = "中等难度"; 118 | } else if (difficulty == "Hard") { 119 | file_color.color = this.ITEM_COLOR.red; 120 | file_color.tooltip = "困难难度"; 121 | } 122 | } 123 | return file_color; 124 | } 125 | 126 | private isDifficultyBadgeEnabled(): boolean { 127 | const configuration: WorkspaceConfiguration = workspace.getConfiguration("leetcode-problem-rating"); 128 | return configuration.get("colorizeProblems", false); 129 | } 130 | } 131 | 132 | export const treeColor: TreeColor = new TreeColor(); 133 | -------------------------------------------------------------------------------- /src/utils/OutputUtils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Filename: https://github.com/ccagml/leetcode-extension/src/utils/OutputUtils.ts 3 | * Path: https://github.com/ccagml/leetcode-extension 4 | * Created Date: Thursday, October 27th 2022, 7:43:29 pm 5 | * Author: ccagml 6 | * 7 | * Copyright (c) 2022 ccagml . All rights reserved. 8 | */ 9 | 10 | import * as vscode from "vscode"; 11 | import { BABA, BabaStr } from "../BABA"; 12 | import { DialogOptions, OutPutType } from "../model/ConstDefind"; 13 | import { getLeetCodeEndpoint, getVsCodeConfig } from "./ConfigUtils"; 14 | 15 | export async function openUrl(url: string): Promise { 16 | vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(url)); 17 | } 18 | 19 | export async function promptHintMessage( 20 | config: string, 21 | message: string, 22 | choiceConfirm: string, 23 | onConfirm: () => Promise 24 | ): Promise { 25 | if (getVsCodeConfig().get(config)) { 26 | const choiceNoShowAgain: string = "Don't show again"; 27 | const choice: string | undefined = await vscode.window.showInformationMessage( 28 | message, 29 | choiceConfirm, 30 | choiceNoShowAgain 31 | ); 32 | if (choice === choiceConfirm) { 33 | await onConfirm(); 34 | } else if (choice === choiceNoShowAgain) { 35 | await getVsCodeConfig().update(config, false, true /* UserSetting */); 36 | } 37 | } 38 | } 39 | 40 | export async function promptForSignIn(): Promise { 41 | const choice: vscode.MessageItem | undefined = await vscode.window.showInformationMessage( 42 | "Please sign in to LeetCode.", 43 | DialogOptions.yes, 44 | DialogOptions.no, 45 | DialogOptions.singUp 46 | ); 47 | switch (choice) { 48 | case DialogOptions.yes: 49 | await vscode.commands.executeCommand("lcpr.signin"); 50 | break; 51 | case DialogOptions.singUp: 52 | if (getLeetCodeEndpoint()) { 53 | openUrl("https://leetcode.cn"); 54 | } else { 55 | openUrl("https://leetcode.com"); 56 | } 57 | break; 58 | default: 59 | break; 60 | } 61 | } 62 | 63 | export async function ShowMessage(message: string, type: OutPutType): Promise { 64 | let result: vscode.MessageItem | undefined; 65 | switch (type) { 66 | case OutPutType.info: 67 | result = await vscode.window.showInformationMessage(message, DialogOptions.open, DialogOptions.no); 68 | break; 69 | case OutPutType.warning: 70 | result = await vscode.window.showWarningMessage(message, DialogOptions.open, DialogOptions.no); 71 | break; 72 | case OutPutType.error: 73 | result = await vscode.window.showErrorMessage(message, DialogOptions.open, DialogOptions.no); 74 | break; 75 | default: 76 | break; 77 | } 78 | 79 | if (result === DialogOptions.open) { 80 | BABA.getProxy(BabaStr.LogOutputProxy).get_log().show(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/utils/problemUtils.ts: -------------------------------------------------------------------------------- 1 | import * as fse from "fs-extra"; 2 | import * as path from "path"; 3 | import * as vscode from "vscode"; 4 | 5 | import { useWsl, isWindows, usingCmd } from "./SystemUtils"; 6 | 7 | const beforeStubReg: RegExp = /@lcpr-before-debug-begin([\s\S]*?)@lcpr-before-debug-end/; 8 | const afterStubReg: RegExp = /@lcpr-after-debug-begin([\s\S]*?)@lcpr-after-debug-end/; 9 | 10 | interface IExtensionState { 11 | context: vscode.ExtensionContext; 12 | cachePath: string; 13 | } 14 | 15 | export interface IDebugConfig { 16 | type: string; 17 | program?: string; 18 | env?: { 19 | [key: string]: any; 20 | }; 21 | [x: string]: any; 22 | } 23 | 24 | export interface IProblemType { 25 | funName: string; 26 | paramTypes: string[]; 27 | returnType: string; 28 | testCase?: string; 29 | specialFunName?: { 30 | [x: string]: string; 31 | }; 32 | } 33 | 34 | export interface IDebugResult { 35 | type: "success" | "error"; 36 | message: string; 37 | problemNum: number; 38 | language: string; 39 | filePath: string; 40 | testString: string; 41 | } 42 | 43 | export const extensionState: IExtensionState = { 44 | context: null as any, 45 | cachePath: "", 46 | }; 47 | 48 | export const languages: string[] = [ 49 | "bash", 50 | "c", 51 | "cpp", 52 | "csharp", 53 | "golang", 54 | "java", 55 | "javascript", 56 | "kotlin", 57 | "mysql", 58 | "php", 59 | "python", 60 | "python3", 61 | "ruby", 62 | "rust", 63 | "scala", 64 | "swift", 65 | ]; 66 | 67 | export const langExt: Map = new Map([ 68 | ["bash", "sh"], 69 | ["c", "c"], 70 | ["cpp", "cpp"], 71 | ["csharp", "cs"], 72 | ["golang", "go"], 73 | ["java", "java"], 74 | ["javascript", "js"], 75 | ["kotlin", "kt"], 76 | ["mysql", "sql"], 77 | ["php", "php"], 78 | ["python", "py"], 79 | ["python3", "py"], 80 | ["ruby", "rb"], 81 | ["rust", "rs"], 82 | ["scala", "scala"], 83 | ["swift", "swift"], 84 | ]); 85 | 86 | export const supportDebugLanguages: string[] = ["javascript", "python3", "cpp"]; 87 | 88 | export interface ProblemMeta { 89 | id: string; 90 | lang: string; 91 | } 92 | 93 | export function genFileExt(language: string): string { 94 | const ext: string | undefined = langExt.get(language); 95 | if (!ext) { 96 | throw new Error(`The language "${language}" is not supported.`); 97 | } 98 | return ext; 99 | } 100 | 101 | export function fileMeta(content: string): ProblemMeta | null { 102 | const result: RegExpExecArray | null = /@lc app=(.*) id=(.*|\w*|\W*|[\\u4e00-\\u9fa5]*) lang=(.*)/.exec(content); 103 | if (result != null) { 104 | return { 105 | id: result[2], 106 | lang: result[3], 107 | }; 108 | } 109 | return null; 110 | } 111 | 112 | export async function getUnstubedFile(filePath: string): Promise { 113 | const content: string = (await fse.readFile(filePath)).toString(); 114 | const stripped: string = content.replace(beforeStubReg, "").replace(afterStubReg, ""); 115 | 116 | if (content.length === stripped.length) { 117 | // no stub, return original filePath 118 | return filePath; 119 | } 120 | 121 | const meta: { id: string; lang: string } | null = fileMeta(content); 122 | if (meta == null) { 123 | vscode.window.showErrorMessage( 124 | "File meta info has been changed, please check the content: '@lc app=leetcode.cn id=xx lang=xx'." 125 | ); 126 | throw new Error(""); 127 | } 128 | 129 | const newPath: string = path.join(extensionState.cachePath, `${meta.id}-${meta.lang}`); 130 | await fse.writeFile(newPath, stripped); 131 | return newPath; 132 | } 133 | 134 | export async function getProblemSpecialCode( 135 | language: string, 136 | problem: string, 137 | fileExt: string, 138 | extDir: string 139 | ): Promise { 140 | const problemPath: string = path.join(extDir, "resources/debug/entry", language, "problems", `${problem}.${fileExt}`); 141 | const isSpecial: boolean = await fse.pathExists(problemPath); 142 | if (isSpecial) { 143 | const specialContent: Buffer = await fse.readFile(problemPath); 144 | return specialContent.toString(); 145 | } 146 | if (language === "cpp") { 147 | return ""; 148 | } 149 | const fileContent: Buffer = await fse.readFile( 150 | path.join(extDir, "resources/debug/entry", language, "problems", `common.${fileExt}`) 151 | ); 152 | return fileContent.toString(); 153 | } 154 | 155 | export async function getEntryFile(language: string, problem: string): Promise { 156 | const extDir: string = vscode.extensions.getExtension("ccagml.vscode-leetcode-problem-rating")!.extensionPath; 157 | const fileExt: string = genFileExt(language); 158 | const specialCode: string = await getProblemSpecialCode(language, problem, fileExt, extDir); 159 | const tmpEntryCode: string = ( 160 | await fse.readFile(path.join(extDir, "resources/debug/entry", language, `entry.${fileExt}`)) 161 | ).toString(); 162 | const entryCode: string = tmpEntryCode.replace(/\/\/ @@stub-for-code@@/, specialCode); 163 | const entryPath: string = path.join(extensionState.cachePath, `${language}problem${problem}.${fileExt}`); 164 | await fse.writeFile(entryPath, entryCode); 165 | return entryPath; 166 | } 167 | 168 | export function parseTestString(test: string): string { 169 | if (useWsl() || !isWindows()) { 170 | return `'${test}'`; 171 | } 172 | 173 | // In windows and not using WSL 174 | if (usingCmd()) { 175 | return `"${test.replace(/"/g, '\\"')}"`; 176 | } else { 177 | // Assume using PowerShell 178 | return `'${test.replace(/"/g, '\\"')}'`; 179 | } 180 | } 181 | 182 | export function randomString(len: number): string { 183 | len = len || 32; 184 | const $chars: string = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"; 185 | const maxPos: number = $chars.length; 186 | let pwd: string = ""; 187 | for (let i: number = 0; i < len; i++) { 188 | pwd += $chars.charAt(Math.floor(Math.random() * maxPos)); 189 | } 190 | return pwd; 191 | } 192 | -------------------------------------------------------------------------------- /src/utils/testHot.ts: -------------------------------------------------------------------------------- 1 | export function getSubmissionResult() { 2 | return JSON.stringify({ 3 | messages: ["Finished"], 4 | system_message: { 5 | fid: "1796", 6 | id: 1904, 7 | qid: 1904, 8 | sub_type: "test", 9 | accepted: true, 10 | }, 11 | "Your Input": ['"dfa12321afd"'], 12 | "Output (0 ms)": ["1"], 13 | "Expected Answer": ["2"], 14 | Stdout: [""], 15 | }); 16 | } 17 | 18 | export function test_add_table(sections) { 19 | sections.push(`\n\n\n### aaaaaa\n`); 20 | sections.push(`| a1a1 | a2a2 |\n| :---------: | :---------: |\n| s1s1 | s2s2 | `); 21 | sections.push(`| __\`aaaaaaaaa\`__ | bbbbbbbbbbb | `); 22 | sections.push(`| __\`ccccccccccccc\`__ | __\`ddddddddddtext\`__ | `); 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": ".", 11 | "noUnusedLocals": true, 12 | "noImplicitThis": true, 13 | "noImplicitReturns": true, 14 | "strictNullChecks": true, 15 | "noUnusedParameters": true, 16 | "alwaysStrict": true, 17 | // Tells TypeScript to read JS files, as 18 | // normally they are ignored as source files 19 | // "allowJs": true, 20 | // Generate d.ts files 21 | // "declaration": true, 22 | // // This compiler run should 23 | // // only output d.ts files 24 | // "emitDeclarationOnly": true, 25 | // "declarationMap": true 26 | }, 27 | "exclude": [ 28 | "node_modules", 29 | ".vscode-test" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "forin": false, 9 | "object-literal-sort-keys": false, 10 | "indent": [ 11 | true, 12 | "spaces" 13 | ], 14 | "no-string-literal": false, 15 | "no-namespace": false, 16 | "max-line-length": [ 17 | false, 18 | 120 19 | ], 20 | "typedef": [ 21 | true, 22 | "call-signature", 23 | "arrow-call-signature", 24 | "parameter", 25 | "arrow-parameter", 26 | "property-declaration", 27 | "variable-declaration", 28 | "member-variable-declaration" 29 | ], 30 | "variable-name": [ 31 | true, 32 | "allow-leading-underscore" 33 | ] 34 | }, 35 | "rulesDirectory": [] 36 | } 37 | --------------------------------------------------------------------------------