diff --git a/CHANGELOG.md b/CHANGELOG.md index deff3a3..84cf254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## version 2.3.1 + +- 状态栏增加简单的计时器 + ## version 2.2.4 - 优化说明简介 diff --git a/README.md b/README.md index 5d9566c..3c17c3e 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ - 从[zerotrac.github.io](https://zerotrac.github.io/leetcode_problem_rating/data.json)获取数据进行缓存,数据更新时,可以尝试使用 deleteAllCache,重新获取数据 - [新增区块测试用例](#区块测试用例) - [新增搬砖功能(重复练习?)](#搬砖功能的说明) +- [状态栏增加简易计时器](#状态栏增加简易计时器) # 关于本项目 @@ -64,17 +65,22 @@ - 简单的比较这些用例字符串是否相同 +## 状态栏增加简易计时器 + +- 查看一个题目时会开始计时,提交一个题目通过后会停止计时 + ## 搬砖功能的说明 diff --git a/package.json b/package.json index 7cc8925..3cd455d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-leetcode-problem-rating", "displayName": "LeetCode", "description": "LeetCode 官方插件增强, 代码开源, 增加 LeetCode 题目难度分, 给个star吧, 球球了", - "version": "2.2.4", + "version": "2.3.1", "author": "ccagml", "publisher": "ccagml", "license": "MIT", @@ -182,6 +182,44 @@ "title": "Sort Problems", "category": "LeetCode", "icon": "$(sort-precedence)" + }, + { + "command": "mywiki.commentcreateNote", + "title": "Create Note", + "enablement": "!commentIsEmpty" + }, + { + "command": "mywiki.commentreplyNote", + "title": "Reply", + "enablement": "!commentIsEmpty" + }, + { + "command": "mywiki.commenteditNote", + "title": "Edit", + "icon": { + "dark": "resources/edit_inverse.svg", + "light": "resources/edit.svg" + } + }, + { + "command": "mywiki.commentdeleteNoteComment", + "title": "Delete", + "icon": { + "dark": "resources/close_inverse.svg", + "light": "resources/close.svg" + } + }, + { + "command": "mywiki.commentsaveNote", + "title": "Save" + }, + { + "command": "mywiki.commentcancelsaveNote", + "title": "Cancel" + }, + { + "command": "mywiki.commentdispose", + "title": "Remove All Notes" } ], "viewsContainers": { @@ -206,6 +244,57 @@ ] }, "menus": { + "commandPalette": [ + { + "command": "mywiki.commentcreateNote", + "when": "false" + }, + { + "command": "mywiki.commentreplyNote", + "when": "false" + }, + { + "command": "mywiki.commentdeleteNoteComment", + "when": "false" + } + ], + "comments/commentThread/title": [], + "comments/commentThread/context": [ + { + "command": "mywiki.commentcreateNote", + "group": "inline", + "when": "commentController == comment-sample && commentThreadIsEmpty" + }, + { + "command": "mywiki.commentreplyNote", + "group": "inline", + "when": "commentController == comment-sample && !commentThreadIsEmpty" + } + ], + "comments/comment/title": [ + { + "command": "mywiki.commenteditNote", + "group": "group@1", + "when": "commentController == comment-sample" + }, + { + "command": "mywiki.commentdeleteNoteComment", + "group": "group@2", + "when": "commentController == comment-sample && comment == canDelete" + } + ], + "comments/comment/context": [ + { + "command": "mywiki.commentcancelsaveNote", + "group": "inline@1", + "when": "commentController == comment-sample" + }, + { + "command": "mywiki.commentsaveNote", + "group": "inline@2", + "when": "commentController == comment-sample" + } + ], "view/title": [ { "command": "lcpr.toggleLeetCodeCn", diff --git a/resources/close.svg b/resources/close.svg new file mode 100644 index 0000000..fde3440 --- /dev/null +++ b/resources/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/close_inverse.svg b/resources/close_inverse.svg new file mode 100644 index 0000000..d88aa12 --- /dev/null +++ b/resources/close_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/edit.svg b/resources/edit.svg new file mode 100644 index 0000000..ecde924 --- /dev/null +++ b/resources/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/edit_inverse.svg b/resources/edit_inverse.svg new file mode 100644 index 0000000..da956cb --- /dev/null +++ b/resources/edit_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/controller/EventController.ts b/src/controller/EventController.ts index 658cfc1..1089baa 100644 --- a/src/controller/EventController.ts +++ b/src/controller/EventController.ts @@ -8,6 +8,7 @@ */ import { eventService } from "../service/EventService"; +import { statusBarTimeService } from "../service/StatusBarTimeService"; // 事件的控制器 /* The EventController class has a method called addEvent that calls the addEvent method on the eventService class */ @@ -22,6 +23,10 @@ class EventContorller { public addEvent() { eventService.addEvent(); } + + public every_second() { + statusBarTimeService.updateSecond(); + } } export const eventController: EventContorller = new EventContorller(); diff --git a/src/controller/TreeViewController.ts b/src/controller/TreeViewController.ts index 648b4cf..58a3971 100644 --- a/src/controller/TreeViewController.ts +++ b/src/controller/TreeViewController.ts @@ -769,6 +769,12 @@ class TreeViewController implements Disposable { if (descriptionConfig.showInWebview) { promises.push(this.showDescriptionView(node)); } + promises.push( + new Promise(async (resolve, _) => { + await eventService.emit("showProblemFinish", node); + resolve(1); + }) + ); await Promise.all(promises); } catch (error) { diff --git a/src/dao/bricksDao.ts b/src/dao/bricksDao.ts index 4ed8503..edd5c28 100644 --- a/src/dao/bricksDao.ts +++ b/src/dao/bricksDao.ts @@ -143,12 +143,14 @@ class BricksDao { public async addSubmitTimeByQid(qid: string) { let temp_data = await this.getInfoByQid(qid); let submit_time = temp_data.submit_time || []; - submit_time.push(getDayNow()); + let submit_now = getDayNow(); + submit_time.push(submit_now); temp_data.submit_time = submit_time; if (!temp_data.type) { temp_data.type = BricksType.TYPE_2; } await this.setInfoByQid(qid, temp_data); + return submit_now; } public async setTypeByQid(qid: string, type) { let temp_data = await this.getInfoByQid(qid); diff --git a/src/extension.ts b/src/extension.ts index 5de553b..2a24103 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,7 +7,27 @@ * Copyright (c) 2022 ccagml . All rights reserved. */ -import { ExtensionContext, window, commands, Uri } from "vscode"; +import { + ExtensionContext, + window, + commands, + Uri, + // MarkdownString, + // CommentMode, + // CommentAuthorInformation, + // CommentThread, + // Comment, + // CommentReply, + // CommentThreadCollapsibleState, + // CommentReaction, + // CommentController, + // CommentingRangeProvider, + // CommentOptions, + // TextDocument, + // CancellationToken, + // comments, + // Range, +} from "vscode"; import { fileButtonController } from "./controller/FileButtonController"; import { treeViewController } from "./controller/TreeViewController"; import { NodeModel } from "./model/NodeModel"; @@ -27,12 +47,30 @@ import { getLeetCodeEndpoint } from "./utils/ConfigUtils"; import { BricksType, OutPutType } from "./model/Model"; import { bricksDataService } from "./service/BricksDataService"; import { bricksViewController } from "./controller/BricksViewController"; +import { statusBarTimeService } from "./service/StatusBarTimeService"; +// let commentId = 1; + +// class NoteComment implements Comment { +// id: number; +// label: string | undefined; +// constructor( +// public body: string | MarkdownString, +// public mode: CommentMode, +// public author: CommentAuthorInformation, +// public parent?: CommentThread, +// public contextValue?: string +// ) { +// this.id = ++commentId; +// } +// } // 激活插件 /** * The main function of the extension. It is called when the extension is activated. * @param {ExtensionContext} context - ExtensionContext */ + +let lcpr_timer; export async function activate(context: ExtensionContext): Promise { try { // 初始化控制器 @@ -51,6 +89,7 @@ export async function activate(context: ExtensionContext): Promise { solutionService, executeService, markdownService, + statusBarTimeService, fileButtonController, treeViewController, window.registerFileDecorationProvider(treeItemDecorationService), @@ -76,6 +115,9 @@ export async function activate(context: ExtensionContext): Promise { commands.registerCommand("lcpr.addFavorite", (node: NodeModel) => treeViewController.addFavorite(node)), commands.registerCommand("lcpr.removeFavorite", (node: NodeModel) => treeViewController.removeFavorite(node)), commands.registerCommand("lcpr.problems.sort", () => treeViewController.switchSortingStrategy()), + commands.registerCommand("lcpr.statusBarTime.start", () => statusBarTimeService.start()), + commands.registerCommand("lcpr.statusBarTime.stop", () => statusBarTimeService.stop()), + commands.registerCommand("lcpr.statusBarTime.reset", () => statusBarTimeService.reset()), commands.registerCommand("lcpr.setBricksType0", (node: NodeModel) => bricksViewController.setBricksType(node, BricksType.TYPE_0) ), @@ -104,12 +146,128 @@ export async function activate(context: ExtensionContext): Promise { // 获取登录状态 await loginContorller.getLoginStatus(); await bricksViewController.initialize(); + // let aaa; + // // A `CommentController` is able to provide comments for documents. + // const commentController = comments.createCommentController("comment-sample", "Comment API Sample"); + // commentController.commentingRangeProvider = { + // provideCommentingRanges: (document: TextDocument, token: CancellationToken) => { + // let lineCount = document.lineCount; + // aaa = commentController.createCommentThread(document.uri, new Range(5, 0, 10, 0), []); + // aaa.dispose = () => { + // let cac = aaa; + // console.log("ssss"); + // }; + // console.log(aaa); + // return undefined; + // }, + // }; + // let bbb; + // commands.registerCommand("lcpr.previewProblem", (a, b, c) => { + // bbb = commentController.createCommentThread(a, new Range(5, 0, 10, 0), [ + // new NoteComment("ssss", CommentMode.Preview, { name: "vscode" }), + // // new NoteComment("bbbb", CommentMode.Preview, { name: "vscode" }), + // ]); + // }), + // context.subscriptions.push(commentController); + + // context.subscriptions.push( + // commands.registerCommand("mywiki.commentcreateNote", (reply: CommentReply) => { + // replyNote(reply); + // }) + // ); + + // context.subscriptions.push( + // commands.registerCommand("mywiki.commentreplyNote", (reply: CommentReply) => { + // replyNote(reply); + // }) + // ); + + // context.subscriptions.push( + // commands.registerCommand("mywiki.commentdeleteNoteComment", (comment: NoteComment) => { + // let thread = comment.parent; + // if (!thread) { + // return; + // } + + // thread.comments = thread.comments.filter((cmt) => (cmt as NoteComment).id !== comment.id); + + // if (thread.comments.length === 0) { + // thread.dispose(); + // } + // }) + // ); + + // context.subscriptions.push( + // commands.registerCommand("mywiki.commentcancelsaveNote", (comment: NoteComment) => { + // if (!comment.parent) { + // return; + // } + + // comment.parent.comments = comment.parent.comments.map((cmt) => { + // if ((cmt as NoteComment).id === comment.id) { + // cmt.mode = CommentMode.Preview; + // } + + // return cmt; + // }); + // }) + // ); + + // context.subscriptions.push( + // commands.registerCommand("mywiki.commentsaveNote", (comment: NoteComment) => { + // if (!comment.parent) { + // return; + // } + + // comment.parent.comments = comment.parent.comments.map((cmt) => { + // if ((cmt as NoteComment).id === comment.id) { + // cmt.mode = CommentMode.Preview; + // } + + // return cmt; + // }); + // }) + // ); + + // context.subscriptions.push( + // commands.registerCommand("mywiki.commenteditNote", (comment: NoteComment) => { + // if (!comment.parent) { + // return; + // } + + // comment.parent.comments = comment.parent.comments.map((cmt) => { + // if ((cmt as NoteComment).id === comment.id) { + // cmt.mode = CommentMode.Editing; + // } + + // return cmt; + // }); + // }) + // ); + + // function replyNote(reply: CommentReply) { + // let thread = reply.thread; + // let newComment = new NoteComment( + // reply.text, + // CommentMode.Preview, + // { name: "vscode" }, + // thread, + // thread.comments.length ? "canDelete" : undefined + // ); + // if (thread.contextValue === "draft") { + // newComment.label = "pending"; + // } + + // thread.comments = [...thread.comments, newComment]; + // } } catch (error) { logOutput.appendLine(error.toString()); promptForOpenOutputChannel( "Extension initialization failed. Please open output channel for details.", OutPutType.error ); + } finally { + lcpr_timer = setInterval(eventController.every_second, 1000); } } @@ -119,4 +277,8 @@ export function deactivate(): void { let a = 0; console.log(a); } + if (lcpr_timer != undefined) { + clearInterval(lcpr_timer); + lcpr_timer = undefined; + } } diff --git a/src/service/EventService.ts b/src/service/EventService.ts index 7ced495..0565c03 100644 --- a/src/service/EventService.ts +++ b/src/service/EventService.ts @@ -9,11 +9,12 @@ import { EventEmitter } from "events"; -import { UserStatus } from "../model/Model"; +import { IProblem, UserStatus } from "../model/Model"; import { ISubmitEvent } from "../model/Model"; import { statusBarService } from "../service/StatusBarService"; import { treeDataService } from "../service/TreeDataService"; import { bricksDataService } from "./BricksDataService"; +import { statusBarTimeService } from "./StatusBarTimeService"; class EventService extends EventEmitter { constructor() { @@ -34,6 +35,7 @@ class EventService extends EventEmitter { this.on("submit", (e: ISubmitEvent) => { treeDataService.checkSubmit(e); bricksDataService.checkSubmit(e); + statusBarTimeService.checkSubmit(e); }); this.on("searchUserContest", (tt) => { @@ -46,6 +48,10 @@ class EventService extends EventEmitter { this.on("explorerNodeMapSet", () => { bricksDataService.refresh(); }); + + this.on("showProblemFinish", (node: IProblem) => { + statusBarTimeService.showProblemFinish(node); + }); } } diff --git a/src/service/StatusBarService.ts b/src/service/StatusBarService.ts index 7d5f321..1d95a0e 100644 --- a/src/service/StatusBarService.ts +++ b/src/service/StatusBarService.ts @@ -72,7 +72,7 @@ class StatusBarService implements Disposable { } constructor() { - this.instance = window.createStatusBarItem(); + this.instance = window.createStatusBarItem(undefined, 999); this.setStatusBarVisibility(); this.currentUser = undefined; this.userStatus = UserStatus.SignedOut; diff --git a/src/service/StatusBarTimeService.ts b/src/service/StatusBarTimeService.ts new file mode 100644 index 0000000..d9ea7dd --- /dev/null +++ b/src/service/StatusBarTimeService.ts @@ -0,0 +1,143 @@ +/* + * Filename: /home/cc/vscode-leetcode-problem-rating/src/service/StatusBarTimeService.ts + * Path: /home/cc/vscode-leetcode-problem-rating + * Created Date: Saturday, November 26th 2022, 2:14:53 pm + * Author: ccagml + * + * Copyright (c) 2022 ccagml . All rights reserved. + */ + +import { Disposable, StatusBarItem, window } from "vscode"; +import { IProblem, ISubmitEvent, OutPutType } from "../model/Model"; +import { promptForOpenOutputChannel } from "../utils/OutputUtils"; +import { getDayNow } from "../utils/SystemUtils"; + +// 状态栏工具 +class StatusBarTimeService implements Disposable { + private showBar: StatusBarItem; + private startBar: StatusBarItem; + private stopBar: StatusBarItem; + private resetBar: StatusBarItem; + private startTime: number; + private saveTime: number; + private questionNode: IProblem; + + constructor() { + this.showBar = window.createStatusBarItem(undefined, 1004); + this.showBar.show(); + + this.startBar = window.createStatusBarItem(undefined, 1003); + this.startBar.name = "start"; + this.startBar.text = "start"; + this.startBar.tooltip = "开始计时"; + this.startBar.show(); + this.startBar.command = "lcpr.statusBarTime.start"; + + this.stopBar = window.createStatusBarItem(undefined, 1002); + this.stopBar.name = "stop"; + this.stopBar.text = "stop"; + this.stopBar.tooltip = "暂停计时"; + this.stopBar.show(); + this.stopBar.command = "lcpr.statusBarTime.stop"; + + this.resetBar = window.createStatusBarItem(undefined, 1001); + this.resetBar.name = "reset"; + this.resetBar.text = "reset"; + this.resetBar.tooltip = "重置计时"; + this.resetBar.show(); + this.resetBar.command = "lcpr.statusBarTime.reset"; + + this.startTime = 0; + this.saveTime = 0; + } + + public showProblemFinish(node) { + this.questionNode = node; + this.reset(); + this.start(); + } + + private getDiffStr(diff: number) { + if (diff < 1) { + return "0:0:0"; + } else { + let second = diff % 60; + diff = Math.floor(diff / 60); + let minute = diff % 60; + diff = Math.floor(diff / 60); + let hour = diff; + return `${hour}:${minute}:${second}`; + } + } + + public getCostTimeStr() { + if (this.startTime && this.startTime > 0) { + let diff = getDayNow() - this.startTime + this.saveTime; + return this.getDiffStr(diff); + } + return; + } + + public async checkSubmit(e: ISubmitEvent) { + if (e.sub_type == "submit" && e.accepted) { + let msg = this.getCostTimeStr(); + if (msg) { + promptForOpenOutputChannel(`${e.fid}耗时${msg}`, OutPutType.info); + } + this.stop(); + } + } + + public start() { + this.startTime = getDayNow(); + } + public stop() { + if (this.startTime > 0) { + this.saveTime += getDayNow() - this.startTime; + this.startTime = 0; + } + } + + public reset() { + this.startTime = 0; + this.saveTime = 0; + } + + // 更新状态栏的数据 + public update_instance(): void { + if (this.startTime && this.startTime > 0) { + let diff = getDayNow() - this.startTime + this.saveTime; + let diffstr = this.getDiffStr(diff); + let pre = "做题"; + if (this.questionNode?.id) { + pre = `${this.questionNode?.id}题`; + } + this.showBar.text = `${pre}计时:${diffstr}`; + } else if (this.saveTime > 0) { + let diff = this.saveTime; + let diffstr = this.getDiffStr(diff); + let pre = "做题"; + if (this.questionNode?.id) { + pre = `${this.questionNode?.id}题`; + } + this.showBar.text = `${pre}题计时:${diffstr}`; + } else { + this.showBar.text = `做题计时:${this.getDiffStr(0)}`; + } + } + + // 更新数据 + public updateSecond(): void { + this.update_instance(); + } + + //销毁数据 + public dispose(): void { + this.showBar.dispose(); + this.startBar.dispose(); + this.stopBar.dispose(); + this.resetBar.dispose(); + } +} + +export const statusBarTimeService: StatusBarTimeService = new StatusBarTimeService();