diff --git a/package-lock.json b/package-lock.json index 2ac17cf..aa5d515 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-leetcode", - "version": "0.18.1", + "version": "0.18.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-leetcode", - "version": "0.18.1", + "version": "0.18.4", "license": "MIT", "dependencies": { "axios": "^1.6.8", diff --git a/package.json b/package.json index 53552b7..427d885 100644 --- a/package.json +++ b/package.json @@ -141,6 +141,11 @@ "title": "Sort Problems", "category": "LeetCode", "icon": "$(sort-precedence)" + }, + { + "command": "leetcode.pickDaily", + "title": "Pick Daily Chanllenge", + "category": "LeetCode" } ], "viewsContainers": { @@ -223,6 +228,11 @@ "command": "leetcode.removeFavorite", "when": "view == leetCodeExplorer && viewItem == problem-favorite", "group": "inline" + }, + { + "command": "leetcode.pickDaily", + "when": "view == leetCodeExplorer", + "group": "leetcode@4" } ], "commandPalette": [ @@ -705,6 +715,12 @@ "default": true, "scope": "application", "description": "Allow LeetCode to report anonymous usage data to improve the product." + }, + "leetcode.codeSnippets": { + "type": "string", + "default": "", + "scope": "application", + "description": "Custom code snippets for LeetCode problems." } } } diff --git a/src/commands/plugin.ts b/src/commands/plugin.ts index d2ed4b6..94e9ff4 100644 --- a/src/commands/plugin.ts +++ b/src/commands/plugin.ts @@ -4,7 +4,7 @@ import * as vscode from "vscode"; import { leetCodeTreeDataProvider } from "../explorer/LeetCodeTreeDataProvider"; import { leetCodeExecutor } from "../leetCodeExecutor"; -import { IQuickItemEx } from "../shared"; +import { getEndpoint, IQuickItemEx } from "../shared"; import { Endpoint, SortingStrategy } from "../shared"; import { DialogType, promptForOpenOutputChannel, promptForSignIn } from "../utils/uiUtils"; import { deleteCache } from "./cache"; @@ -50,8 +50,7 @@ export async function switchEndpoint(): Promise { } export function getLeetCodeEndpoint(): string { - const leetCodeConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("leetcode"); - return leetCodeConfig.get("endpoint", Endpoint.LeetCode); + return getEndpoint(); } const SORT_ORDER: SortingStrategy[] = [ diff --git a/src/commands/show.ts b/src/commands/show.ts index eccf557..122d479 100644 --- a/src/commands/show.ts +++ b/src/commands/show.ts @@ -30,6 +30,7 @@ import { leetCodeSolutionProvider } from "../webview/leetCodeSolutionProvider"; import * as list from "./list"; import { getLeetCodeEndpoint } from "./plugin"; import { globalState } from "../globalState"; +import { queryDailyChallenge } from "../request/query-daily-challange"; export async function previewProblem(input: IProblem | vscode.Uri, isSideMode: boolean = false): Promise { let node: IProblem; @@ -70,6 +71,16 @@ export async function pickOne(): Promise { await showProblemInternal(randomProblem); } +export async function pickDailyChallenge(): Promise { + const dailyChallengeID: string = await queryDailyChallenge(); + const node: IProblem | undefined = explorerNodeManager.getNodeById(dailyChallengeID); + if (!node) { + vscode.window.showErrorMessage(`No daily challenge found for today.`); + return; + } + await showProblemInternal(node); +} + export async function showProblem(node?: LeetCodeNode): Promise { if (!node) { return; diff --git a/src/explorer/LeetCodeTreeDataProvider.ts b/src/explorer/LeetCodeTreeDataProvider.ts index 9c29894..f6adc72 100644 --- a/src/explorer/LeetCodeTreeDataProvider.ts +++ b/src/explorer/LeetCodeTreeDataProvider.ts @@ -85,6 +85,8 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider = new Map(); @@ -53,6 +54,10 @@ class ExplorerNodeManager implements Disposable { id: Category.Favorite, name: Category.Favorite, }), false), + new LeetCodeNode(Object.assign({}, defaultProblem, { + id: Category.Daily, + name: Category.Daily, + }), false) ]; } @@ -148,6 +153,11 @@ class ExplorerNodeManager implements Disposable { return this.applySortingStrategy(res); } + public async getDailyChallengeNode(): Promise { + const dailyChallengeID: string = await queryDailyChallenge(); + return this.getNodeById(dailyChallengeID) ? [this.getNodeById(dailyChallengeID)!] : []; + } + public dispose(): void { this.explorerNodeMap.clear(); this.companySet.clear(); diff --git a/src/extension.ts b/src/extension.ts index 439673f..7a899e0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -97,7 +97,8 @@ export async function activate(context: vscode.ExtensionContext): Promise vscode.commands.registerCommand("leetcode.switchDefaultLanguage", () => switchDefaultLanguage()), vscode.commands.registerCommand("leetcode.addFavorite", (node: LeetCodeNode) => star.addFavorite(node)), vscode.commands.registerCommand("leetcode.removeFavorite", (node: LeetCodeNode) => star.removeFavorite(node)), - vscode.commands.registerCommand("leetcode.problems.sort", () => plugin.switchSortingStrategy()) + vscode.commands.registerCommand("leetcode.problems.sort", () => plugin.switchSortingStrategy()), + vscode.commands.registerCommand("leetcode.pickDaily", () => show.pickDailyChallenge()) ); await leetCodeExecutor.switchEndpoint(plugin.getLeetCodeEndpoint()); diff --git a/src/leetCodeExecutor.ts b/src/leetCodeExecutor.ts index d2332c7..3f11247 100644 --- a/src/leetCodeExecutor.ts +++ b/src/leetCodeExecutor.ts @@ -1,6 +1,7 @@ // Copyright (c) jdneo. All rights reserved. // Licensed under the MIT license. +import * as vscode from "vscode"; import * as cp from "child_process"; import * as fse from "fs-extra"; import * as os from "os"; @@ -110,7 +111,19 @@ class LeetCodeExecutor implements Disposable { if (!await fse.pathExists(filePath)) { await fse.createFile(filePath); - const codeTemplate: string = await this.executeCommandWithProgressEx("Fetching problem data...", this.nodeExecutable, cmd); + let codeTemplate: string = await this.executeCommandWithProgressEx("Fetching problem data...", this.nodeExecutable, cmd); + const lines = codeTemplate.split(/\r?\n/); + const targetIndex = lines.findIndex(line => line.includes('// @lc code=start')); + const codeSnippet: string = vscode.workspace.getConfiguration('leetcode').get('codeSnippets', ''); + if (targetIndex !== -1 && codeSnippet.trim() !== '') { + let insertIndex = targetIndex; + while (insertIndex - 1 >= 0 && lines[insertIndex - 1].trim() === '') { + lines.splice(insertIndex - 1, 1); + insertIndex--; + } + lines.splice(insertIndex, 0, codeSnippet); + } + codeTemplate = lines.join('\n'); await fse.writeFile(filePath, codeTemplate); } } diff --git a/src/request/query-daily-challange.ts b/src/request/query-daily-challange.ts new file mode 100644 index 0000000..251b888 --- /dev/null +++ b/src/request/query-daily-challange.ts @@ -0,0 +1,57 @@ +import {getUrl, getEndpoint, Endpoint} from "../shared"; +import {LcAxios} from "../utils/httpUtils"; +import {AxiosResponse} from "axios"; + + +export const getDailyQueryStr = (): string => { + const dailyQueryStrs = { + LeetCode: ` + query questionOfToday { + activeDailyCodingChallengeQuestion { + question { + frontendQuestionId: questionFrontendId + } + } + } + `, + LeetCodeCN: ` + query questionOfToday { + todayRecord { + question { + frontendQuestionId: questionFrontendId + } + } + } + ` + } + const point: string = getEndpoint(); + switch (point) { + case Endpoint.LeetCodeCN: + return dailyQueryStrs.LeetCodeCN; + case Endpoint.LeetCode: + return dailyQueryStrs.LeetCode; + } + return ""; +} + +export const getDailyProblemID = (res: AxiosResponse): string => { + const point = getEndpoint(); + switch (point) { + case Endpoint.LeetCodeCN: + return res.data.data.todayRecord[0].question.frontendQuestionId; + case Endpoint.LeetCode: + return res.data.data.todayRecord[0].question.frontendQuestionId; + } + return ""; +} + +export const queryDailyChallenge = async (): Promise => { + return LcAxios(getUrl("graphql"), { + method: "POST", + data: { + query: getDailyQueryStr(), + variables: {}, + operationName: 'questionOfToday' + }, + }).then((res) => getDailyProblemID(res)); +}; diff --git a/src/shared.ts b/src/shared.ts index e8b59d8..e155e04 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -101,6 +101,7 @@ export enum Category { Tag = "Tag", Company = "Company", Favorite = "Favorite", + Daily = "Daily Challenge" } export const supportedPlugins: string[] = ["company", "solution.discuss", "leetcode.cn"]; @@ -146,8 +147,7 @@ export const urlsCn = { }; export const getUrl = (key: string) => { - const leetCodeConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("leetcode"); - const point = leetCodeConfig.get("endpoint", Endpoint.LeetCode); + const point = getEndpoint(); switch (point) { case Endpoint.LeetCodeCN: return urlsCn[key]; @@ -156,3 +156,8 @@ export const getUrl = (key: string) => { return urls[key]; } }; + +export const getEndpoint = (): string => { + const leetCodeConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("leetcode"); + return leetCodeConfig.get("endpoint", Endpoint.LeetCode); +}