11// Copyright (c) jdneo. All rights reserved.
22// Licensed under the MIT license.
33
4- import * as _ from "lodash" ;
54import * as os from "os" ;
65import * as path from "path" ;
76import * as vscode from "vscode" ;
8- import * as list from "../commands/list" ;
9- import { leetCodeChannel } from "../leetCodeChannel" ;
107import { leetCodeManager } from "../leetCodeManager" ;
11- import { Category , defaultProblem , IProblem , ProblemState } from "../shared" ;
12- import { getWorkspaceConfiguration } from "../utils/workspaceUtils " ;
8+ import { Category , defaultProblem , ProblemState } from "../shared" ;
9+ import { explorerNodeManager } from "./explorerNodeManager " ;
1310import { LeetCodeNode } from "./LeetCodeNode" ;
1411
1512export class LeetCodeTreeDataProvider implements vscode . TreeDataProvider < LeetCodeNode > {
1613
17- private treeData :{
18- Difficulty :Map < string , IProblem [ ] > ,
19- Tag :Map < string , IProblem [ ] > ,
20- Company :Map < string , IProblem [ ] > ,
21- Favorite :IProblem [ ] ,
22- } ;
23-
2414private onDidChangeTreeDataEvent :vscode . EventEmitter < any > = new vscode . EventEmitter < any > ( ) ;
2515// tslint:disable-next-line:member-ordering
2616public readonly onDidChangeTreeData :vscode . Event < any > = this . onDidChangeTreeDataEvent . event ;
2717
2818constructor ( private context :vscode . ExtensionContext ) { }
2919
3020public async refresh ( ) :Promise < void > {
31- await this . getProblemData ( ) ;
21+ await explorerNodeManager . refreshCache ( ) ;
3222this . onDidChangeTreeDataEvent . fire ( ) ;
3323}
3424
@@ -49,7 +39,7 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
4939return {
5040label :element . isProblem ?`[${ element . id } ]${ element . name } ` :element . name ,
5141tooltip :this . getSubCategoryTooltip ( element ) ,
52- id :`${ idPrefix } .${ element . parentName } . ${ element . id } ` ,
42+ id :`${ idPrefix } .${ element . id } ` ,
5343collapsibleState :element . isProblem ?vscode . TreeItemCollapsibleState . None :vscode . TreeItemCollapsibleState . Collapsed ,
5444contextValue :element . isProblem ?"problem" :element . id . toLowerCase ( ) ,
5545iconPath :this . parseIconPathFromProblemState ( element ) ,
@@ -63,91 +53,28 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
6353new LeetCodeNode ( Object . assign ( { } , defaultProblem , {
6454id :"notSignIn" ,
6555name :"Sign in to LeetCode" ,
66- } ) , "ROOT" , false ) ,
56+ } ) , false ) ,
6757] ;
6858}
6959if ( ! element ) { // Root view
70- return [
71- new LeetCodeNode ( Object . assign ( { } , defaultProblem , {
72- id :Category . Difficulty ,
73- name :Category . Difficulty ,
74- } ) , "ROOT" , false ) ,
75- new LeetCodeNode ( Object . assign ( { } , defaultProblem , {
76- id :Category . Tag ,
77- name :Category . Tag ,
78- } ) , "ROOT" , false ) ,
79- new LeetCodeNode ( Object . assign ( { } , defaultProblem , {
80- id :Category . Company ,
81- name :Category . Company ,
82- } ) , "ROOT" , false ) ,
83- new LeetCodeNode ( Object . assign ( { } , defaultProblem , {
84- id :Category . Favorite ,
85- name :Category . Favorite ,
86- } ) , "ROOT" , false ) ,
87- ] ;
60+ return explorerNodeManager . getRootNodes ( ) ;
8861} else {
89- switch ( element . name ) { // First-level
62+ switch ( element . id ) { // First-level
9063case Category . Favorite :
91- const nodes :IProblem [ ] = this . treeData [ Category . Favorite ] ;
92- return nodes . map ( ( p :IProblem ) => new LeetCodeNode ( p , Category . Favorite ) ) ;
64+ return explorerNodeManager . getFavoriteNodes ( ) ;
9365case Category . Difficulty :
66+ return explorerNodeManager . getAllDifficultyNodes ( ) ;
9467case Category . Tag :
68+ return explorerNodeManager . getAllTagNodes ( ) ;
9569case Category . Company :
96- return this . composeSubCategoryNodes ( element ) ;
97- default :// Second and lower levels
98- return element . isProblem ?[ ] :this . composeProblemNodes ( element ) ;
99- }
100- }
101- }
102-
103- private async getProblemData ( ) :Promise < void > {
104- // clear cache
105- this . treeData = {
106- Difficulty :new Map < string , IProblem [ ] > ( ) ,
107- Tag :new Map < string , IProblem [ ] > ( ) ,
108- Company :new Map < string , IProblem [ ] > ( ) ,
109- Favorite :[ ] ,
110- } ;
111- for ( const problem of await list . listProblems ( ) ) {
112- // Add favorite problem, no matter whether it is solved.
113- if ( problem . isFavorite ) {
114- this . treeData [ Category . Favorite ] . push ( problem ) ;
115- }
116- // Hide solved problem in other category.
117- if ( problem . state === ProblemState . AC && getWorkspaceConfiguration ( ) . get < boolean > ( "hideSolved" ) ) {
118- continue ;
70+ return explorerNodeManager . getAllCompanyNodes ( ) ;
71+ default :
72+ if ( element . isProblem ) {
73+ return [ ] ;
74+ }
75+ return explorerNodeManager . getChildrenNodesById ( element . id ) ;
11976}
120-
121- this . addProblemToTreeData ( problem ) ;
122- }
123- }
124-
125- private composeProblemNodes ( node :LeetCodeNode ) :LeetCodeNode [ ] {
126- const map :Map < string , IProblem [ ] > | undefined = this . treeData [ node . parentName ] ;
127- if ( ! map ) {
128- leetCodeChannel . appendLine ( `Category:${ node . parentName } is not available.` ) ;
129- return [ ] ;
130- }
131- const problems :IProblem [ ] = map . get ( node . name ) || [ ] ;
132- const problemNodes :LeetCodeNode [ ] = [ ] ;
133- for ( const problem of problems ) {
134- problemNodes . push ( new LeetCodeNode ( problem , node . name ) ) ;
13577}
136- return problemNodes ;
137- }
138-
139- private composeSubCategoryNodes ( node :LeetCodeNode ) :LeetCodeNode [ ] {
140- const category :Category = node . name as Category ;
141- if ( category === Category . Favorite ) {
142- leetCodeChannel . appendLine ( "No sub-level for Favorite nodes" ) ;
143- return [ ] ;
144- }
145- const map :Map < string , IProblem [ ] > | undefined = this . treeData [ category ] ;
146- if ( ! map ) {
147- leetCodeChannel . appendLine ( `Category:${ category } is not available.` ) ;
148- return [ ] ;
149- }
150- return this . getSubCategoryNodes ( map , category ) ;
15178}
15279
15380private parseIconPathFromProblemState ( element :LeetCodeNode ) :string {
@@ -171,16 +98,16 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
17198
17299private getSubCategoryTooltip ( element :LeetCodeNode ) :string {
173100// return '' unless it is a sub-category node
174- if ( element . isProblem || ! this . treeData [ element . parentName ] ) {
101+ if ( element . isProblem || element . id === "ROOT" || element . id in Category ) {
175102return "" ;
176103}
177104
178- const problems : IProblem [ ] = this . treeData [ element . parentName ] . get ( element . id ) ;
105+ const childernNodes : LeetCodeNode [ ] = explorerNodeManager . getChildrenNodesById ( element . id ) ;
179106
180107let acceptedNum :number = 0 ;
181108let failedNum :number = 0 ;
182- for ( const prob of problems ) {
183- switch ( prob . state ) {
109+ for ( const node of childernNodes ) {
110+ switch ( node . state ) {
184111case ProblemState . AC :
185112acceptedNum ++ ;
186113break ;
@@ -195,73 +122,7 @@ export class LeetCodeTreeDataProvider implements vscode.TreeDataProvider<LeetCod
195122return [
196123`AC:${ acceptedNum } ` ,
197124`Failed:${ failedNum } ` ,
198- `Total:${ problems . length } ` ,
125+ `Total:${ childernNodes . length } ` ,
199126] . join ( os . EOL ) ;
200127}
201-
202- private addProblemToTreeData ( problem :IProblem ) :void {
203- this . putProblemToMap ( this . treeData . Difficulty , problem . difficulty , problem ) ;
204- for ( const tag of problem . tags ) {
205- this . putProblemToMap ( this . treeData . Tag , _ . startCase ( tag ) , problem ) ;
206- }
207- for ( const company of problem . companies ) {
208- this . putProblemToMap ( this . treeData . Company , _ . startCase ( company ) , problem ) ;
209- }
210- }
211-
212- private putProblemToMap ( map :Map < string , IProblem [ ] > , key :string , problem :IProblem ) :void {
213- const problems :IProblem [ ] | undefined = map . get ( key ) ;
214- if ( problems ) {
215- problems . push ( problem ) ;
216- } else {
217- map . set ( key , [ problem ] ) ;
218- }
219- }
220-
221- private getSubCategoryNodes ( map :Map < string , IProblem [ ] > , category :Category ) :LeetCodeNode [ ] {
222- const subCategoryNodes :LeetCodeNode [ ] = Array . from ( map . keys ( ) ) . map ( ( subCategory :string ) => {
223- return new LeetCodeNode ( Object . assign ( { } , defaultProblem , {
224- id :subCategory ,
225- name :subCategory ,
226- } ) , category . toString ( ) , false ) ;
227- } ) ;
228- this . sortSubCategoryNodes ( subCategoryNodes , category ) ;
229- return subCategoryNodes ;
230- }
231-
232- private sortSubCategoryNodes ( subCategoryNodes :LeetCodeNode [ ] , category :Category ) :void {
233- switch ( category ) {
234- case Category . Difficulty :
235- subCategoryNodes . sort ( ( a :LeetCodeNode , b :LeetCodeNode ) :number => {
236- function getValue ( input :LeetCodeNode ) :number {
237- switch ( input . name . toLowerCase ( ) ) {
238- case "easy" :
239- return 1 ;
240- case "medium" :
241- return 2 ;
242- case "hard" :
243- return 3 ;
244- default :
245- return Number . MAX_SAFE_INTEGER ;
246- }
247- }
248- return getValue ( a ) - getValue ( b ) ;
249- } ) ;
250- break ;
251- case Category . Tag :
252- case Category . Company :
253- subCategoryNodes . sort ( ( a :LeetCodeNode , b :LeetCodeNode ) :number => {
254- if ( a . name === "Unknown" ) {
255- return 1 ;
256- } else if ( b . name === "Unknown" ) {
257- return - 1 ;
258- } else {
259- return Number ( a . name > b . name ) - Number ( a . name < b . name ) ;
260- }
261- } ) ;
262- break ;
263- default :
264- break ;
265- }
266- }
267128}