4
4
5
5
; ; Author: Wang Kai <[email protected] >
6
6
; ; Keywords: extensions, tools
7
- ; ; Package-Version: 20200101.1111
8
7
; ; 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
11
10
12
11
; ; This program is free software; you can redistribute it and/or modify
13
12
; ; it under the terms of the GNU General Public License as published by
46
45
(require 'graphql ) ; Some requests of LeetCode use GraphQL
47
46
(require 'aio )
48
47
(require 'spinner )
48
+ (require 'log4e )
49
49
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 ))
50
82
51
83
(defgroup leetcode nil
52
84
" A Leetcode client."
@@ -323,7 +355,7 @@ Return a object with following attributes:
323
355
(list (cons " titleSlug" slug-title)))))
324
356
(result (aio-await (aio-url-retrieve leetcode--api-graphql))))
325
357
(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)
327
359
(with-current-buffer (cdr result)
328
360
(goto-char url-http-end-of-headers)
329
361
(alist-get 'question (alist-get 'data (json-read )))))))
@@ -466,7 +498,7 @@ Return a list of rows, each row is a vector:
466
498
(result (aio-await (aio-url-retrieve leetcode--api-all-problems))))
467
499
(leetcode--loading-mode -1 )
468
500
(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)
470
502
(with-current-buffer (cdr result)
471
503
(goto-char url-http-end-of-headers)
472
504
(json-read ))))))
@@ -497,7 +529,7 @@ Return a list of rows, each row is a vector:
497
529
(progn
498
530
(leetcode--set-user-and-problems users-and-problems)
499
531
(leetcode--set-tags all-tags))
500
- (message " LeetCode parse user and problems failed " ))
532
+ (leetcode--warn " LeetCode parse user and problems failed" ))
501
533
(leetcode-reset-filter)
502
534
(leetcode-refresh))
503
535
@@ -515,29 +547,36 @@ Return a list of rows, each row is a vector:
515
547
" A wrapper for `leetcode--async' , because emacs-aio can not be autoloaded.
516
548
see: https://github.com/skeeto/emacs-aio/issues/3."
517
549
(interactive )
518
- (leetcode--async))
550
+ (if (leetcode--check-deps)
551
+ (leetcode--async)
552
+ (message " installing leetcode dependencies... " )))
519
553
520
554
(defun leetcode--buffer-content (buf )
521
555
" Get content without text properties of BUF."
522
556
(with-current-buffer buf
523
557
(buffer-substring-no-properties
524
558
(point-min ) (point-max ))))
525
559
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
+
526
566
(aio-defun leetcode-try ()
527
567
" Asynchronously test the code using customized testcase."
528
568
(interactive )
529
569
(leetcode--loading-mode t )
530
570
(let* ((code-buf (current-buffer ))
531
571
(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)
541
580
(let* ((url-request-method " POST" )
542
581
(url-request-extra-headers
543
582
`(, leetcode--User-Agent
@@ -551,11 +590,11 @@ see: https://github.com/skeeto/emacs-aio/issues/3."
551
590
`((data_input . ,(leetcode--buffer-content testcase-buf))
552
591
(judge_type . " small" )
553
592
(lang . , leetcode--lang )
554
- (question_id . , id )
593
+ (question_id . , problem- id )
555
594
(typed_code . ,(leetcode--buffer-content code-buf)))))
556
595
(result (aio-await (aio-url-retrieve (format leetcode--api-try slug-title)))))
557
596
(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)
559
598
(let ((data (with-current-buffer (cdr result)
560
599
(goto-char url-http-end-of-headers)
561
600
(json-read )))
@@ -599,7 +638,7 @@ see: https://github.com/skeeto/emacs-aio/issues/3."
599
638
(dolist (item (append .code_output nil ))
600
639
(insert (concat item " \n " ))))
601
640
(insert " \n\n " )))
602
- (message " LeetCode try timeout. " )))
641
+ (leetcode--warn " LeetCode try timeout." )))
603
642
(leetcode--loading-mode -1 )))))))
604
643
605
644
(aio-defun leetcode--check-submission (submission-id slug-title)
@@ -619,7 +658,7 @@ nil."
619
658
(if-let ((error-info (plist-get (car result) :error )))
620
659
(progn
621
660
(leetcode--loading-mode -1 )
622
- (message " LeetCode check submission failed: %S " error-info))
661
+ (leetcode--warn " LeetCode check submission failed: %S" error-info))
623
662
(with-current-buffer (cdr result)
624
663
(let ((submission-res
625
664
(progn (goto-char url-http-end-of-headers)
@@ -727,14 +766,14 @@ following possible value:
727
766
(leetcode--loading-mode t )
728
767
(let* ((code-buf (current-buffer ))
729
768
(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 )
738
777
(let* ((url-request-method " POST" )
739
778
(url-request-extra-headers
740
779
`(, leetcode--User-Agent
@@ -745,13 +784,13 @@ following possible value:
745
784
,(cons leetcode--X-CSRFToken (aio-await (leetcode--csrf-token)))))
746
785
(url-request-data
747
786
(json-encode `((lang . , leetcode--lang )
748
- (question_id . , id )
787
+ (question_id . , problem- id )
749
788
(typed_code . , code ))))
750
789
(result (aio-await (aio-url-retrieve (format leetcode--api-submit slug-title)))))
751
790
(if-let ((error-info (plist-get (car result) :error )))
752
791
(progn
753
792
(leetcode--loading-mode -1 )
754
- (message " LeetCode check submit failed: %S " error-info))
793
+ (leetcode--warn " LeetCode check submit failed: %S" error-info))
755
794
(let* ((resp
756
795
(with-current-buffer (cdr result)
757
796
(progn (goto-char url-http-end-of-headers)
@@ -766,7 +805,7 @@ following possible value:
766
805
(setq retry-times (1+ retry-times)))
767
806
(if (< retry-times leetcode-retry-threshold)
768
807
(leetcode--show-submission-result submission-res)
769
- (message " LeetCode submit timeout. " ))
808
+ (leetcode--warn " LeetCode submit timeout." ))
770
809
(leetcode--loading-mode -1 ))))))
771
810
772
811
(defun leetcode--problem-link (title )
@@ -831,7 +870,9 @@ Get current entry by using `tabulated-list-get-entry' and use
831
870
(leetcode--kill-buff-and-delete-window (get-buffer leetcode--description-buffer-name))
832
871
(leetcode--kill-buff-and-delete-window (get-buffer leetcode--result-buffer-name))
833
872
(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))))
835
876
leetcode--problem-titles))
836
877
837
878
(defvar leetcode-prefer-language " python3"
@@ -843,11 +884,11 @@ python3, ruby, rust, scala, swift.")
843
884
" LeetCode sql implementation.
844
885
mysql, mssql, oraclesql." )
845
886
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" )
850
889
890
+ (defvar leetcode-save-solutions nil
891
+ " If it's t, save leetcode solutions to `leetcode-directory' " )
851
892
852
893
(defvar leetcode--lang leetcode-prefer-language
853
894
" LeetCode programming language or sql for current problem internally.
@@ -876,10 +917,34 @@ python3, ruby, rust, scala, swift, mysql, mssql, oraclesql.")
876
917
877
918
(defun leetcode--get-code-buffer-name (title )
878
919
" 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 " %0 4d_%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 )))
883
948
884
949
(defun leetcode--start-coding (title snippets testcase )
885
950
" Create a buffer for coding.
@@ -891,41 +956,28 @@ for current problem."
891
956
(add-to-list 'leetcode--problem-titles title)
892
957
(leetcode--solving-layout)
893
958
(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))
895
963
(suffix (assoc-default
896
964
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)))
907
966
(unless code-buf
908
- (with-current-buffer
909
- (find-file-noselect
910
- (format " %s /%0 4d_%s " leetcode-source-directory cur-id (leetcode--get-code-buffer-name title))
911
- )
967
+ (with-current-buffer (leetcode--get-code-buffer buf-name)
912
968
(setq code-buf (current-buffer ))
913
969
(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))
928
979
(leetcode--replace-in-buffer " " " " ))))
980
+
929
981
(display-buffer code-buf
930
982
'((display-buffer-reuse-window
931
983
leetcode--display-code)
@@ -992,7 +1044,6 @@ for current problem."
992
1044
:require 'leetcode
993
1045
:lighter leetcode--loading-lighter
994
1046
:group 'leetcode
995
- :global t
996
1047
(if leetcode--loading-mode
997
1048
(spinner-start leetcode--spinner)
998
1049
(spinner-stop leetcode--spinner)))
0 commit comments