Skip to content

Commit e178708

Browse files
committed
feat: automatically install my_cookies, add option for saving solutions
closes #51, closes #36
1 parent d8811dc commit e178708

File tree

3 files changed

+134
-86
lines changed

3 files changed

+134
-86
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
2020-05-23 Wang Kai <[email protected]>
2+
3+
* leetcode.el: add option for saving solutions, install my_cookies automatically.
4+
15
2020-01-01 Wang Kai <[email protected]>
26

37
* leetcode.el: leetcode.com don't allow third party login, so we retrieve LeetCode session from Chrome cookies

README.md

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ LeetCode brings you offer, and now Emacs brings you LeetCode!
88
- Vanilla Emacs: `package-install` it from melpa directly
99
- [Spacemacs](https://github.com/syl20bnr/spacemacs): [leetcode-emacs-layer](https://github.com/anmoljagetia/leetcode-emacs-layer)
1010

11-
LeetCode do not allow third party login, one workaround is restore LeetCode session from local Chrome cookies. To do this, you need to install a Python3 package called [my\_cookies](https://github.com/kaiwk/my_cookies): `pip3 install my_cookies`
11+
LeetCode do not allow third party login, one workaround is restore LeetCode session from local Chrome cookies. By default, this package will install a Python3 package called [my\_cookies](https://github.com/kaiwk/my_cookies), or you can install it manually: `pip3 install my_cookies`.
1212

1313
## Manually
1414

@@ -27,6 +27,13 @@ You can set your preferred LeetCode programming language and SQL by setting `lee
2727

2828
All supported languages can be found in variable `leetcode--prefer-language-suffixes`.
2929

30+
You can save solution by setting `leetcode-save-solutions`:
31+
32+
```
33+
(setq leetcode-save-solutions t)
34+
(setq leetcode-directory "~/leetcode")
35+
```
36+
3037
# Usage
3138

3239
1. Execute `leetcode` command.
@@ -54,18 +61,4 @@ In leetcode problems list buffer:
5461

5562
# Debug
5663

57-
If you are unable to start Leetcode, set these variables and try again to see a full stacktrace:
58-
59-
```elisp
60-
(setq url-debug t)
61-
```
62-
63-
# Contributing
64-
65-
This package use [Cask](https://cask.readthedocs.io/en/latest/guide/introduction.html) to develop, build and test.
66-
67-
It is a suggestion for you to use `Cask`, but if you don't want to bother to use it, it's totally fine too.
68-
69-
`Cask` is a build tools for emacs lisp, you can think it of `npm` for emacs lisp.
70-
71-
Enter project root, execute `cask install`, this command will install all dependencies. After that, execute `cask emacs` which will start a emacs with extra `load-path` to load dependencies.
64+
Call `leetcode-toggle-debug`, log will output in `*leetcode-log*` buffer.

leetcode.el

Lines changed: 121 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55
;; Author: Wang Kai <[email protected]>
66
;; Keywords: extensions, tools
7-
;; Package-Version: 20200101.1111
87
;; URL: https://github.com/kaiwk/leetcode.el
9-
;; Package-Requires: ((emacs "26") (dash "2.16.0") (graphql "0.1.1") (spinner "1.7.3") (aio "1.0"))
10-
;; Version: 0.1.10
8+
;; Package-Requires: ((emacs "26") (dash "2.16.0") (graphql "0.1.1") (spinner "1.7.3") (aio "1.0") (log4e "0.3.3"))
9+
;; Version: 0.1.12
1110

1211
;; This program is free software; you can redistribute it and/or modify
1312
;; it under the terms of the GNU General Public License as published by
@@ -46,7 +45,40 @@
4645
(require 'graphql) ; Some requests of LeetCode use GraphQL
4746
(require 'aio)
4847
(require 'spinner)
48+
(require 'log4e)
4949

50+
(log4e:deflogger "leetcode" "%t [%l] %m" "%H:%M:%S" '((fatal . "fatal")
51+
(error . "error")
52+
(warn . "warn")
53+
(info . "info")
54+
(debug . "debug")
55+
(trace . "trace")))
56+
(setq log4e--log-buffer-leetcode "*leetcode-log*")
57+
58+
;;;###autoload
59+
(defun leetcode-toggle-debug ()
60+
(interactive)
61+
(if (leetcode--log-debugging-p)
62+
(progn
63+
(leetcode--log-set-level 'info)
64+
(leetcode--log-disable-debugging)
65+
(message "leetcode disable debug"))
66+
(progn
67+
(leetcode--log-set-level 'debug)
68+
(leetcode--log-enable-debugging)
69+
(message "leetcode enable debug"))))
70+
71+
(defun leetcode--install-my-cookie ()
72+
(let ((async-shell-command-display-buffer t))
73+
(async-shell-command
74+
"pip3 install my_cookies"
75+
(get-buffer-create "*leetcode-install*"))))
76+
77+
(defun leetcode--check-deps ()
78+
(if (executable-find "my_cookies")
79+
t
80+
(leetcode--install-my-cookie)
81+
nil))
5082

5183
(defgroup leetcode nil
5284
"A Leetcode client."
@@ -323,7 +355,7 @@ Return a object with following attributes:
323355
(list (cons "titleSlug" slug-title)))))
324356
(result (aio-await (aio-url-retrieve leetcode--api-graphql))))
325357
(if-let ((error-info (plist-get (car result) :error)))
326-
(message "LeetCode fetch problem ERROR: %S" error-info)
358+
(leetcode--warn "LeetCode fetch problem ERROR: %S" error-info)
327359
(with-current-buffer (cdr result)
328360
(goto-char url-http-end-of-headers)
329361
(alist-get 'question (alist-get 'data (json-read)))))))
@@ -466,7 +498,7 @@ Return a list of rows, each row is a vector:
466498
(result (aio-await (aio-url-retrieve leetcode--api-all-problems))))
467499
(leetcode--loading-mode -1)
468500
(if-let ((error-info (plist-get (car result) :error)))
469-
(message "LeetCode fetch user and problems failed: %S" error-info)
501+
(leetcode--warn "LeetCode fetch user and problems failed: %S" error-info)
470502
(with-current-buffer (cdr result)
471503
(goto-char url-http-end-of-headers)
472504
(json-read))))))
@@ -497,7 +529,7 @@ Return a list of rows, each row is a vector:
497529
(progn
498530
(leetcode--set-user-and-problems users-and-problems)
499531
(leetcode--set-tags all-tags))
500-
(message "LeetCode parse user and problems failed"))
532+
(leetcode--warn "LeetCode parse user and problems failed"))
501533
(leetcode-reset-filter)
502534
(leetcode-refresh))
503535

@@ -515,29 +547,36 @@ Return a list of rows, each row is a vector:
515547
"A wrapper for `leetcode--async', because emacs-aio can not be autoloaded.
516548
see: https://github.com/skeeto/emacs-aio/issues/3."
517549
(interactive)
518-
(leetcode--async))
550+
(if (leetcode--check-deps)
551+
(leetcode--async)
552+
(message "installing leetcode dependencies...")))
519553

520554
(defun leetcode--buffer-content (buf)
521555
"Get content without text properties of BUF."
522556
(with-current-buffer buf
523557
(buffer-substring-no-properties
524558
(point-min) (point-max))))
525559

560+
(defun leetcode--get-slug-title-before-try/submit (code-buf)
561+
(with-current-buffer code-buf
562+
(if leetcode-save-solutions
563+
(file-name-base (cadr (split-string (buffer-name) "_")))
564+
(file-name-base (buffer-name)))))
565+
526566
(aio-defun leetcode-try ()
527567
"Asynchronously test the code using customized testcase."
528568
(interactive)
529569
(leetcode--loading-mode t)
530570
(let* ((code-buf (current-buffer))
531571
(testcase-buf (get-buffer leetcode--testcase-buffer-name))
532-
(slug-title (with-current-buffer code-buf
533-
(file-name-base (car (cdr (split-string (buffer-name) "_"))))))
534-
535-
(cur-problem (seq-find (lambda (p)
536-
(equal slug-title
537-
(leetcode--slugify-title
538-
(plist-get p :title))))
539-
(plist-get leetcode--all-problems :problems)))
540-
(id (plist-get cur-problem :id)))
572+
(slug-title (leetcode--get-slug-title-before-try/submit code-buf))
573+
(problem (seq-find (lambda (p)
574+
(equal slug-title
575+
(leetcode--slugify-title
576+
(plist-get p :title))))
577+
(plist-get leetcode--all-problems :problems)))
578+
(problem-id (plist-get problem :id)))
579+
(leetcode--debug "leetcode try slug-title: %s, problem-id: %s" slug-title problem-id)
541580
(let* ((url-request-method "POST")
542581
(url-request-extra-headers
543582
`(,leetcode--User-Agent
@@ -551,11 +590,11 @@ see: https://github.com/skeeto/emacs-aio/issues/3."
551590
`((data_input . ,(leetcode--buffer-content testcase-buf))
552591
(judge_type . "small")
553592
(lang . ,leetcode--lang)
554-
(question_id . ,id)
593+
(question_id . ,problem-id)
555594
(typed_code . ,(leetcode--buffer-content code-buf)))))
556595
(result (aio-await (aio-url-retrieve (format leetcode--api-try slug-title)))))
557596
(if-let ((error-info (plist-get (car result) :error)))
558-
(message "LeetCode try failed: %S" error-info)
597+
(leetcode--warn "LeetCode try failed: %S" error-info)
559598
(let ((data (with-current-buffer (cdr result)
560599
(goto-char url-http-end-of-headers)
561600
(json-read)))
@@ -599,7 +638,7 @@ see: https://github.com/skeeto/emacs-aio/issues/3."
599638
(dolist (item (append .code_output nil))
600639
(insert (concat item "\n"))))
601640
(insert "\n\n")))
602-
(message "LeetCode try timeout.")))
641+
(leetcode--warn "LeetCode try timeout.")))
603642
(leetcode--loading-mode -1)))))))
604643

