@@ -61,33 +61,41 @@ The object with following attributes:
61
61
:medium Number
62
62
:hard Number" )
63
63
64
- (defvar leetcode--problems nil
64
+ (defvar leetcode--all- problems nil
65
65
" Problems info with a list of problem object.
66
66
The object with following attributes:
67
67
:num Number
68
68
:tag String
69
69
:problems List
70
+
70
71
The elements of :problems has attributes:
71
72
:status String
72
73
:id Number
73
74
:pos Number
74
75
:title String
75
76
:acceptance String
76
77
:difficulty Number {1,2,3}
77
- :paid-only Boolean {t|nil}" )
78
+ :paid-only Boolean {t|nil}
79
+ :tags List" )
80
+
81
+ (defvar leetcode--all-tags nil
82
+ " Problems tags" )
78
83
79
84
(defvar leetcode--problem-titles nil
80
85
" Problem titles that have been open in solving layout." )
81
86
82
- (defvar leetcode-checkmark " ✓" " Checkmark for accepted problem." )
87
+ (defvar leetcode--filter-regex nil " Filter rows by regex." )
88
+ (defvar leetcode--filter-tag nil " Filter rows by leetcode tag." )
89
+
90
+ (defconst leetcode--checkmark " ✓" " Checkmark for accepted problem." )
83
91
(defconst leetcode--buffer-name " *leetcode*" )
84
92
(defconst leetcode--description-buffer-name " *leetcode-description*" )
85
93
(defconst leetcode--testcase-buffer-name " *leetcode-testcase*" )
86
94
(defconst leetcode--result-buffer-name " *leetcode-result*" )
87
95
88
96
(defface leetcode-checkmark-face
89
97
'((t (:foreground " #5CB85C" )))
90
- " Face for `leetcode-checkmark' "
98
+ " Face for `leetcode-- checkmark' "
91
99
:group 'leetcode )
92
100
93
101
(defface leetcode-easy-face
@@ -121,13 +129,16 @@ The elements of :problems has attributes:
121
129
(defconst leetcode--api-root (concat leetcode--base-url " /api" ))
122
130
(defconst leetcode--api-graphql (concat leetcode--base-url " /graphql" ))
123
131
(defconst leetcode--api-all-problems (concat leetcode--api-root " /problems/all/" ))
132
+ (defconst leetcode--api-all-tags (concat leetcode--base-url " /problems/api/tags" ))
124
133
; ; submit
125
134
(defconst leetcode--api-submit (concat leetcode--base-url " /problems/%s/submit/" ))
126
135
(defconst leetcode--api-problems-submission (concat leetcode--base-url " /problems/%s/submissions/" ))
127
136
(defconst leetcode--api-check-submission (concat leetcode--base-url " /submissions/detail/%s/check/" ))
128
137
; ; try testcase
129
138
(defconst leetcode--api-try (concat leetcode--base-url " /problems/%s/interpret_solution/" ))
130
139
140
+ (defun to-list (vec )
141
+ (append vec '()))
131
142
132
143
(defun leetcode--referer (value )
133
144
" It will return an alist as the HTTP Referer Header.
@@ -219,20 +230,21 @@ When ACCOUNT or PASSWORD is empty string it will show a prompt."
219
230
" LEETCODE_SESSION" ))
220
231
(url-cookie-retrieve leetcode--domain " /" t )))))
221
232
222
- (defun leetcode--set-user-and-problems (res )
223
- " Set `leetcode--user' and `leetcode--problems' .
224
- If user isn't login, only `leetcode--problems' will be set. RES
225
- is an alist comes from `leetcode--api-all-problems' ."
233
+ (defun leetcode--set-user-and-problems (user-and-problems )
234
+ " Set `leetcode--user' and `leetcode--all-problems' .
235
+ If user isn't login, only `leetcode--all-problems' will be set.
236
+ USER-AND-PROBLEMS is an alist comes from
237
+ `leetcode--api-all-problems' ."
226
238
; ; user
227
- (let-alist res
239
+ (let-alist user-and-problems
228
240
(setq leetcode--user
229
241
(list :username .user_name
230
242
:solved .num_solved
231
243
:easy .ac_easy
232
244
:medium .ac_medium
233
245
:hard .ac_hard))
234
246
; ; problem list
235
- (setq leetcode--problems
247
+ (setq leetcode--all- problems
236
248
(list
237
249
:num .num_total
238
250
:tag " all"
@@ -257,6 +269,24 @@ is an alist comes from `leetcode--api-all-problems'."
257
269
problems)))
258
270
problems)))))
259
271
272
+ (defun leetcode--set-tags (all-tags )
273
+ " Set `leetcode--all-tags' and `leetcode--all-problems' with
274
+ ALL-TAGS."
275
+ (let-alist all-tags
276
+ ; ; set problems tags
277
+ (dolist (problem (plist-get leetcode--all-problems :problems ))
278
+ (dolist (topic (to-list .topics))
279
+ (let-alist topic
280
+ (when (member (plist-get problem :id ) (to-list .questions))
281
+ (let ((cur-tags (plist-get problem :tags )))
282
+ (push .slug cur-tags)
283
+ (plist-put problem :tags cur-tags))))))
284
+ ; ; set leetcode--all-tags
285
+ (dolist (topic (to-list .topics))
286
+ (let-alist topic
287
+ (unless (member topic leetcode--all-tags)
288
+ (push .slug leetcode--all-tags))))))
289
+
260
290
(defun leetcode--slugify-title (title )
261
291
" Make TITLE a slug title.
262
292
Such as 'Two Sum' will be converted to 'two-sum'."
@@ -339,10 +369,10 @@ row."
339
369
header-names widths))))
340
370
341
371
(defun leetcode--problems-rows ()
342
- " Generate tabulated list rows from `leetcode--problems' .
372
+ " Generate tabulated list rows from `leetcode--all- problems' .
343
373
Return a list of rows, each row is a vector:
344
374
\( [<checkmark> <position> <title> <acceptance> <difficulty>] ...)"
345
- (let ((problems (reverse (plist-get leetcode--problems :problems )))
375
+ (let ((problems (reverse (plist-get leetcode--all- problems :problems )))
346
376
(easy-tag " easy" )
347
377
(medium-tag " medium" )
348
378
(hard-tag " hard" )
@@ -351,15 +381,20 @@ Return a list of rows, each row is a vector:
351
381
(setq rows
352
382
(cons
353
383
(vector
384
+ ; ; status
354
385
(if (equal (plist-get p :status ) " ac" )
355
- (prog1 leetcode-checkmark
386
+ (prog1 leetcode-- checkmark
356
387
(put-text-property
357
- 0 (length leetcode-checkmark)
358
- 'font-lock-face 'leetcode-checkmark-face leetcode-checkmark))
388
+ 0 (length leetcode-- checkmark)
389
+ 'font-lock-face 'leetcode-checkmark-face leetcode-- checkmark))
359
390
" " )
391
+ ; ; position
360
392
(number-to-string (plist-get p :pos ))
393
+ ; ; title
361
394
(plist-get p :title )
395
+ ; ; acceptance
362
396
(plist-get p :acceptance )
397
+ ; ; difficulty
363
398
(cond
364
399
((eq 1 (plist-get p :difficulty ))
365
400
(prog1 easy-tag
@@ -375,13 +410,60 @@ Return a list of rows, each row is a vector:
375
410
(prog1 hard-tag
376
411
(put-text-property
377
412
0 (length hard-tag)
378
- 'font-lock-face 'leetcode-hard-face hard-tag)))))
413
+ 'font-lock-face 'leetcode-hard-face hard-tag))))
414
+ ; ; tags
415
+ (string-join (plist-get p :tags ) " , " ))
379
416
rows)))
380
417
rows))
381
418
382
- (aio-defun leetcode--fetch-user-and-problems ()
383
- " Refresh problems and update `tabulated-list-entries' ."
419
+ (defun leetcode--filter (rows )
420
+ " Filter ROWS by `leetcode--filter-regex' and `leetcode--filter-tag' "
421
+ (seq-filter
422
+ (lambda (row )
423
+ (and
424
+ (if leetcode--filter-regex
425
+ (let ((title (aref row 2 )))
426
+ (string-match-p leetcode--filter-regex title))
427
+ t )
428
+ (if leetcode--filter-tag
429
+ (let ((tags (split-string (aref row 5 ) " ," )))
430
+ (member leetcode--filter-tag tags))
431
+ t )))
432
+ rows))
433
+
434
+ (defun leetcode-reset-filter ()
435
+ " Reset filter."
436
+ (interactive )
437
+ (setq leetcode--filter-regex nil )
438
+ (setq leetcode--filter-tag nil )
439
+ (leetcode-refresh))
440
+
441
+ (defun leetcode-set-filter-regex (regex )
442
+ " Set `leetcode--filter-regex' as REGEX."
443
+ (interactive " sSearch: " )
444
+ (setq leetcode--filter-regex regex)
445
+ (leetcode-refresh))
446
+
447
+ (defun leetcode-set-filter-tag ()
448
+ " Set `leetcode--filter-tag' "
384
449
(interactive )
450
+ (setq leetcode--filter-tag
451
+ (completing-read " Tags: " leetcode--all-tags))
452
+ (leetcode-refresh))
453
+
454
+ (aio-defun leetcode--fetch-all-tags ()
455
+ (let* ((url-request-method " GET" )
456
+ (url-request-extra-headers
457
+ `(, leetcode--User-Agent
458
+ , leetcode--X-Requested-With
459
+ ,(leetcode--referer leetcode--url-login)))
460
+ (result (aio-await (aio-url-retrieve leetcode--api-all-tags))))
461
+ (with-current-buffer (cdr result)
462
+ (goto-char url-http-end-of-headers)
463
+ (json-read ))))
464
+
465
+ (aio-defun leetcode--fetch-user-and-problems ()
466
+ " Fetch user and problems info."
385
467
(if leetcode--loading-mode
386
468
(message " LeetCode has been refreshing... " )
387
469
(leetcode--loading-mode t )
@@ -398,15 +480,11 @@ Return a list of rows, each row is a vector:
398
480
(goto-char url-http-end-of-headers)
399
481
(json-read ))))))
400
482
401
- (aio- defun leetcode-refresh ()
402
- " Refresh problems and update `tabulated-list-entries' ."
483
+ (defun leetcode-refresh ()
484
+ " Make `tabulated-list-entries' ."
403
485
(interactive )
404
- (if-let ((users-and-problems
405
- (aio-await (leetcode--fetch-user-and-problems))))
406
- (leetcode--set-user-and-problems users-and-problems)
407
- (message " LeetCode parse user and problems failed " ))
408
- (let* ((header-names '(" " " #" " Problem" " Acceptance" " Difficulty" ))
409
- (rows (leetcode--problems-rows))
486
+ (let* ((header-names '(" " " #" " Problem" " Acceptance" " Difficulty" " Tags" ))
487
+ (rows (leetcode--filter (leetcode--problems-rows)))
410
488
(headers (leetcode--make-tabulated-headers header-names rows)))
411
489
(with-current-buffer (get-buffer-create leetcode--buffer-name)
412
490
(leetcode--problems-mode)
@@ -420,13 +498,25 @@ Return a list of rows, each row is a vector:
420
498
(tabulated-list-print t )
421
499
(leetcode--loading-mode -1 ))))
422
500
501
+ (aio-defun leetcode-refresh-fetch ()
502
+ " Refresh problems and update `tabulated-list-entries' ."
503
+ (interactive )
504
+ (if-let ((users-and-problems (aio-await (leetcode--fetch-user-and-problems)))
505
+ (all-tags (aio-await (leetcode--fetch-all-tags))))
506
+ (progn
507
+ (leetcode--set-user-and-problems users-and-problems)
508
+ (leetcode--set-tags all-tags))
509
+ (message " LeetCode parse user and problems failed " ))
510
+ (leetcode-reset-filter)
511
+ (leetcode-refresh))
512
+
423
513
(aio-defun leetcode--async ()
424
514
" Show leetcode problems buffer."
425
515
(if (get-buffer leetcode--buffer-name)
426
516
(switch-to-buffer leetcode--buffer-name)
427
517
(unless (leetcode--login-p)
428
518
(aio-await (leetcode--login)))
429
- (aio-await (leetcode-refresh))
519
+ (aio-await (leetcode-refresh-fetch ))
430
520
(switch-to-buffer leetcode--buffer-name)))
431
521
432
522
;;;### autoload
@@ -454,7 +544,7 @@ see: https://github.com/skeeto/emacs-aio/issues/3."
454
544
(equal slug-title
455
545
(leetcode--slugify-title
456
546
(plist-get p :title ))))
457
- (plist-get leetcode--problems :problems ))
547
+ (plist-get leetcode--all- problems :problems ))
458
548
:id )))
459
549
(let* ((url-request-method " POST" )
460
550
(url-request-extra-headers
@@ -650,7 +740,7 @@ following possible value:
650
740
(equal slug-title
651
741
(leetcode--slugify-title
652
742
(plist-get p :title ))))
653
- (plist-get leetcode--problems :problems ))
743
+ (plist-get leetcode--all- problems :problems ))
654
744
:id )))
655
745
(let* ((url-request-method " POST" )
656
746
(url-request-extra-headers
@@ -837,7 +927,11 @@ for current problem."
837
927
(define-key map (kbd " RET" ) #'leetcode-show-current-problem )
838
928
(define-key map " n" #'next-line )
839
929
(define-key map " p" #'previous-line )
930
+ (define-key map " s" #'leetcode-set-filter-regex )
931
+ (define-key map " t" #'leetcode-set-filter-tag )
840
932
(define-key map " g" #'leetcode-refresh )
933
+ (define-key map " G" #'leetcode-refresh-fetch )
934
+ (define-key map " /" #'leetcode-reset-filter )
841
935
(define-key map " q" #'quit-window )))
842
936
" Keymap for `leetcode--problems-mode' ." )
843
937
0 commit comments