Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Star Problems Extension && All Problems Category#188

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
Vigilans wants to merge10 commits intoLeetCode-OpenSource:masterfromVigilans:enhanced-context
Closed
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
10 commits
Select commitHold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletionspackage.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -94,6 +94,11 @@
"title": "Show Problem",
"category": "LeetCode"
},
{
"command": "leetcode.toggleFavorite",
"title": "Toggle Favorite Problem",
"category": "LeetCode"
},
{
"command": "leetcode.searchProblem",
"title": "Search Problem",
Expand DownExpand Up@@ -164,12 +169,21 @@
"command": "leetcode.showProblem",
"when": "view == leetCodeExplorer && viewItem == problem",
"group": "leetcode@1"
},
{
"command": "leetcode.toggleFavorite",
"when": "view == leetCodeExplorer && viewItem == problem",
"group": "leetcode@1"
}
],
"commandPalette": [
{
"command": "leetcode.showProblem",
"when": "never"
},
{
"command": "leetcode.toggleFavorite",
"when": "never"
}
],
"explorer/context": [
Expand Down
19 changes: 19 additions & 0 deletionssrc/commands/star.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
// Copyright (c) jdneo. All rights reserved.
// Licensed under the MIT license.

import { LeetCodeNode } from "../explorer/LeetCodeNode";
import { LeetCodeTreeDataProvider } from "../explorer/LeetCodeTreeDataProvider";
import { leetCodeExecutor } from "../leetCodeExecutor";
import { IProblem } from "../shared";
import { DialogType, promptForOpenOutputChannel } from "../utils/uiUtils";

export async function toggleFavorite(provider: LeetCodeTreeDataProvider, node: LeetCodeNode): Promise<void> {
try {
const problem: IProblem = Object.assign({}, node.nodeData, {
isFavorite: await leetCodeExecutor.toggleFavorite(node, !node.isFavorite),
});
provider.updateProblem(problem);
} catch (error) {
await promptForOpenOutputChannel("Failed to star the problem. Please open the output channel for details.", DialogType.error);
}
}
34 changes: 21 additions & 13 deletionssrc/explorer/LeetCodeNode.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,19 +4,31 @@
import { IProblem, ProblemState } from "../shared";

export class LeetCodeNode {
constructor(private data: IProblem, private parentNodeName: string, private isProblemNode: boolean = true) { }
constructor(
private data: IProblem,
private parentNodeId: string,
private isProblemNode: boolean = true) { }

public get nodeData(): IProblem {
return this.data;
}

public get isProblem(): boolean {
return this.isProblemNode;
}

public get parentId(): string {
return this.parentNodeId;
}

public get locked(): boolean {
return this.data.locked;
}

public get name(): string {
return this.data.name;
}

public get state(): ProblemState {
return this.data.state;
}

public get id(): string {
return this.data.id;
}
Expand All@@ -37,15 +49,11 @@ export class LeetCodeNode {
return this.data.companies;
}

public get isFavorite(): boolean {
return this.data.isFavorite;
}

public get isProblem(): boolean {
return this.isProblemNode;
public get state(): ProblemState {
return this.data.state;
}

public getparentName():string {
return this.parentNodeName;
public getisFavorite():boolean {
return this.data.isFavorite;
}
}
158 changes: 99 additions & 59 deletionssrc/explorer/LeetCodeTreeDataProvider.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,26 +13,37 @@ import { LeetCodeNode } from "./LeetCodeNode";

export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCodeNode> {

private allProblems: Map<string, IProblem>; // maintains the ownership of all problems.

private treeData: {
Difficulty: Map<string, IProblem[]>,
Tag: Map<string, IProblem[]>,
Company: Map<string, IProblem[]>,
Favorite: IProblem[],
[Category.All]: IProblem[],
[Category.Difficulty]: Map<string, IProblem[]>,
[Category.Tag]: Map<string, IProblem[]>,
[Category.Company]: Map<string, IProblem[]>,
[Category.Favorite]: IProblem[],
};

private onDidChangeTreeDataEvent: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
private onDidChangeTreeDataEvent: vscode.EventEmitter<LeetCodeNode> = new vscode.EventEmitter<LeetCodeNode>();
Copy link
Member

@jdneojdneoMar 8, 2019
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

We can also support refresh the whole explorer, so the parameter type can be written asLeetCodeNode | null | undefined

You can address it in the next PR

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The event fire's parameter isfire(data: T?) where T=LeetCodeNode, so no need to append null and undefined here

// tslint:disable-next-line:member-ordering
public readonly onDidChangeTreeData: vscode.Event<any> = this.onDidChangeTreeDataEvent.event;
public readonly onDidChangeTreeData: vscode.Event<LeetCodeNode> = this.onDidChangeTreeDataEvent.event;

constructor(private context: vscode.ExtensionContext) { }

public async refresh(): Promise<void> {
await this.getProblemData();
await this.getFullProblemData();
this.onDidChangeTreeDataEvent.fire();
}

public async updateProblem(problem: IProblem): Promise<void> {
if (this.allProblems.has(problem.id)) {
this.updateTreeDataByProblem(problem); // only modify the content of tree data, problem is not updated.
Object.assign(this.allProblems.get(problem.id), problem); // update problem, where reference is preserved.
this.onDidChangeTreeDataEvent.fire();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I guess that the reason that explorer returns to uncollapsed state is that, we do not pass the node element intothis.onDidChangeTreeDataEvent.fire() (This API can accept a node element. Then all the data are cleared before the explorer rerendering.

Copy link
ContributorAuthor

@VigilansVigilansMar 6, 2019
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

After some investigations I seems to get the hang of this API. Maybe we can keep another reference container here:

allTreeNodes:Map<string,LeetcodeNode[]>// id -> relative nodes

Then, inupdateProblem function, we can fire events to request all relative nodes to be updated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@Vigilans Cool. Noticed that now the same problem in the different category has different id. So if we refresh theTwo Sum inAll Problem, will theTwo Sum inDifficulty be refreshed?

That means, I'm thinking that if we should make the same problem has the same id in the whole explorer. Not sure the real behavior of it.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

After further investigations, to accomplish this feature, there are some important changes which I think should be discussed in next PR. In this PR, we may temporarily accept the current behavior?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

SoupdateProblem() is not used in this PR, right?

}
}

public getTreeItem(element: LeetCodeNode): vscode.TreeItem | Thenable<vscode.TreeItem> {
if (element.id === "notSignIn") {
if (element.id === "NotSignIn") {
return {
label: element.name,
id: element.id,
Expand All@@ -42,71 +53,75 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
title: "Sign in to LeetCode",
},
};
} else if (!element.isProblem) { // category
return {
label: element.name,
tooltip: this.getSubCategoryTooltip(element),
id: `LeetCode.Category::${element.parentId}.${element.id}`,
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Now that we haveelement.parentId,Data.Now() prefix seems no longer necessary.

Btw, the TreeItem Generation code here is separated here for better readability, as well as convenience for next PR(store generated TreeItem somewhere in TreeProvider or LeetCodeNode)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

So you will send out another PR to removeelement.parentId &Data.Now(), right?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Data.Now() has already been removed here, which is taken place by element.parentId

collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
contextValue: `${element.parentId}.${element.id}`.toLowerCase(),
};
} else { // problem
return {
label: `[${element.id}] ${element.name} ${element.isFavorite ? "♥" : ""}`,
tooltip: "", // TODO: Add Problem Tooltip
id: `LeetCode.Problem::${element.parentId}.${element.id}`,
collapsibleState: vscode.TreeItemCollapsibleState.None,
contextValue: "problem",
iconPath: this.parseIconPathFromProblemState(element),
};
}

const idPrefix: number = Date.now();
return {
label: element.isProblem ? `[${element.id}] ${element.name}` : element.name,
tooltip: this.getSubCategoryTooltip(element),
id: `${idPrefix}.${element.parentName}.${element.id}`,
collapsibleState: element.isProblem ? vscode.TreeItemCollapsibleState.None : vscode.TreeItemCollapsibleState.Collapsed,
contextValue: element.isProblem ? "problem" : element.id.toLowerCase(),
iconPath: this.parseIconPathFromProblemState(element),
};
}

public getChildren(element?: LeetCodeNode | undefined): vscode.ProviderResult<LeetCodeNode[]> {
if (!leetCodeManager.getUser()) {
return [
new LeetCodeNode(Object.assign({}, defaultProblem, {
id: "notSignIn",
id: "NotSignIn",
name: "Sign in to LeetCode",
}), "ROOT", false),
}), "Root", false),
];
}
if (!element) { // Root view
return [
new LeetCodeNode(Object.assign({}, defaultProblem, {
id: Category.Difficulty,
name: Category.Difficulty,
}), "ROOT", false),
new LeetCodeNode(Object.assign({}, defaultProblem, {
id: Category.Tag,
name: Category.Tag,
}), "ROOT", false),
new LeetCodeNode(Object.assign({}, defaultProblem, {
id: Category.Company,
name: Category.Company,
}), "ROOT", false),
new LeetCodeNode(Object.assign({}, defaultProblem, {
id: Category.Favorite,
name: Category.Favorite,
}), "ROOT", false),
];
Category.All,
Category.Difficulty,
Category.Tag,
Category.Company,
Category.Favorite,
].map((c: Category) => new LeetCodeNode(
Object.assign({}, defaultProblem, { id: c, name: c }), "Root", false,
));
} else {
switch (element.name) { // First-level
// First-level
switch (element.name) {
case Category.All:
case Category.Favorite:
const nodes: IProblem[] = this.treeData[Category.Favorite];
return nodes.map((p: IProblem) => new LeetCodeNode(p,Category.Favorite));
const nodes: IProblem[] = this.treeData[element.name];
return nodes.map((p: IProblem) => new LeetCodeNode(p,element.name));
case Category.Difficulty:
case Category.Tag:
case Category.Company:
return this.composeSubCategoryNodes(element);
default: // Second and lower levels
return element.isProblem ? [] : this.composeProblemNodes(element);
}
// Second and lower levels
return element.isProblem ? [] : this.composeProblemNodes(element);
}
}

private asyncgetProblemData(): Promise<void> {
private asyncgetFullProblemData(): Promise<void> {
// clear cache
this.allProblems = new Map<string, IProblem>();
this.treeData = {
Difficulty: new Map<string, IProblem[]>(),
Tag: new Map<string, IProblem[]>(),
Company: new Map<string, IProblem[]>(),
Favorite: [],
[Category.All]: [],
[Category.Difficulty]: new Map<string, IProblem[]>(),
[Category.Tag]: new Map<string, IProblem[]>(),
[Category.Company]: new Map<string, IProblem[]>(),
[Category.Favorite]: [],
};
for (const problem of await list.listProblems()) {
// Add every problem to problem pool
this.allProblems.set(problem.id, problem);
// Add favorite problem, no matter whether it is solved.
if (problem.isFavorite) {
this.treeData[Category.Favorite].push(problem);
Expand All@@ -121,9 +136,9 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
}

private composeProblemNodes(node: LeetCodeNode): LeetCodeNode[] {
const map: Map<string, IProblem[]> | undefined = this.treeData[node.parentName];
const map: Map<string, IProblem[]> | undefined = this.treeData[node.parentId];
if (!map) {
leetCodeChannel.appendLine(`Category: ${node.parentName} is not available.`);
leetCodeChannel.appendLine(`Category: ${node.parentId} is not available.`);
return [];
}
const problems: IProblem[] = map.get(node.name) || [];
Expand All@@ -136,8 +151,8 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod

private composeSubCategoryNodes(node: LeetCodeNode): LeetCodeNode[] {
const category: Category = node.name as Category;
if (category === Category.Favorite) {
leetCodeChannel.appendLine("No sub-level for Favorite nodes");
if (category === Category.All || category === Category.Favorite) {
leetCodeChannel.appendLine("No sub-level forAll orFavorite nodes");
return [];
}
const map: Map<string, IProblem[]> | undefined = this.treeData[category];
Expand All@@ -149,7 +164,7 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
}

private parseIconPathFromProblemState(element: LeetCodeNode): string {
if (!element.isProblem) {
if (!element.isProblem) { // In fact will never be satisfied
return "";
}
switch (element.state) {
Expand All@@ -168,12 +183,23 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
}

private getSubCategoryTooltip(element: LeetCodeNode): string {
// return ''unless itis a sub-category node
if (element.isProblem || !this.treeData[element.parentName]) {
// return ''if itdoes not directly hold problems.
if (element.isProblem) { // In fact will never be satisfied
return "";
}

const problems: IProblem[] = this.treeData[element.parentName].get(element.id);
let problems: IProblem[];
switch (element.name) {
case Category.Difficulty:
case Category.Tag:
case Category.Company:
return "";
case Category.All:
case Category.Favorite:
problems = this.treeData[element.name];
break;
default:
problems = this.treeData[element.parentId].get(element.id);
}

let acceptedNum: number = 0;
let failedNum: number = 0;
Expand All@@ -197,13 +223,27 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
].join(os.EOL);
}

private updateTreeDataByProblem(problem: IProblem): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The method name her is confusing. Cuz we only handleisFavorite here, but the method name looks like we can update all the fields.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I thought we might handle the case of updating othertreeData fields the at a later time. Seems a overdesign here?
Should I leave a comment announcingmodification of other categories is not needed yet here, or just change the method name?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Make sure now other fields will be updated here in the near PRs, e.g. when a problem is accepted, there will be some code here to update other fields.

const origin: IProblem | undefined = this.allProblems.get(problem.id);
if (origin && origin.isFavorite !== problem.isFavorite) {
// Find appropriate index to insert/delete a problem
const problemIndex: number = this.treeData[Category.Favorite].findIndex((p: LeetCodeNode) => Number(p.id) >= Number(problem.id));
if (problem.isFavorite) {
this.treeData[Category.Favorite].splice(problemIndex, 0, origin); // insert original problem's reference as favorite
} else {
this.treeData[Category.Favorite].splice(problemIndex, 1); // delete favorite
}
}
}

private addProblemToTreeData(problem: IProblem): void {
this.putProblemToMap(this.treeData.Difficulty, problem.difficulty, problem);
this.treeData[Category.All].push(problem);
this.putProblemToMap(this.treeData[Category.Difficulty], problem.difficulty, problem);
for (const tag of problem.tags) {
this.putProblemToMap(this.treeData.Tag, this.beautifyCategoryName(tag), problem);
this.putProblemToMap(this.treeData[Category.Tag], this.beautifyCategoryName(tag), problem);
}
for (const company of problem.companies) {
this.putProblemToMap(this.treeData.Company, this.beautifyCategoryName(company), problem);
this.putProblemToMap(this.treeData[Category.Company], this.beautifyCategoryName(company), problem);
}
}

Expand Down
2 changes: 2 additions & 0 deletionssrc/extension.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ import { switchDefaultLanguage } from "./commands/language";
import * as plugin from "./commands/plugin";
import * as session from "./commands/session";
import * as show from "./commands/show";
import * as star from "./commands/star";
import * as submit from "./commands/submit";
import * as test from "./commands/test";
import { LeetCodeNode } from "./explorer/LeetCodeNode";
Expand DownExpand Up@@ -44,6 +45,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
vscode.commands.registerCommand("leetcode.selectSessions", () => session.selectSession()),
vscode.commands.registerCommand("leetcode.createSession", () => session.createSession()),
vscode.commands.registerCommand("leetcode.showProblem", (node: LeetCodeNode) => show.showProblem(node)),
vscode.commands.registerCommand("leetcode.toggleFavorite", (node: LeetCodeNode) => star.toggleFavorite(leetCodeTreeDataProvider, node)),
vscode.commands.registerCommand("leetcode.searchProblem", () => show.searchProblem()),
vscode.commands.registerCommand("leetcode.refreshExplorer", () => leetCodeTreeDataProvider.refresh()),
vscode.commands.registerCommand("leetcode.testSolution", (uri?: vscode.Uri) => test.testSolution(uri)),
Expand Down
10 changes: 10 additions & 0 deletionssrc/leetCodeExecutor.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -87,6 +87,16 @@ class LeetCodeExecutor {
return filePath;
}

public async toggleFavorite(node: IProblem, markStarred: boolean): Promise<boolean> {
let description: string = "";
if (markStarred) {
description = await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "star", node.id]);
} else {
description = await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "star", node.id, "-d"]);
}
return description.includes("♥");
}

public async listSessions(): Promise<string> {
return await this.executeCommandEx("node", [await this.getLeetCodeBinaryPath(), "session"]);
}
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp