aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.lsp-session-v12
-rw-r--r--bookmarks.eld24
-rw-r--r--config.el461
-rw-r--r--config.org548
-rw-r--r--core/core-basic.el54
-rw-r--r--core/core-dired.el45
-rw-r--r--core/core-editing.el25
-rw-r--r--core/core-ui.el21
-rw-r--r--core/core.el5
-rw-r--r--init.el21
-rw-r--r--packages/.hugo_build.lock0
-rw-r--r--packages/packages-editing.el95
-rw-r--r--packages/packages-email.el153
-rw-r--r--packages/packages-leetcode.el1386
-rw-r--r--packages/packages-misc.el8
-rw-r--r--packages/packages-ui.el41
-rw-r--r--packages/packages.el14
-rw-r--r--tramp2
18 files changed, 1042 insertions, 1863 deletions
diff --git a/.lsp-session-v1 b/.lsp-session-v1
index f6abe7e..f00efc3 100644
--- a/.lsp-session-v1
+++ b/.lsp-session-v1
@@ -1 +1 @@
-#s(lsp-session ("/tmp") nil #s(hash-table test equal) #s(hash-table test equal) #s(hash-table test equal)) \ No newline at end of file
+#s(lsp-session ("/home/verdant/blog" "/home/verdant/dev/Leetcode" "/tmp") nil #s(hash-table test equal) #s(hash-table test equal) #s(hash-table test equal)) \ No newline at end of file
diff --git a/bookmarks.eld b/bookmarks.eld
index 64a3eb1..a0f1d6e 100644
--- a/bookmarks.eld
+++ b/bookmarks.eld
@@ -2,12 +2,24 @@
;;; This format is meant to be slightly human-readable;
;;; nevertheless, you probably don't want to edit it.
;;; -*- End Of Bookmark File Format Version Stamp -*-
-((".emacs.d"
- (filename . "~/.emacs.d/")
- (front-context-string . "core\n -rw-r--r-")
- (rear-context-string . "96 Apr 24 23:58 ")
- (position . 274)
- (last-modified 27116 5874 383590 841000))
+(("sicp"
+ (filename . "~/dev/SICP/")
+ (front-context-string . " -rw-r--r-- 1 ")
+ (rear-context-string . "Apr 26 09:44 ..\n")
+ (position . 138)
+ (last-modified 27117 36230 984757 78000))
+("leetcode"
+ (filename . "~/dev/Leetcode/")
+ (front-context-string . "3783\n drwxr-xr-")
+ (rear-context-string . "96 Apr 22 22:46 ")
+ (position . 965)
+ (last-modified 27116 14552 772552 90000))
+(".emacs.d"
+ (filename . "~/.emacs.d/")
+ (front-context-string . "core\n -rw-r--r-")
+ (rear-context-string . "96 Apr 24 23:58 ")
+ (position . 274)
+ (last-modified 27116 5874 383590 841000))
("blog"
(filename . "~/blog/")
(front-context-string . "archetypes\n drw")
diff --git a/config.el b/config.el
new file mode 100644
index 0000000..ed48203
--- /dev/null
+++ b/config.el
@@ -0,0 +1,461 @@
+;; -*- lexical-binding: t; -*-
+(use-package evil
+ :init
+ (setq evil-want-keybinding nil
+ evil-want-C-u-scroll t)
+ :config
+ (evil-mode)
+ :ensure t)
+
+(use-package evil-collection
+ :after evil
+ :ensure t
+ :config
+ (evil-collection-init))
+
+(use-package olivetti)
+
+(use-package yasnippet
+ :ensure t
+ :config
+ (yas-global-mode 1))
+
+(use-package ivy
+ :ensure t
+ :init
+ (ivy-mode 1)
+ (counsel-mode 1)
+ :config
+ (setq ivy-use-selectable-prompt t)
+ (setq ivy-use-preview t)
+ (setq ivy-fixed-height-minibuffer t)
+ (setq ivy-use-preview t)
+ (setq ivy-use-virtual-buffers t)
+ (setq search-default-mode #'char-fold-to-regexp)
+ (setq ivy-count-format "(%d/%d) ")
+ (setq ivy-initial-inputs-alist nil) ;; 不预设初始输入
+ (setq ivy-use-selectable-prompt t) ;; 允许选择提示
+ :bind
+ (("C-s" . 'swiper)
+ ("C-x b" . 'ivy-switch-buffer)
+ ("C-c v" . 'ivy-push-view)
+ ("C-c s" . 'ivy-switch-view)
+ ("C-c V" . 'ivy-pop-view)
+ ("C-x C-@" . 'counsel-mark-ring); 在某些终端上 C-x C-SPC 会被映射为 C-x C-@,比如在 macOS 上,所以要手动设置
+ ("C-x C-SPC" . 'counsel-mark-ring)
+ :map minibuffer-local-map
+ ("C-r" . counsel-minibuffer-history)))
+
+;; (use-package ivy-posframe
+;; :ensure t
+;; :config
+;; (ivy-posframe-mode t))
+
+(use-package ivy-rich
+ :ensure t
+ :config
+ (ivy-rich-mode t))
+
+;; 启用 company-mode 全局补全
+(use-package company
+ :ensure t
+ :config
+ (setq company-idle-delay 0.0
+ company-minimum-prefix-length 1)
+ (global-company-mode)
+
+ (with-eval-after-load 'company
+ ;; 补全列表背景
+ (set-face-attribute 'company-tooltip nil
+ :foreground "white" :background "gray20")
+ ;; 选中项背景
+ (set-face-attribute 'company-tooltip-selection nil
+ :foreground "blue" :background "gray20")
+ ;; 输入前缀高亮
+ (set-face-attribute 'company-tooltip-common nil
+ :foreground "orange" :background "gray20")
+ ;; 右侧注释/类型
+ (set-face-attribute 'company-tooltip-annotation nil
+ :foreground "cyan" :background "gray20")))
+
+(use-package lsp-mode
+ :ensure t
+ :init
+ (setq read-process-output-max (* 1024 1024))
+ :hook (
+ (go-mode .lsp)
+ (css-mode . lsp)
+ (html-mode . lsp))
+ :config
+ (setq lsp-enable-on-type-formatting nil))
+
+(use-package lsp-ui
+ :ensure t
+ :commands lsp-ui-mode)
+
+(use-package clang-format
+ :ensure t
+ :bind
+ (:map c-mode-base-map
+ ("C-c C-f" . clang-format-buffer))
+ :config
+ (setq clang-format-executable (executable-find "clang-format")))
+
+(use-package sis
+ :config
+ (sis-ism-lazyman-config "1" "2" 'fcitx5)
+ (sis-global-cursor-color-mode t)
+ ;; 启用 /respect/ 模式
+ (sis-global-respect-mode t)
+ ;; 为所有缓冲区启用 /context/ 模式
+ (sis-global-context-mode t)
+ ;; 为所有缓冲区启用 /inline english/ 模式
+ (sis-global-inline-mode t)
+ (with-eval-after-load 'evil
+ ;; 强制在进入插入模式时触发 sis 的恢复逻辑
+ ;;(add-hook 'evil-insert-state-entry-hook #'sis-context-restore)
+ ; (add-hook 'evil-insert-state-entry-hook #'sis-set-chinese))
+ ))
+
+(require 'smtpmail)
+
+(add-to-list 'load-path "/usr/share/emacs/site-lisp/elpa-src/mu4e-1.8.14")
+
+(setq gnutls-algorithm-priority "NORMAL:%COMPAT")
+
+(defun mu4e-goodies~break-cjk-word (word)
+ "Break CJK word into list of bi-grams like: 我爱你 -> 我爱 爱你"
+ (if (or (<= (length word) 2)
+ (equal (length word) (string-bytes word)))
+ word
+ (let ((pos nil)
+ (char-list nil)
+ (br-word nil))
+ (if (setq pos (string-match ":" word))
+ (concat (substring word 0 (+ 1 pos))
+ (mu4e-goodies~break-cjk-word (substring word (+ 1 pos))))
+ (if (memq 'ascii (find-charset-string word))
+ word
+ (progn
+ (setq char-list (split-string word "" t))
+ (while (cdr char-list)
+ (setq br-word (concat br-word (concat (car char-list) (cadr char-list)) " "))
+ (setq char-list (cdr char-list)))
+ br-word))))))
+
+(defun mu4e-goodies~break-cjk-query (expr)
+ "Break CJK strings into bi-grams in query."
+ (let ((word-list (split-string expr " " t))
+ (new ""))
+ (dolist (word word-list new)
+ (setq new (concat new (mu4e-goodies~break-cjk-word word) " ")))))
+
+(setq mu4e-query-rewrite-function 'mu4e-goodies~break-cjk-query)
+
+(use-package mu4e
+ :ensure nil
+ :if (executable-find "mu")
+ :commands (mu4e)
+ :bind (:map mu4e-view-mode-map
+ ("9" . scroll-down-command)
+ ("0" . scroll-up-command)
+ :map mu4e-search-minor-mode-map
+ ("/" . mu4e-search-maildir)
+ :map mu4e-main-mode-map
+ ("g" . mu4e-update-mail-and-index)
+ :map mu4e-headers-mode-map
+ ("<backspace>" . scroll-down-command)
+ ("j" . mu4e-headers-next)
+ ("k" . mu4e-headers-prev)
+ ("r" . mu4e-headers-mark-for-read)
+ ("!" . mu4e-headers-flag-all-read)
+ ("f" . mu4e-headers-mark-for-flag))
+
+ :custom
+ (mu4e-headers-fields '((:human-date . 10)
+ (:flags . 6)
+ (:from-or-to . 22)
+ (:thread-subject . nil)))
+ (mu4e-view-fields '(:from :to :cc :bcc :subject :flags
+ :date :maildir :mailing-list :tags))
+ (mu4e-modeline-show-global nil)
+ (mu4e-hide-index-messages t)
+
+ :init
+ (setq user-mail-address "im@verdant.ee"
+ user-full-name "Verdant"
+ mu4e-debug t)
+
+ (setq message-send-mail-function 'sendmail-send-it
+ sendmail-program "/usr/bin/msmtp"
+ mail-specify-envelope-from t
+ mail-envelope-from 'header)
+
+ (setq message-citation-line-format "\nOn %a, %b %d, %Y at %r %z, %N wrote:\n"
+ message-citation-line-function 'message-insert-formatted-citation-line
+ mm-discouraged-alternatives '("text/html" "text/richtext")
+ gnus-article-time-format "%a, %Y-%m-%d %T %z"
+ gnus-article-date-headers '(user-defined original))
+
+ :config
+ (require 'mu4e-contrib)
+
+ (setq mail-user-agent 'mu4e-user-agent)
+
+ (setq mu4e-contexts
+ (list
+ (make-mu4e-context
+ :name "Verdant"
+ :match-func (lambda (msg)
+ (when msg
+ (string-prefix-p "/ljc" (mu4e-message-field msg :maildir))))
+ :vars '((mu4e-sent-folder . "/Verdant/Sent")
+ (mu4e-trash-folder . "/Verdant/Trash")
+ (mu4e-refile-folder . "/Verdant/Archive")
+ (mu4e-drafts-folder . "/Verdant/Drafts")
+ (user-mail-address . "im@verdant.ee")))))
+
+ (setq mu4e-compose-complete-only-personal t
+ mu4e-view-show-addresses t
+ mu4e-view-show-images nil
+ mu4e-attachment-dir "~/Downloads"
+ mu4e-sent-messages-behavior 'sent
+ mu4e-context-policy 'pick-first
+ mu4e-compose-context-policy 'ask-if-none
+ mu4e-compose-dont-reply-to-self t
+ mu4e-confirm-quit nil
+ mu4e-headers-date-format "%+4Y-%m-%d"
+ mu4e-view-html-plaintext-ratio-heuristic most-positive-fixnum
+ mu4e-update-interval (* 30 60)
+ mu4e-get-mail-command "true"
+ mu4e-compose-format-flowed t
+ mu4e-completing-read-function 'ido-completing-read)
+
+ (setq mu4e-bookmarks '((:name "All Inbox"
+ :query "maildir:/Verdant/INBOX"
+ :key ?i)
+ (:name "Unread messages"
+ :query "flag:unread AND NOT flag:trashed"
+ :key ?u)
+ (:name "Today's messages"
+ :query "date:today..now AND NOT flag:trashed"
+ :key ?t)
+ (:name "Last 7 days"
+ :query "date:7d..now AND NOT flag:trashed"
+ :hide-unread t
+ :key ?w)
+ (:name "Flagged"
+ :query "flag:flagged"
+ :key ?f)
+ (:name "Sent"
+ :query "maildir:/Verdant/Sent"
+ :key ?s)))
+
+ (add-to-list 'mu4e-view-actions '("browser" . mu4e-action-view-in-browser) t)
+
+ (defun my/mu4e-pre-update-hook ()
+ (let ((inhibit-message t))
+ (message "Update and index mu4e at %s" (format-time-string "%D %-I:%M %p"))))
+
+ (defun my/mu4e-stop-update-task ()
+ (interactive)
+ (when mu4e--update-timer
+ (cancel-timer mu4e--update-timer)
+ (setq mu4e--update-timer nil)))
+
+ (setq mu4e-update-pre-hook 'my/mu4e-pre-update-hook)
+
+ (add-to-list 'mu4e-view-fields :bcc))
+
+(use-package ace-window
+ :ensure t
+ :bind
+ (("C-x o" . ace-window)))
+
+(use-package dashboard
+ :ensure t
+ :config
+ (setq dashboard-startup-banner 'logo
+ dashboard-banner-logo-title "Welcome to Verdant's Emacverse!!!"
+ dashboard-center-content t
+ dashboard-set-heading-icons t
+ dashboard-items '((recents . 10)
+ (bookmarks . 5))
+ dashboard-footer-messages '("verdant.el"))
+
+ ;; 核心三件套
+ (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")))
+ (dashboard-setup-startup-hook)
+ (add-hook 'after-init-hook #'dashboard-open t))
+
+(setq custom-safe-themes t)
+(load-theme 'doom-Iosvkem)
+
+(set-face-attribute
+ 'default nil
+ :height 120)
+
+(setq window-combination-resize t)
+
+(setq x-stretch-cursor t)
+
+(setq scheme-program-name "mit-scheme")
+
+(require 'cmuscheme)
+
+(global-set-key (kbd "C-c C-e") 'scheme-send-last-sexp) ; 求值前一个表达式
+(global-set-key (kbd "C-c C-r") 'scheme-send-region) ; 求值选中区域
+(global-set-key (kbd "C-c C-l") 'scheme-load-file) ; 加载整个文件
+(global-set-key (kbd "C-c C-z") 'run-scheme) ; 启动/切换 REPL
+
+(add-to-list 'auto-mode-alist '("\\.scm\\'" . scheme-mode))
+
+(defun setup-markdown-writing-environment ()
+ "为 Markdown 写作优化的环境:开启 Olivetti,关闭行号。"
+ (interactive)
+ (variable-pitch-mode 1)
+ (display-line-numbers-mode -1)
+ (pixel-scroll-precision-mode 1)
+ (olivetti-mode))
+
+(add-hook 'markdown-mode-hook
+ (lambda ()
+ ;; 取消 Evil 的 TAB 绑定,使用 markdown-cycle
+ (define-key evil-normal-state-local-map (kbd "TAB") 'markdown-cycle)
+ (define-key evil-insert-state-local-map (kbd "TAB") 'indent-for-tab-command)))
+
+(setq dired-recursive-copies 'always)
+(setq dired-recursive-deletes 'always)
+(setq dired-dwim-target t)
+
+(put 'dired-find-alternate-file 'disabled nil)
+(with-eval-after-load 'dired
+ (define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file)
+ (define-key dired-mode-map (kbd "^") (lambda () (interactive) (find-alternate-file "..")))) ; was dired-up-directory)
+
+(setq dired-dwim-target t)
+
+;; dired-sort
+(defun dired-sort-size ()
+ "Dired sort by size."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "S")))
+
+(defun dired-sort-extension ()
+ "Dired sort by extension."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "X")))
+
+(defun dired-sort-ctime ()
+ "Dired sort by create time."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "ct")))
+
+(defun dired-sort-utime ()
+ "Dired sort by access time."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "ut")))
+
+(defun dired-sort-time ()
+ "Dired sort by time."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "t")))
+
+(defun dired-sort-name ()
+ "Dired sort by name."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "")))
+;; 在 Dired 中,按`l`进入文件,按`h`回到上一级目录
+(add-hook 'evil-mode-hook (lambda ()
+ (with-eval-after-load 'dired
+ (add-hook 'dired-mode-hook
+ (lambda ()
+ (define-key evil-normal-state-local-map (kbd "h") nil)
+ (define-key evil-normal-state-local-map (kbd "l") nil)
+ (define-key evil-normal-state-local-map (kbd "h") #'dired-up-directory)
+ (define-key evil-normal-state-local-map (kbd "l") #'dired-find-file))))))
+
+(add-hook 'dired-mode-hook
+ (lambda ()
+ (define-key dired-mode-map (kbd "C-b") nil)
+ (define-key dired-mode-map (kbd "C-f") nil)
+ (define-key dired-mode-map (kbd "C-b") #'dired-up-directory)
+ (define-key dired-mode-map (kbd "C-f") #'dired-find-file)))
+
+(global-set-key (kbd "<escape>") #'my/keyboard-escape-quit)
+
+(global-auto-revert-mode t) ; 另一程序修改文件让 Emacs 及时刷新 Buffer
+
+(use-package org-bullets
+ :ensure t
+ :config
+ (setq org-bullets-bullet-list '("☰" "☷" "☯" "☭")))
+(org-bullets-mode)
+
+(lambda () (progn
+ (setq left-margin-width 2)
+ (setq right-margin-width 2)
+ (set-window-buffer nil (current-buffer))))
+
+(setq org-startup-indented t
+ org-ellipsis "  " ;; folding symbol
+ org-pretty-entities t
+ org-hide-emphasis-markers t
+ ;; show actually italicized text instead of /italicized text/
+ org-agenda-block-separator ""
+ org-fontify-whole-heading-line t
+ org-fontify-done-headline t
+ org-fontify-quote-and-verse-blocks t)
+
+(auto-save-mode 1)
+
+(setq ring-bell-function 'ignore
+ custom-safe-themes t ; 禁用非法操作提示
+ cursor-type 'box
+ fringes-outside-margins t
+ display-time-24hr-format t ; 时间使用 24 小时制
+ display-time-day-and-date t ; 时间显示包括日期和时间
+ display-time-interval 60 ; 刷新频率
+ display-time-format "%a %b %-e %H:%M" ; 时间格式
+ scroll-step 1
+ scroll-conservatively 10000)
+(when (display-graphic-p)
+ (set-frame-size (selected-frame) 143 40))
+(global-display-line-numbers-mode t)
+(display-time-mode 1)
+
+(setq confirm-kill-emacs #'yes-or-no-p ; 关闭 Emacs 时询问 y or n
+ auto-save-visited-interval 5
+ native-comp-async-report-warinings-errors nil
+ backup-directory-alist `((".*" . ,temporary-file-directory))
+ auto-save-file-name-transforms `((".*" ,temporary-file-directory t))
+ select-enable-clipboard t
+ select-enable-primary t
+ interprogram-cut-function
+ (lambda (text &optional push)
+ (let ((process-connection-type nil))
+ (let ((proc (start-process "xclip" nil "xclip" "-selection" "clipboard")))
+ (process-send-string proc text)
+ (process-send-eof proc))))
+ interprogram-paste-function
+ (lambda ()
+ (shell-command-to-string "xclip -o -selection clipboard")))
+
+(setq-default delete-by-moving-to-transh t)
+
+(defun my/keyboard-escape-quit()
+ "快速的 Esc 退出 minibuffer"
+ (interactive)
+ (keyboard-escape-quit))
+
+(global-set-key (kbd "<escape>") #'my/keyboard-escape-quit)
+
+(show-paren-mode 1) ; 减轻数括号的痛苦
+
+(provide 'config)
+
+(setq c-basic-offset 4
+ tab-width 4)
+
+(use-package counsel
+ :ensure t)
diff --git a/config.org b/config.org
new file mode 100644
index 0000000..9a80b28
--- /dev/null
+++ b/config.org
@@ -0,0 +1,548 @@
+#+title: config
+#+property: header-args :tangle yes
+
+* 编辑相关
+** Evil
+#+begin_src emacs-lisp
+ ;; -*- lexical-binding: t; -*-
+ (use-package evil
+ :init
+ (setq evil-want-keybinding nil
+ evil-want-C-u-scroll t)
+ :config
+ (evil-mode)
+ :ensure t)
+
+ (use-package evil-collection
+ :after evil
+ :ensure t
+ :config
+ (evil-collection-init))
+
+#+end_src
+** olivetti
+#+begin_src emacs-lisp
+ (use-package olivetti)
+#+end_src
+** yasnippet
+#+begin_src emacs-lisp
+ (use-package yasnippet
+ :ensure t
+ :config
+ (yas-global-mode 1))
+#+end_src
+** ivy
+#+begin_src emacs-lisp
+ (use-package ivy
+ :ensure t
+ :init
+ (ivy-mode 1)
+ (counsel-mode 1)
+ :config
+ (setq ivy-use-selectable-prompt t)
+ (setq ivy-use-preview t)
+ (setq ivy-fixed-height-minibuffer t)
+ (setq ivy-use-preview t)
+ (setq ivy-use-virtual-buffers t)
+ (setq search-default-mode #'char-fold-to-regexp)
+ (setq ivy-count-format "(%d/%d) ")
+ (setq ivy-initial-inputs-alist nil) ;; 不预设初始输入
+ (setq ivy-use-selectable-prompt t) ;; 允许选择提示
+ :bind
+ (("C-s" . 'swiper)
+ ("C-x b" . 'ivy-switch-buffer)
+ ("C-c v" . 'ivy-push-view)
+ ("C-c s" . 'ivy-switch-view)
+ ("C-c V" . 'ivy-pop-view)
+ ("C-x C-@" . 'counsel-mark-ring); 在某些终端上 C-x C-SPC 会被映射为 C-x C-@,比如在 macOS 上,所以要手动设置
+ ("C-x C-SPC" . 'counsel-mark-ring)
+ :map minibuffer-local-map
+ ("C-r" . counsel-minibuffer-history)))
+
+ ;; (use-package ivy-posframe
+ ;; :ensure t
+ ;; :config
+ ;; (ivy-posframe-mode t))
+
+ (use-package ivy-rich
+ :ensure t
+ :config
+ (ivy-rich-mode t))
+#+end_src
+** company-mode
+#+begin_src emacs-lisp
+ ;; 启用 company-mode 全局补全
+ (use-package company
+ :ensure t
+ :config
+ (setq company-idle-delay 0.0
+ company-minimum-prefix-length 1)
+ (global-company-mode)
+
+ (with-eval-after-load 'company
+ ;; 补全列表背景
+ (set-face-attribute 'company-tooltip nil
+ :foreground "white" :background "gray20")
+ ;; 选中项背景
+ (set-face-attribute 'company-tooltip-selection nil
+ :foreground "blue" :background "gray20")
+ ;; 输入前缀高亮
+ (set-face-attribute 'company-tooltip-common nil
+ :foreground "orange" :background "gray20")
+ ;; 右侧注释/类型
+ (set-face-attribute 'company-tooltip-annotation nil
+ :foreground "cyan" :background "gray20")))
+#+end_src
+** LSP
+
+#+begin_src emacs-lisp
+ (use-package lsp-mode
+ :ensure t
+ :init
+ (setq read-process-output-max (* 1024 1024))
+ :hook (
+ (go-mode .lsp)
+ (css-mode . lsp)
+ (html-mode . lsp))
+ :config
+ (setq lsp-enable-on-type-formatting nil))
+
+ (use-package lsp-ui
+ :ensure t
+ :commands lsp-ui-mode)
+#+end_src
+** clang-format
+#+begin_src emacs-lisp
+ (use-package clang-format
+ :ensure t
+ :bind
+ (:map c-mode-base-map
+ ("C-c C-f" . clang-format-buffer))
+ :config
+ (setq clang-format-executable (executable-find "clang-format")))
+#+end_src
+** sis
+
+#+begin_src emacs-lisp
+ (use-package sis
+ :config
+ (sis-ism-lazyman-config "1" "2" 'fcitx5)
+ (sis-global-cursor-color-mode t)
+ ;; 启用 /respect/ 模式
+ (sis-global-respect-mode t)
+ ;; 为所有缓冲区启用 /context/ 模式
+ (sis-global-context-mode t)
+ ;; 为所有缓冲区启用 /inline english/ 模式
+ (sis-global-inline-mode t)
+ (with-eval-after-load 'evil
+ ;; 强制在进入插入模式时触发 sis 的恢复逻辑
+ ;;(add-hook 'evil-insert-state-entry-hook #'sis-context-restore)
+ ; (add-hook 'evil-insert-state-entry-hook #'sis-set-chinese))
+ ))
+#+end_src
+
+* 邮件
+** mu4e
+这个东西的配置实在是太庞大了,懒得细拆了。
+#+begin_src emacs-lisp
+ (require 'smtpmail)
+
+ (add-to-list 'load-path "/usr/share/emacs/site-lisp/elpa-src/mu4e-1.8.14")
+
+ (setq gnutls-algorithm-priority "NORMAL:%COMPAT")
+
+ (defun mu4e-goodies~break-cjk-word (word)
+ "Break CJK word into list of bi-grams like: 我爱你 -> 我爱 爱你"
+ (if (or (<= (length word) 2)
+ (equal (length word) (string-bytes word)))
+ word
+ (let ((pos nil)
+ (char-list nil)
+ (br-word nil))
+ (if (setq pos (string-match ":" word))
+ (concat (substring word 0 (+ 1 pos))
+ (mu4e-goodies~break-cjk-word (substring word (+ 1 pos))))
+ (if (memq 'ascii (find-charset-string word))
+ word
+ (progn
+ (setq char-list (split-string word "" t))
+ (while (cdr char-list)
+ (setq br-word (concat br-word (concat (car char-list) (cadr char-list)) " "))
+ (setq char-list (cdr char-list)))
+ br-word))))))
+
+ (defun mu4e-goodies~break-cjk-query (expr)
+ "Break CJK strings into bi-grams in query."
+ (let ((word-list (split-string expr " " t))
+ (new ""))
+ (dolist (word word-list new)
+ (setq new (concat new (mu4e-goodies~break-cjk-word word) " ")))))
+
+ (setq mu4e-query-rewrite-function 'mu4e-goodies~break-cjk-query)
+
+ (use-package mu4e
+ :ensure nil
+ :if (executable-find "mu")
+ :commands (mu4e)
+ :bind (:map mu4e-view-mode-map
+ ("9" . scroll-down-command)
+ ("0" . scroll-up-command)
+ :map mu4e-search-minor-mode-map
+ ("/" . mu4e-search-maildir)
+ :map mu4e-main-mode-map
+ ("g" . mu4e-update-mail-and-index)
+ :map mu4e-headers-mode-map
+ ("<backspace>" . scroll-down-command)
+ ("j" . mu4e-headers-next)
+ ("k" . mu4e-headers-prev)
+ ("r" . mu4e-headers-mark-for-read)
+ ("!" . mu4e-headers-flag-all-read)
+ ("f" . mu4e-headers-mark-for-flag))
+
+ :custom
+ (mu4e-headers-fields '((:human-date . 10)
+ (:flags . 6)
+ (:from-or-to . 22)
+ (:thread-subject . nil)))
+ (mu4e-view-fields '(:from :to :cc :bcc :subject :flags
+ :date :maildir :mailing-list :tags))
+ (mu4e-modeline-show-global nil)
+ (mu4e-hide-index-messages t)
+
+ :init
+ (setq user-mail-address "im@verdant.ee"
+ user-full-name "Verdant"
+ mu4e-debug t)
+
+ (setq message-send-mail-function 'sendmail-send-it
+ sendmail-program "/usr/bin/msmtp"
+ mail-specify-envelope-from t
+ mail-envelope-from 'header)
+
+ (setq message-citation-line-format "\nOn %a, %b %d, %Y at %r %z, %N wrote:\n"
+ message-citation-line-function 'message-insert-formatted-citation-line
+ mm-discouraged-alternatives '("text/html" "text/richtext")
+ gnus-article-time-format "%a, %Y-%m-%d %T %z"
+ gnus-article-date-headers '(user-defined original))
+
+ :config
+ (require 'mu4e-contrib)
+
+ (setq mail-user-agent 'mu4e-user-agent)
+
+ (setq mu4e-contexts
+ (list
+ (make-mu4e-context
+ :name "Verdant"
+ :match-func (lambda (msg)
+ (when msg
+ (string-prefix-p "/ljc" (mu4e-message-field msg :maildir))))
+ :vars '((mu4e-sent-folder . "/Verdant/Sent")
+ (mu4e-trash-folder . "/Verdant/Trash")
+ (mu4e-refile-folder . "/Verdant/Archive")
+ (mu4e-drafts-folder . "/Verdant/Drafts")
+ (user-mail-address . "im@verdant.ee")))))
+
+ (setq mu4e-compose-complete-only-personal t
+ mu4e-view-show-addresses t
+ mu4e-view-show-images nil
+ mu4e-attachment-dir "~/Downloads"
+ mu4e-sent-messages-behavior 'sent
+ mu4e-context-policy 'pick-first
+ mu4e-compose-context-policy 'ask-if-none
+ mu4e-compose-dont-reply-to-self t
+ mu4e-confirm-quit nil
+ mu4e-headers-date-format "%+4Y-%m-%d"
+ mu4e-view-html-plaintext-ratio-heuristic most-positive-fixnum
+ mu4e-update-interval (* 30 60)
+ mu4e-get-mail-command "true"
+ mu4e-compose-format-flowed t
+ mu4e-completing-read-function 'ido-completing-read)
+
+ (setq mu4e-bookmarks '((:name "All Inbox"
+ :query "maildir:/Verdant/INBOX"
+ :key ?i)
+ (:name "Unread messages"
+ :query "flag:unread AND NOT flag:trashed"
+ :key ?u)
+ (:name "Today's messages"
+ :query "date:today..now AND NOT flag:trashed"
+ :key ?t)
+ (:name "Last 7 days"
+ :query "date:7d..now AND NOT flag:trashed"
+ :hide-unread t
+ :key ?w)
+ (:name "Flagged"
+ :query "flag:flagged"
+ :key ?f)
+ (:name "Sent"
+ :query "maildir:/Verdant/Sent"
+ :key ?s)))
+
+ (add-to-list 'mu4e-view-actions '("browser" . mu4e-action-view-in-browser) t)
+
+ (defun my/mu4e-pre-update-hook ()
+ (let ((inhibit-message t))
+ (message "Update and index mu4e at %s" (format-time-string "%D %-I:%M %p"))))
+
+ (defun my/mu4e-stop-update-task ()
+ (interactive)
+ (when mu4e--update-timer
+ (cancel-timer mu4e--update-timer)
+ (setq mu4e--update-timer nil)))
+
+ (setq mu4e-update-pre-hook 'my/mu4e-pre-update-hook)
+
+ (add-to-list 'mu4e-view-fields :bcc))
+#+end_src
+* 外观和 UI
+** ace-window
+#+begin_src emacs-lisp
+ (use-package ace-window
+ :ensure t
+ :bind
+ (("C-x o" . ace-window)))
+#+end_src
+** dashboard
+#+begin_src emacs-lisp
+ (use-package dashboard
+ :ensure t
+ :config
+ (setq dashboard-startup-banner 'logo
+ dashboard-banner-logo-title "Welcome to Verdant's Emacverse!!!"
+ dashboard-center-content t
+ dashboard-set-heading-icons t
+ dashboard-items '((recents . 10)
+ (bookmarks . 5))
+ dashboard-footer-messages '("verdant.el"))
+
+ ;; 核心三件套
+ (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")))
+ (dashboard-setup-startup-hook)
+ (add-hook 'after-init-hook #'dashboard-open t))
+#+end_src
+
+** 主题
+#+begin_src emacs-lisp
+ (setq custom-safe-themes t)
+ (load-theme 'doom-Iosvkem)
+#+end_src
+
+** 窗口大小
+#+begin_src emacs-lisp
+ (set-face-attribute
+ 'default nil
+ :height 120)
+#+end_src
+
+** 新窗口平均其他左右窗口
+#+begin_src emacs-lisp
+ (setq window-combination-resize t)
+#+end_src
+
+** 光标拉伸到字符宽度
+#+begin_src emacs-lisp
+ (setq x-stretch-cursor t)
+#+end_src
+
+* MIT-Scheme
+#+begin_src emacs-lisp
+ (setq scheme-program-name "mit-scheme")
+
+ (require 'cmuscheme)
+
+ (global-set-key (kbd "C-c C-e") 'scheme-send-last-sexp) ; 求值前一个表达式
+ (global-set-key (kbd "C-c C-r") 'scheme-send-region) ; 求值选中区域
+ (global-set-key (kbd "C-c C-l") 'scheme-load-file) ; 加载整个文件
+ (global-set-key (kbd "C-c C-z") 'run-scheme) ; 启动/切换 REPL
+
+ (add-to-list 'auto-mode-alist '("\\.scm\\'" . scheme-mode))
+#+end_src
+* Markdown
+** 优化环境
+#+begin_src emacs-lisp
+ (defun setup-markdown-writing-environment ()
+ "为 Markdown 写作优化的环境:开启 Olivetti,关闭行号。"
+ (interactive)
+ (variable-pitch-mode 1)
+ (display-line-numbers-mode -1)
+ (pixel-scroll-precision-mode 1)
+ (olivetti-mode))
+
+ (add-hook 'markdown-mode-hook
+ (lambda ()
+ ;; 取消 Evil 的 TAB 绑定,使用 markdown-cycle
+ (define-key evil-normal-state-local-map (kbd "TAB") 'markdown-cycle)
+ (define-key evil-insert-state-local-map (kbd "TAB") 'indent-for-tab-command)))
+#+end_src
+* Dired
+
+#+begin_src emacs-lisp
+ (setq dired-recursive-copies 'always)
+ (setq dired-recursive-deletes 'always)
+ (setq dired-dwim-target t)
+
+ (put 'dired-find-alternate-file 'disabled nil)
+ (with-eval-after-load 'dired
+ (define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file)
+ (define-key dired-mode-map (kbd "^") (lambda () (interactive) (find-alternate-file "..")))) ; was dired-up-directory)
+
+ (setq dired-dwim-target t)
+
+ ;; dired-sort
+ (defun dired-sort-size ()
+ "Dired sort by size."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "S")))
+
+ (defun dired-sort-extension ()
+ "Dired sort by extension."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "X")))
+
+ (defun dired-sort-ctime ()
+ "Dired sort by create time."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "ct")))
+
+ (defun dired-sort-utime ()
+ "Dired sort by access time."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "ut")))
+
+ (defun dired-sort-time ()
+ "Dired sort by time."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "t")))
+
+ (defun dired-sort-name ()
+ "Dired sort by name."
+ (interactive)
+ (dired-sort-other (concat dired-listing-switches "")))
+ ;; 在 Dired 中,按`l`进入文件,按`h`回到上一级目录
+ (add-hook 'evil-mode-hook (lambda ()
+ (with-eval-after-load 'dired
+ (add-hook 'dired-mode-hook
+ (lambda ()
+ (define-key evil-normal-state-local-map (kbd "h") nil)
+ (define-key evil-normal-state-local-map (kbd "l") nil)
+ (define-key evil-normal-state-local-map (kbd "h") #'dired-up-directory)
+ (define-key evil-normal-state-local-map (kbd "l") #'dired-find-file))))))
+
+ (add-hook 'dired-mode-hook
+ (lambda ()
+ (define-key dired-mode-map (kbd "C-b") nil)
+ (define-key dired-mode-map (kbd "C-f") nil)
+ (define-key dired-mode-map (kbd "C-b") #'dired-up-directory)
+ (define-key dired-mode-map (kbd "C-f") #'dired-find-file)))
+
+ (global-set-key (kbd "<escape>") #'my/keyboard-escape-quit)
+
+ (global-auto-revert-mode t) ; 另一程序修改文件让 Emacs 及时刷新 Buffer
+#+end_src
+* Org
+** org-bullets
+#+begin_src emacs-lisp
+ (use-package org-bullets
+ :ensure t
+ :config
+ (setq org-bullets-bullet-list '("☰" "☷" "☯" "☭")))
+ (org-bullets-mode)
+#+end_src
+** 边距
+#+begin_src emacs-lisp
+ (lambda () (progn
+ (setq left-margin-width 2)
+ (setq right-margin-width 2)
+ (set-window-buffer nil (current-buffer))))
+#+end_src
+** 微调
+#+begin_src emacs-lisp
+ (setq org-startup-indented t
+ org-ellipsis "  " ;; folding symbol
+ org-pretty-entities t
+ org-hide-emphasis-markers t
+ ;; show actually italicized text instead of /italicized text/
+ org-agenda-block-separator ""
+ org-fontify-whole-heading-line t
+ org-fontify-done-headline t
+ org-fontify-quote-and-verse-blocks t)
+#+end_src
+* 杂项
+** 自动保存
+#+begin_src emacs-lisp
+ (auto-save-mode 1)
+#+end_src
+** 时间显示
+#+begin_src emacs-lisp
+ (setq ring-bell-function 'ignore
+ custom-safe-themes t ; 禁用非法操作提示
+ cursor-type 'box
+ fringes-outside-margins t
+ display-time-24hr-format t ; 时间使用 24 小时制
+ display-time-day-and-date t ; 时间显示包括日期和时间
+ display-time-interval 60 ; 刷新频率
+ display-time-format "%a %b %-e %H:%M" ; 时间格式
+ scroll-step 1
+ scroll-conservatively 10000)
+ (when (display-graphic-p)
+ (set-frame-size (selected-frame) 143 40))
+ (global-display-line-numbers-mode t)
+ (display-time-mode 1)
+#+end_src
+
+** 关闭 Emacs 时询问
+#+begin_src emacs-lisp
+ (setq confirm-kill-emacs #'yes-or-no-p ; 关闭 Emacs 时询问 y or n
+ auto-save-visited-interval 5
+ native-comp-async-report-warinings-errors nil
+ backup-directory-alist `((".*" . ,temporary-file-directory))
+ auto-save-file-name-transforms `((".*" ,temporary-file-directory t))
+ select-enable-clipboard t
+ select-enable-primary t
+ interprogram-cut-function
+ (lambda (text &optional push)
+ (let ((process-connection-type nil))
+ (let ((proc (start-process "xclip" nil "xclip" "-selection" "clipboard")))
+ (process-send-string proc text)
+ (process-send-eof proc))))
+ interprogram-paste-function
+ (lambda ()
+ (shell-command-to-string "xclip -o -selection clipboard")))
+#+end_src
+
+** 删除文件移动到垃圾箱
+#+begin_src emacs-lisp
+ (setq-default delete-by-moving-to-transh t)
+#+end_src
+
+** 快速退出 minibuffer
+#+begin_src emacs-lisp
+ (defun my/keyboard-escape-quit()
+ "快速的 Esc 退出 minibuffer"
+ (interactive)
+ (keyboard-escape-quit))
+
+ (global-set-key (kbd "<escape>") #'my/keyboard-escape-quit)
+#+end_src
+
+** 减轻数括号的痛苦
+#+begin_src emacs-lisp
+ (show-paren-mode 1) ; 减轻数括号的痛苦
+#+end_src
+
+#+begin_src emacs-lisp
+ (provide 'config)
+#+end_src
+
+** 编码风格
+#+begin_src emacs-lisp
+ (setq c-basic-offset 4
+ tab-width 4)
+#+end_src
+
+** counsel
+#+begin_src emacs-lisp
+ (use-package counsel
+ :ensure t)
+#+end_src
diff --git a/core/core-basic.el b/core/core-basic.el
deleted file mode 100644
index ce5375d..0000000
--- a/core/core-basic.el
+++ /dev/null
@@ -1,54 +0,0 @@
-;; -*- lexical-binding: t; -*-
-
-(setq confirm-kill-emacs #'yes-or-no-p ; 关闭 Emacs 时询问 y or n
- auto-save-visited-interval 5
- native-comp-async-report-warinings-errors nil
- backup-directory-alist `((".*" . ,temporary-file-directory))
- auto-save-file-name-transforms `((".*" ,temporary-file-directory t))
- select-enable-clipboard t
- select-enable-primary t
- interprogram-cut-function
- (lambda (text &optional push)
- (let ((process-connection-type nil))
- (let ((proc (start-process "xclip" nil "xclip" "-selection" "clipboard")))
- (process-send-string proc text)
- (process-send-eof proc))))
- interprogram-paste-function
- (lambda ()
- (shell-command-to-string "xclip -o -selection clipboard")))
-
-(setq-default delete-by-moving-to-transh t ; 删除文件移动到垃圾箱
- window-combination-resize t ; 新窗口平均其他左右窗口
- x-stretch-cursor t ; 将光标拉伸到字符宽度
- )
-
-(defun my/keyboard-escape-quit()
- "快速的 Esc 退出 minibuffer"
- (interactive)
- (keyboard-escape-quit))
-
-(global-set-key (kbd "<escape>") #'my/keyboard-escape-quit)
-
-;; 在 Dired 中,按`l`进入文件,按`h`回到上一级目录
-(add-hook 'evil-mode-hook (lambda ()
-(with-eval-after-load 'dired
- (add-hook 'dired-mode-hook
- (lambda ()
- (define-key evil-normal-state-local-map (kbd "h") nil)
- (define-key evil-normal-state-local-map (kbd "l") nil)
- (define-key evil-normal-state-local-map (kbd "h") #'dired-up-directory)
- (define-key evil-normal-state-local-map (kbd "l") #'dired-find-file))))))
-
- (add-hook 'dired-mode-hook
- (lambda ()
- (define-key dired-mode-map (kbd "C-b") nil)
- (define-key dired-mode-map (kbd "C-f") nil)
- (define-key dired-mode-map (kbd "C-b") #'dired-up-directory)
- (define-key dired-mode-map (kbd "C-f") #'dired-find-file)))
-
-(global-set-key (kbd "<escape>") #'my/keyboard-escape-quit)
-
-(global-auto-revert-mode t) ; 另一程序修改文件让 Emacs 及时刷新 Buffer
-(auto-save-mode 1)
-
-(provide 'core-basic)
diff --git a/core/core-dired.el b/core/core-dired.el
deleted file mode 100644
index a68e2c4..0000000
--- a/core/core-dired.el
+++ /dev/null
@@ -1,45 +0,0 @@
-;; -*- lexical-binding: t; -*-
-
-(setq dired-recursive-copies 'always)
-(setq dired-recursive-deletes 'always)
-(setq dired-dwim-target t)
-
-(put 'dired-find-alternate-file 'disabled nil)
-(with-eval-after-load 'dired
- (define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file)
- (define-key dired-mode-map (kbd "^") (lambda () (interactive) (find-alternate-file "..")))) ; was dired-up-directory)
-
-(setq dired-dwim-target t)
-
-;; dired-sort
-(defun dired-sort-size ()
- "Dired sort by size."
- (interactive)
- (dired-sort-other (concat dired-listing-switches "S")))
-
-(defun dired-sort-extension ()
- "Dired sort by extension."
- (interactive)
- (dired-sort-other (concat dired-listing-switches "X")))
-
-(defun dired-sort-ctime ()
- "Dired sort by create time."
- (interactive)
- (dired-sort-other (concat dired-listing-switches "ct")))
-
-(defun dired-sort-utime ()
- "Dired sort by access time."
- (interactive)
- (dired-sort-other (concat dired-listing-switches "ut")))
-
-(defun dired-sort-time ()
- "Dired sort by time."
- (interactive)
- (dired-sort-other (concat dired-listing-switches "t")))
-
-(defun dired-sort-name ()
- "Dired sort by name."
- (interactive)
- (dired-sort-other (concat dired-listing-switches "")))
-
-(provide 'core-dired)
diff --git a/core/core-editing.el b/core/core-editing.el
deleted file mode 100644
index d6c10aa..0000000
--- a/core/core-editing.el
+++ /dev/null
@@ -1,25 +0,0 @@
-;; -*- lexical-binding: t; -*-
-(show-paren-mode t)
-(save-place-mode 1)
-(global-subword-mode 1)
-(add-hook 'prog-mode-hook #'show-paren-mode)
-
-(electric-pair-mode 1)
-
-(defun setup-markdown-writing-environment ()
- "为 Markdown 写作优化的环境:开启 Olivetti,关闭行号。"
- (interactive)
- (variable-pitch-mode 1)
- (display-line-numbers-mode -1)
- (pixel-scroll-precision-mode 1)
- (olivetti-mode))
-
-(add-hook 'markdown-mode-hook
- (lambda ()
- ;; 取消 Evil 的 TAB 绑定,使用 markdown-cycle
- (define-key evil-normal-state-local-map (kbd "TAB") 'markdown-cycle)
- (define-key evil-insert-state-local-map (kbd "TAB") 'indent-for-tab-command)))
-
-(setq evil-want-C-u-scroll t)
-
-(provide 'core-editing)
diff --git a/core/core-ui.el b/core/core-ui.el
deleted file mode 100644
index f0190fd..0000000
--- a/core/core-ui.el
+++ /dev/null
@@ -1,21 +0,0 @@
-;; -*- lexical-binding: t; -*-
-(display-time-mode 1)
-
-(setq custom-safe-themes t
- ring-bell-function 'ignore
- cursor-type 'box
- fringes-outside-margins t
- display-time-24hr-format t ; 时间使用 24 小时制
- display-time-day-and-date t ; 时间显示包括日期和时间
- display-time-interval 60 ; 刷新频率
- display-time-format "%a %b %-e %H:%M" ; 时间格式
- scroll-step 1
- scroll-conservatively 10000)
-
-(load-theme 'doom-Iosvkem)
-
-(when (display-graphic-p)
- (set-frame-size (selected-frame) 143 40))
-
-(global-display-line-numbers-mode t)
-(provide 'core-ui)
diff --git a/core/core.el b/core/core.el
deleted file mode 100644
index 767ca63..0000000
--- a/core/core.el
+++ /dev/null
@@ -1,5 +0,0 @@
-;; -*- lexical-binding: t; -*-
-(require 'core-ui)
-(require 'core-basic)
-(require 'core-editing)
-(provide 'core)
diff --git a/init.el b/init.el
index d373a05..c9cdceb 100644
--- a/init.el
+++ b/init.el
@@ -1,12 +1,16 @@
;; -*- lexical-binding: t; -*-
+(require 'org)
(setq user-emacs-directory (expand-file-name "~/.emacs.d"))
+(add-to-list 'load-path user-emacs-directory)
+(let ((org-file (expand-file-name "config.org" user-emacs-directory))
+ (el-file (expand-file-name "config.el" user-emacs-directory)))
+ (when (or (not (file-exists-p el-file))
+ (file-newer-than-file-p org-file el-file))
+ (org-babel-tangle-file org-file))
+ (load-file el-file))
+(require 'config)
-(add-to-list 'load-path (expand-file-name "core" user-emacs-directory))
-(add-to-list 'load-path (expand-file-name "packages" user-emacs-directory))
-
-(require 'core)
-(require 'packages)
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
@@ -14,9 +18,10 @@
;; If there is more than one, they won't work right.
'(package-selected-packages
'(ace-window clang-format color-theme-sanityinc-solarized company
- counsel dashboard diredfl doom-themes evil-collection
- evil-mu4e hugoista ivy-posframe ivy-rich leetcode
- lsp-ui olivetti yasnippet)))
+ counsel dashboard diredfl doom-themes evil-collection
+ evil-mu4e hugoista ivy-posframe ivy-rich leetcode
+ lsp-ui magit olivetti org-bullets ox-hugo sis
+ yasnippet)))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
diff --git a/packages/.hugo_build.lock b/packages/.hugo_build.lock
deleted file mode 100644
index e69de29..0000000
--- a/packages/.hugo_build.lock
+++ /dev/null
diff --git a/packages/packages-editing.el b/packages/packages-editing.el
deleted file mode 100644
index c2e8b5c..0000000
--- a/packages/packages-editing.el
+++ /dev/null
@@ -1,95 +0,0 @@
-;; -*- lexical-binding: t; -*-
-(use-package evil
- :init
- (setq evil-want-keybinding nil)
- :config
- (evil-mode)
- :ensure t)
-
-(use-package evil-collection
- :after evil
- :ensure t
- :config
- (evil-collection-init))
-
-(use-package olivetti)
-
-(use-package yasnippet
- :ensure t
- :config
- (yas-global-mode 1))
-
-(use-package ivy
- :ensure t
- :init
- (ivy-mode 1)
- (counsel-mode 1)
- :config
- (setq ivy-use-selectable-prompt t)
- (setq ivy-use-preview t)
- (setq ivy-fixed-height-minibuffer t)
- (setq ivy-use-preview t)
- (setq ivy-use-virtual-buffers t)
- (setq search-default-mode #'char-fold-to-regexp)
- (setq ivy-count-format "(%d/%d) ")
- (setq ivy-initial-inputs-alist nil) ;; 不预设初始输入
- (setq ivy-use-selectable-prompt t) ;; 允许选择提示
- :bind
- (("C-s" . 'swiper)
- ("C-x b" . 'ivy-switch-buffer)
- ("C-c v" . 'ivy-push-view)
- ("C-c s" . 'ivy-switch-view)
- ("C-c V" . 'ivy-pop-view)
- ("C-x C-@" . 'counsel-mark-ring); 在某些终端上 C-x C-SPC 会被映射为 C-x C-@,比如在 macOS 上,所以要手动设置
- ("C-x C-SPC" . 'counsel-mark-ring)
- :map minibuffer-local-map
- ("C-r" . counsel-minibuffer-history)))
-
-;; 启用 company-mode 全局补全
-(use-package company
- :ensure t
- :config
- (setq company-idle-delay 0.0
- company-minimum-prefix-length 1)
- (global-company-mode)
-
- (with-eval-after-load 'company
- ;; 补全列表背景
- (set-face-attribute 'company-tooltip nil
- :foreground "white" :background "gray20")
- ;; 选中项背景
- (set-face-attribute 'company-tooltip-selection nil
- :foreground "blue" :background "gray20")
- ;; 输入前缀高亮
- (set-face-attribute 'company-tooltip-common nil
- :foreground "orange" :background "gray20")
- ;; 右侧注释/类型
- (set-face-attribute 'company-tooltip-annotation nil
- :foreground "cyan" :background "gray20")))
-
-(use-package lsp-mode
- :ensure t
- :init
- (setq read-process-output-max (* 1024 1024))
- :hook (
- (c-mode . lsp)
- (go-mode .lsp)
- (css-mode . lsp)
- (html-mode . lsp))
- )
-
-(use-package lsp-ui
- :ensure t
- :commands lsp-ui-mode)
-
-(use-package clang-format
- :ensure t
- :hook
- (c-mode-common-hook . (lambda () (add-hook 'before-save-hook 'clang-format-buffer nil t)))
- :bind
- (:map c-mode-base-map
- ("C-c C-f" . clang-format-buffer))
- :config
- (setq clang-format-executable (executable-find "clang-format")))
-
-(provide 'packages-editing)
diff --git a/packages/packages-email.el b/packages/packages-email.el
deleted file mode 100644
index a519c42..0000000
--- a/packages/packages-email.el
+++ /dev/null
@@ -1,153 +0,0 @@
-;; -*- lexical-binding: t; -*-
-
-(require 'smtpmail)
-
-(add-to-list 'load-path "/usr/share/emacs/site-lisp/elpa-src/mu4e-1.8.14")
-
-(setq gnutls-algorithm-priority "NORMAL:%COMPAT")
-
-(defun mu4e-goodies~break-cjk-word (word)
- "Break CJK word into list of bi-grams like: 我爱你 -> 我爱 爱你"
- (if (or (<= (length word) 2)
- (equal (length word) (string-bytes word)))
- word
- (let ((pos nil)
- (char-list nil)
- (br-word nil))
- (if (setq pos (string-match ":" word))
- (concat (substring word 0 (+ 1 pos))
- (mu4e-goodies~break-cjk-word (substring word (+ 1 pos))))
- (if (memq 'ascii (find-charset-string word))
- word
- (progn
- (setq char-list (split-string word "" t))
- (while (cdr char-list)
- (setq br-word (concat br-word (concat (car char-list) (cadr char-list)) " "))
- (setq char-list (cdr char-list)))
- br-word))))))
-
-(defun mu4e-goodies~break-cjk-query (expr)
- "Break CJK strings into bi-grams in query."
- (let ((word-list (split-string expr " " t))
- (new ""))
- (dolist (word word-list new)
- (setq new (concat new (mu4e-goodies~break-cjk-word word) " ")))))
-
-(setq mu4e-query-rewrite-function 'mu4e-goodies~break-cjk-query)
-
-(use-package mu4e
- :ensure nil
- :if (executable-find "mu")
- :commands (mu4e)
- :bind (:map mu4e-view-mode-map
- ("9" . scroll-down-command)
- ("0" . scroll-up-command)
- :map mu4e-search-minor-mode-map
- ("/" . mu4e-search-maildir)
- :map mu4e-main-mode-map
- ("g" . mu4e-update-mail-and-index)
- :map mu4e-headers-mode-map
- ("<backspace>" . scroll-down-command)
- ("j" . mu4e-headers-next)
- ("k" . mu4e-headers-prev)
- ("r" . mu4e-headers-mark-for-read)
- ("!" . mu4e-headers-flag-all-read)
- ("f" . mu4e-headers-mark-for-flag))
-
- :custom
- (mu4e-headers-fields '((:human-date . 12)
- (:flags . 6)
- (:from-or-to . 22)
- (:thread-subject . nil)))
- (mu4e-view-fields '(:from :to :cc :bcc :subject :flags
- :date :maildir :mailing-list :tags))
- (mu4e-modeline-show-global nil)
- (mu4e-hide-index-messages t)
-
- :init
- (setq user-mail-address "im@verdant.ee"
- user-full-name "Verdant"
- mu4e-debug t)
-
- (setq message-send-mail-function 'sendmail-send-it
- sendmail-program "/usr/bin/msmtp"
- mail-specify-envelope-from t
- mail-envelope-from 'header)
-
- (setq message-citation-line-format "\nOn %a, %b %d, %Y at %r %z, %N wrote:\n"
- message-citation-line-function 'message-insert-formatted-citation-line
- mm-discouraged-alternatives '("text/html" "text/richtext")
- gnus-article-time-format "%a, %Y-%m-%d %T %z"
- gnus-article-date-headers '(user-defined original))
-
- :config
- (require 'mu4e-contrib)
-
- (setq mail-user-agent 'mu4e-user-agent)
-
- (setq mu4e-contexts
- (list
- (make-mu4e-context
- :name "Verdant"
- :match-func (lambda (msg)
- (when msg
- (string-prefix-p "/ljc" (mu4e-message-field msg :maildir))))
- :vars '((mu4e-sent-folder . "/Verdant/Sent")
- (mu4e-trash-folder . "/Verdant/Trash")
- (mu4e-refile-folder . "/Verdant/Archive")
- (mu4e-drafts-folder . "/Verdant/Drafts")
- (user-mail-address . "im@verdant.ee")))))
-
- (setq mu4e-compose-complete-only-personal t
- mu4e-view-show-addresses t
- mu4e-view-show-images nil
- mu4e-attachment-dir "~/Downloads"
- mu4e-sent-messages-behavior 'sent
- mu4e-context-policy 'pick-first
- mu4e-compose-context-policy 'ask-if-none
- mu4e-compose-dont-reply-to-self t
- mu4e-confirm-quit nil
- mu4e-headers-date-format "%+4Y-%m-%d"
- mu4e-view-html-plaintext-ratio-heuristic most-positive-fixnum
- mu4e-update-interval (* 30 60)
- mu4e-get-mail-command "true"
- mu4e-compose-format-flowed t
- mu4e-completing-read-function 'ido-completing-read)
-
- (setq mu4e-bookmarks '((:name "All Inbox"
- :query "maildir:/Verdant/INBOX"
- :key ?i)
- (:name "Unread messages"
- :query "flag:unread AND NOT flag:trashed"
- :key ?u)
- (:name "Today's messages"
- :query "date:today..now AND NOT flag:trashed"
- :key ?t)
- (:name "Last 7 days"
- :query "date:7d..now AND NOT flag:trashed"
- :hide-unread t
- :key ?w)
- (:name "Flagged"
- :query "flag:flagged"
- :key ?f)
- (:name "Sent"
- :query "maildir:/Verdant/Sent"
- :key ?s)))
-
- (add-to-list 'mu4e-view-actions '("browser" . mu4e-action-view-in-browser) t)
-
- (defun my/mu4e-pre-update-hook ()
- (let ((inhibit-message t))
- (message "Update and index mu4e at %s" (format-time-string "%D %-I:%M %p"))))
-
- (defun my/mu4e-stop-update-task ()
- (interactive)
- (when mu4e--update-timer
- (cancel-timer mu4e--update-timer)
- (setq mu4e--update-timer nil)))
-
- (setq mu4e-update-pre-hook 'my/mu4e-pre-update-hook)
-
- (add-to-list 'mu4e-view-fields :bcc))
-
-(provide 'packages-email)
diff --git a/packages/packages-leetcode.el b/packages/packages-leetcode.el
deleted file mode 100644
index a192b80..0000000
--- a/packages/packages-leetcode.el
+++ /dev/null
@@ -1,1386 +0,0 @@
-;;; leetcode.el --- An leetcode client -*- lexical-binding: t; no-byte-compile: t -*-
-
-;; Copyright (C) 2019 Wang Kai
-
-;; Author: Wang Kai <kaiwkx@gmail.com>
-;; Keywords: extensions, tools
-;; Package-Version: 20220206.1515
-;; Package-Commit: b3103bd08c8943091f702c66d674f0f27ef7fe0b
-;; URL: https://github.com/kaiwk/leetcode.el
-;; Package-Requires: ((emacs "26") (dash "2.16.0") (graphql "0.1.1") (spinner "1.7.3") (aio "1.0") (log4e "0.3.3"))
-;; Version: 0.1.24
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; leetcode.el is an unofficial LeetCode client.
-;;
-;; Now it implements several API:
-;; - Check problems list
-;; - Try testcase
-;; - Submit code
-;;
-;; Since most HTTP requests works asynchronously, it won't block Emacs.
-;;
-;;; Code:
-(eval-when-compile
- (require 'let-alist))
-
-(require 'json)
-(require 'shr)
-(require 'seq)
-(require 'subr-x)
-(require 'mm-url)
-(require 'cl-lib)
-
-(require 'dash)
-(require 'graphql) ; Some requests of LeetCode use GraphQL
-(require 'aio)
-(require 'spinner)
-(require 'log4e)
-
-(log4e:deflogger "leetcode" "%t [%l] %m" "%H:%M:%S" '((fatal . "fatal")
- (error . "error")
- (warn . "warn")
- (info . "info")
- (debug . "debug")
- (trace . "trace")))
-(setq log4e--log-buffer-leetcode "*leetcode-log*")
-
-;;;###autoload
-(defun leetcode-toggle-debug ()
- "Toggle debug."
- (interactive)
- (if (leetcode--log-debugging-p)
- (progn
- (leetcode--log-set-level 'info)
- (leetcode--log-disable-debugging)
- (message "leetcode disable debug"))
- (progn
- (leetcode--log-set-level 'debug)
- (leetcode--log-enable-debugging)
- (message "leetcode enable debug"))))
-
-(defun leetcode--install-my-cookie ()
- "Install leetcode dependencies."
- (let ((async-shell-command-display-buffer t))
- (async-shell-command
- "pip3 install my_cookies"
- (get-buffer-create "*leetcode-install*"))))
-
-(defun leetcode--check-deps ()
- "Check if all dependencies installed."
- (if (executable-find "my_cookies")
- t
- (leetcode--install-my-cookie)
- nil))
-
-(defgroup leetcode nil
- "A Leetcode client."
- :prefix 'leetcode-
- :group 'tools)
-
-(defvar leetcode--user nil
- "User object.
-The object with following attributes:
-:username String
-:solved Number
-:easy Number
-:medium Number
-:hard Number")
-
-(defvar leetcode--all-problems nil
- "Problems info with a list of problem object.
-The object with following attributes:
-:num Number
-:tag String
-:problems List
-
-The elements of :problems has attributes:
-:status String
-:id Number
-:backend-id Number
-:title String
-:acceptance String
-:difficulty Number {1,2,3}
-:paid-only Boolean {t|nil}
-:tags List")
-
-(defvar leetcode--all-tags nil
- "All problems tags.")
-
-(defvar leetcode--problem-titles nil
- "Problem titles that have been open in solving layout.")
-
-(defvar leetcode-retry-threshold 20 "`leetcode-try' or `leetcode-submit' retry times.")
-(defvar leetcode--filter-regex nil "Filter rows by regex.")
-(defvar leetcode--filter-tag nil "Filter rows by tag.")
-(defvar leetcode--filter-difficulty nil
- "Filter rows by difficulty, it can be \"easy\", \"medium\" and \"hard\".")
-(defconst leetcode--all-difficulties '("easy" "medium" "hard"))
-
-(defconst leetcode--paid "•" "Paid mark.")
-(defconst leetcode--checkmark "✓" "Checkmark for accepted problem.")
-(defconst leetcode--buffer-name "*leetcode*")
-(defconst leetcode--description-buffer-name "*leetcode-description*")
-(defconst leetcode--testcase-buffer-name "*leetcode-testcase*")
-(defconst leetcode--result-buffer-name "*leetcode-result*")
-
-(defface leetcode-paid-face
- '((t (:foreground "gold")))
- "Face for `leetcode--paid'."
- :group 'leetcode)
-
-(defface leetcode-checkmark-face
- '((t (:foreground "#5CB85C")))
- "Face for `leetcode--checkmark'."
- :group 'leetcode)
-
-(defface leetcode-easy-face
- '((t (:foreground "#5CB85C")))
- "Face for easy problems."
- :group 'leetcode)
-
-(defface leetcode-medium-face
- '((t (:foreground "#F0AD4E")))
- "Face for medium problems."
- :group 'leetcode)
-
-(defface leetcode-hard-face
- '((t (:foreground "#D9534E")))
- "Face for hard problems."
- :group 'leetcode)
-
-;;; Login
-;; URL
-(defconst leetcode--domain "leetcode.cn")
-(defconst leetcode--base-url "https://leetcode.cn")
-(defconst leetcode--url-login (concat leetcode--base-url "/accounts/login"))
-
-;; Header
-(defconst leetcode--User-Agent '("User-Agent" .
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:66.0) Gecko/20100101 Firefox/66.0"))
-(defconst leetcode--X-Requested-With '("X-Requested-With" . "XMLHttpRequest"))
-(defconst leetcode--X-CSRFToken "X-CSRFToken")
-
-;; API
-(defconst leetcode--api-root (concat leetcode--base-url "/api"))
-(defconst leetcode--api-graphql (concat leetcode--base-url "/graphql"))
-(defconst leetcode--api-all-problems (concat leetcode--api-root "/problems/all/"))
-(defconst leetcode--api-all-tags (concat leetcode--base-url "/problems/api/tags/"))
-(defconst leetcode--api-daily-challenge
- "query todayRecord { todayRecord { date question { questionFrontendId title titleSlug status } } }")
-;; submit
-(defconst leetcode--api-submit (concat leetcode--base-url "/problems/%s/submit/"))
-(defconst leetcode--api-problems-submission (concat leetcode--base-url "/problems/%s/submissions/"))
-(defconst leetcode--api-check-submission (concat leetcode--base-url "/submissions/detail/%s/check/"))
-;; try testcase
-(defconst leetcode--api-try (concat leetcode--base-url "/problems/%s/interpret_solution/"))
-(defconst leetcode--api-try-referer (concat leetcode--base-url "/problems/%s/"))
-
-(defun to-list (vec)
- "Convert VEC to list."
- (append vec '()))
-
-(defmacro dovec (spec &rest body)
- "Loop over a vector.
-EVALUATE BODY with VAR bound to each element in VEC, in turn.
-SPEC is just like (VAR VEC [RESULT]). Then evaluate RESULT to
-get the return value (nil if RESULT is omitted).
-
-\(fn (VAR VEC [RESULT]) BODY...)"
- (declare (indent 1))
- (let ((start 0)
- (counter (gensym))
- (end (gensym)))
- `(let ((,counter ,start)
- (,(car spec) nil)
- (,end (length ,(cadr spec))))
- (while (< ,counter ,end)
- (setq ,(car spec) (aref ,(cadr spec) ,counter))
- ,@body
- (setq ,counter (1+ ,counter)))
- ,@(cddr spec))))
-
-(defun leetcode--referer (value)
- "It will return an alist as the HTTP Referer Header.
-VALUE should be the referer."
- (cons "Referer" value))
-
-(defun leetcode--maybe-csrf-token ()
- "Return csrf token if it exists, otherwise return nil."
- (if-let ((cookie (seq-find
- (lambda (item)
- (string= (aref item 1)
- "csrftoken"))
- (url-cookie-retrieve leetcode--domain "/" t))))
- (aref cookie 2)))
-
-(aio-defun leetcode--csrf-token ()
- "Return csrf token."
- (unless (leetcode--maybe-csrf-token)
- (aio-await (aio-url-retrieve leetcode--url-login)))
- (leetcode--maybe-csrf-token))
-
-(defun leetcode--credentials ()
- "Receive user account and password."
- (let ((auth-source-creation-prompts
- '((user . "LeetCode user: ")
- (secret . "LeetCode password for %u: ")))
- (found (car (auth-source-search
- :max 1
- :host leetcode--domain
- :require '(:user :secret)
- :create t))))
- (if found
- (list (plist-get found :user)
- (let ((secret (plist-get found :secret)))
- (if (functionp secret)
- (funcall secret)
- secret))
- (plist-get found :save-function)))))
-
-(defun leetcode--multipart-form-data (name value)
- "Generate multipart form data with NAME and VALUE."
- `("file"
- ("name" . ,name)
- ("filedata" . ,value)
- ("filename" . "")
- ("content-type" . "")))
-
-(aio-defun leetcode--login ()
- "Steal LeetCode login session from local browser.
-It also cleans LeetCode cookies in `url-cookie-file'."
- (leetcode--loading-mode t)
- (ignore-errors (url-cookie-delete-cookies leetcode--domain))
- (aio-await (leetcode--csrf-token)) ;knock knock, whisper me the mysterious information
- (let* ((my-cookies (executable-find "my_cookies"))
- (my-cookies-output (shell-command-to-string (concat (shell-quote-argument my-cookies) " cn")))
- (cookies-list (seq-filter
- (lambda (s) (not (string-empty-p s)))
- (split-string my-cookies-output "\n")))
- (cookies-pairs (seq-map
- (lambda (s) (split-string s))
- cookies-list))
- (leetcode-session (cadr (assoc "LEETCODE_SESSION" cookies-pairs)))
- (leetcode-csrftoken (cadr (assoc "csrftoken" cookies-pairs))))
- (leetcode--debug "login session: '%s'" leetcode-session)
- (leetcode--debug "login csrftoken: '%s'" leetcode-csrftoken)
- (url-cookie-store "LEETCODE_SESSION" leetcode-session nil leetcode--domain "/" t)
- (url-cookie-store "csrftoken" leetcode-csrftoken nil leetcode--domain "/" t))
- (aio-await (leetcode--csrf-token)) ;knock knock, whisper me the mysterious information
- (leetcode--loading-mode -1))
-
-(defun leetcode--login-p ()
- "Whether user is login."
- (let ((username (plist-get leetcode--user :username)))
- (and username
- (not (string-empty-p username))
- (seq-find
- (lambda (item)
- (string= (aref item 1)
- "LEETCODE_SESSION"))
- (url-cookie-retrieve leetcode--domain "/" t)))))
-
-(defun leetcode--set-user-and-problems (user-and-problems)
- "Set `leetcode--user' and `leetcode--all-problems'.
-If user isn't login, only `leetcode--all-problems' will be set.
-USER-AND-PROBLEMS is an alist comes from
-`leetcode--api-all-problems'."
- ;; user
- (let-alist user-and-problems
- (setq leetcode--user
- (list :username .user_name
- :solved .num_solved
- :easy .ac_easy
- :medium .ac_medium
- :hard .ac_hard))
- (leetcode--debug "set user: %s, solved %s in %s problems" .user_name .num_solved .num_total)
- ;; problem list
- (setq leetcode--all-problems
- (list
- :num .num_total
- :tag "all"
- :problems
- (let* ((len .num_total)
- (problems nil))
- (dotimes (i len)
- (let-alist (aref .stat_status_pairs i)
- (leetcode--debug "frontend_question_id: %s, question_id: %s, title: %s, status: %s"
- .stat.frontend_question_id .stat.question_id .stat.question__title .status)
- (push (list
- :status .status
- :id .stat.question_id
- :backend-id .stat.question_id
- :frontend-id .stat.frontend_question_id
- :title .stat.question__title
- :title-slug .stat.question__title_slug
- :acceptance (format
- "%.1f%%"
- (* 100
- (/ (float .stat.total_acs)
- .stat.total_submitted)))
- :difficulty .difficulty.level
- :paid-only (eq .paid_only t))
- problems)))
- problems)))))
-
-(defun leetcode--set-tags (all-tags)
- "Set `leetcode--all-tags' and `leetcode--all-problems' with ALL-TAGS."
- (let ((tags-table (make-hash-table :size 3200)))
- (let-alist all-tags
- (dolist (topic (to-list .topics))
- (let-alist topic
- ;; set leetcode--all-tags
- (unless (member .slug leetcode--all-tags)
- (push .slug leetcode--all-tags))
- ;; tags-table cache
- (dolist (id (to-list .questions))
- (puthash id (cons .slug (gethash id tags-table)) tags-table)))))
- ;; set problems tags with tags-table
- (dolist (problem (plist-get leetcode--all-problems :problems))
- (let ((backend-id (plist-get problem :backend-id)))
- (plist-put problem :tags (gethash backend-id tags-table))))))
-
-(defun leetcode--slugify-title (title)
- "Make TITLE a slug title.
-Such as 'Two Sum' will be converted to 'two-sum'."
- (let* ((str1 (replace-regexp-in-string "[\s-]+" "-" (downcase title)))
- (res (replace-regexp-in-string "[(),]" "" str1)))
- res))
-
-(defun leetcode--problem-graphql-params (operation &optional vars)
- "Construct a GraphQL parameter.
-OPERATION and VARS are LeetCode GraphQL parameters."
- (list
- (cons "operationName" operation)
- (cons "query"
- (graphql-query
- problemsetQuestionList
- (:arguments
- (($titleSlug . String!))
- (question
- :arguments
- ((titleSlug . ($ titleSlug)))
- likes
- dislikes
- content
- translatedContent
- status
- sampleTestCase
- (topicTags slug)
- (codeSnippets langSlug code)))))
- (if vars (cons "variables" vars))))
-
-(aio-defun leetcode--fetch-problem (title)
- "Fetch single problem.
-TITLE is a problem's title.
-Return a object with following attributes:
-:likes Number
-:dislikes Number
-:content String
-:topicTags String"
- (let* ((slug-title (leetcode--slugify-title title))
- (url-request-method "POST")
- (url-request-extra-headers
- `(,leetcode--User-Agent
- ,(cons "Content-Type" "application/json")))
- (url-request-data
- (json-encode (leetcode--problem-graphql-params
- "problemsetQuestionList"
- (list (cons "titleSlug" slug-title)))))
- (result (aio-await (aio-url-retrieve leetcode--api-graphql))))
- (if-let ((error-info (plist-get (car result) :error)))
- (progn
- (switch-to-buffer (cdr result))
- (leetcode--warn "LeetCode fetch problem ERROR: %S" error-info))
- (with-current-buffer (cdr result)
- (goto-char url-http-end-of-headers)
- (let* ((json (json-read))
- (question (alist-get 'question (alist-get 'data json))))
- question)))))
-
-(defun leetcode--markdown-to-html (markdown-text)
- "Convert simple Markdown to HTML for better rendering.
-Supports: **bold**, *italic*, - lists, > blockquotes, code blocks."
- (let ((html-text markdown-text))
- ;; Convert **text** to <strong>text</strong>
- (setq html-text (replace-regexp-in-string "\\*\\*\\([^*]+\\)\\*\\*" "<strong>\\1</strong>" html-text))
- ;; Convert *text* to <em>text</em>
- (setq html-text (replace-regexp-in-string "\\*\\([^*]+\\)\\*" "<em>\\1</em>" html-text))
- ;; Convert `text` to <code>text</code>
- (setq html-text (replace-regexp-in-string "`\\([^`]+\\)`" "<code>\\1</code>" html-text))
- ;; Convert newlines to <br/>
- (setq html-text (replace-regexp-in-string "\n" "<br/>" html-text))
- ;; Convert - list items (with indentation)
- (setq html-text (replace-regexp-in-string "^- " "&nbsp;&nbsp;&nbsp;•&nbsp;" html-text))
- ;; Convert > blockquotes to indented text
- (setq html-text (replace-regexp-in-string "^> " "&nbsp;&nbsp;&nbsp;&nbsp;" html-text))
- html-text))
-
-(defun leetcode--replace-in-buffer (regex to)
- "Replace string matched REGEX in `current-buffer' to TO."
- (with-current-buffer (current-buffer)
- (save-excursion
- (goto-char (point-min))
- (save-match-data
- (while (re-search-forward regex (point-max) t)
- (replace-match to))))))
-
-(defun leetcode--make-tabulated-headers (header-names rows)
- "Calculate headers width.
-Column width calculated by picking the max width of every cell
-under that column and the HEADER-NAMES. HEADER-NAMES are a list
-of header name, ROWS are a list of vector, each vector is one
-row."
- (let ((widths
- (seq-reduce
- (lambda (acc row)
- (cl-mapcar
- (lambda (a col) (max a (length col)))
- acc
- (append row '())))
- rows
- (seq-map #'length header-names))))
- (vconcat
- (cl-mapcar
- (lambda (col size) (list col size nil))
- header-names widths))))
-
-(defun leetcode--stringify-difficulty (difficulty)
- "Stringify DIFFICULTY level (number) to 'easy', 'medium' or 'hard'."
- (let ((easy-tag "easy")
- (medium-tag "medium")
- (hard-tag "hard"))
- (cond
- ((eq 1 difficulty)
- (prog1 easy-tag
- (put-text-property
- 0 (length easy-tag)
- 'font-lock-face 'leetcode-easy-face easy-tag)))
- ((eq 2 difficulty)
- (prog1 medium-tag
- (put-text-property
- 0 (length medium-tag)
- 'font-lock-face 'leetcode-medium-face medium-tag)))
- ((eq 3 difficulty)
- (prog1 hard-tag
- (put-text-property
- 0 (length hard-tag)
- 'font-lock-face 'leetcode-hard-face hard-tag))))))
-
-(defun leetcode--problems-rows ()
- "Generate tabulated list rows from `leetcode--all-problems'.
-Return a list of rows, each row is a vector:
-\([<checkmark> <position> <title> <acceptance> <difficulty>] ...)"
- (let ((problems (plist-get leetcode--all-problems :problems))
- (easy-tag "easy")
- (medium-tag "medium")
- (hard-tag "hard")
- rows)
- (dolist (p problems)
- (if (or leetcode--display-paid
- (not (plist-get p :paid-only)))
- (setq rows
- (cons
- (vector
- ;; status
- (if (equal (plist-get p :status) "ac")
- (prog1 leetcode--checkmark
- (put-text-property
- 0 (length leetcode--checkmark)
- 'font-lock-face 'leetcode-checkmark-face leetcode--checkmark))
- " ")
- ;; id
- (number-to-string (plist-get p :id))
- ;; title
- (concat
- (plist-get p :title)
- " "
- (if (eq (plist-get p :paid-only) t)
- (prog1 leetcode--paid
- (put-text-property
- 0 (length leetcode--paid)
- 'font-lock-face 'leetcode-paid-face leetcode--paid))
- " "))
- ;; acceptance
- (plist-get p :acceptance)
- ;; difficulty
- (leetcode--stringify-difficulty (plist-get p :difficulty))
- ;; tags
- (if leetcode--display-tags (string-join (plist-get p :tags) ", ") ""))
- rows))))
- (reverse rows)))
-
-(defun leetcode--row-tags (row)
- "Get tags from ROW."
- (aref row 5))
-
-(defun leetcode--row-difficulty (row)
- "Get difficulty from ROW."
- (aref row 4))
-
-(defun leetcode--filter (rows)
- "Filter ROWS by `leetcode--filter-regex', `leetcode--filter-tag' and `leetcode--filter-difficulty'."
- (seq-filter
- (lambda (row)
- (and
- (if leetcode--filter-regex
- (let ((title (aref row 2)))
- (string-match-p leetcode--filter-regex title))
- t)
- (if leetcode--filter-tag
- (let ((tags (split-string (leetcode--row-tags row) ", ")))
- (member leetcode--filter-tag tags))
- t)
- (if leetcode--filter-difficulty
- (let ((difficulty (leetcode--row-difficulty row)))
- (string= difficulty leetcode--filter-difficulty))
- t)))
- rows))
-
-(defun leetcode-reset-filter ()
- "Reset filter."
- (interactive)
- (setq leetcode--filter-regex nil)
- (setq leetcode--filter-tag nil)
- (setq leetcode--filter-difficulty nil)
- (leetcode-refresh))
-
-(defun leetcode-set-filter-regex (regex)
- "Set `leetcode--filter-regex' as REGEX and refresh."
- (interactive "sSearch: ")
- (setq leetcode--filter-regex regex)
- (leetcode-refresh))
-
-(defun leetcode-set-filter-tag ()
- "Set `leetcode--filter-tag' from `leetcode--all-tags' and refresh."
- (interactive)
- (setq leetcode--filter-tag
- (completing-read "Tags: " leetcode--all-tags))
- (leetcode-refresh))
-
-(defun leetcode-set-prefer-language ()
- "Set `leetcode-prefer-language' from `leetcode--lang-suffixes' and refresh."
- (interactive)
- (setq leetcode-prefer-language
- (completing-read "Language: " leetcode--lang-suffixes))
- (leetcode-refresh))
-
-(defun leetcode-set-filter-difficulty ()
- "Set `leetcode--filter-difficulty' from `leetcode--all-difficulties' and refresh."
- (interactive)
- (setq leetcode--filter-difficulty
- (completing-read "Difficulty: " leetcode--all-difficulties))
- (leetcode-refresh))
-
-(defun leetcode-toggle-tag-display ()
- "Toggle `leetcode--display-tags` and refresh"
- (interactive)
- (setq leetcode--display-tags (not leetcode--display-tags))
- (leetcode-refresh))
-
-(defun leetcode-toggle-paid-display ()
- "Toggle `leetcode--display-paid` and refresh"
- (interactive)
- (setq leetcode--display-paid (not leetcode--display-paid))
- (leetcode-refresh))
-
-(aio-defun leetcode--fetch-all-tags ()
- (let* ((url-request-method "GET")
- (url-request-extra-headers
- `(,leetcode--User-Agent
- ,leetcode--X-Requested-With
- ,(leetcode--referer leetcode--url-login)))
- (result (aio-await (aio-url-retrieve leetcode--api-all-tags))))
- (with-current-buffer (cdr result)
- (goto-char url-http-end-of-headers)
- (json-read))))
-
-(aio-defun leetcode--fetch-user-and-problems ()
- "Fetch user and problems info."
- (if leetcode--loading-mode
- (message "LeetCode has been refreshing...")
- (leetcode--loading-mode t)
- (let ((url-request-method "GET")
- (url-request-extra-headers
- `(,leetcode--User-Agent
- ,leetcode--X-Requested-With
- ,(leetcode--referer leetcode--url-login)))
- (result (aio-await (aio-url-retrieve leetcode--api-all-problems))))
- (leetcode--loading-mode -1)
- (if-let ((error-info (plist-get (car result) :error)))
- (progn
- (switch-to-buffer (cdr result))
- (leetcode--warn "LeetCode fetch user and problems failed: %S" error-info))
- (with-current-buffer (cdr result)
- (goto-char url-http-end-of-headers)
- (json-read))))))
-
-(defun leetcode-refresh ()
- "Make `tabulated-list-entries'."
- (interactive)
- (let* ((header-names (append '(" " "#" "Problem" "Acceptance" "Difficulty")
- (if leetcode--display-tags '("Tags"))))
- (rows (leetcode--filter (leetcode--problems-rows)))
- (headers (leetcode--make-tabulated-headers header-names rows)))
- (with-current-buffer (get-buffer-create leetcode--buffer-name)
- (leetcode--problems-mode)
- (setq tabulated-list-format headers)
- (setq tabulated-list-entries
- (cl-mapcar
- (lambda (i x) (list i x))
- (number-sequence 0 (1- (length rows)))
- rows))
- (tabulated-list-init-header)
- (tabulated-list-print t)
- (leetcode--loading-mode -1))))
-
-(aio-defun leetcode-refresh-fetch ()
- "Refresh problems and update `tabulated-list-entries'."
- (interactive)
- (if-let ((users-and-problems (aio-await (leetcode--fetch-user-and-problems)))
- (all-tags (aio-await (leetcode--fetch-all-tags))))
- (progn
- (leetcode--set-user-and-problems users-and-problems)
- (leetcode--set-tags all-tags))
- (leetcode--warn "LeetCode parse user and problems failed"))
- (setq leetcode--display-tags leetcode-prefer-tag-display)
- (leetcode-reset-filter)
- (leetcode-refresh))
-
-(aio-defun leetcode--async ()
- "Show leetcode problems buffer."
- (if (get-buffer leetcode--buffer-name)
- (switch-to-buffer leetcode--buffer-name)
- (unless (leetcode--login-p)
- (aio-await (leetcode--login)))
- (aio-await (leetcode-refresh-fetch))
- (switch-to-buffer leetcode--buffer-name)))
-
-;;;###autoload
-(defun leetcode ()
- "A wrapper for `leetcode--async', because emacs-aio can not be autoloaded.
-see: https://github.com/skeeto/emacs-aio/issues/3."
- (interactive)
- (if (leetcode--check-deps)
- (leetcode--async)
- (message "installing leetcode dependencies...")))
-
-;;;###autoload(autoload 'leetcode-daily "leetcode" nil t)
-(aio-defun leetcode-daily ()
- "Open the daily challenge."
- (interactive)
- (unless (leetcode--login-p)
- (aio-await (leetcode)))
- (let* ((url-request-method "POST")
- (url-request-extra-headers
- `(,leetcode--User-Agent
- ("Content-Type" . "application/json")
- ,(leetcode--referer leetcode--url-login)
- ,(cons leetcode--X-CSRFToken (leetcode--maybe-csrf-token))))
- (url-request-data
- (json-encode
- `((operationName . "todayRecord")
- (query . ,leetcode--api-daily-challenge)))))
- (with-current-buffer (url-retrieve-synchronously leetcode--api-graphql)
- (goto-char url-http-end-of-headers)
- (let-alist (json-read)
- (let ((qid .data.todayRecord.0.question.questionFrontendId))
- (leetcode-show-problem (string-to-number qid)))))))
-
-(defun leetcode--buffer-content (buf)
- "Get content without text properties of BUF."
- (with-current-buffer buf
- (buffer-substring-no-properties
- (point-min) (point-max))))
-
-(defun leetcode--get-slug-title-before-try/submit (code-buf)
- "Get slug title before try or submit with CODE-BUF.
-LeetCode require slug-title as the request parameters."
- (with-current-buffer code-buf
- (if leetcode-save-solutions
- (file-name-base (cadr (split-string (buffer-name) "_")))
- (file-name-base (buffer-name)))))
-
-(aio-defun leetcode-try ()
- "Asynchronously test the code using customized testcase."
- (interactive)
- (leetcode--loading-mode t)
- (aio-await (leetcode--csrf-token)) ;knock knock, whisper me the mysterious information
- (leetcode--debug "csrf token: %s" (aio-await (leetcode--csrf-token)))
- (let* ((code-buf (current-buffer))
- (testcase-buf (get-buffer leetcode--testcase-buffer-name))
- (slug-title (leetcode--get-slug-title-before-try/submit code-buf))
- (problem (seq-find (lambda (p)
- (equal slug-title
- (leetcode--slugify-title
- (plist-get p :title))))
- (plist-get leetcode--all-problems :problems)))
- (problem-id (plist-get problem :backend-id)))
- (leetcode--debug "leetcode try slug-title: %s, problem-id: %s" slug-title problem-id)
- (let* ((url-request-method "POST")
- (url-request-extra-headers
- `(,leetcode--User-Agent
- ("Content-Type" . "application/json")
- ,(leetcode--referer (format
- leetcode--api-try-referer
- slug-title))
- ,(cons leetcode--X-CSRFToken (aio-await (leetcode--csrf-token)))))
- (url-request-data
- (json-encode
- `((data_input . ,(leetcode--buffer-content testcase-buf))
- (lang . ,leetcode--lang)
- (question_id . ,problem-id)
- (typed_code . ,(leetcode--buffer-content code-buf)))))
- (result (aio-await (aio-url-retrieve (format leetcode--api-try slug-title)))))
- (if-let ((error-info (plist-get (car result) :error)))
- (progn
- (switch-to-buffer (cdr result))
- (leetcode--warn "LeetCode try failed: %S" error-info))
- (let ((data (with-current-buffer (cdr result)
- (goto-char url-http-end-of-headers)
- (json-read)))
- (res-buf (get-buffer leetcode--result-buffer-name)))
- (let-alist data
- (with-current-buffer res-buf
- (erase-buffer)
- (insert (concat "Your input:\n" .test_case "\n\n")))
- ;; poll expected
- (leetcode--debug "interpret_expected_id: %s" .interpret_expected_id)
- (let ((expect_res (aio-await (leetcode--check-submission .interpret_expected_id slug-title)))
- (retry-times 0))
- (while (and (not expect_res) (< retry-times leetcode-retry-threshold))
- (aio-await (aio-sleep 0.5))
- (setq expect_res (aio-await (leetcode--check-submission .interpret_expected_id slug-title)))
- (setq retry-times (1+ retry-times)))
- (if (< retry-times leetcode-retry-threshold)
- (let-alist expect_res
- (with-current-buffer res-buf
- (goto-char (point-max))
- (cond
- ((eq .status_code 10)
- (insert "Expected:\n")
- (dotimes (i (length .code_answer))
- (insert (aref .code_answer i))
- (insert "\n"))
- (insert "\n"))
- ((not (eq .status_code 10))
- (insert "Expected:\nGot None\n"))
- )))))
-
- ;; poll interpreted
- (let ((actual_res (aio-await (leetcode--check-submission .interpret_id slug-title)))
- (retry-times 0))
- (while (and (not actual_res) (< retry-times leetcode-retry-threshold))
- (aio-await (aio-sleep 0.5))
- (setq actual_res (aio-await (leetcode--check-submission .interpret_id slug-title)))
- (setq retry-times (1+ retry-times)))
- (if (< retry-times leetcode-retry-threshold)
- (let-alist actual_res
- (with-current-buffer res-buf
- (goto-char (point-max))
- (cond
- ((eq .status_code 10)
- (insert "Output:\n")
- (dotimes (i (length .code_answer))
- (insert (aref .code_answer i))
- (insert "\n"))
- (insert "\n")
- (insert "Expected:\n")
- (dotimes (i (length .expected_code_answer))
- (insert (aref .expected_code_answer i))
- (insert "\n"))
- (insert "\n"))
- ((eq .status_code 14)
- (insert .status_msg))
- ((eq .status_code 15)
- (insert .status_msg)
- (insert "\n\n")
- (insert .full_runtime_error))
- ((eq .status_code 20)
- (insert .status_msg)
- (insert "\n\n")
- (insert .full_compile_error)))
- (when (> (length .code_output) 0)
- (insert "\n\n")
- (insert "Code output:\n")
- (dolist (item (append .code_output nil))
- (insert (concat item "\n"))))
- (insert "\n\n")))
- (leetcode--warn "LeetCode try timeout.")))
- (leetcode--loading-mode -1)))))))
-
-(aio-defun leetcode--check-submission (submission-id slug-title)
- "Polling to check submission detail.
-After each submission, either try testcase or submit, LeetCode
-returns a SUBMISSION-ID. With the SUBMISSION-ID, client will poll
-for the submission detail. SLUG-TITLE is a slugified problem
-title. Return response data if submission success, otherwise
-nil."
- (leetcode--loading-mode t)
- (let* ((url-request-method "GET")
- (url-request-extra-headers
- `(,leetcode--User-Agent
- ,(leetcode--referer (format leetcode--api-problems-submission slug-title))
- ,(cons leetcode--X-CSRFToken (aio-await (leetcode--csrf-token)))))
- (result (aio-await (aio-url-retrieve (format leetcode--api-check-submission submission-id)))))
- (if-let ((error-info (plist-get (car result) :error)))
- (progn
- (leetcode--loading-mode -1)
- (switch-to-buffer (cdr result))
- (leetcode--warn "LeetCode check submission failed: %S" error-info))
- (with-current-buffer (cdr result)
- (let ((submission-res
- (progn (goto-char url-http-end-of-headers)
- (json-read))))
- (if (equal (alist-get 'state submission-res) "SUCCESS")
- submission-res))))))
-
-(defun leetcode--solving-layout ()
- "Specify layout for solving problem.
-+---------------+----------------+
-| | |
-| | Description |
-| | |
-| +----------------+
-| Code | Customize |
-| | Testcases |
-| +----------------+
-| |Submit/Testcases|
-| | Result |
-+---------------+----------------+"
- (delete-other-windows)
- (split-window-horizontally)
- (other-window 1)
- (split-window-below)
- (other-window 1)
- (split-window-below)
- (other-window -1)
- (other-window -1))
-
-(defun leetcode--display-result (buffer &optional alist)
- "Display function for LeetCode result.
-BUFFER is the one to show LeetCode result. ALIST is a combined
-alist specified in `display-buffer-alist'."
- (let ((window (window-next-sibling
- (window-next-sibling
- (window-top-child
- (window-next-sibling
- (window-left-child
- (frame-root-window))))))))
- (set-window-buffer window buffer)
- window))
-
-(defun leetcode--display-testcase (buffer &optional alist)
- "Display function for LeetCode testcase.
-BUFFER is the one to show LeetCode testcase. ALIST is a combined
-alist specified in `display-buffer-alist'."
- (let ((window (window-next-sibling
- (window-top-child
- (window-next-sibling
- (window-left-child
- (frame-root-window)))))))
- (set-window-buffer window buffer)
- window))
-
-(defun leetcode--display-code (buffer &optional alist)
- "Display function for LeetCode code.
-BUFFER is the one to show LeetCode code. ALIST is a combined
-alist specified in `display-buffer-alist'."
- (let ((window (window-left-child (frame-root-window))))
- (set-window-buffer window buffer)
- window))
-
-(defun leetcode--show-submission-result (submission-detail)
- "Show error info in `leetcode--result-buffer-name' based on status code.
-Error info comes from SUBMISSION-DETAIL. STATUS_CODE has
-following possible value:
-- 10: Accepted
-- 11: Wrong Anwser
-- 14: Time Limit Exceeded
-- 15: Runtime Error. full_runtime_error
-- 20: Compile Error. full_compile_error"
- (let-alist submission-detail
- (with-current-buffer (get-buffer-create leetcode--result-buffer-name)
- (erase-buffer)
- (insert (format "Status: %s" .status_msg))
- (cond
- ((eq .status_code 10)
- (insert (format " (%s/%s)\n\n" .total_correct .total_testcases))
- (insert (format "Runtime: %s, faster than %.2f%% of %s submissions.\n\n"
- .status_runtime .runtime_percentile .pretty_lang))
- (insert (format "Memory Usage: %s, less than %.2f%% of %s submissions."
- .status_memory .memory_percentile .pretty_lang)))
- ((eq .status_code 11)
- (insert (format " (%s/%s)\n\n" .total_correct .total_testcases))
- (insert (format "Test Case: \n%s\n\n" .input))
- (insert (format "Answer: %s\n\n" .code_output))
- (insert (format "Expected Answer: %s\n\n" .expected_output))
- (insert (format "Stdout: \n%s\n" .std_output)))
- ((eq .status_code 14)
- (insert "\n"))
- ((eq .status_code 15)
- (insert "\n\n")
- (insert (format (alist-get 'full_runtime_error submission-detail))))
- ((eq .status_code 20)
- (insert "\n\n")
- (insert (format (alist-get 'full_compile_error submission-detail)))))
- (display-buffer (current-buffer)
- '((display-buffer-reuse-window
- leetcode--display-result)
- (reusable-frames . visible))))))
-
-(aio-defun leetcode-submit ()
- "Asynchronously submit the code and show result."
- (interactive)
- (leetcode--loading-mode t)
- (let* ((code-buf (current-buffer))
- (code (leetcode--buffer-content code-buf))
- (slug-title (leetcode--get-slug-title-before-try/submit code-buf))
- (problem-id (plist-get (seq-find (lambda (p)
- (equal slug-title
- (leetcode--slugify-title
- (plist-get p :title))))
- (plist-get leetcode--all-problems :problems))
- :backend-id)))
- (leetcode--debug "leetcode submit slug-title: %s, problem-id: %s" slug-title problem-id)
- (let* ((url-request-method "POST")
- (url-request-extra-headers
- `(,leetcode--User-Agent
- ,(leetcode--referer (format
- leetcode--api-problems-submission
- slug-title))
- ,(cons "Content-Type" "application/json")
- ,(cons leetcode--X-CSRFToken (aio-await (leetcode--csrf-token)))))
- (url-request-data
- (json-encode `((lang . ,leetcode--lang)
- (question_id . ,problem-id)
- (typed_code . ,code))))
- (result (aio-await (aio-url-retrieve (format leetcode--api-submit slug-title)))))
- (if-let ((error-info (plist-get (car result) :error)))
- (progn
- (leetcode--loading-mode -1)
- (switch-to-buffer (cdr result))
- (leetcode--warn "LeetCode check submit failed: %S" error-info))
- (let* ((resp
- (with-current-buffer (cdr result)
- (progn (goto-char url-http-end-of-headers)
- (json-read))))
- (submission-id (alist-get 'submission_id resp))
- (submission-res (aio-await (leetcode--check-submission submission-id slug-title)))
- (retry-times 0))
- ;; poll submission result
- (while (and (not submission-res) (< retry-times leetcode-retry-threshold))
- (aio-await (aio-sleep 0.5))
- (setq submission-res (aio-await (leetcode--check-submission submission-id slug-title)))
- (setq retry-times (1+ retry-times)))
- (if (< retry-times leetcode-retry-threshold)
- (leetcode--show-submission-result submission-res)
- (leetcode--warn "LeetCode submit timeout."))
- (leetcode--loading-mode -1))))))
-
-(defun leetcode--problem-link (title)
- "Generate problem link from TITLE."
- (concat leetcode--base-url "/problems/" (leetcode--slugify-title title)))
-
-(defun leetcode--show-problem (problem problem-info)
- "Show the description of PROBLEM, whose meta data is PROBLEM-INFO.
-Use `shr-render-buffer' to render problem description. This action
-will show the description in other window and jump to it."
- (let* ((problem-id (plist-get problem-info :id))
- (title (plist-get problem-info :title))
- (difficulty-level (plist-get problem-info :difficulty))
- (difficulty (leetcode--stringify-difficulty difficulty-level))
- (buf-name leetcode--description-buffer-name)
- (html-margin "&nbsp;&nbsp;&nbsp;&nbsp;"))
- (leetcode--debug "select title: %s" title)
- (let-alist problem
- (when (get-buffer buf-name)
- (kill-buffer buf-name))))
- (let* ((content-val (alist-get 'content problem))
- (translated-val (alist-get 'translatedContent problem))
- (likes-val (alist-get 'likes problem))
- (dislikes-val (alist-get 'dislikes problem))
- (desc-content (if (and content-val
- (string-match-p "English description is not available" content-val))
- translated-val
- content-val)))
- (with-temp-buffer
- (insert (concat "<h1>" (number-to-string problem-id) ". " title "</h1>"))
- (insert (concat (capitalize difficulty) html-margin
- "likes: " (number-to-string likes-val) html-margin
- "dislikes: " (number-to-string dislikes-val)))
- (insert "<hr/>")
- ;; Convert markdown to HTML
- (let ((converted (leetcode--markdown-to-html (or desc-content ""))))
- (insert converted))
- (setq shr-current-font t)
- ;; NOTE: shr.el can't render "https://xxxx.png", so we use "http"
- (leetcode--replace-in-buffer "https" "http")
- (shr-render-buffer (current-buffer)))
- (with-current-buffer "*html*"
- (save-match-data
- (re-search-forward "dislikes: .*" nil t)
- (insert (make-string 4 ?\s))
- (insert-text-button "Solve it"
- 'action (lambda (btn)
- (leetcode--start-coding problem problem-info))
- 'help-echo "solve the problem.")
- (insert (make-string 4 ?\s))
- (insert-text-button "Link"
- 'action (lambda (btn)
- (browse-url (leetcode--problem-link title)))
- 'help-echo "open the problem in browser.")
- (insert (make-string 4 ?\s))
- (insert-text-button "Solution"
- 'action (lambda (btn)
- (browse-url (concat (leetcode--problem-link title) "/solution")))
- 'help-echo "Open the problem solution page in browser.")
- ;; Would be best to parse the solution in Emacs, but the url-retrieve-synchronously only get the Javascript which generate the solution in HTML, not directly text
- )
- (rename-buffer buf-name)
- (leetcode--problem-description-mode)
- (switch-to-buffer (current-buffer))))))
-
-(aio-defun leetcode-show-problem (problem-id)
- "Show the description of problem with id PROBLEM-ID.
-Get problem by id and use `shr-render-buffer' to render problem
-description. This action will show the description in other
-window and jump to it."
- (interactive (list (read-number "Show problem by problem id: "
- (leetcode--get-current-problem-id))))
- (let* ((problem-info (leetcode--get-problem-by-id problem-id))
- (title-slug (or (plist-get problem-info :title-slug)
- (leetcode--slugify-title (plist-get problem-info :title))))
- (problem (aio-await (leetcode--fetch-problem title-slug))))
- (leetcode--show-problem problem problem-info)))
-
-(defun leetcode-show-problem-by-slug (slug-title)
- "Show the description of problem with slug title. This function will work after first run M-x leetcode. This can be used with org-link elisp:(leetcode-show-problem-by-slug \"two-sum\").
-Get problem by slug title and use `shr-render-buffer' to render problem
-description. This action will show the description in other
-window and jump to it."
- (interactive (list (completing-read "Problem slug (e.g., two-sum): "
- (mapcar (lambda (p)
- (leetcode--slugify-title (plist-get p :title)))
- (plist-get leetcode--all-problems :problems)))))
- (let* ((problem (seq-find (lambda (p)
- (equal slug-title
- (leetcode--slugify-title
- (plist-get p :title))))
- (plist-get leetcode--all-problems :problems)))
- (problem-id (plist-get problem :id))
- (problem-info (leetcode--get-problem-by-id problem-id))
- (title-slug (or (plist-get problem-info :title-slug)
- (leetcode--slugify-title (plist-get problem-info :title))))
- (problem (leetcode--fetch-problem title-slug)))
- (leetcode-show-problem problem-id)))
-
-(defun leetcode-show-current-problem ()
- "Show current problem's description.
-Call `leetcode-show-problem' on the current problem id. This
-action will show the description in other window and jump to it."
- (interactive)
- (leetcode-show-problem (leetcode--get-current-problem-id)))
-
-(aio-defun leetcode-view-problem (problem-id)
- "View problem by PROBLEM-ID while staying in `LC Problems' window.
-Similar with `leetcode-show-problem', but instead of jumping to the
-description window, this action will jump back in `LC Problems'."
- (interactive (list (read-number "View problem by problem id: "
- (leetcode--get-current-problem-id))))
- (aio-await (leetcode-show-problem problem-id))
- (leetcode--jump-to-window-by-buffer-name leetcode--buffer-name))
-
-(defun leetcode-view-current-problem ()
- "View current problem while staying in `LC Problems' window.
-Similar with `leetcode-show-current-problem', but instead of jumping to
-the description window, this action will jump back in `LC Problems'."
- (interactive)
- (leetcode-view-problem (leetcode--get-current-problem-id)))
-
-(defun leetcode-show-problem-in-browser (problem-id)
- "Open the problem with id PROBLEM-ID in browser."
- (interactive (list (read-number "Show in browser by problem id: "
- (leetcode--get-current-problem-id))))
- (let* ((problem (leetcode--get-problem-by-id problem-id))
- (title (plist-get problem :title))
- (link (leetcode--problem-link title)))
- (leetcode--debug "Open in browser: %s" link)
- (browse-url link)))
-
-(defun leetcode-show-current-problem-in-browser ()
- "Open the current problem in browser.
-Call `leetcode-show-problem-in-browser' on the current problem id."
- (interactive)
- (leetcode-show-problem-in-browser (leetcode--get-current-problem-id)))
-
-(aio-defun leetcode-solve-problem (problem-id)
- "Start coding the problem with id PROBLEM-ID."
- (interactive (list (read-number "Solve the problem with id: "
- (leetcode--get-current-problem-id))))
- (let* ((problem-info (leetcode--get-problem-by-id problem-id))
- (title-slug (or (plist-get problem-info :title-slug)
- (leetcode--slugify-title (plist-get problem-info :title))))
- (problem (aio-await (leetcode--fetch-problem title-slug))))
- (leetcode--show-problem problem problem-info)
- (leetcode--start-coding problem problem-info)))
-
-(defun leetcode-solve-current-problem ()
- "Start coding the current problem.
-Call `leetcode-solve-problem' on the current problem id."
- (interactive)
- (leetcode-solve-problem (leetcode--get-current-problem-id)))
-
-(defun leetcode--jump-to-window-by-buffer-name (buffer-name)
- "Jump to window by BUFFER-NAME."
- (select-window (get-buffer-window buffer-name)))
-
-(defun leetcode--kill-buff-and-delete-window (buf)
- "Kill BUF and delete its window."
- (delete-windows-on buf t)
- (kill-buffer buf))
-
-(defun leetcode-quit ()
- "Close and delete leetcode related buffers and windows."
- (interactive)
- (leetcode--kill-buff-and-delete-window (get-buffer leetcode--buffer-name))
- (leetcode--kill-buff-and-delete-window (get-buffer leetcode--description-buffer-name))
- (leetcode--kill-buff-and-delete-window (get-buffer leetcode--result-buffer-name))
- (leetcode--kill-buff-and-delete-window (get-buffer leetcode--testcase-buffer-name))
- (mapc (lambda (title)
- (leetcode--kill-buff-and-delete-window
- (get-buffer (leetcode--get-code-buffer-name title))))
- leetcode--problem-titles))
-
-(defcustom leetcode-prefer-tag-display t
- "Whether to display tags by default in the *leetcode* buffer."
- :type :boolean)
-
-;;(defcustom leetcode-use-cn-source nil
-;; "Whether to use leetcode-cn.com source."
-;; :type 'boolean
-;; :group 'leetcode)
-
-(defvar leetcode--display-tags leetcode-prefer-tag-display
- "(Internal) Whether tags are displayed the *leetcode* buffer.")
-
-(defvar leetcode--display-paid nil
- "(Internal) Whether paid problems are displayed the *leetcode* buffer.")
-
-(defvar leetcode-prefer-language "python3"
- "LeetCode programming language.
-c, cpp, csharp, golang, java, javascript, typescript, kotlin, php, python,
-python3, ruby, rust, scala, swift.")
-
-(defvar leetcode-prefer-sql "mysql"
- "LeetCode sql implementation.
-mysql, mssql, oraclesql.")
-
-(defvar leetcode-directory "~/leetcode"
- "Directory to save solutions.")
-
-(defvar leetcode-save-solutions nil
- "If it's t, save leetcode solutions to `leetcode-directory'.")
-
-(defvar leetcode--lang leetcode-prefer-language
- "LeetCode programming language or sql for current problem internally.
-Default is programming language.")
-
-(defconst leetcode--lang-suffixes
- '(("c" . ".c") ("cpp" . ".cpp") ("csharp" . ".cs")
- ("golang" . ".go") ("java" . ".java") ("javascript" . ".js")
- ("typescript" . ".ts") ("kotlin" . ".kt") ("php" . ".php")
- ("python" . ".py") ("python3" . ".py") ("ruby" . ".rb")
- ("rust" . ".rs") ("scala" . ".scala") ("swift" . ".swift")
- ("mysql" . ".sql") ("mssql" . ".sql") ("oraclesql" . ".sql"))
- "LeetCode programming language suffixes.
-c, cpp, csharp, golang, java, javascript, typescript, kotlin, php, python,
-python3, ruby, rust, scala, swift, mysql, mssql, oraclesql.")
-
-(defun leetcode--set-lang (snippets)
- "Set `leetcode--lang' based on langSlug in SNIPPETS."
- (setq leetcode--lang
- (if (seq-find (lambda (s)
- (equal (alist-get 'langSlug s)
- leetcode-prefer-sql))
- snippets)
- leetcode-prefer-sql
- leetcode-prefer-language)))
-
-(defun leetcode--get-code-buffer-name (title)
- "Get code buffer name by TITLE and `leetcode-prefer-language'."
- (let* ((suffix (assoc-default
- leetcode--lang
- leetcode--lang-suffixes))
- (slug-title (leetcode--slugify-title title))
- (title-with-suffix (concat slug-title suffix)))
- (if leetcode-save-solutions
- (format "%04d_%s" (leetcode--get-problem-id slug-title) title-with-suffix)
- title-with-suffix)))
-
-(defun leetcode--get-code-buffer (buf-name)
- "Get code buffer by BUF-NAME."
- (if (not leetcode-save-solutions)
- (get-buffer-create buf-name)
- (unless (file-directory-p leetcode-directory)
- (make-directory leetcode-directory))
- (find-file-noselect
- (concat (file-name-as-directory leetcode-directory)
- buf-name))))
-
-(defun leetcode--get-problem (slug-title)
- "Get problem from `leetcode--all-problems' by SLUG-TITLE."
- (seq-find (lambda (p)
- (equal slug-title
- (leetcode--slugify-title
- (plist-get p :title))))
- (plist-get leetcode--all-problems :problems)))
-
-(defun leetcode--get-problem-by-id (id)
- "Get problem from `leetcode--all-problems' by ID."
- (seq-find (lambda (p)
- (equal id (plist-get p :id)))
- (plist-get leetcode--all-problems :problems)))
-
-(defun leetcode--get-problem-id (slug-title)
- "Get problem id by SLUG-TITLE."
- (let ((problem (leetcode--get-problem slug-title)))
- (plist-get problem :id)))
-
-(defun leetcode--get-current-problem-id ()
- "Get id of the current problem."
- (string-to-number (aref (tabulated-list-get-entry) 1)))
-
-(defun leetcode--start-coding (problem problem-info)
- "Create a buffer for coding PROBLEM with meta-data PROBLEM-INFO.
-The buffer will be not associated with any file. It will choose
-major mode by `leetcode-prefer-language'and `auto-mode-alist'."
- (let-alist problem
- (let* ((title (plist-get problem-info :title))
- (snippets (append .codeSnippets nil))
- (testcase .sampleTestCase))
- (add-to-list 'leetcode--problem-titles title)
- (leetcode--solving-layout)
- (leetcode--set-lang snippets)
- (let* ((slug-title (leetcode--slugify-title title))
- (buf-name (leetcode--get-code-buffer-name title))
- (code-buf (get-buffer buf-name))
- (suffix (assoc-default
- leetcode--lang
- leetcode--lang-suffixes)))
- (unless code-buf
- (with-current-buffer (leetcode--get-code-buffer buf-name)
- (setq code-buf (current-buffer))
- (funcall (assoc-default suffix auto-mode-alist #'string-match-p))
- (let* ((snippet (seq-find (lambda (s)
- (equal (alist-get 'langSlug s)
- leetcode--lang))
- snippets))
- (template-code (alist-get 'code snippet)))
- (unless (save-mark-and-excursion
- (goto-char (point-min))
- (search-forward (string-trim template-code) nil t))
- (insert template-code))
- (leetcode--replace-in-buffer "
-" ""))))
-
- (display-buffer code-buf
- '((display-buffer-reuse-window
- leetcode--display-code)
- (reusable-frames . visible))))
- (with-current-buffer (get-buffer-create leetcode--testcase-buffer-name)
- (erase-buffer)
- (insert testcase)
- (display-buffer (current-buffer)
- '((display-buffer-reuse-window
- leetcode--display-testcase)
- (reusable-frames . visible))))
- (with-current-buffer (get-buffer-create leetcode--result-buffer-name)
- (erase-buffer)
- (display-buffer (current-buffer)
- '((display-buffer-reuse-window
- leetcode--display-result)
- (reusable-frames . visible))))
- )))
-
-(defvar leetcode--problems-mode-map
- (let ((map (make-sparse-keymap)))
- (prog1 map
- (suppress-keymap map)
- (define-key map (kbd "RET") #'leetcode-show-current-problem)
- (define-key map (kbd "TAB") #'leetcode-view-current-problem)
- (define-key map "o" #'leetcode-show-current-problem)
- (define-key map "O" #'leetcode-show-problem)
- (define-key map "v" #'leetcode-view-current-problem)
- (define-key map "V" #'leetcode-view-problem)
- (define-key map "b" #'leetcode-show-current-problem-in-browser)
- (define-key map "B" #'leetcode-show-problem-in-browser)
- (define-key map "c" #'leetcode-solve-current-problem)
- (define-key map "C" #'leetcode-solve-problem)
- (define-key map "n" #'next-line)
- (define-key map "p" #'previous-line)
- (define-key map "s" #'leetcode-set-filter-regex)
- (define-key map "l" #'leetcode-set-prefer-language)
- (define-key map "t" #'leetcode-set-filter-tag)
- (define-key map "T" #'leetcode-toggle-tag-display)
- (define-key map "P" #'leetcode-toggle-paid-display)
- (define-key map "d" #'leetcode-set-filter-difficulty)
- (define-key map "g" #'leetcode-refresh)
- (define-key map "G" #'leetcode-refresh-fetch)
- (define-key map "/" #'leetcode-reset-filter)
- (define-key map "q" #'quit-window)))
- "Keymap for `leetcode--problems-mode'.")
-
-(define-derived-mode leetcode--problems-mode
- tabulated-list-mode "LC Problems"
- "Major mode for browsing a list of problems."
- (setq tabulated-list-padding 2)
- (add-hook 'tabulated-list-revert-hook #'leetcode-refresh nil t)
- :group 'leetcode)
-
-(add-hook 'leetcode--problems-mode-hook #'hl-line-mode)
-
-(defvar leetcode--problem-description-mode-map
- (let ((map (make-sparse-keymap)))
- (prog1 map
- (suppress-keymap map)
- (define-key map "n" #'next-line)
- (define-key map "p" #'previous-line)))
- "Keymap for `leetcode--problem-description-mode'.")
-
-(define-derived-mode leetcode--problem-description-mode
- special-mode "LC Descri"
- "Major mode for display problem description."
- :group 'leetcode)
-
-;;; Use spinner.el to show progress indicator
-(defvar leetcode--spinner (spinner-create 'progress-bar-filled)
- "Progress indicator to show request progress.")
-(defconst leetcode--loading-lighter
- '(" [LeetCode" (:eval (spinner-print leetcode--spinner)) "]"))
-
-(define-minor-mode leetcode--loading-mode
- "Minor mode to showing leetcode loading status."
- :require 'leetcode
- :lighter leetcode--loading-lighter
- :group 'leetcode
- (if leetcode--loading-mode
- (spinner-start leetcode--spinner)
- (spinner-stop leetcode--spinner)))
-
-(provide 'packages-leetcode)
-;;; leetcode.el ends here
diff --git a/packages/packages-misc.el b/packages/packages-misc.el
deleted file mode 100644
index baa7537..0000000
--- a/packages/packages-misc.el
+++ /dev/null
@@ -1,8 +0,0 @@
-;; -*- lexical-binding: t; -*-
-(use-package hugoista
- :ensure t
- :custom
- (hugoista-site-dir "~/blog")
- (hugoista-hugo-command "~/go/bin/hugo"))
-
-(provide 'packages-misc)
diff --git a/packages/packages-ui.el b/packages/packages-ui.el
deleted file mode 100644
index 7f7e303..0000000
--- a/packages/packages-ui.el
+++ /dev/null
@@ -1,41 +0,0 @@
-;; -*- lexical-binding: t; y-*-
-(use-package ace-window
- :ensure t
- :bind
- (("C-x o" . ace-window)))
-
-(use-package counsel
- :ensure t)
-
-(use-package dashboard
- :ensure t
- :config
- (setq dashboard-startup-banner 'logo
- dashboard-banner-logo-title "Welcome to Verdant's Emacverse!!!"
- dashboard-center-content t
- dashboard-set-heading-icons t
- dashboard-items '((recents . 10)
- (bookmarks . 5))
- dashboard-footer-messages '("verdant.el"))
-
- ;; 核心三件套
- (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")))
- (dashboard-setup-startup-hook)
- (add-hook 'after-init-hook #'dashboard-open t))
-
-(use-package ivy-posframe
- :ensure t
- :config
- (ivy-posframe-mode t))
-
-(use-package ivy-rich
- :ensure t
- :config
- (ivy-rich-mode t))
-
-(use-package diredfl
- :ensure t
- :hook (dired-mode . diredfl-mode))
-
-
-(provide 'packages-ui)
diff --git a/packages/packages.el b/packages/packages.el
deleted file mode 100644
index ea0032b..0000000
--- a/packages/packages.el
+++ /dev/null
@@ -1,14 +0,0 @@
-;; -*- lexical-binding: t; -*-
-(require 'package)
-(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
-(add-to-list 'package-archives '("gnu" . "https://https://elpa.gnu.org/packages/") t)
-(add-to-list 'package-archives '("melpa" . "https://mirrors.ustc.edu.cn/elpa/melpa/") t)
-(add-to-list 'package-archives '("nongnu" . "https://mirrors.ustc.edu.cn/elpa/nongnu/") t)
-(package-initialize)
-
-(require 'packages-editing)
-(require 'packages-ui)
-(require 'packages-email)
-(require 'packages-misc)
-;; (require 'packages-leetcode)
-(provide 'packages)
diff --git a/tramp b/tramp
index f0166e1..1c61d00 100644
--- a/tramp
+++ b/tramp
@@ -1,4 +1,4 @@
-;; -*- lisp-data -*- <26/04/25 10:36:12 ~/.emacs.d/tramp>
+;; -*- lisp-data -*- <26/05/02 00:10:27 ~/.emacs.d/tramp>
;; Tramp connection history. Don't change this file.
;; Run `M-x tramp-cleanup-all-connections' instead.