605644
(aio-defun leetcode--check-submission (submission-id slug-title)
@@ -619,7 +658,7 @@ nil."
619658
(if-let ((error-info (plist-get (car result) :error)))
620659
(progn
621660
(leetcode--loading-mode -1)
622-
(message "LeetCode check submission failed: %S" error-info))
661+
(leetcode--warn "LeetCode check submission failed: %S" error-info))
623662
(with-current-buffer (cdr result)
624663
(let ((submission-res
625664
(progn (goto-char url-http-end-of-headers)
@@ -727,14 +766,14 @@ following possible value:
727766
(leetcode--loading-mode t)
728767
(let* ((code-buf (current-buffer))
729768
(code (leetcode--buffer-content code-buf))
730-
(slug-title (with-current-buffer code-buf
731-
(file-name-base (car (cdr (split-string (buffer-name) "_"))))))
732-
(id (plist-get (seq-find (lambda (p)
733-
(equal slug-title
734-
(leetcode--slugify-title
735-
(plist-get p :title))))
736-
(plist-get leetcode--all-problems :problems))
737-
:id)))
769+
(slug-title (leetcode--get-slug-title-before-try/submit code-buf))
770+
(problem-id (plist-get (seq-find (lambda (p)
771+
(equal slug-title
772+
(leetcode--slugify-title
773+
(plist-get p :title))))
774+
(plist-get leetcode--all-problems :problems))
775+
:id)))
776+
(leetcode--debug "leetcode submit slug-title: %s, problem-id: %s" slug-title problem-id)
738777
(let* ((url-request-method "POST")
739778
(url-request-extra-headers
740779
`(,leetcode--User-Agent
@@ -745,13 +784,13 @@ following possible value:
745784
,(cons leetcode--X-CSRFToken (aio-await (leetcode--csrf-token)))))
746785
(url-request-data
747786
(json-encode `((lang . ,leetcode--lang)
748-
(question_id . ,id)
787+
(question_id . ,problem-id)
749788
(typed_code . ,code))))
750789
(result (aio-await (aio-url-retrieve (format leetcode--api-submit slug-title)))))
751790
(if-let ((error-info (plist-get (car result) :error)))
752791
(progn
753792
(leetcode--loading-mode -1)
754-
(message "LeetCode check submit failed: %S" error-info))
793+
(leetcode--warn "LeetCode check submit failed: %S" error-info))
755794
(let* ((resp
756795
(with-current-buffer (cdr result)
757796
(progn (goto-char url-http-end-of-headers)
@@ -766,7 +805,7 @@ following possible value:
766805
(setq retry-times (1+ retry-times)))
767806
(if (< retry-times leetcode-retry-threshold)
768807
(leetcode--show-submission-result submission-res)
769-
(message "LeetCode submit timeout."))
808+
(leetcode--warn "LeetCode submit timeout."))
770809
(leetcode--loading-mode -1))))))
771810

772811
(defun leetcode--problem-link (title)
@@ -831,7 +870,9 @@ Get current entry by using `tabulated-list-get-entry' and use
831870
(leetcode--kill-buff-and-delete-window (get-buffer leetcode--description-buffer-name))
832871
(leetcode--kill-buff-and-delete-window (get-buffer leetcode--result-buffer-name))
833872
(leetcode--kill-buff-and-delete-window (get-buffer leetcode--testcase-buffer-name))
834-
(mapc (lambda (x) (leetcode--kill-buff-and-delete-window (get-buffer (leetcode--get-code-buffer-name x))))
873+
(mapc (lambda (title)
874+
(leetcode--kill-buff-and-delete-window
875+
(get-buffer (leetcode--get-code-buffer-name title))))
835876
leetcode--problem-titles))
836877

837878
(defvar leetcode-prefer-language "python3"
@@ -843,11 +884,11 @@ python3, ruby, rust, scala, swift.")
843884
"LeetCode sql implementation.
844885
mysql, mssql, oraclesql.")
845886

846-
(defcustom leetcode-source-directory "~/leetcode"
847-
"Location to save source files."
848-
:group 'leetcode-location
849-
:type 'string)
887+
(defvar leetcode-directory "~/leetcode"
888+
"Directory to save solutions")
850889

890+
(defvar leetcode-save-solutions nil
891+
"If it's t, save leetcode solutions to `leetcode-directory'")
851892

852893
(defvar leetcode--lang leetcode-prefer-language
853894
"LeetCode programming language or sql for current problem internally.
@@ -876,10 +917,34 @@ python3, ruby, rust, scala, swift, mysql, mssql, oraclesql.")
876917

877918
(defun leetcode--get-code-buffer-name (title)
878919
"Get code buffer name by TITLE and `leetcode-prefer-language'."
879-
(let ((suffix (assoc-default
880-
leetcode--lang
881-
leetcode--lang-suffixes)))
882-
(concat (leetcode--slugify-title title) suffix)))
920+
(let* ((suffix (assoc-default
921+
leetcode--lang
922+
leetcode--lang-suffixes))
923+
(slug-title (leetcode--slugify-title title))
924+
(title-with-suffix (concat slug-title suffix)))
925+
(if leetcode-save-solutions
926+
(format "%04d_%s" (leetcode--get-problem-id slug-title) title-with-suffix)
927+
title-with-suffix)))
928+
929+
(defun leetcode--get-code-buffer (buf-name)
930+
(if (not leetcode-save-solutions)
931+
(get-buffer-create buf-name)
932+
(unless (file-directory-p leetcode-directory)
933+
(make-directory leetcode-directory))
934+
(find-file-noselect
935+
(concat (file-name-as-directory leetcode-directory)
936+
buf-name))))
937+
938+
(defun leetcode--get-problem (slug-title)
939+
(seq-find (lambda (p)
940+
(equal slug-title
941+
(leetcode--slugify-title
942+
(plist-get p :title))))
943+
(plist-get leetcode--all-problems :problems)))
944+
945+
(defun leetcode--get-problem-id (slug-title)
946+
(let ((problem (leetcode--get-problem "two-sum")))
947+
(plist-get problem :id)))
883948

884949
(defun leetcode--start-coding (title snippets testcase)
885950
"Create a buffer for coding.
@@ -891,41 +956,28 @@ for current problem."
891956
(add-to-list 'leetcode--problem-titles title)
892957
(leetcode--solving-layout)
893958
(leetcode--set-lang snippets)
894-
(let* ((code-buf (get-buffer (leetcode--get-code-buffer-name title)))
959+
(let* ((slug-title (leetcode--slugify-title title))
960+
(problem-id (leetcode--get-problem-id slug-title))
961+
(buf-name (leetcode--get-code-buffer-name title))
962+
(code-buf (get-buffer buf-name))
895963
(suffix (assoc-default
896964
leetcode--lang
897-
leetcode--lang-suffixes))
898-
(slug-title (leetcode--slugify-title title))
899-
(cur-problem (seq-find (lambda (p)
900-
(equal slug-title
901-
(leetcode--slugify-title
902-
(plist-get p :title))))
903-
(plist-get leetcode--all-problems :problems)))
904-
(cur-id (plist-get cur-problem :id))
905-
)
906-
965+
leetcode--lang-suffixes)))
907966
(unless code-buf
908-
(with-current-buffer
909-
(find-file-noselect
910-
(format "%s/%04d_%s" leetcode-source-directory cur-id (leetcode--get-code-buffer-name title))
911-
)
967+
(with-current-buffer (leetcode--get-code-buffer buf-name)
912968
(setq code-buf (current-buffer))
913969
(funcall (assoc-default suffix auto-mode-alist #'string-match-p))
914-
(let ((snippet (seq-find (lambda (s)
915-
(equal (alist-get 'langSlug s)
916-
leetcode--lang))
917-
snippets)))
918-
919-
(setq code-inserted
920-
(save-excursion
921-
(goto-char (point-min))
922-
(search-forward (string-trim (alist-get 'code snippet) ) nil t))
923-
)
924-
(unless code-inserted
925-
(insert (alist-get 'code snippet))
926-
)
927-
970+
(let* ((snippet (seq-find (lambda (s)
971+
(equal (alist-get 'langSlug s)
972+
leetcode--lang))
973+
snippets))
974+
(template-code (alist-get 'code snippet)))
975+
(unless (save-mark-and-excursion
976+
(goto-char (point-min))
977+
(search-forward (string-trim template-code) nil t))
978+
(insert template-code))
928979
(leetcode--replace-in-buffer "" ""))))
980+
929981
(display-buffer code-buf
930982
'((display-buffer-reuse-window
931983
leetcode--display-code)
@@ -992,7 +1044,6 @@ for current problem."
9921044
:require 'leetcode
9931045
:lighter leetcode--loading-lighter
9941046
:group 'leetcode
995-
:global t
9961047
(if leetcode--loading-mode
9971048
(spinner-start leetcode--spinner)
9981049
(spinner-stop leetcode--spinner)))

0 commit comments

Comments
 (0)