保存文件时自动格式化, 支持 html/java/js/js2/typescript/json/yaml/python/sh 等语言, 详情参考变量 prettier-major-mode-parsers:
(use-package prettier
;; TRAMP 支持的有问题, 故关闭。
:disabled
:ensure-system-package (prettier . "npm -g install prettier")
:diminish
:hook (prog-mode . prettier-mode)
:init (setq prettier-mode-sync-config-flag nil))
默认将 lsp java server 安装到 ~/.emacs.d/.cache/lsp/eclipse.jdt.ls 目录。
手动安装 lombok:
mvn dependency:get -DrepoUrl=http://download.java.net/maven/2/ -DgroupId=org.projectlombok -DartifactId=lombok -Dversion=1.18.6
(use-package lsp-java
:disabled
:after (lsp-mode)
:init
;; 指定运行 jdtls 的 java 程序
(setq lsp-java-java-path "/Library/Java/JavaVirtualMachines/jdk-11.0.9.jdk/Contents/Home")
;; 指定 jdtls 编译源码使用的 jdk 版本(默认是启动 jdtls 的 java 版本)。
;; https://marketplace.visualstudio.com/items?itemName=redhat.java
;; 查看所有 java 版本:/usr/libexec/java_home -verbose
(setq lsp-java-configuration-runtimes
'[(:name "Java SE 8" :path "/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home" :default t)
(:name "Java SE 11.0.9" :path "/Library/Java/JavaVirtualMachines/jdk-11.0.9.jdk/Contents/Home")
(:name "Java SE 15.0.1" :path "/Library/Java/JavaVirtualMachines/jdk-15.0.1.jdk/Contents/Home")])
;; jdk11 不支持 -Xbootclasspath/a: 参数。
(setq lsp-java-vmargs
(list "-noverify" "-Xmx2G" "-XX:+UseG1GC" "-XX:+UseStringDeduplication"
(concat "-javaagent:" (expand-file-name "~/.m2/repository/org/projectlombok/lombok/1.18.6/lombok-1.18.6.jar"))))
:hook (java-mode . lsp)
:config
(use-package dap-java :disabled t))
符号高亮和跳转, 已被 ctrlf 替代:
(use-package symbol-overlay
:diminish
:functions
(turn-off-symbol-overlay turn-on-symbol-overlay)
:custom-face
(symbol-overlay-default-face ((t (:inherit (region bold)))))
:bind
(("M-i" . symbol-overlay-put)
("M-n" . symbol-overlay-jump-next)
("M-p" . symbol-overlay-jump-prev)
("M-N" . symbol-overlay-switch-forward)
("M-P" . symbol-overlay-switch-backward)
("M-C" . symbol-overlay-remove-all)
([M-f3] . symbol-overlay-remove-all))
:hook
(((prog-mode yaml-mode) . symbol-overlay-mode)
(iedit-mode . turn-off-symbol-overlay)
(iedit-mode-end . turn-on-symbol-overlay))
:init
(setq symbol-overlay-idle-time 0.1)
:config
;; Disable symbol highlighting while selecting
(defun turn-off-symbol-overlay (&rest _)
"Turn off symbol highlighting."
(interactive)
(symbol-overlay-mode -1))
(advice-add #'set-mark :after #'turn-off-symbol-overlay)
(defun turn-on-symbol-overlay (&rest _)
"Turn on symbol highlighting."
(interactive)
(when (derived-mode-p 'prog-mode 'yaml-mode)
(symbol-overlay-mode 1)))
(advice-add #'deactivate-mark :after #'turn-on-symbol-overlay))
离线查询 devdocs 文档(已被 dash 替代):
(use-package devdocs
:bind ("C-c b" . devdocs-lookup)
:config
(add-to-list 'completion-category-defaults '(devdocs (styles . (flex))))
(add-hook 'python-mode-hook (lambda () (setq-local devdocs-current-docs '("python~3.9"))))
(add-hook 'go-mode-hook (lambda () (setq-local devdocs-current-docs '("go")))))
- 安装文档: M-x devdocs-install
- 更新当前使用的文档目录: C-u M-x dovdocs-lookup
(setq rime-show-candidate 'posfram)
(setq rime-posframe-properties (list :font "Sarasa SC Gothic" :internal-border-width 2))
;; 有了更好的 ctrlf 替换
;; 直接在 minibuffer 中编辑 query(RIME 探测到 minibuffer 时自动关闭输入法)
(use-package isearch-mb
:demand t
:config
(setq-default
;;启动 isearch 进行搜索时,M-<, M->, C-v 和 M-v 这些按键不会打断搜索
isearch-allow-motion t
;; Match count next to the minibuffer prompt
isearch-lazy-count t
;; Don't be stingy with history; default is to keep just 16 entries
search-ring-max 200
regexp-search-ring-max 200)
;; 习惯使用 regexp 类型的 isearch
(global-set-key (kbd "C-s") 'isearch-forward-regexp)
(global-set-key (kbd "C-r") 'isearch-backward-regexp)
(add-to-list 'isearch-mb--with-buffer #'consult-isearch)
(define-key isearch-mb-minibuffer-map (kbd "M-r") #'consult-isearch)
(add-to-list 'isearch-mb--after-exit #'consult-line)
(define-key isearch-mb-minibuffer-map (kbd "M-s l") 'consult-line)
(isearch-mb-mode t))
(use-package visual-regexp
:config
(setq vr/match-separator-use-custom-face t)
(setq vr/match-separator-string "⇛")
(define-key global-map (kbd "C-c r") 'vr/replace)
(define-key global-map (kbd "C-c q") 'vr/query-replace))
;; diff 时显示空白字符。
(defun my/diff-spaces ()
(setq-local whitespace-style
'(face
tabs
tab-mark
spaces
space-mark
trailing
indentation::space
indentation::tab
newline
newline-mark))
(whitespace-mode 1))
(add-hook 'diff-mode-hook 'my/diff-spaces)
;; diff-hl 会导致 buffer 内容错乱
(use-package diff-hl
:disabled
:after (magit)
:config
(setq diff-hl-disable-on-remote t)
(global-diff-hl-mode)
(diff-hl-flydiff-mode 1)
(diff-hl-show-hunk-mouse-mode 1)
(add-hook 'dired-mode-hook 'diff-hl-dired-mode)
(add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh)
(add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)
)
;; 中文不加空格使用行内格式, 如强调。(export pdf 时还是需要加空格)
;; 由于分词不准确,故不再使用
(setq org-emphasis-regexp-components
'("-[:multibyte:][:space:]('\"{"
"-[:multibyte:][:space:].,:!?;'\")}\\["
"[:space:]"
"[^=~*_]"
1))
(org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components)
(org-element-update-syntax)
;; 对于远程目录文件直接返回 nil(使用 default-directory),防止 TRAMP 卡主。
;; -- 更好的解决办法是在 projectile 中集中修改 projectile-project-root 的实现, 如果时远程 project, 直接返回 default-directory
;; (setq consult-project-root-function
;; (lambda ()
;; (unless (file-remote-p default-directory)
;; ;; 使用 projectile.el:
;; (projectile-project-root)
;; ;; 使用 project.el:
;; ;;(when-let (project (project-current))
;; ;; (car (project-roots project)))
;; )))
;; 指针宽度与字符一致
(setq-default x-stretch-cursor t)
;; 智能扩展区域,被 easy-kill/mark 取代
(use-package expand-region
:demand
:bind
("M-@" . er/expand-region))
(use-package manage-minor-mode
:straight (manage-minor-mode :host github :repo "emacsorphanage/manage-minor-mode")
:config
(setq manage-minor-mode-bals-exclude-list
'((global (recentf-mode global-font-lock-mode delete-selection-mode transient-mark-mode vertico-mode consult-mode))
(text-mode (line-number-mode))
(json-mode (tree-sitter-mode ts-fold-mode))
(javascript-mode (tree-sitter-mode ts-fold-mode))
(org-mode (line-number-mode blink-cursor-mode)))))
(defvar hidden-minor-modes '(whitespace-mode))
(use-package posframe-project-term
:straight (posframe-project-term :host github :repo "zwpaper/posframe-project-term")
:bind
(("C-c t" . posframe-project-term-toggle)))
;; 还是 go-translate 提供的 bing 翻译较为准确
(use-package youdao-dictionary
:bind
(("C-c y" . youdao-dictionary-search-at-point))
:init
(setq url-automatic-caching t)
(setq youdao-dictionary-use-chinese-word-segmentation t)
:config
;; 使用 jieba 进行中文分词: pip install jieba
(use-package chinese-word-at-point :demand t))
(use-package posframe :demand)
(use-package vertico-posframe
:straight (vertico-posframe :host github :repo "tumashu/vertico-posframe")
:disabled
:config
(setq vertico-posframe-parameters
'((left-fringe . 8)
(right-fringe . 8)
;;(alpha . 80)
))
;; 在光标位置的上方显示 posframe, 避免遮住光标下方的内容
(defun my/posframe-poshandler-p0.5p0-to-f0.5p1 (info)
(let ((x (car (posframe-poshandler-p0.5p0-to-f0.5f0 info)))
;; 第三个参数 t 表示 upward
(y (cdr (posframe-poshandler-point-1 info nil t))))
(cons x y)))
(setq vertico-posframe-poshandler 'my/posframe-poshandler-p0.5p0-to-f0.5p1)
(vertico-posframe-mode 1))
;; no-littering 会导致 recentf 等不工作, 故关闭.
(use-package no-littering
:demand
:config
(with-eval-after-load 'recentf
(add-to-list 'recentf-exclude no-littering-var-directory)
(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("py" . "src python"))
(add-to-list 'org-structure-template-alist '("go" . "src go"))
(add-to-list 'org-structure-template-alist '("yaml" . "src yaml"))
(add-to-list 'org-structure-template-alist '("json" . "src json"))
(use-package grammatical-edit
:demand
:straight (grammatical-edit :host github :repo "manateelazycat/grammatical-edit")
:config
(dolist (hook (list
'c-mode-common-hook
'c-mode-hook
'c++-mode-hook
'java-mode-hook
'haskell-mode-hook
'emacs-lisp-mode-hook
'lisp-interaction-mode-hook
'lisp-mode-hook
'maxima-mode-hook
'ielm-mode-hook
'sh-mode-hook
'makefile-gmake-mode-hook
'php-mode-hook
'python-mode-hook
'js-mode-hook
'go-mode-hook
'qml-mode-hook
'jade-mode-hook
'css-mode-hook
'ruby-mode-hook
'coffee-mode-hook
'rust-mode-hook
'qmake-mode-hook
'lua-mode-hook
'swift-mode-hook
'minibuffer-inactive-mode-hook
))
(add-hook hook '(lambda () (grammatical-edit-mode 1))))
(define-key grammatical-edit-mode-map (kbd "(") 'grammatical-edit-open-round)
(define-key grammatical-edit-mode-map (kbd "[") 'grammatical-edit-open-bracket)
(define-key grammatical-edit-mode-map (kbd "{") 'grammatical-edit-open-curly)
(define-key grammatical-edit-mode-map (kbd ")") 'grammatical-edit-close-round)
(define-key grammatical-edit-mode-map (kbd "]") 'grammatical-edit-close-bracket)
(define-key grammatical-edit-mode-map (kbd "}") 'grammatical-edit-close-curly)
(define-key grammatical-edit-mode-map (kbd "=") 'grammatical-edit-equal)
(define-key grammatical-edit-mode-map (kbd "%") 'grammatical-edit-match-paren)
(define-key grammatical-edit-mode-map (kbd "\"") 'grammatical-edit-double-quote)
(define-key grammatical-edit-mode-map (kbd "SPC") 'grammatical-edit-space)
(define-key grammatical-edit-mode-map (kbd "RET") 'grammatical-edit-newline)
(define-key grammatical-edit-mode-map (kbd "M-o") 'grammatical-edit-backward-delete)
(define-key grammatical-edit-mode-map (kbd "C-d") 'grammatical-edit-forward-delete)
(define-key grammatical-edit-mode-map (kbd "C-k") 'grammatical-edit-kill)
(define-key grammatical-edit-mode-map (kbd "M-\"") 'grammatical-edit-wrap-double-quote)
(define-key grammatical-edit-mode-map (kbd "M-[") 'grammatical-edit-wrap-bracket)
(define-key grammatical-edit-mode-map (kbd "M-{") 'grammatical-edit-wrap-curly)
(define-key grammatical-edit-mode-map (kbd "M-(") 'grammatical-edit-wrap-round)
(define-key grammatical-edit-mode-map (kbd "M-)") 'grammatical-edit-unwrap)
(define-key grammatical-edit-mode-map (kbd "M-p") 'grammatical-edit-jump-right)
(define-key grammatical-edit-mode-map (kbd "M-n") 'grammatical-edit-jump-left)
(define-key grammatical-edit-mode-map (kbd "M-:") 'grammatical-edit-jump-out-pair-and-newline)
)
书签管理器:
(use-package ebuku
:ensure-system-package (buku . "pip3 install buku")
:config
;; 不限制结果
(setq ebuku-results-limit 0))
buku --ai
导入 Firefox/Chrome 书签;M-x ebuku
: 浏览和编辑导入的书签, 点击 URL 使用 Mac 浏览器打开。
(use-package shackle
:demand
:config
(setq shackle-default-size 0.3)
(setq shackle-default-alignment 'below)
(setq shackle-default-rule nil)
(setq shackle-select-reused-windows t)
(setq shackle-rules
'((("*Warnings*" "*Messages*" "*Completions*" "*Alerts*") :size 0.3 :align 'below :autoclose t)
(compilation-mode :select t :size 0.3 :align 'below :autoclose t)
("*quickrun*" :select t :size 15 :align 'below :autoclose t)
("*Backtrace*" :select t :size 15 :align 'below :autoclose t)
(("*Help*" "*Apropos*" "*Occur*") :select t :size 0.3 :align 'below :autoclose t)
(helpful-mode :select t :size 0.5 :popup t :align 'below :autoclose t)
("^\\*.*Shell Command.*\\*$" :regexp t :size 0.3 :align 'below :autoclose t)
(" *Flycheck checkers*" :select t :size 0.3 :align 'below :autoclose t)
((flycheck-error-list-mode flymake-diagnostics-buffer-mode) :select t :size 0.25 :align 'below :autoclose t)
(("*lsp-help*" "*lsp session*" "*LSP Error List*") :size 0.3 :align 'below :autoclose t)
("*DAP Templates*" :select t :size 0.4 :align 'below :autoclose t)
(dap-server-log-mode :size 15 :align 'below :autoclose t)
(profiler-report-mode :select t :size 0.5 :align 'below)
("*ELP Profiling Restuls*" :select t :size 0.5 :align 'below)
(("*Gofmt Errors*" "*Go Test*") :select t :size 0.3 :align 'below :autoclose t)
(godoc-mode :select t :size 0.4 :align 'below :autoclose t)
((grep-mode occur-mode rg-mode deadgrep-mode ag-mode pt-mode) :select t :size 0.4 :align 'below)
(hover-mode :select t :size 0.2 :align 'below :autoclose t)
(vterm-mode :select t :size 0.7 :align 'below)
(vc-annotate-mode :select t :size 0.8 :align 'below :autoclose t)
(special-mode :select t :size 0.5 :align 'below :autoclose t)
(git-log-view-mode :select t :size 0.5 :align 'below :autoclose t)
("*tldr*" :size 0.4 :align 'below :autoclose t)
("*Finder*" :select t :size 0.3 :align 'below :autoclose t)
("^\\*elfeed-entry" :regexp t :size 0.7 :align 'below :autoclose t)
(" *Install vterm* " :size 0.35 :same t :align 'below)
((youdao-dictionary-mode osx-dictionary-mode fanyi-mode) :select t :size 0.5 :align 'below :autoclose t)
("*Calendar*" :select t :size 0.3 :align 'below)
(" *undo-tree*" :select t)
(("*Org Agenda*" " *Agenda Commands*" " *Org todo*" "*Org Dashboard*" "*Org Select*") :select t :size 0.1 :align 'below :autoclose t)
(("\\*Capture\\*" "^CAPTURE-.*\\.org*") :regexp t :select t :size 0.3 :align 'below :autoclose t)
(Buffer-menu-mode :select t :size 0.5 :align 'below :autoclose t)
((process-menu-mode list-environment-mode) :select t :size 0.3 :align 'below)
(bookmark-bmenu-mode :select t :size 0.4 :align 'below)
(tabulated-list-mode :size 0.4 :autoclose t)
((inferior-python-mode inf-ruby-mode swift-repl-mode) :size 0.4 :align 'below)
("*prolog*" :size 0.4 :align 'below)))
(shackle-mode t))
;; 高亮当前行。由于容易与候选者背景混淆, 所以不开启。
(global-hl-line-mode t)
project 配置参考:
(use-package company
:bind
(:map company-mode-map
([remap completion-at-point] . company-complete)
:map company-active-map
([escape] . company-abort)
("C-p" . company-select-previous)
("C-n" . company-select-next)
("C-s" . company-filter-candidates)
([tab] . company-complete-common-or-cycle)
([backtab] . company-select-previous-or-abort)
:map company-search-map
([escape] . company-search-abort)
("C-p" . company-select-previous)
("C-n" . company-select-next))
:custom
;; trigger completion immediately.
(company-idle-delay 0)
(company-echo-delay 0)
;; allow input string that do not match candidate words
;; 开启后有大量不匹配的候选情况,故关闭
;;(company-require-match nil)
;; number the candidates (use M-1, M-2 etc to select completions).
(company-show-numbers t)
;; pop up a completion menu by tapping a character
(company-minimum-prefix-length 1)
(company-tooltip-limit 14)
(company-tooltip-align-annotations t)
;; Only search the current buffer for `company-dabbrev' (a backend that
;; suggests text your open buffers). This prevents Company from causing
;; lag once you have a lot of buffers open.
(company-dabbrev-other-buffers nil)
;; Make `company-dabbrev' fully case-sensitive, to improve UX with
;; domain-specific words with particular casing.
(company-dabbrev-ignore-case nil)
;; Don't downcase the returned candidates.
(company-dabbrev-downcase nil)
;; 候选框宽度
(company-tooltip-minimum-width 70)
(company-tooltip-maximum-width 100)
(company-global-modes '(not message-mode help-mode eshell-mode))
;; 补全后端
(company-backends '(company-capf
(company-dabbrev-code company-keywords company-files)
company-dabbrev))
:config
;; 高亮候选者(orderless 排序)。
(defun just-one-face (fn &rest args)
(let ((orderless-match-faces [completions-common-part]))
(apply fn args)))
(advice-add 'company-capf--candidates :around #'just-one-face)
(global-company-mode t))
(use-package company-emoji
:demand t
:after (company)
:config
(company-emoji-init)
(add-to-list 'company-backends 'company-emoji))
(use-package company-ansible
:after (ansible)
:config
(add-hook 'ansible-hook (lambda() (add-to-list 'company-backends 'company-ansible))))
(use-package restclient
:mode ("\\.http\\'" . restclient-mode)
:config
(use-package restclient-test :diminish :hook (restclient-mode . restclient-test-mode))
(with-eval-after-load 'company
(use-package company-restclient
:defines company-backends
:init (add-to-list 'company-backends 'company-restclient))))
;;company-box 为候选者显示图标和帮助文档, 会导致 RIME 输入法提示时卡住, 故关闭。
(use-package company-box
:after (company all-the-icons)
:init
;;(setq company-box-doc-enable nil)
(setq company-box-doc-delay 0.1)
:hook (company-mode . company-box-mode))
;;类似于 consult-grep 和 consult-find, 但前后端都异步且支持 fuzzy 搜索。
(use-package affe
:after (orderless)
:ensure-system-package
((gfind . findutils)
(fd . fd)
(fzf . fzf)
(rg . ripgrep))
:bind
(;; bind-c bindings (mode-specific-map)
("M-s g" . affe-grep)
("M-s f" . affe-find))
:config
(setq affe-count 200)
;; Configure Orderless
(setq affe-regexp-function #'orderless-pattern-compiler
affe-highlight-function #'orderless--highlight)
;; Manual preview key for `affe-grep'
(consult-customize affe-grep :preview-key (kbd "M-.")))
(use-package origami
:straight (origami :host github :repo "elp-revive/origami.el")
:demand t
:config
(define-prefix-command 'origami-mode-map)
(global-set-key (kbd "C-x C-z") 'origami-mode-map)
(global-origami-mode)
:bind
(:map origami-mode-map
("o" . origami-open-node)
("O" . origami-open-node-recursively)
("c" . origami-close-node)
("C" . origami-close-node-recursively)
("a" . origami-toggle-node)
("A" . origami-recursively-toggle-node)
("R" . origami-open-all-nodes)
("M" . origami-close-all-nodes)
("v" . origami-show-only-node)
("k" . origami-previous-fold)
("j" . origami-forward-fold)
("x" . origami-reset)))
(use-package lsp-origami
:after (lsp origami)
:demand t
:config
(add-hook 'lsp-after-open-hook #'lsp-origami-try-enable))
版本 2:
;;origami 提供代码折叠功能,最新版本[[https://github.com/elp-revive/origami.el/issues/1][从 celpa 源安装]]:
;; 由于可以使用 consult-line 和 occur 来替代,所以不再使用。
(use-package origami
:config
(define-prefix-command 'origami-mode-map)
(global-set-key (kbd "C-x C-z") 'origami-mode-map)
(global-origami-mode)
:bind
(:map origami-mode-map
("o" . origami-open-node)
("O" . origami-open-node-recursively)
("c" . origami-close-node)
("C" . origami-close-node-recursively)
("a" . origami-toggle-node)
("A" . origami-recursively-toggle-node)
("R" . origami-open-all-nodes)
("M" . origami-close-all-nodes)
("v" . origami-show-only-node)
("k" . origami-previous-fold)
("j" . origami-forward-fold)
("x" . origami-reset)))
(use-package mini-frame
:disabled
:config
(setq x-gtk-resize-child-frames 'resize-mode)
;; 光标位置显示 minibuffer
(setq mini-frame-show-parameters
(lambda ()
(let* ((info (posframe-poshandler-argbuilder))
(posn (posframe-poshandler-point-bottom-left-corner info))
(left (car posn))
(top (cdr posn)))
`((left . ,left)
(top . ,top)))))
;; 固定在 frame 顶部显式。
;;(custom-set-variables '(mini-frame-show-parameters '((top . 10) (width . 0.7) (left . 0.5) (height . 10))))
(mini-frame-mode))
;; https://github.com/minad/mini-popup
;; https://raw.githubusercontent.com/minad/mini-popup/main/mini-popup.el
(use-package mini-popup
:ensure nil
:load-path "/Users/zhangjun/.emacs.d/site-lisp"
:config
;; Configure a height function (Example for Vertico)
(defun mini-popup-height-resize ()
(* (1+ (min vertico--total vertico-count)) (default-line-height)))
(defun mini-popup-height-fixed ()
(* (1+ (if vertico--input vertico-count 0)) (default-line-height)))
(setq mini-popup--height-function #'mini-popup-height-resize)
;; Disable the minibuffer resizing of Vertico (HACK)
(advice-add #'vertico--resize-window :around
(lambda (&rest args)
(unless mini-popup-mode
(apply args))))
;; Ensure that the popup is updated after refresh (Consult-specific)
(add-hook 'consult--completion-refresh-hook
(lambda (&rest _) (mini-popup--setup)) 99)
(mini-popup-mode t))
;; 第一个 frame 规格
(setq initial-frame-alist '((top . 10 ) (left . 10) (width . 200) (height . 60)))
;; 后续 frame 规格
(setq default-frame-alist '((top . 10 ) (left . 10) (width . 200) (height . 60)))
(use-package pdf-continuous-scroll-mode
:straight (:host github :repo "dalanicolai/pdf-continuous-scroll-mode.el")
:demand
:after (pdf-tools)
:config
(add-hook 'pdf-view-mode-hook 'pdf-continuous-scroll-mode))
(use-package find-file-in-project
:config
;; ffip adds `ffap-guess-file-name-at-point' automatically and it is crazy slow on TRAMP buffers.
;; https://github.com/mpereira/.emacs.d/#find-file-in-project
(remove-hook 'file-name-at-point-functions 'ffap-guess-file-name-at-point))
(require 'package)
(setq package-archives '(("celpa" . "https://celpa.conao3.com/packages/")
("elpa" . "https://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")))
;; activate all the packages (in particular autoloads)
(package-initialize)
;; fetch the list of packages available
(unless package-archive-contents (package-refresh-contents))
(setq package-native-compile t)
(setq use-package-always-ensure t
use-package-always-demand t)
(setq use-package-verbose t)
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(setq use-package-compute-statistics t)
;; 由于编译 emacs 29 时指定了 no title-bar, 所以不再需要这个配置了
(use-package ns-auto-titlebar
:demand t
:config
(when (eq system-type 'darwin)
(ns-auto-titlebar-mode)))
;; Mac native fullscreen 会导致白屏和左右滑动问题,故使用传统全屏模式。
;; Emacs 28 开启后不能正常 max-frame 或 fullscreen
(when (eq system-type 'darwin)
(setq ns-use-native-fullscreen nil
ns-use-fullscreen-animation nil))
;; Make cursor movement an order of magnitude faster
;; https://emacs.stackexchange.com/questions/28736/emacs-pointcursor-movement-lag/28746
;; 会导致 buffer 部分 fontify 不准确
(setq fast-but-imprecise-scrolling 't)
(use-package emmet-mode
:after(web-mode js2-mode)
:config
(add-hook 'sgml-mode-hook 'emmet-mode)
(add-hook 'css-mode-hook 'emmet-mode)
(add-hook 'web-mode-hook 'emmet-mode)
(add-hook 'emmet-mode-hook (lambda () (setq emmet-indent-after-insert nil)))
(add-hook 'emmet-mode-hook (lambda () (setq emmet-indentation 2)))
(setq emmet-expand-jsx-className? t)
;; Make `emmet-expand-yas' not conflict with yas/mode
(setq emmet-preview-default nil))
;; flycheck
;; 在当前窗口底部显示错误列表
(add-to-list 'display-buffer-alist
`(,(rx bos "*Flycheck errors*" eos)
(display-buffer-reuse-window
display-buffer-in-side-window)
(side . bottom)
(reusable-frames . visible)
(window-height . 0.33)))
(defun my/faces (&optional theme &rest _)
(interactive)
;; Main typeface (英文字体)
(set-face-attribute 'default nil :font "Iosevka SS14-14")
;; Proportionately spaced typeface
(set-face-attribute 'variable-pitch nil :family "Iosevka SS14")
;; Monospaced typeface
(set-face-attribute 'fixed-pitch nil :family "Iosevka SS14")
(when (display-graphic-p)
;; 中文字体
(dolist (charset '(kana han symbol cjk-misc bopomofo))
(set-fontset-font
(frame-parameter nil 'font)
charset
(font-spec :name "Sarasa Mono SC" :weight 'normal :slant 'normal :size 15.0)))
;; 设置字体缩放比例, 使字体对齐。
(setq face-font-rescale-alist '(("Iosevka SS14" . 1.0)
("Sarasa Mono SC" . 1.0714285714285714)
("HanaMinB" . 1.1428571428571428)))))
(transient-mark-mode t)
(add-to-list 'default-frame-alist '(height . 600))
(add-to-list 'default-frame-alist '(width . 600))
;; That tells auth-source (the package responsible for retrieving and storing
;; passwords from the environment) to consult the keychain for your credentials
;; - instead of putting them into ~/.authinfo in plaintext
;; https://www.reddit.com/r/emacs/comments/ew75ib/comment/fg23tcj/?utm_source=share&utm_medium=web2x&context=3
(eval-after-load 'auth-source
'(when (member window-system '(mac ns))
(add-to-list 'auth-sources 'macos-keychain-internet)
(add-to-list 'auth-sources 'macos-keychain-generic)))
;; buffer 智能分组(取代 ibuffer)
;; 显示 buffer 列表时会自动连接 TRAMP buffer, 可能会卡住。
(use-package bufler :config (global-set-key (kbd "C-x C-b") 'bufler))
;; 多光标编辑
(use-package iedit)
(defconst sys/macp (eq system-type 'darwin) "Are we running on a Mac system?")
(defconst sys/mac-x-p (and (display-graphic-p) sys/macp) "Are we running under X on a Mac system?")
(defconst sys/mac-ns-p (eq window-system 'ns) "Are we running on a GNUstep or Macintosh Cocoa display?")
(defconst sys/mac-cocoa-p (featurep 'cocoa) "Are we running with Cocoa on a Mac system?")
(defconst sys/mac-port-p (eq window-system 'mac) "Are we running a macport build on a Mac system?")
;; cnfont 会自动设置 hook,开启 cnfont 的情况下,不需要配置这个 hook
(add-hook 'emacs-startup-hook #'my/faces)
;; 不能在 load-theme 时执行这个函数,否则字体缩放有问题。
;; 这个函数只被 modus-theme 在切换主题时调用。
;;(advice-add #'load-theme :after #'my/faces)
;; (add-hook 'emacs-startup-hook
;; (lambda () (load-theme 'doom-dracula t))
;; 'append)
;; Get rid of "For information about GNU Emacs..." message at startup, unless
;; we're in a daemon session where it'll say "Starting Emacs daemon." instead,
;; which isn't so bad.
(unless (daemonp)
(advice-add #'display-startup-echo-area-message :override #'ignore))
;; 中英文之间自动加空格
(use-package pangu-spacing
:config
;; 只是在中英文之间显示空格
(global-pangu-spacing-mode 1)
;; 保存时真正插入空格
(setq pangu-spacing-real-insert-separtor t))
(use-package eshell-toggle
:custom
(eshell-toggle-size-fraction 3)
;;(eshell-toggle-use-projectile-root t)
(eshell-toggle-run-command nil)
(eshell-toggle-init-function #'eshell-toggle-init-ansi-term)
:bind
("s-`" . eshell-toggle))
(use-package native-complete
:custom
(with-eval-after-load 'shell
(native-complete-setup-bash)))
(use-package company-native-complete
:after (company)
:custom
(add-to-list 'company-backends 'company-native-complete))
(use-package persp-mode
:custom
(persp-keymap-prefix (kbd "C-x p"))
:config
(persp-mode))
(use-package treemacs-persp
:after (treemacs persp-mode)
:config
(treemacs-set-scope-type 'Perspectives))
;; pyenv-mode 通过给项目设置环境变量 ~PYENV_VERSION~ 来达到指定 pyenv 环境的目的:
(use-package pyenv-mode
;;:after (projectile)
:init
(add-to-list 'exec-path "~/.pyenv/shims")
(setenv "WORKON_HOME" "~/.pyenv/versions/")
:config
(pyenv-mode)
;; (defun projectile-pyenv-mode-set ()
;; (let ((project (projectile-project-name)))
;; (if (member project (pyenv-mode-versions))
;; (pyenv-mode-set project)
;; (pyenv-mode-unset))))
;;(add-hook 'projectile-after-switch-project-hook 'projectile-pyenv-mode-set)
:bind
;; 防止和 org-mode 快捷键冲突
(:map pyenv-mode-map ("C-c C-u") . nil)
(:map pyenv-mode-map ("C-c C-s") . nil))
(use-package selectrum :init (selectrum-mode +1))
(use-package prescient :config (prescient-persist-mode +1))
(use-package selectrum-prescient :init (selectrum-prescient-mode +1))
;;company-prescient 精准排序:
(use-package company-prescient
:after (company prescient)
:init (company-prescient-mode +1))
(defun my/faces (&optional theme &rest _)
(interactive)
;; Main typeface (英文字体)
(set-face-attribute 'default nil :font "Iosevka SS14-14")
;; Proportionately spaced typeface
(set-face-attribute 'variable-pitch nil :family "Iosevka SS14")
;; Monospaced typeface
(set-face-attribute 'fixed-pitch nil :family "Iosevka SS14")
(when (display-graphic-p)
;; 中文字体
(dolist (charset '(kana han symbol cjk-misc bopomofo))
(set-fontset-font
(frame-parameter nil 'font)
charset
(font-spec :name "Sarasa Mono SC" :weight 'normal :slant 'normal :size 15.0)))
;; 设置字体缩放比例, 使字体对齐。
(setq face-font-rescale-alist '(("Iosevka SS14" . 1.0)
("Sarasa Mono SC" . 1.0714285714285714)
("HanaMinB" . 1.1428571428571428)))))
;; cnfont 会自动设置字体和缩放,开启 cnfont 时不需要配置这个 hook 。而且这个只是
;; 很对特定字号的scale, 如果缩放屏幕就会出现中英文混乱的情况,所以最好使用
;; cnfonts
(add-hook 'emacs-startup-hook (lambda ()
;; 只会对初始 frame 生效
(my/faces)
;; 创建新 frame 时也生效
(add-to-list 'after-make-frame-functions
(lambda (new-frame)
(select-frame new-frame)
(if window-system
(my/faces))))))
;; 画图
(use-package svg
:ensure nil
:load-path "/Users/zhangjun/.emacs.d/site-lisp")
;; 自动调整窗口大小
(use-package zoom
:disabled
:custom
(zoom-size '(0.618 . 0.618))
(zoom-ignored-major-modes '(dired-mode markdown-mode ediff-mode))
(zoom-ignored-buffer-names '("zoom.el" "init.el" "*Ediff Control Panel*"))
(zoom-ignored-buffer-name-regexps '("^\\*calc" "^\\*[eE]diff.*"))
(zoom-ignore-predicates (list (lambda () (< (count-lines (point-min) (point-max)) 20))))
:config
(zoom-mode t))
;; 使用 embark C-h 替换 which-key
;; which-key 会导致 ediff 的 gX 命令卡住,解决办法是向 Emacs 发送 USR2 信号
(use-package which-key
:init (which-key-mode)
:diminish which-key-mode
:config (setq which-key-idle-delay 0.8))
;; org-msg 在回复消息时,只能看到回复引用的内容,而看不到消息本身,故不再使用。
(use-package org-msg
:ensure t
:disabled
:config
(setq mail-user-agent 'mu4e-user-agent)
(setq org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil author:nil email:nil \\n:t"
org-msg-startup "hidestars indent inlineimages"
org-msg-greeting-fmt "\nHi%s,\n\n"
org-msg-recipient-names '(("geekard@qq.com" . "zhangjun"))
org-msg-greeting-name-limit 3
org-msg-default-alternatives '((new . (text html))
(reply-to-html . (text html))
(reply-to-text . (text)))
org-msg-convert-citation t)
(org-msg-mode)
)
;; 快速跳转当前标记符
(use-package symbol-overlay
:config
(global-set-key (kbd "M-i") 'symbol-overlay-put)
(global-set-key (kbd "M-n") 'symbol-overlay-jump-next)
(global-set-key (kbd "M-p") 'symbol-overlay-jump-prev)
(global-set-key (kbd "<f7>") 'symbol-overlay-mode)
(global-set-key (kbd "<f8>") 'symbol-overlay-remove-all)
:hook (prog-mode . symbol-overlay-mode))
;;isearch 与 rime [[https://github.com/DogLooksGood/emacs-rime/issues/21][不兼
;;容]],会导致输入的中文不能候选,可以使用 phi-search 解决:
;; 注: occur 比 isearch 更好用,与 rime 兼容。
(use-package phi-search
:after (rime)
:config
(global-set-key (kbd "C-s") 'phi-search)
(global-set-key (kbd "C-r") 'phi-search-backward))
(defun my/disable-vertico (orig-fun &rest args)
(print args)
(apply orig-fun args)
;; (if (string-match ".*(ssh|scp):.*" args)
;; (progn (vertico-mode -1)
;; (apply orig-fun args))
;; (progn (vertico-mode t)
;; (apply orig-fun args))
)
(advice-add 'find-file-noselect :around #'my/disable-vertico)
(defun my/time-advice (func-orig &rest r)
;;(print r)
(apply func-orig r))
(setq my/completion-func-to-advise #'completion-all-completions)
(advice-add my/completion-func-to-advise :around #'my/time-advice)
(use-package mu4e-dashboard
:straight (mu4e-dashboard :host github :repo "rougier/mu4e-dashboard"))
;;Make invisible parts of Org elements appear visible.
(use-package org-appear
:custom
(org-appear-autolinks t)
:hook (org-mode . org-appear-mode))
;; 在 side-window 显示窗口,side-window 会一直显示,为 vterm mode 专用(不能最大化),
;; vterm-toggle-forward 和 'vterm-toggle-backward 也都显示在这个 side-window 中。
(setq vterm-toggle-fullscreen-p nil)
(add-to-list 'display-buffer-alist
'((lambda(bufname _) (with-current-buffer bufname (equal major-mode 'vterm-mode)))
(display-buffer-reuse-window display-buffer-in-side-window)
(side . bottom)
(dedicated . t)
(reusable-frames . visible)
(window-height . 0.4)))
;; 增强窗口背景对比度
(use-package solaire-mode
:demand
:config (solaire-global-mode +1))
:config
(doom-modeline-def-modeline 'main
;; left-hand segment list, 去掉 remote-host,避免编辑远程文件时卡住。
'(bar workspace-name window-number modals matches buffer-info buffer-position word-count parrot selection-info)
;; right-hand segment list,尾部增加空格,避免溢出。
'(objed-state misc-info battery grip debug repl lsp minor-modes input-method major-mode process vcs checker " "))
;; 窗口大小调整快捷键。
(global-set-key (kbd "S-C-<left>") 'shrink-window-horizontally)
(global-set-key (kbd "S-C-<right>") 'enlarge-window-horizontally)
(global-set-key (kbd "S-C-<down>") 'shrink-window)
(global-set-key (kbd "S-C-<up>") 'enlarge-window)
(use-package ansible
:after (yaml-mode)
:config
(add-hook 'yaml-mode-hook (lambda () (ansible 1))))
;; ansible-doc 使用系统的 ansible-doc 命令搜索文档
(use-package ansible-doc
:ensure-system-package (ansible-doc . "pip install ansible")
:after (ansible yasnippet)
:config
(add-hook 'ansible-hook (lambda() (ansible-doc-mode) (yas-minor-mode-on)))
(define-key ansible-doc-mode-map (kbd "M-?") #'ansible-doc))
在线词典:
(use-package go-translate
:straight (:host github :repo "lorniu/go-translate")
:config
(setq gts-translate-list '(("en" "zh")))
(setq gts-default-translator
(gts-translator
:picker (gts-prompt-picker)
:engines (list
(gts-bing-engine)
(gts-google-engine :parser (gts-google-summary-parser))
(gts-google-rpc-engine)
(gts-youdao-dict-engine))
:render (gts-buffer-render))))
;; pip install jieba
(use-package chinese-word-at-point)
;;; go-translate
(global-set-key (kbd "C-c d t") #'gts-do-translate)
在线搜索:
(use-package engine-mode
:config
(engine-mode t)
;;(setq engine/browser-function 'eww-browse-url)
(defengine github
"https://github.com/search?ref=simplesearch&q=%s"
:keybinding "h")
(defengine google
"http://www.google.com/search?ie=utf-8&oe=utf-8&q=%s"
:keybinding "g")
(defengine twitter
"https://twitter.com/search?q=%s"
:keybinding "t")
(defengine wikipedia
"http://www.wikipedia.org/search-redirect.php?language=en&go=Go&search=%s"
:keybinding "w"
:docstring "Searchin' the wikis."))
- 搜索前缀命令:
C-x /
, 可以先选中 region 再执行上面的搜索。 - 修复启动报错:
rm ~/.emacs.d/elpa/engine-mode*/engine-mode-*.el*
;
使用 Mac 默认浏览器打开 URL:
;; 执行 browser-url 时使用 Mac 默认浏览器。
(setq browse-url-browser-function 'browse-url-default-macosx-browser)
;; 也可以使用自定义程序
;; (setq browse-url-browser-function 'browse-url-generic
;; browse-url-generic-program "mychrome")
;;(setq browse-url-chrome-program "mychrome")
上面引用的 mychrome 程序:
#!/bin/bash
open -a 'Google Chrome' $*
;; 记录最近 100 次按键,可以通过 M-x view-lossage 来查看输入的内容。
(lossage-size 100)
(use-package uniquify
:straight (:type built-in)
:config
;; Better unique buffer names for files with the same base name.
(setq uniquify-buffer-name-style 'forward)
(setq uniquify-strip-common-suffix t)
(setq uniquify-after-kill-buffer-p t))
;; Framework for mode-specific buffer indexes
(use-package imenu
:straight (:type built-in)
:bind (("C-c i" . imenu)))
Org-mode Web 站点:
(use-package easy-hugo
:init
(setq easy-hugo-basedir "~/blog/my_website/")
(setq easy-hugo-url "https://blog.opsnull.com")
(setq easy-hugo-previewtime "300")
:bind ("C-c C-e" . easy-hugo))
;; youdao
(defun my-youdao-dictionary-search-at-point ()
"Search word at point and display result with `posframe', `pos-tip', or buffer."
(interactive)
(if (display-graphic-p)
(youdao-dictionary-search-at-point-posframe)
(youdao-dictionary-search-at-point)))
(with-no-warnings
(defun my-youdao-dictionary--posframe-tip (string)
"Show STRING using posframe-show."
(unless (and (require 'posframe nil t) (posframe-workable-p))
(error "Posframe not workable"))
(let ((word (youdao-dictionary--region-or-word)))
(if word
(progn
(with-current-buffer (get-buffer-create youdao-dictionary-buffer-name)
(let ((inhibit-read-only t))
(erase-buffer)
(youdao-dictionary-mode)
(insert (propertize "\n" 'face '(:height 0.5)))
(insert string)
(insert (propertize "\n" 'face '(:height 0.5)))
(set (make-local-variable 'youdao-dictionary-current-buffer-word) word)))
(posframe-show youdao-dictionary-buffer-name
:position (point)
:left-fringe 16
:right-fringe 16
:posframe-width 100
:background-color (face-background 'tooltip nil t)
:internal-border-color (face-foreground 'font-lock-comment-face nil t)
:internal-border-width 1)
(unwind-protect
(push (read-event) unread-command-events)
(progn
(posframe-hide youdao-dictionary-buffer-name)
(other-frame 0))))
(message "Nothing to look up"))))
(advice-add #'youdao-dictionary--posframe-tip
:override #'my-youdao-dictionary--posframe-tip))
(use-package cnfonts
:demand
:disabled
:init
;; 中英文均使用 Sarasa Term SC 字体。
(setq cnfonts-personal-fontnames '(("Sarasa Term SC") ("Sarasa Term SC") ("HanaMinB")))
;; 允许字体缩放(部分主题如 lenven 依赖) 。
(setq cnfonts-use-face-font-rescale t)
:config
;; emoji 和 symbol 字体, 必须通过 cnfonts-set-font-finish-hook 调用才会生效。
(defun my/set-fonts (&optional font)
(setq use-default-font-for-symbols nil)
(set-fontset-font t '(#x1f000 . #x1faff) (font-spec :family "Apple Color Emoji"))
(set-fontset-font t 'symbol (font-spec :family "Apple Symbols" :size 20)))
(add-hook 'cnfonts-set-font-finish-hook 'my/set-fonts)
(cnfonts-enable))
版本 2:
(use-package cnfonts
:demand
:init
;; 中英文均使用 Sarasa Mono SC 字体。
(setq cnfonts-personal-fontnames '(("Sarasa Mono SC") ("Sarasa Mono SC") ("HanaMinB")))
;; 允许字体缩放(部分主题如 lenven 依赖) 。
(setq cnfonts-use-face-font-rescale t)
:config
;; emoji 和 symbol 字体, 必须通过 cnfonts-set-font-finish-hook 调用才会生效。
(defun my/set-fonts (&optional font)
(setq use-default-font-for-symbols nil)
(set-fontset-font t '(#x1f000 . #x1faff) (font-spec :family "Apple Color Emoji"))
(set-fontset-font t 'symbol (font-spec :family "Apple Symbols" :size 20)))
(add-hook 'cnfonts-set-font-finish-hook 'my/set-fonts)
(cnfonts-enable))
;; New link type for Org-Hugo internal links
(org-link-set-parameters "hugo"
:complete (lambda ()
(concat "{{% ref "(file-name-nondirectory (read-file-name "File: "))" %}}")))
(add-hook 'before-save-hook 'whitespace-cleanup)
(add-hook 'prog-mode-hook (lambda () (interactive) (setq show-trailing-whitespace 1)))
连续按键快捷键:
(use-package key-chord
:demand
:config
(key-chord-mode 1)
(key-chord-define-global "ll" 'avy-goto-line)
(key-chord-define-global "cc" 'avy-goto-char-2)
(key-chord-define-global ",," 'indent-for-comment)
(setq key-chord-one-key-delay 0.4)
(setq key-chord-two-keys-delay 0.3))
版本 2:
;; 键盘黏滞键
(use-package key-chord
:config
(key-chord-mode 1)
(key-chord-define-global ".." 'ebuku)
(key-chord-define-global ",," '(lamba ()(find-file "~/Downloads/history.json"))))
如果 M-x dap-chrome-setup
命令执行失败,则可以手动编译安装 VS Code - Debugger for Chrome:
$ pwd
/Users/zhangjun/.emacs.d/.extension/vscode/msjsdiag.debugger-for-chrome
$ git clone git clone git@github.com:microsoft/vscode-chrome-debug.git
$ mv vscode-chrome-debug extension/
$ cd extension
$ npm install -g gulp
$ gulp build
$ ls -l out/src/
total 116K
-rw-r--r-- 1 zhangjun 2.0K 5 30 21:02 chromeDebug.js
-rw-r--r-- 1 zhangjun 884 5 30 21:02 chromeDebug.js.map
-rw-r--r-- 1 zhangjun 31K 5 30 21:02 chromeDebugAdapter.js
-rw-r--r-- 1 zhangjun 19K 5 30 21:02 chromeDebugAdapter.js.map
(use-package dap-mode
:disabled
:demand
:config
(dap-auto-configure-mode 1)
(require 'dap-chrome))
- 执行
M-x dap-chrome-setup
安装VSCode Chrome Debug Extension
.
(use-package emacs
:straight (:type built-in)
:ensure-system-package
((mu . mu)
(mbsync . isync)
(gpg . gnupg)
(proxychains4 . proxychains-ng)
(openssl . openssl@1.1)))
- mbsync(isync): 同步邮件到本地;
- mu(带 mu4e emacs 包): 索引和读取邮件;
- proxychains-ng: 任意 socket 代理, 访问 gmail 使用;
- gnupg: 加密;
- openssl@1.1: 提供 isync 和 msmtp 所需的根证书;
参考: 阮一峰 GPG 入门教程
创建加密 key 。为了提高 key 安全系数,一般推荐在创建 key 时设置一个密码,这样即使密钥丢了,别人也无法使用:
# 生成加密 key
$ gpg --gen-key
# 生成吊销证书
$ gpg --gen-revoke B1D06C306F507C66
# 查看 key
$ gpg --list-keys
/Users/zhangjun/.gnupg/pubring.kbx
----------------------------------
pub ed25519 2021-10-03 [SC] [有效至:2023-10-03]
10BC65EE905F64CCAFF5E123B1D06C306F507C66
uid [ 绝对 ] zhangjun <geekard@qq.com>
sub cv25519 2021-10-03 [E] [有效至:2023-10-03]
uid 是 zhangjun 或 geekard@qq.com 或 hash 值。获取 hash 值:
gpg -a --export |gpg --list-packets --verbose
M-x epa-list-keys
为了避免每次使用 key 时都输入密码,可以配置 gpg-agent
来记住密码,这样只需在系统第一次使用时输入即可:
allow-emacs-pinentry
allow-loopback-pinentry
为了让 Emacs 能在首次使用 GPG 时,捕获密码输入框,在 minibuffer 中输入密码,需要做如下的配置:
(setq epa-pinentry-mode 'loopback)
- pinentry 是 GPG 提供的程序, “它会让用户输入的密码不会因内存不足而换出到磁盘” 。
创建 qq 和 gmail 加密密码文件:
$ mkdir ~/.mail
$ echo my.qq.password >.mail/qq.pwd
$ gpg --encrypt --recipient 'geekard@qq.com' ~/.mail/qq.pwd
$ echo my.gmail.password >.mail/gmail/gmail.pwd
$ gpg --encrypt --recipient 'geekard@qq.com' ~/.mail/gmail.pwd
$ ls ~/.mail/*.gpg
/Users/zhangjun/.mail/gmail.pwd.gpg /Users/zhangjun/.mail/qq.pwd.gpg
# 删除原始明文密码
rm ~/.mail/{qq,gmail}.pwd
解压密码文件: gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/.mail/qq.pwd.gpg
########################################
# qq.com
########################################
IMAPAccount qq
Host imap.qq.com
User geekard@qq.com
PassCmd "gpg --quiet --for-your-eyes-only --no-tty --decrypt \~/.mail/qq.pwd.gpg"
Port 993
AuthMechs LOGIN
SSLType IMAPS
#CertificateFile /etc/ssl/certs/ca-certificates.crt # Linux
CertificateFile /usr/local/etc/openssl@1.1/cert.pem # MacOS
IMAPStore qq-remote
Account qq
MaildirStore qq-local
# The trailing "/" is important
Path ~/.mail/qq/
Inbox ~/.mail/qq/Inbox/
# The SubFolders option allows to represent all IMAP subfolders as local subfolders
SubFolders Verbatim
## Connections
Channel qq-inbox
Far :qq-remote:"INBOX"
Near :qq-local:"Inbox"
Create Near
Expunge Both
SyncState *
Channel qq-drafts
Far :qq-remote:"Drafts"
Near :qq-local:"Drafts"
Create Near
Expunge Both
SyncState *
Channel qq-sent
Far :qq-remote:"Sent Messages"
Near :qq-local:"Sent"
Create Near
Expunge Both
SyncState *
Channel qq-trash
Far :qq-remote:"Deleted Messages"
Near :qq-local:"Trash"
Create Near
Expunge Both
SyncState *
## Groups
Group qq
Channel qq-inbox
Channel qq-drafts
Channel qq-sent
Channel qq-trash
########################################
# gmail
########################################
IMAPAccount gmail
Host imap.gmail.com
User geekard@gmail.com
PassCmd "gpg --quiet --for-your-eyes-only --no-tty --decrypt \~/.mail/gmail.pwd.gpg"
SSLType IMAPS
AuthMechs PLAIN
CertificateFile /usr/local/etc/openssl@1.1/cert.pem # MacOS
IMAPStore gmail-remote
Account gmail
MaildirStore gmail-local
# The trailing "/" is important
Path ~/.mail/gmail/
Inbox ~/.mail/gmail/inbox
Channel gmail-default
Far :gmail-remote:
Near :gmail-local:Inbox
#Patterns INBOX
Create Near
Expunge Both
SyncState *
Channel gmail-sent
Far :gmail-remote:"[Gmail]/Sent Mail"
Near :gmail-local:Sent
Create Near
Expunge Both
SyncState *
Channel gmail-trash
Far :gmail-remote:"[Gmail]/Trash"
Near :gmail-local:Trash
Create Near
Expunge Both
SyncState *
Channel gmail-archive
Far :gmail-remote:"[Gmail]/All Mail"
Near :gmail-local:All
Create Near
Expunge Both
SyncState *
Channel gmail-junk
Far :gmail-remote:"[Gmail]/Spam"
Near :gmail-local:Junk
Create Near
Expunge Both
SyncState *
Group gmail
Channel gmail-default
Channel gmail-trash
Channel gmail-archive
Channel gmail-sent
Channel gmail-junk
同步邮件:
$ mkdir -p ~/.mail/qq/{Sent,Drafts,Trash,Archive}
$ mkdir -p ~/.mail/gmail/{All,Sent,Drafts,Junk,Trash}
$ mbsync --all
- 使用 https://gitlab.com/shackra/goimapnotify 可以实现自动调用 mbsync 同步邮件。
proxychains 为不支持代理的命令行程序(如 mbsync )提供任意 socks 代理功能:
$ mkdir .proxychains/
$ cp /usr/local/Cellar/proxychains-ng/4.14/.bottle/etc/proxychains.conf ~/.proxychains/proxychains.conf
在 proxychains.conf 的 ProxyList 中添加 socks5 代理地址:
[ProxyList]
socks5 127.0.0.1 13659
测试 gmail:
$ proxychains4 mbsync gmail
# 初始化索引, 指定自己的 email 地址列表
$ mu init --maildir ~/.mail/ --my-address=geekard@qq.com --my-address=geekard@gmail.com
# 建立索引
$ mu index
# 检索索引
$ mu find github
# 查看信息
$ mu info
- 索引位置:
~/.cache/mu
(defvar attachments-directory "~/.mail/attachments")
(if (not (file-exists-p attachments-directory))
(make-directory attachments-directory t))
(use-package mu4e
:disabled
;; 使用 mu4e/* 目录下的 lisp 文件, 跳过 straight 的 build 过程;
:straight (:host github :repo "djcb/mu" :branch "master" :files ("mu4e/*") :build nil)
:config
;; Run mu4e in the background to sync mail periodically
(mu4e t)
(setq shr-color-visible-luminance-min 80)
;; View images inline in message view buffer
(setq mu4e-view-show-images t)
(setq mu4e-view-image-max-width 800)
(when (fboundp 'imagemagick-register-types)
(imagemagick-register-types))
;; show full addresses in view message (instead of just names)
(setq mu4e-view-show-addresses t)
;; Do not insert signature in sent emails
(setq mu4e-compose-signature-auto-include nil)
;; every new email composition using current frame
(setq mu4e-compose-in-new-frame nil)
(setq mu4e-compose-format-flowed nil)
;; It is OK to use non-ascii characters
(setq mu4e-use-fancy-chars t)
(setq mu4e-attachment-dir attachments-directory)
;; This enabled the thread like viewing of email similar to gmail's UI.
(setq mu4e-headers-include-related t)
;; Do not display duplicate messages
(setq mu4e-headers-skip-duplicates t)
(setq mu4e-headers-date-format "%Y/%m/%d")
(setq mu4e-change-filenames-when-moving t)
(setq mu4e-display-update-status-in-modeline t)
(setq mu4e-hide-index-messages t)
(setq mu4e-date-format "%y/%m/%d")
;; Do not confirm on quit
(setq mu4e-confirm-quit nil)
;; use mu4e as MUA in emacs
(setq mail-user-agent 'mu4e-user-agent)
;; Kill message buffer after email is sent
(setq message-kill-buffer-on-exit t)
;; 回复邮件时,插入邮件引用信息
(setq message-citation-line-function 'message-insert-formatted-citation-line)
(setq message-citation-line-format "On %a, %b %d %Y, %f wrote:\n")
(setq gnus-unbuttonized-mime-types nil)
;; mu find 搜索任意单个中文字符。
(setenv "XAPIAN_CJK_NGRAM" "yes")
(add-to-list 'mu4e-view-actions '("browser" . mu4e-action-view-in-browser) t)
(add-hook 'mu4e-view-mode-hook
(lambda()
;; try to emulate some of the eww key-bindings
(local-set-key (kbd "<tab>") 'shr-next-link)
(local-set-key (kbd "<backtab>") 'shr-previous-link)))
;; 使用 proxychains4 socks5 代理周期同步邮件
(setq mu4e-get-mail-command "proxychains4 mbsync -a")
(setq mu4e-update-interval 3600)
;; 使用 gnus 发送邮件
(setq message-send-mail-function 'smtpmail-send-it)
(setq smtpmail-debug-info t)
(setq smtpmail-debug-verb t)
(setq mu4e-user-mailing-lists '("geekard@qq.com" "geekard@gmail.com"))
;; root maildir
(setq mu4e-maildir "~/.mail")
(setq mu4e-contexts
`( ,(make-mu4e-context
:name "gmail"
:enter-func (lambda () (mu4e-message "Switch to the gmail context"))
:match-func (lambda (msg)
(when msg
(or (mu4e-message-contact-field-matches msg '(:to :bcc :cc) "geekard@gmail.com")
(string-match-p "^/gmail" (mu4e-message-field msg :maildir)))))
:leave-func (lambda () (mu4e-clear-caches))
:vars '((user-mail-address . "geekard@gmail.com")
(user-full-name . "张俊(Jun Zhang)")
(smtpmail-default-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-user . "geekard@gmail.com")
(smtpmail-smtp-service . 587)
(smtpmail-stream-type . starttls)
(mu4e-compose-signature . (concat "---\n zhangjun \n"))
(mu4e-sent-folder . "/gmail/Sent") ;; folder for sent messages
(mu4e-drafts-folder . "/gmail/Drafts") ;; unfinished messages
(mu4e-trash-folder . "/gmail/Junk") ;; trashed messages
(mu4e-refile-folder . "/gmail/Archive"))) ;; ;; saved messages
,(make-mu4e-context
:name "qq"
:enter-func (lambda () (mu4e-message "Switch to the qq context"))
:match-func (lambda (msg)
(when msg
(or (mu4e-message-contact-field-matches msg '(:to :bcc :cc) "geekard@qq.com")
(string-match-p "^/qq" (mu4e-message-field msg :maildir)))))
:leave-func (lambda () (mu4e-clear-caches))
:vars '(
(user-mail-address . "geekard@qq.com")
(user-full-name . "张俊(Jun Zhang)")
(smtpmail-default-smtp-server . "smtp.qq.com")
(smtpmail-smtp-server . "smtp.qq.com")
(smtpmail-smtp-user . "geekard@qq.com")
(smtpmail-smtp-service . 465)
(smtpmail-stream-type . ssl)
(mu4e-compose-signature . (concat "---\n Zhang Jun \n"))
(mu4e-sent-folder . "/qq/Sent")
(mu4e-drafts-folder . "/qq/Drafts")
(mu4e-trash-folder . "/qq/Trash")
(mu4e-refile-folder . "/qq/Archive")
)))))
;; 为 message 添加 Tag
(with-eval-after-load 'mu4e
(add-to-list 'mu4e-marks
'(tag
:char "g"
:prompt "gtag"
:ask-target (lambda () (read-string "Add Tag: "))
:action (lambda (docid msg target)
(mu4e-action-retag-message msg (concat "+" target)))))
(mu4e~headers-defun-mark-for tag)
(define-key mu4e-headers-mode-map (kbd "g") 'mu4e-headers-mark-for-tag)
;; 在 Dired 中标记文件, 然后 C-c RET C-a 来发送附件
(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)
;; 发送前确认
(add-hook 'message-send-hook
(lambda ()
(unless (yes-or-no-p "Sure you want to send this?")
(signal 'quit nil))))
;; 先选择邮件, 然后按 r, 自动 refile 到对应目录
(setq mu4e-refile-folder
(lambda (msg)
(cond
;; messages to the mu mailing list go to the /mu folder
((mu4e-message-contact-field-matches msg :to "mu-discuss@googlegroups.com") "/mu")
;; messages sent directly to some spefic address me go to /private
((mu4e-message-contact-field-matches msg :to "me@example.com") "/private")
;; messages with football or soccer in the subject go to /football
((string-match "football\\|soccer" (mu4e-message-field msg :subject)) "/football")
;; messages sent by me go to the sent folder
((mu4e-message-sent-by-me msg (mu4e-personal-addresses)) mu4e-sent-folder)
;; everything else goes to /archive
;; important to have a catch-all at the end!
(t "/archive")))))
- mu4e 的使用详情参考在线 Info 手册 mu4e#Top。
mu4e 默认使用 gnus 发送 SMTP 邮件, 而 gnus 从 ~/.authinfo.gpg
读取 SMTP 服务器的帐号信息:
machine smtp.qq.com login geekard@qq.com password {QQ 授权码}
machine smtp.gmail.com login geekard@gmail.com password {Gmail 密码}
使用 mu4e-alert 和 notifier(通过 terminal-notifier 程序) 进行桌面通知:
(use-package mu4e-alert
:disabled
:after mu4e
:config
(mu4e-alert-set-default-style 'notifier)
;; (mu4e-alert-set-default-style 'growl)
(add-hook 'after-init-hook #'mu4e-alert-enable-notifications)
;; enable mode line display
(add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display)
(setq mu4e-alert-email-notification-types '(count)))
使用 mu4e-maildirs-extension 在 mu4e-main-view 展示 Maildirs 概览。
(use-package mu4e-maildirs-extension
:after mu4e
:config
(mu4e-maildirs-extension))
mu4e-views 使用 xwdigets 来显示 html 格式邮件:
(use-package mu4e-views
:after mu4e
:bind (:map mu4e-headers-mode-map
("v" . mu4e-views-mu4e-select-view-msg-method) ;; 切换展示类型
("M-n" . mu4e-views-cursor-msg-view-window-down) ;; from headers window scroll the email view
("M-p" . mu4e-views-cursor-msg-view-window-up) ;; from headers window scroll the email view
("f" . mu4e-views-toggle-auto-view-selected-message) ;; toggle opening messages automatically when moving in the headers view
("i" . mu4e-views-mu4e-view-as-nonblocked-html) ;; show currently selected email with all remote content
)
:config
(setq mu4e-views-completion-method 'default) ;; use ivy for completion
(setq mu4e-views-default-view-method "html") ;; make xwidgets default
(mu4e-views-mu4e-use-view-msg-method "html") ;; select the default
(setq mu4e-views-next-previous-message-behaviour 'stick-to-current-window) ;; when pressing n and p stay in the current window
(setq mu4e-views-auto-view-selected-message t)) ;; automatically open messages when moving in the headers view
- 测试 Emacs 是否支持 xwdigets: (xwidget-webkit-browse-url “https://www.gnu.org/”);
- 在 mu4e-header 中使用 v 来切换邮件显示方式;
org-mime 使用 org-mode 来编辑和发送 html 格式邮件:
(use-package org-mime
:after mu4e
:config
(setq org-mime-export-options '(:section-numbers nil :with-author nil :with-toc nil))
;; Prompt for confirmation if message has no HTML
(add-hook 'message-send-hook 'org-mime-confirm-when-no-multipart))
使用方法:
- M-x org-mime-htmlize
- M-x org-mime-edit-mail-in-org-mode
- M-x org-mime-revert-to-plain-text-mail
(use-package pdf-tools
:ensure-system-package
((pdfinfo . poppler)
(automake . automake)
(mutool . mupdf)
("/usr/local/opt/zlib" . zlib))
:init
;; 使用 scaling 确保中文字体不模糊
(setq pdf-view-use-scaling t)
(setq pdf-view-use-imagemagick nil)
(setq pdf-annot-activate-created-annotations t)
(setq pdf-view-resize-factor 1.1)
(setq-default pdf-view-display-size 'fit-page)
(setq pdf-annot-activate-created-annotations t)
:hook
((pdf-view-mode . pdf-view-themed-minor-mode)
(pdf-view-mode . pdf-view-auto-slice-minor-mode)
(pdf-view-mode . pdf-isearch-minor-mode))
:config
(define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward)
(add-hook 'pdf-view-mode-hook (lambda() (linum-mode -1)))
(setq pdf-info-epdfinfo-program "/usr/local/bin/epdfinfo")
(setenv "PKG_CONFIG_PATH" "/usr/local/opt/zlib/lib/pkgconfig:/usr/local/opt/pkgconfig:/usr/local/lib/pkgconfig")
(pdf-tools-install))
;; pdf 转为 png 时使用更高分辨率(默认 90)。
(setq doc-view-resolution 144)
(use-package org-noter)
- pdf-tools 默认是白底黑字,可以:
- 深色模式:
M-x pdf-view-midnight-minor-mode
- 主题模式:
M-x pdf-view-themed-minor-mode
- 深色模式:
- 搜索中文时,需要使用系统中文输入法和 isearch 模式, 或者使用
M-s o(occur)
;phi-search 与 pdf-tools 不兼容;
(use-package twittering-mode
:commands (twit)
:init
;; 解决报错 "epa--decode-coding-string not defined"
(defalias 'epa--decode-coding-string 'decode-coding-string)
(setq twittering-icon-mode t)
(setq twittering-use-icon-storage t)
;; 解决内置的 twitter 根证书失效的问题。
(setq twittering-allow-insecure-server-cert t)
(setq twittering-use-master-password t))
- 默认将 OAuth Token 加密保存到
~/.twittering-mode.gpg
, 第一次需要输入两次相同的加密密码。
;; 未选中窗口。
(setq-default cursor-in-non-selected-windows nil)
(setq highlight-nonselected-windows nil)
;; 高亮粘贴的内容。
(use-package volatile-highlights
:after(undo-tree)
:config
(volatile-highlights-mode t)
(vhl/define-extension 'undo-tree 'undo-tree-yank 'undo-tree-move)
(vhl/install-extension 'undo-tree))
不再使用 fira-code-mode:
;; all-the-icons 和 fire-code-mode 只能在 GUI 模式下使用。
(when (display-graphic-p)
(use-package all-the-icons :demand)
;; (use-package fira-code-mode
;; :custom
;; (fira-code-mode-disabled-ligatures '("[]" "#{" "#(" "#_" "#_(" "x"))
;; :hook prog-mode)
)
- 安装、更新 FiraCode Symbol 字体:
M-x fira-code-mode-install-fonts
不开启 variable-pitch-mode, 否则 doom-modeline 右侧容易溢出。
;; 参考: https://github.com/DogLooksGood/dogEmacs/blob/master/elisp/init-font.el
;; 缺省字体。
(setq +font-family "Fira Code Retina")
(setq +modeline-font-family "Fira Code Retina")
;; org-table 使用 fixed-pitch 字体, Sarasa Term SC 可以让对齐效果更好。
(setq +fixed-pitch-family "Sarasa Term SC")
(setq +variable-pitch-family "LXGW WenKai Screen")
(setq +font-unicode-family "LXGW WenKai Screen")
(setq +font-size-list '(10 11 12 13 14 15 16 17 18))
(setq +font-size 14)
;; 设置缺省字体。
(defun +load-base-font ()
;; 为缺省字体设置 size, 其它字体都是通过 :height 进行动态伸缩。
(let* ((font-spec (format "%s-%d" +font-family +font-size)))
(set-frame-parameter nil 'font font-spec)
(add-to-list 'default-frame-alist `(font . ,font-spec))))
;; 设置各特定 face 的字体。
(defun +load-face-font (&optional frame)
(let ((font-spec (format "%s" +font-family))
(line-font-spec (format "%s" +modeline-font-family))
(variable-pitch-font-spec (format "%s" +variable-pitch-family))
(fixed-pitch-font-spec (format "%s" +fixed-pitch-family)))
(set-face-attribute 'variable-pitch frame :font variable-pitch-font-spec :height 1.2)
(set-face-attribute 'fixed-pitch frame :font fixed-pitch-font-spec :height 1.0)
(set-face-attribute 'fixed-pitch-serif frame :font fixed-pitch-font-spec :height 1.0)
(set-face-attribute 'tab-bar frame :font font-spec :height 1.0)
(set-face-attribute 'mode-line frame :font line-font-spec :height 1.0)
(set-face-attribute 'mode-line-inactive frame :font line-font-spec :height 1.0)))
;; 设置中文字体。
(defun +load-ext-font ()
(when window-system
(let ((font (frame-parameter nil 'font))
(font-spec (font-spec :family +font-unicode-family)))
(dolist (charset '(kana han hangul cjk-misc bopomofo symbol))
(set-fontset-font font charset font-spec)))))
;; 设置 emobji 字体。
(defun +load-emoji-font ()
(when window-system
(setq use-default-font-for-symbols nil)
(set-fontset-font t '(#x1f000 . #x1faff) (font-spec :family "Apple Color Emoji"))
(set-fontset-font t 'symbol (font-spec :family "Symbola"))))
(defun +load-font ()
(+load-base-font)
(+load-face-font)
(+load-ext-font)
(+load-emoji-font))
(+load-font)
(add-hook 'after-make-frame-functions
( lambda (f)
(+load-face-font f)
(+load-ext-font)
(+load-emoji-font)))
;; 只为 org-mode 和 markdown-mode 开启 variable-pitch-mode 。
(add-hook 'org-mode-hook 'variable-pitch-mode)
(add-hook 'markdown-mode-hook 'variable-pitch-mode)
(defun +larger-font ()
(interactive)
(if-let ((size (--find (> it +font-size) +font-size-list)))
(progn (setq +font-size size)
(+load-font)
(message "Font size: %s" +font-size))
(message "Using largest font")))
(defun +smaller-font ()
(interactive)
(if-let ((size (--find (< it +font-size) (reverse +font-size-list))))
(progn (setq +font-size size)
(message "Font size: %s" +font-size)
(+load-font))
(message "Using smallest font")))
(global-set-key (kbd "M-+") #'+larger-font)
(global-set-key (kbd "M--") #'+smaller-font)
(defun +use-fixed-pitch ()
(interactive)
(setq buffer-face-mode-face `(:family ,+fixed-pitch-family))
(buffer-face-mode +1))
;; fire-code-mode 只能在 GUI 模式下使用。
(when (display-graphic-p)
(use-package fira-code-mode
:custom
(fira-code-mode-disabled-ligatures '("[]" "#{" "#(" "#_" "#_(" "x"))
:hook prog-mode))
;; 使用字体缓存,避免卡顿。
(setq inhibit-compacting-font-caches t)
(setq +font-family "Fira Code Retina")
(setq +modeline-font-family "Fira Code Retina")
;; 其它均使用 Sarasa Mono SC 字体。
(setq +fixed-pitch-family "Sarasa Mono SC")
(setq +variable-pitch-family "Sarasa Mono SC")
(setq +font-unicode-family "Sarasa Mono SC")
(setq +font-size 13)
(use-package fontaine
:straight (:host github :repo "protesilaos/fontaine")
:config
;; This is defined in Emacs C code: it belongs to font settings.
(setq x-underline-at-descent-line nil)
;; And this is for Emacs28.
(setq-default text-scale-remap-header-line t)
;; This is the default value. Just including it here for completeness.
(setq fontaine-latest-state-file (locate-user-emacs-file "fontaine-latest-state.eld"))
;; Iosevka Comfy is my highly customised build of Iosevka with monospaced and duospaced (quasi-proportional)
;; variants as well as support or no support for ligatures: <https://git.sr.ht/~protesilaos/iosevka-comfy>.
;;
;; Iosevka Comfy == monospaced, supports ligatures
;; Iosevka Comfy Fixed == monospaced, no ligatures
;; Iosevka Comfy Duo == quasi-proportional, supports ligatures
;; Iosevka Comfy Wide == like Iosevka Comfy, but wider
;; Iosevka Comfy Wide Fixed == like Iosevka Comfy Fixed, but wider
;; Iosevka Comfy Motion == monospaced, supports ligatures, fancier glyphs
;; Iosevka Comfy Motion Duo == as above, but quasi-proportional
(setq fontaine-presets
'((small
:default-family "Iosevka Comfy Fixed"
:default-height 80
:variable-pitch-family "Iosevka Comfy Wide Duo")
(regular
:default-height 100)
(large
:default-weight semilight
:default-height 140
:bold-weight extrabold)
(code-demo
:default-family "Iosevka Comfy Fixed"
:default-weight semilight
:default-height 170
:variable-pitch-family "Iosevka Comfy Duo"
:bold-weight extrabold)
(presentation
:inherit code-demo
:default-height 220)
(t
;; I keep all properties for didactic purposes, but most can be
;; omitted. See the fontaine manual for the technicalities:
;; <https://protesilaos.com/emacs/fontaine>.
:default-family "Iosevka Comfy"
:default-weight regular
:default-height 100
:fixed-pitch-family nil ; falls back to :default-family
:fixed-pitch-weight nil ; falls back to :default-weight
:fixed-pitch-height 1.0
:fixed-pitch-serif-family nil ; falls back to :default-family
:fixed-pitch-serif-weight nil ; falls back to :default-weight
:fixed-pitch-serif-height 1.0
:variable-pitch-family "Iosevka Comfy Motion Duo"
:variable-pitch-weight nil
:variable-pitch-height 1.0
:bold-family nil ; use whatever the underlying face has
:bold-weight bold
:italic-family nil
:italic-slant italic
:line-spacing nil)))
;; Set last preset or fall back to desired style from `fontaine-presets'.
(fontaine-set-preset (or (fontaine-restore-latest-preset) 'regular))
;; The other side of `fontaine-restore-latest-preset'.
(add-hook 'kill-emacs-hook #'fontaine-store-latest-preset)
;; Persist font configurations while switching themes (doing it with
;; my `modus-themes' and `ef-themes' via the hooks they provide).
;;(dolist (hook '(modus-themes-after-load-theme-hook ef-themes-post-load-hook))
;;(add-hook hook #'fontaine-apply-current-preset))
(define-key global-map (kbd "C-c f") #'fontaine-set-preset)
(define-key global-map (kbd "C-c F") #'fontaine-set-face-font)
;; 中文字体
(when window-system
(let ((font (frame-parameter nil 'font))
(font-spec (font-spec :family +font-unicode-family)))
(dolist (charset '(kana han hangul cjk-misc bopomofo symbol))
(set-fontset-font font charset font-spec))))
)
(use-package all-the-icons-ibuffer :init (all-the-icons-ibuffer-mode 1))
(use-package all-the-icons-completion
:config
(all-the-icons-completion-mode)
(add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup))
;; 选择 buffer: b, 选择 project: p, 选择文件:f 。
(use-package consult-projectile
:straight (consult-projectile :type git :host gitlab :repo "OlMon/consult-projectile" :branch "master")
:bind
("C-x p p" . consult-projectile))
(use-package consult-dir
:bind
(("C-x C-d" . consult-dir)
:map minibuffer-local-completion-map
("C-x C-d" . consult-dir)
("C-x C-j" . consult-dir-jump-file)))
(use-package kind-icon
:straight '(kind-icon :host github :repo "jdtsmith/kind-icon")
:after corfu
:demand
:custom
(kind-icon-default-face 'corfu-default)
:config
(add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
(use-package kind-icon
:after corfu
:demand
:custom
(kind-icon-default-face 'corfu-default)
:config
(add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
(use-package marginalia
:init
;; 显示绝对时间。
(setq marginalia-max-relative-age 0)
(marginalia-mode)
;;:config
;; 不给 file 加注释,防止 TRAMP 变慢。
;; (setq marginalia-annotator-registry
;; (assq-delete-all 'file marginalia-annotator-registry))
;; (setq marginalia-annotator-registry
;; (assq-delete-all 'project-file marginalia-annotator-registry))
)
Cape 为 Corfu 提供多种类型的 Completion At Point 扩展。
(use-package cape
:demand
:straight '(cape :host github :repo "minad/cape")
:init
(add-to-list 'completion-at-point-functions #'cape-file)
(add-to-list 'completion-at-point-functions #'cape-keyword)
;; Complete word from current buffers
(add-to-list 'completion-at-point-functions #'cape-dabbrev)
;; Complete Elisp symbol
(add-to-list 'completion-at-point-functions #'cape-symbol)
;; Complete abbreviation
;;(add-to-list 'completion-at-point-functions #'cape-abbrev)
;;(add-to-list 'completion-at-point-functions #'cape-ispell)
;; Complete word from dictionary file
;;(add-to-list 'completion-at-point-functions #'cape-dict)
;; Complete entire line from file
;;(add-to-list 'completion-at-point-functions #'cape-line)
:config
(setq cape-dabbrev-min-length 3)
;; 前缀长度达到 3 时才调用 CAPF,避免频繁调用自动补全。
(cape-wrap-prefix-length #'cape-dabbrev 3))
(use-package easy-kill-extras
:demand
:bind
(([remap kill-ring-save] . easy-kill) ;; M-w
([remap mark-sexp] . easy-mark-sexp) ;; C-M-SPC
([remap mark-word] . easy-mark-word) ;; M-@
;; 集成 zap-to-char.
([remap zap-to-char] . easy-mark-to-char)
([remap zap-up-to-char] . easy-mark-up-to-char))
:init
(setq kill-ring-max 200
;; 替换前先保存剪贴板内容。
save-interprogram-paste-before-kill t
easy-kill-alist '((?w word " ")
(?s sexp "\n")
(?l list "\n")
(?d defun "\n\n")
(?D defun-name " ")
(?e line "\n")
(?b buffer-file-name)
(?^ backward-line-edge "")
(?$ forward-line-edge "")
(?h buffer "")
(?< buffer-before-point "")
(?> buffer-after-point "")
(?f string-to-char-forward "")
(?F string-up-to-char-forward "")
(?t string-to-char-backward "")
(?T string-up-to-char-backward "")
(?W WORD " ") ;; 非空白字符序列。
(?\' squoted-string "")
(?\" dquoted-string "")
(?\` bquoted-string "")
(?q quoted-string "") ;; 任何字符串类型
(?Q quoted-string-universal "")
(?\) parentheses-pair-content "\n")
(?\( parentheses-pair "\n")
(?\] brackets-pair-content "\n")
(?\[ brackets-pair "\n")
(?} curlies-pair-content "\n")
(?{ curlies-pair "\n")
(?> angles-pair-content "\n")
(?< angles-pair "\n")))
:config
;; 加载 extra-things 后, 上面 WORD 开始的 alist 才生效。
(require 'extra-things))
M-w 是前缀,默认选择当前行,使用 easy-kill-alist 中的快捷键可以用其它选择方式,然后用下面的键来修改选择的内容:
- @: 将选择区域添加到前一次的 kill, 如 M-w d @;
- C-w: 剪切选择;
- +, - and 1..9: 扩大或缩小选择;
- 0: 将选择缩小到初始大小;
- SPC: 根据 easy-kill-alist 中循环操作;
- C-SPC: 将选择转为 active region;
- C-g: 终止;
- ?: 帮助;
;; org-mode table 中英文像素对齐。
(use-package valign
:config
(add-hook 'org-mode-hook #'valign-mode))
;; 快速窗口切换。选择 s-X 是由于 vterm 默认将 M-x 等快捷键拦截,所以不生效,但不拦截 s-X。
;; vterm 默认不拦截的快捷键参考 vterm-keymap-exceptions, 包含 M-o。
(global-set-key (kbd "s-o") 'other-window)
(global-set-key (kbd "s-C-o") #'prev-window)
(defun prev-window ()
(interactive)
(other-window -1))
;; 选择 buffer: b, 选择 project: p, 选择文件:f 。
(use-package consult-projectile
:straight (consult-projectile :type git :host gitlab :repo "OlMon/consult-projectile" :branch "master")
:after (projectile)
:bind
("C-c p p" . consult-projectile))
;; 如果当前打开的有 tramp buffer, 则 list buffer 时 ibuffer-project 会重新打开该 buffer 导致卡顿,故关闭。
(use-package ibuffer-project
:disabled
:hook
((ibuffer . (lambda ()
(setq ibuffer-filter-groups (ibuffer-project-generate-filter-groups))
(unless (eq ibuffer-sorting-mode 'project-file-relative)
(ibuffer-do-sort-by-project-file-relative)))))
:config
;; 显示的文件名是相对于 project root 的相对路径。
(setq ibuffer-formats
'((mark modified read-only " "
(name 18 18 :left :elide)
" "
(size 9 -1 :right)
" "
(mode 16 16 :left :elide)
" "
project-file-relative))))
;; 基于 project 来对 buffer 进行分组。
(use-package ibuffer-projectile
:after (ibuffer projectile)
:hook
((ibuffer . (lambda ()
(ibuffer-projectile-set-filter-groups)
(unless (eq ibuffer-sorting-mode 'alphabetic)
(ibuffer-do-sort-by-alphabetic)))))
:config
;; 显示的文件名是相对于 project root 的相对路径。
(setq ibuffer-formats
'((mark modified read-only " "
(name 18 18 :left :elide)
" "
(size 9 -1 :right)
" "
(mode 16 16 :left :elide)
" "
project-relative-file))))
(use-package projectile
:demand
:config
(projectile-global-mode)
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
(projectile-mode +1)
;; selectrum/vertico 使用 'default 。
(setq projectile-completion-system 'default)
(add-to-list 'projectile-ignored-projects (concat (getenv "HOME") "/" "/root" "/tmp" "/etc" "/home"))
(dolist (dir '("^\\.cache$"
"^elpa$"
"^bak$"
"^__pycache__$"
"^vendor$"
"^node_modules$"
"^logs$"
"^target$"
"^build$"
"^\\.devcontainer$"
"^\\.settings$"
"^\\.gradle$"))
(add-to-list 'projectile-globally-ignored-directories dir))
(dolist (item '("GPATH"
"GRTAGS"
"GTAGS"
"TAGS"
".classpath"
".project"
".DS_Store"))
(add-to-list 'projectile-globally-ignored-files item))
(dolist (list '("\\.elc\\'"
"\\.o\\'"
"\\.class\\'"
"\\.out\\'"
"\\.pdf\\'"
"\\.pyc\\'"
"\\.rel\\'"
"\\.rip\\'"
"\\.swp\\'"
"\\.iml\\'"
"\\.bak\\'"
"\\.log\\'"
"~\\'"))
(add-to-list 'projectile-globally-ignored-file-suffixes list))
;; Disable projectile on remote buffers
;; https://www.murilopereira.com/a-rabbit-hole-full-of-lisp/
;; https://github.com/syl20bnr/spacemacs/issues/11381#issuecomment-481239700
(defadvice projectile-project-root (around ignore-remote first activate)
(unless (file-remote-p default-directory 'no-identification) ad-do-it))
;; 开启 cache 解决 TRAMP 慢的问题,https://github.com/bbatsov/projectile/pull/1129
(setq projectile-enable-caching t)
(setq projectile-file-exists-remote-cache-expire (* 10 60))
(setq projectile-mode-line-prefix "")
(setq projectile-dynamic-mode-line nil)
(setq projectile-sort-order 'recentf)
(setq projectile-require-project-root 'prompt)
;; 添加 :project-file "go.mod", 这样能正确探测 go module (非 git 仓库)根目录。
(projectile-register-project-type 'go projectile-go-project-test-function
:project-file "go.mod"
:compile "go build"
:test "go test ./..."
:test-suffix "_test"))
(defun my/projectile-discover ()
(interactive)
(dolist (search-path '("~/go/src/github.com/*" "~/go/src/github.com/*/*" "~/go/src/k8s.io/*" "~/go/src/gitlab.*/*/*"))
(dolist (file (file-expand-wildcards search-path))
(when (file-directory-p (concat file "/.git"))
(message "-> %s" file)
(projectile-add-known-project file)
(message "added project %s" file)))))
跳转到上次修改位置:
(use-package goto-chg
:config
(global-set-key (kbd "C->") 'goto-last-change)
(global-set-key (kbd "C-<") 'goto-last-change-reverse))
;; 在 dired buffer 中使用 treemacs icons。
(require 'treemacs-icons-dired)
(treemacs-icons-dired-mode t)
;; 单击打开或折叠目录.
(define-key treemacs-mode-map [mouse-1] #'treemacs-single-click-expand-action)
;; 使用 treemacs 自带的 all-the-icons 主题。
;; 注: 当使用 doom-themes 主题时, 它会自动设置 treemacs theme, 就不需要再调用这个函数了.
(require 'treemacs-all-the-icons)
(treemacs-load-theme "all-the-icons")
(require 'treemacs-projectile)
;; 在窗口左上角显示位置字符。
(setq aw-char-position 'top-left)
;; 调大窗口选择字符。
(custom-set-faces
'(aw-leading-char-face
((t (:inherit ace-jump-face-foreground :foreground "red" :height 1.5)))))
;; 设置命令显示风格。
(setq vertico-multiform-commands
;; 参数是 vertico-<name>-mode 中的 <name>, 可以多个联合使用。
;; 在单独 buffer 中显示结果 consult-imenu 结果。
'((consult-imenu buffer)
(consult-imenu-multi buffer)
(consult-line buffer)
(consult-mark buffer)
(consult-global-mark buffer)
(consult-find buffer)))
(use-package centaur-tabs
:disabled
:hook (after-init . centaur-tabs-mode)
:init
;; 有时 icon 显示不对而且大小不一致,故关闭。
(setq centaur-tabs-set-icons nil)
(setq centaur-tabs-height 25)
(setq centaur-tabs-gray-out-icons 'buffer)
(setq centaur-tabs-set-modified-marker t)
(setq centaur-tabs-cycle-scope 'tabs)
(setq centaur-tabs-enable-ido-completion nil)
(setq centaur-tabs-set-bar 'under)
(setq x-underline-at-descent-line t)
(setq centaur-tabs-show-navigation-buttons t)
(setq centaur-tabs-enable-key-bindings t)
:bind
("C-<prior>" . centaur-tabs-backward)
("C-<next>" . centaur-tabs-forward)
("C-c t s" . centaur-tabs-counsel-switch-group)
("C-c t p" . centaur-tabs-group-by-projectile-project)
("C-c t g" . centaur-tabs-group-buffer-groups)
:config
;;(centaur-tabs-mode t)
(centaur-tabs-headline-match)
(centaur-tabs-enable-buffer-reordering)
(centaur-tabs-group-by-projectile-project)
(defun centaur-tabs-buffer-groups ()
"`centaur-tabs-buffer-groups' control buffers' group rules."
(list
(cond
((or (string-equal "*" (substring (buffer-name) 0 1))
(memq major-mode '(magit-process-mode
magit-status-mode
magit-diff-mode
magit-log-mode
magit-file-mode
magit-blob-mode
magit-blame-mode
)))
"Emacs")
((or (derived-mode-p 'prog-mode)
(memq major-mode '(go-mode
python-mode)))
"Coding")
((derived-mode-p 'dired-mode)
"Dired")
((memq major-mode '(helpful-mode
help-mode))
"Help")
((memq major-mode '(org-mode
org-agenda-clockreport-mode
org-src-mode
org-agenda-mode
org-beamer-mode
org-indent-mode
org-bullets-mode
org-cdlatex-mode
org-agenda-log-mode
diary-mode))
"OrgMode")
(t
(centaur-tabs-get-group-name (current-buffer))))))
(defun centaur-tabs-hide-tab (x)
(let ((name (format "%s" x)))
(or
(window-dedicated-p (selected-window))
;; 不显示以 * 开头的 buffer 。
(string-prefix-p "*" name)
(and (string-prefix-p "magit" name)
(not (file-name-extension name)))))))
(defun my/org-faces ()
;; 行之间添加 2 像素的间距。
(setq-default line-spacing 2)
;; (dolist (face '((org-level-1 . 1.2)
;; (org-level-2 . 1.1)
;; (org-level-3 . 1.05)
;; (org-level-4 . 1.0)
;; (org-level-5 . 1.1)
;; (org-level-6 . 1.1)
;; (org-level-7 . 1.1)
;; (org-level-8 . 1.1)))
;; (set-face-attribute (car face) nil :height (cdr face)))
;; 美化 BEGIN_SRC 整行。
(setq org-fontify-whole-block-delimiter-line t)
;; 如果配置参数 :inherit 'fixed-pitch, 则需要明确设置 fixed-pitch 字体,否则选择的缺省字体可能导致显示问题。
;; 不建议配置 org-table 的字体和 height, 否则会导致中英文对不齐。
(custom-theme-set-faces
'user
'(org-block ((t (:height 0.9))))
'(org-code ((t (:height 0.9))))
;; 调小高度 , 并设置下划线。
'(org-block-begin-line ((t (:height 0.8 :underline "#A7A6AA"))))
'(org-block-end-line ((t (:height 0.8 :underline "#A7A6AA"))))
'(org-meta-line ((t (:height 0.7))))
'(org-document-info-keyword ((t (:height 0.6))))
'(org-document-info ((t (:height 0.8))))
'(org-document-title ((t (:foreground "#ffb86c" :weight bold :height 1.5))))
'(org-link ((t (:foreground "royal blue" :underline t))))
'(org-property-value ((t (:height 0.8))) t)
'(org-drawer ((t (:height 0.8))) t)
'(org-special-keyword ((t (:height 0.8 :inherit 'fixed-pitch))))
;; table 使用中英文严格等宽的 Sarasa Mono SC 字体, 避免中英文不对齐。
;;'(org-table ((t (:font "Sarasa Mono SC" :height 0.9))))
'(org-verbatim ((t (:height 0.9))))
'(org-tag ((t (:weight bold :height 0.8))))
'(org-todo ((t (:inherit 'fixed-pitch))))
'(org-done ((t (:inherit 'fixed-pitch))))
'(org-ellipsis ((t (:inherit 'fixed-pitch))))
'(org-property-value ((t (:inherit 'fixed-pitch))))))
;;(add-hook 'org-mode-hook 'my/org-faces)
(add-hook 'org-mode-hook 'prettify-symbols-mode)
(setq-default prettify-symbols-alist '(("#+BEGIN_SRC" . "»") ("#+END_SRC" . "«") ("#+begin_src" . "»") ("#+end_src" . "«")))
(setq prettify-symbols-unprettify-at-point 'right-edge)
(use-package org-superstar
:after (org)
:disabled
:hook
(org-mode . org-superstar-mode)
:custom
(org-superstar-remove-leading-stars t)
(org-superstar-headline-bullets-list '("◉" "○" "✸" "✿" "▷" "🞛"))
;;(org-superstar-headline-bullets-list '("☰" "☱" "☲" "☳" "☴" "☵" "☶" "☷"))
;; (org-superstar-item-bullet-alist '((43 . "⬧") (45 . "⬨")))
;; :custom-face
;; (org-superstar-item ((t (:inherit 'fixed-pitch))))
;; (org-superstar-header-bullet ((t (:height 200 :inherit 'fixed-pitch))))
)
(use-package org-fancy-priorities
:ensure t
:disabled
:hook
(org-mode . org-fancy-priorities-mode)
:config
;; org 默认最低优先级是 C, 这里加一级。
(setq org-priority-lowest ?D)
(setq org-fancy-priorities-list '("[⚡A]" "[⬆B]" "[⬇C]" "[☕D]")))
;; 编辑时显示隐藏的标记。
(use-package org-appear
:config
(add-hook 'org-mode-hook 'org-appear-mode)
;; 删除 * 和 / 类型的标记。
(setq org-appear-elements '(underline strike-through verbatim code)))
(use-package org-modern
:after (org)
:demand
:straight (:host github :repo "minad/org-modern")
:config
(with-eval-after-load 'org (global-org-modern-mode))
)
自动 Capture 浏览器发来的网址或选中的内容:
(require 'org-protocol)
(require 'org-capture)
(setq org-capture-templates
'(("c" "Capture" entry (file+headline "~/docs/orgs/capture.org" "Capture")
"* %^{Title}\nDate: %U\nSource: %:annotation\n\n%:initial" :empty-lines 1)
("t" "Todo" entry (file+headline "~/docs/orgs/gtd.org" "Tasks")
"* TODO %?\n %U %a\n %i" :empty-lines 1)))
(setq org-agenda-time-grid
(quote ((daily today require-timed)
(300 600 900 1200 1500 1800 2100 2400)
"......"
"-----------------------------------------------------"
)))
;; org-agenda 展示的文件。
(setq org-agenda-files
'("~/docs/orgs/gtd.org"
"~/docs/orgs/capture.org"))
(setq org-agenda-start-day "-7d")
(setq org-agenda-span 21)
(setq org-agenda-include-diary t)
;; use org-journal
;;(setq diary-file "~/docs/orgs/diary")
;;(setq diary-mail-addr "geekard@qq.com")
;; 获取经纬度:https://www.latlong.net/
(setq calendar-latitude +39.904202)
(setq calendar-longitude +116.407394)
(setq calendar-location-name "北京")
(setq calendar-remove-frame-by-deleting t)
;; 每周第一天是周一。
(setq calendar-week-start-day 1)
;; 标记有记录的日期。
(setq mark-diary-entries-in-calendar t)
;; 标记节假日。
(setq mark-holidays-in-calendar nil)
;; 不显示节日列表。
(setq view-calendar-holidays-initially nil)
(setq org-agenda-include-diary t)
;; 除去基督徒、希伯来和伊斯兰教的节日。
(setq christian-holidays nil
hebrew-holidays nil
islamic-holidays nil
solar-holidays nil
bahai-holidays nil)
(setq mark-diary-entries-in-calendar t
appt-issue-message nil
mark-holidays-in-calendar t
view-calendar-holidays-initially nil)
(setq diary-date-forms '((year "/" month "/" day "[^/0-9]"))
calendar-date-display-form '(year "/" month "/" day)
calendar-time-display-form '(24-hours ":" minutes (if time-zone " (") time-zone (if time-zone ")")))
(add-hook 'today-visible-calendar-hook 'calendar-mark-today)
(autoload 'chinese-year "cal-china" "Chinese year data" t)
(setq calendar-load-hook '(lambda ()
(set-face-foreground 'diary-face "skyblue")
(set-face-background 'holiday-face "slate blue")
(set-face-foreground 'holiday-face "white")))
(use-package org-super-agenda)
使用 outline 路径来指定要 refile 的文件和位置, 如 emacs.org/packages/org-mode
:
packages/org-mode
是要 refile 的内容的 paret nodes, 如果不存在会提示创建。
;; refile 的位置是 agenda 文件的前三层 headline 。
(setq org-refile-targets '((org-agenda-files :maxlevel . 3)))
;; 使用文件路径的形式显示 filename 和 headline, 方便在文件的 top-head 添加内容。
(setq org-refile-use-outline-path 'file)
;; 必须设置为 nil 才能显示 headline, 否则只显示文件名 。
(setq org-outline-path-complete-in-steps nil)
;; 支持为 subtree 在 refile target 文件指定一个新的父节点 。
(setq org-refile-allow-creating-parent-nodes 'confirm)
倒计时结束通知:
(use-package emacs
:straight (:type built-in)
:ensure-system-package terminal-notifier)
(defvar terminal-notifier-command (executable-find "terminal-notifier") "The path to terminal-notifier.")
(defun terminal-notifier-notify (title message)
(start-process "terminal-notifier"
"terminal-notifier"
terminal-notifier-command
"-title" title
"-sound" "default"
"-message" message
"-activate" "org.gnu.Emacs"))
(defun timed-notification (time msg)
(interactive "sNotification when (e.g: 2 minutes, 60 seconds, 3 days): \nsMessage: ")
(run-at-time time nil (lambda (msg) (terminal-notifier-notify "Emacs" msg)) msg))
;;(terminal-notifier-notify "Emacs notification" "Something amusing happened")
(setq org-show-notification-handler (lambda (msg) (timed-notification nil msg)))
(use-package elfeed
:demand
:config
(setq elfeed-db-directory (expand-file-name "elfeed" user-emacs-directory))
(setq elfeed-show-entry-switch 'display-buffer)
(setq elfeed-curl-timeout 30)
(setf url-queue-timeout 40)
(push "-k" elfeed-curl-extra-arguments)
(setq elfeed-search-filter "@1-months-ago +unread")
;; 在同一个 buffer 中显示条目。
(setq elfeed-show-unique-buffers nil)
(setq elfeed-search-title-max-width 150)
(setq elfeed-search-date-format '("%Y-%m-%d %H:%M" 20 :left))
(setq elfeed-log-level 'warn)
;; 支持收藏 feed, 参考:http://pragmaticemacs.com/emacs/star-and-unstar-articles-in-elfeed/
(defalias 'elfeed-toggle-star (elfeed-expose #'elfeed-search-toggle-all 'star))
(eval-after-load 'elfeed-search '(define-key elfeed-search-mode-map (kbd "m") 'elfeed-toggle-star))
(defface elfeed-search-star-title-face '((t :foreground "#f77")) "Marks a starred Elfeed entry.")
(push '(star elfeed-search-star-title-face) elfeed-search-face-alist))
(use-package elfeed-org
:custom ((rmh-elfeed-org-files (list "~/.emacs.d/elfeed.org")))
:hook
((elfeed-dashboard-mode . elfeed-org)
(elfeed-show-mode . elfeed-org))
:config
(progn
(defun my/reload-org-feeds ()
(interactive)
(rmh-elfeed-org-process rmh-elfeed-org-files rmh-elfeed-org-tree-id))
(advice-add 'elfeed-dashboard-update :before #'my/reload-org-feeds)))
(use-package elfeed-dashboard
:config
(global-set-key (kbd "C-c f") 'elfeed-dashboard)
(setq elfeed-dashboard-file "~/.emacs.d/elfeed-dashboard.org")
(advice-add 'elfeed-search-quit-window :after #'elfeed-dashboard-update-links))
(use-package elfeed-score
:config
(progn
(elfeed-score-enable)
(define-key elfeed-search-mode-map "=" elfeed-score-map)))
(use-package elfeed-goodies
:config
(setq elfeed-goodies/entry-pane-position 'bottom)
(setq elfeed-goodies/feed-source-column-width 30)
(setq elfeed-goodies/tag-column-width 30)
(setq elfeed-goodies/powerline-default-separator 'arrow)
(elfeed-goodies/setup))
;; elfeed-goodies 显示日期栏
;;https://github.com/algernon/elfeed-goodies/issues/15#issuecomment-243358901
(defun elfeed-goodies/search-header-draw ()
"Returns the string to be used as the Elfeed header."
(if (zerop (elfeed-db-last-update))
(elfeed-search--intro-header)
(let* ((separator-left (intern (format "powerline-%s-%s"
elfeed-goodies/powerline-default-separator
(car powerline-default-separator-dir))))
(separator-right (intern (format "powerline-%s-%s"
elfeed-goodies/powerline-default-separator
(cdr powerline-default-separator-dir))))
(db-time (seconds-to-time (elfeed-db-last-update)))
(stats (-elfeed/feed-stats))
(search-filter (cond
(elfeed-search-filter-active
"")
(elfeed-search-filter
elfeed-search-filter)
(""))))
(if (>= (window-width) (* (frame-width) elfeed-goodies/wide-threshold))
(search-header/draw-wide separator-left separator-right search-filter stats db-time)
(search-header/draw-tight separator-left separator-right search-filter stats db-time)))))
(defun elfeed-goodies/entry-line-draw (entry)
"Print ENTRY to the buffer."
(let* ((title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
(date (elfeed-search-format-date (elfeed-entry-date entry)))
(title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
(feed (elfeed-entry-feed entry))
(feed-title
(when feed
(or (elfeed-meta feed :title) (elfeed-feed-title feed))))
(tags (mapcar #'symbol-name (elfeed-entry-tags entry)))
(tags-str (concat "[" (mapconcat 'identity tags ",") "]"))
(title-width (- (window-width) elfeed-goodies/feed-source-column-width
elfeed-goodies/tag-column-width 4))
(title-column (elfeed-format-column
title (elfeed-clamp
elfeed-search-title-min-width
title-width
title-width)
:left))
(tag-column (elfeed-format-column
tags-str (elfeed-clamp (length tags-str)
elfeed-goodies/tag-column-width
elfeed-goodies/tag-column-width)
:left))
(feed-column (elfeed-format-column
feed-title (elfeed-clamp elfeed-goodies/feed-source-column-width
elfeed-goodies/feed-source-column-width
elfeed-goodies/feed-source-column-width)
:left)))
(if (>= (window-width) (* (frame-width) elfeed-goodies/wide-threshold))
(progn
(insert (propertize date 'face 'elfeed-search-date-face) " ")
(insert (propertize feed-column 'face 'elfeed-search-feed-face) " ")
(insert (propertize tag-column 'face 'elfeed-search-tag-face) " ")
(insert (propertize title 'face title-faces 'kbd-help title)))
(insert (propertize title 'face title-faces 'kbd-help title)))))
elfeed-score 规则文件(语法参考):
;;; Elfeed score file -*- lisp -*-
(
;; ("title"
;; (:text "opsnull" :value 250 :type S))
;; ("content"
;; (:text "type erasure" :value 500 :type s))
("title-or-content"
;; (:text "emacs" :title-value 150 :content-value 100 :type s)
(:text "opsnull" :title-value 150 :content-value 100 :type w))
("feed"
(:text "Irreal" :value 250 :type S :attr t)
(:text "emacs-news – sacha chua" :value 350 :type S :attr t :comment "Essential!"))
;; ("authors"
;; (:text "opsnull" :value 500 :type s))
;; ("tag"
;; (:tags (t . reddit-question)
;; :value 750
;; :comment "Add 750 points to any entry with a tag of reddit-question"))
(mark -2500))
;; 显示光标位置。
(use-package beacon
:config
;; 翻页时不高亮位置。
(setq beacon-blink-when-window-scrolls nil)
(setq beacon-blink-duration 0.3)
(beacon-mode 1))
flycheck 是现代的在线语法检查工具, 它使用系统工具对 buffer 进行检查,可以取代 emacs 内置的 flymake。
- C-c ! v (flycheck-verify-setup)
- 查看当前 buffer 使用的 checker。
- C-c ! l (flycheck-list-errors)
- 列出当前 workspace 所有的 error。
- M-g f 或 C-c !!: consult-flycheck。
(use-package flycheck
:disabled
:demand
:config
;; 高亮出现错误的列位置。
(setq flycheck-highlighting-mode (quote columns))
;; 从列表中取掉 idle-change, 避免不必要的错误提示。
;;(setq flycheck-check-syntax-automatically '(save mode-enabled))
;; 只在 save buffer 时才检查。
(setq flycheck-check-syntax-automatically '(save))
(define-key flycheck-mode-map (kbd "M-g n") #'flycheck-next-error)
(define-key flycheck-mode-map (kbd "M-g p") #'flycheck-previous-error)
:hook
(prog-mode . flycheck-mode))
;; 在线显示 flycheck 错误。
(use-package flycheck-pos-tip
:after (flycheck)
:config
(flycheck-pos-tip-mode))
;; flycheck 实时预览。
(use-package consult-flycheck
:after (consult flycheck)
:bind
(:map flycheck-command-map ("!" . consult-flycheck)))
(setenv "LSP_USE_PLISTS" "true")
(use-package lsp-mode
:disabled
:custom
;; debug 时才开启 log, 否则影响性能。
(lsp-log-io nil)
;; 日志记录行数。
(lsp-log-max 1000)
(lsp-keymap-prefix "C-c l")
(lsp-diagnostics-provider :flycheck)
(lsp-diagnostics-flycheck-default-level 'warning)
(lsp-completion-provider :none) ;; corfu.el: :none, company: :capf
(lsp-enable-symbol-highlighting nil)
;; 不显示面包屑。
(lsp-headerline-breadcrumb-enable nil)
(lsp-headerline-breadcrumb-segments '(path-up-to-project file symbols))
;; 启用 snippet 后才支持函数或方法的 placeholder 提示。
(lsp-enable-snippet nil)
;; 后续使用 lsp-ui-doc 替代 eldoc, 前者还支持 mouse 和 cursor hover.
(lsp-eldoc-enable-hover nil)
(lsp-eldoc-render-all t)
;; 刷新高亮、lenses 和 links 的间隔。
(lsp-idle-delay 0.2)
;; 退出最后一个 lsp buffer 时自动 kill lsp-server.
(lsp-keep-workspace-alive nil)
(lsp-file-watch-threshold 2000)
(lsp-enable-file-watchers nil)
;; 关闭 folding。
(lsp-enable-folding nil)
;; lsp 显示的 links 不准确且导致 treemacs 目录显示异常,故关闭。
(lsp-enable-links nil)
(lsp-enable-indentation nil)
;; flycheck 会在 modeline 展示检查结果, 故不需 lsp 再展示。
(lsp-modeline-diagnostics-enable nil)
;; 不在 modeline 上显示 code-actions 信息。
(lsp-modeline-code-actions-enable nil)
(lsp-modeline-workspace-status-enable nil)
(lsp-restart 'auto-restart)
;; 使用 projectile/project 来自动探测项目根目录。
(lsp-auto-guess-root t)
;; 不对 imenu 结果进行排序.
(lsp-imenu-sort-methods '(position))
:init
;; 设置 lsp 使用 corfu 来进行补全。
(defun my/lsp-mode-setup-completion ()
(setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
'(orderless)))
:hook
((java-mode . lsp)
(python-mode . lsp)
(go-mode . lsp)
;;(yaml-mode . lsp)
(js-mode . lsp)
(web-mode . lsp)
(tide-mode . lsp)
(typescript-mode . lsp)
(dockerfile-mode . lsp)
(lsp-completion-mode . my/lsp-mode-setup-completion)
(lsp-mode . lsp-enable-which-key-integration))
:config
(dolist (dir '("[/\\\\][^/\\\\]*\\.\\(json\\|html\\|pyc\\|class\\|log\\|jade\\|md\\)\\'"
"[/\\\\]resources/META-INF\\'"
"[/\\\\]vendor\\'"
"[/\\\\]node_modules\\'"
"[/\\\\]\\.settings\\'"
"[/\\\\]\\.project\\'"
"[/\\\\]\\.travis\\'"
"[/\\\\]bazel-*"
"[/\\\\]\\.cache"
"[/\\\\]\\.clwb$"))
(push dir lsp-file-watch-ignored-directories))
:bind
(:map lsp-mode-map
("C-c f" . lsp-format-region)
("C-c e" . lsp-describe-thing-at-point)
("C-c a" . lsp-execute-code-action)
("C-c r" . lsp-rename)
([remap xref-find-definitions] . lsp-find-definition)
([remap xref-find-references] . lsp-find-references)))
(use-package which-key
:config
(setq which-key-idle-delay 0.5)
(which-key-mode))
consult-lsp 提供两个非常有用的命令:consult-lsp-symbols 和 consult-lsp-diagnostics:
- consult-lsp-symbols: C-M-.
(use-package consult-lsp
:demand
:after (lsp-mode consult)
:config
(define-key lsp-mode-map [remap xref-find-apropos] #'consult-lsp-symbols))
lsp-ui 显示函数签名和帮助文档:
- lsp-mode 和 lsp-ui 的特性可以参考这个页面来进行选择性的打开和关闭;
(use-package lsp-ui
:after (lsp-mode flycheck)
:demand
:custom
;; 显示目录。
(lsp-ui-peek-show-directory t)
;; 文件列表宽度。
(lsp-ui-peek-list-width 80)
;; 光标移动到关键字时自动显示文档。
(lsp-ui-doc-show-with-cursor nil)
(lsp-ui-doc-show-with-mouse nil)
(lsp-ui-doc-delay 0.1)
;;(lsp-ui-doc-position 'at-point)
;; 启用 flycheck 集成。
(lsp-ui-flycheck-enable t)
(lsp-ui-sideline-enable nil)
(lsp-ui-peek-fontify 'never) ;; always, on-demand
;; 不使用 childframe, 否则 frame 带 awesome-tab 的 header.
(lsp-ui-doc-use-childframe nil)
:config
(define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions)
(define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references))
lsp-treemacs:
;; lsp-treemacs 显示 lsp workspace 文件夹和 treemacs projects 。
(use-package lsp-treemacs
:after (lsp-mode treemacs)
:config
(setq lsp-treemacs-error-list-current-project-only t)
(lsp-treemacs-sync-mode 1))
lsp-pyright:
(use-package lsp-pyright
:after (python lsp-mode)
:ensure-system-package
(pyright . "npm update -g pyright")
:init
(defvar pyright-directory "~/.emacs.d/.cache/lsp/npm/pyright/lib")
(if (not (file-exists-p pyright-directory))
(make-directory pyright-directory t))
(when (executable-find "python3")
(setq lsp-pyright-python-executable-cmd "python3"))
:hook
(python-mode . (lambda () (require 'lsp-pyright) )))
(use-package dockerfile-mode
:ensure-system-package
(docker-langserver . "npm install -g dockerfile-language-server-nodejs")
:config
(add-to-list 'auto-mode-alist '("Dockerfile\\'" . dockerfile-mode)))
(use-package awesome-tab
:config
(setq awesome-tab-label-fixed-length 18)
(setq awesome-tab-height 130)
;; 不显示 tab 序号。
(setq awesome-tab-show-tab-index nil)
;; HideRules, 需要先定义才生效。
(defun awesome-tab-hide-tab (x)
(let ((name (format "%s" x)))
(or
(string-prefix-p "*epc" name)
(string-prefix-p "*helm" name)
(string-prefix-p "*Compile-Log*" name)
(string-prefix-p "*lsp" name)
(string-prefix-p "*gopls" name)
(string-prefix-p "*dashboard" name)
(string-prefix-p "*Warnings" name)
(string-prefix-p "*Messages" name)
(string-prefix-p "*scratch" name)
(string-prefix-p "*Help" name)
(and (string-prefix-p "magit" name)
(not (file-name-extension name))))))
(awesome-tab-mode t)
(global-set-key (kbd "s-1") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-2") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-3") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-4") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-5") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-6") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-7") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-8") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-9") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-0") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "C-c j") 'awesome-tab-ace-jump))
(use-package color-rg
:straight (:host github :repo "manateelazycat/color-rg"))
doom-themes:
;;:config
;; 为 treemacs 关闭 variable-pitch 模式,否则显示的较丑!
;; 必须在执行 doom-themes-treemacs-config 前设置该变量为 nil, 否则不生效。
(setq doom-themes-treemacs-enable-variable-pitch nil)
(doom-themes-treemacs-config)
(doom-themes-treemacs-theme "doom-colors")
版本 1:
(use-package treemacs
:disabled
:init
(with-eval-after-load 'winum (define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
:config
(progn
(setq
treemacs-collapse-dirs 3
treemacs-deferred-git-apply-delay 0.1
treemacs-display-in-side-window t
treemacs-eldoc-display t
treemacs-file-event-delay 300
treemacs-file-follow-delay 0.01
treemacs-follow-after-init t
treemacs-git-command-pipe ""
treemacs-goto-tag-strategy 'refetch-index
treemacs-indentation 1
treemacs-indentation-string " "
treemacs-is-never-other-window t
treemacs-max-git-entries 500
treemacs-missing-project-action 'ask
treemacs-no-png-images nil
treemacs-no-delete-other-windows t
treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory)
treemacs-position 'left
treemacs-recenter-distance 0.01
treemacs-recenter-after-file-follow t
treemacs-recenter-after-tag-follow t
treemacs-recenter-after-project-jump 'always
treemacs-recenter-after-project-expand 'on-distance
treemacs-shownn-cursor t
treemacs-show-hidden-files t
treemacs-silent-filewatch nil
treemacs-silent-refresh nil
treemacs-sorting 'alphabetic-asc
treemacs-select-when-already-in-treemacs 'stay
treemacs-space-between-root-nodes nil
treemacs-tag-follow-cleanup t
treemacs-tag-follow-delay 1
treemacs-width 35
treemacs-width-increment 5
treemacs-width-is-initially-locked nil
treemacs-project-follow-cleanup t
imenu-auto-rescan t)
(treemacs-resize-icons 11)
(treemacs-follow-mode t)
;;(treemacs-tag-follow-mode t)
;;(treemacs-project-follow-mode t)
(treemacs-filewatch-mode t)
(treemacs-fringe-indicator-mode 'always)
(treemacs-indent-guide-mode t)
(treemacs-git-mode 'deferred)
(treemacs-hide-gitignored-files-mode nil))
;; 为远程 buffer 关闭 treemacs, 避免建立新连接耗时。
(add-hook 'buffer-list-update-hook
(lambda ()
(when (file-remote-p default-directory)
(require 'treemacs)
(if (string-match "visible" (symbol-name (treemacs-current-visibility)))
(delete-window (treemacs-get-local-window))))))
;; 启动 ediff 前关闭 treemacs frame, 否则 Control-Panel 显示异常。
(add-hook 'ediff-before-setup-hook
(lambda ()
(require 'treemacs)
(if (string-match "visible" (symbol-name (treemacs-current-visibility)))
(delete-window (treemacs-get-local-window)) ) ))
:bind
(:map global-map
("M-0" . treemacs-select-window)
("C-x t 1" . treemacs-delete-other-windows)
("C-x t t" . treemacs)
("C-x t B" . treemacs-bookmark)
("C-x t C-t" . treemacs-find-file)
("C-x t M-t" . treemacs-find-tag)))
(use-package treemacs-magit
:after (treemacs magit)
:ensure t)
(use-package deadgrep
:ensure-system-package (rg . ripgrep)
:bind ("<f5>" . deadgrep))
- 不启用
(treemacs-project-follow-mode t)
, 否则每次 treemacs 只会显示一个 project, 对于 Go 等有多个 module project 的情况不友好。不启用后,可以按需手动将 Go 依赖的 module project 加到当前 workspace 中。
版本 2:
(use-package treemacs
;; 需要如下配置 files, treemacs 才能找到相关 py 脚本,否则报错。
:straight (:files ("src/elisp/*" "src/extra/*" "src/scripts/*" "icons"))
:init
(with-eval-after-load 'winum (define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
:config
(setq
treemacs-collapse-dirs 0 ;; 不 collapse 目录(容易导致路径名过长)。
treemacs-deferred-git-apply-delay 0.5
treemacs-display-in-side-window t
treemacs-eldoc-display 'simple
treemacs-file-event-delay 2000
treemacs-file-follow-delay 0.1
treemacs-follow-after-init t
treemacs-expand-after-init t
treemacs-git-command-pipe ""
treemacs-goto-tag-strategy 'refetch-index
treemacs-indentation 1
treemacs-indentation-string " "
treemacs-is-never-other-window t
treemacs-max-git-entries 100
treemacs-missing-project-action 'ask
treemacs-no-png-images nil
treemacs-no-delete-other-windows t
treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory)
treemacs-position 'left
treemacs-read-string-input 'from-minibuffer
treemacs-recenter-distance 0.01 ;; 缺省 0.1
treemacs-recenter-after-file-follow 'on-distance
treemacs-recenter-after-tag-follow 'on-distance
treemacs-recenter-after-project-jump 'always
treemacs-recenter-after-project-expand 'on-distance
treemacs-litter-directories '("/node_modules" "/.venv" "/.cask" "/vendor")
treemacs-shownn-cursor t
treemacs-show-hidden-files t
treemacs-silent-filewatch nil
treemacs-silent-refresh nil
treemacs-sorting 'alphabetic-asc
treemacs-select-when-already-in-treemacs 'stay
treemacs-space-between-root-nodes nil
treemacs-tag-follow-cleanup t
treemacs-tag-follow-delay 0.5
treemacs-width 35
treemacs-width-increment 5
treemacs-width-is-initially-locked nil
treemacs-project-follow-cleanup t
treemacs-git-commit-diff-mode t
imenu-auto-rescan t)
(treemacs-resize-icons 11)
;; 跟随打开当前打开的文件(需要鼠标在文件内点击下,才会跟随)。
(treemacs-follow-mode t)
;;(treemacs-tag-follow-mode t)
;;(treemacs-project-follow-mode t)
(treemacs-filewatch-mode t)
(treemacs-fringe-indicator-mode 'always)
(treemacs-indent-guide-mode t)
(treemacs-git-mode 'deferred)
(treemacs-hide-gitignored-files-mode nil) ;; nil 开启
;; 为远程 buffer 关闭 treemacs, 避免建立新连接耗时。
(add-hook 'buffer-list-update-hook
(lambda ()
(when (file-remote-p default-directory)
(require 'treemacs)
(if (string-match "visible" (symbol-name (treemacs-current-visibility)))
(delete-window (treemacs-get-local-window))))))
;; 启动 ediff 前关闭 treemacs frame, 否则 Control-Panel 显示异常。
(add-hook 'ediff-before-setup-hook
(lambda ()
(require 'treemacs)
(if (string-match "visible" (symbol-name (treemacs-current-visibility)))
(delete-window (treemacs-get-local-window)) ) ))
;; treemacs-magit 和 treemacs-tab-bar 已经位于 treemacs src/extra 下,被 treemacs 加载,
;; 所以没必要再用 user-package 来安装,否则 treemas-magit 会提示找不到 py 文件。
;;(require 'treemacs-magit)
;;(require 'treemacs-tar-bar)
;;(treemacs-set-scope-type 'Tabs)
:bind
(:map global-map
("M-0" . treemacs-select-window)))
(use-package deadgrep
:ensure-system-package (rg . ripgrep)
:bind ("<f5>" . deadgrep))
- 不启用
(treemacs-project-follow-mode t)
, 否则每次 treemacs 只会显示一个 project, 对于 Go 等有多个 module project 的情况不友好。不启用后,可以按需手动将 Go 依赖的 module project 加到当前 workspace 中。
版本 3:使用更简洁的 neotree 包:
(use-package treemacs
:init
(with-eval-after-load 'winum (define-key winum-keymap (kbd "s-0") #'treemacs-select-window))
:config
(setq
treemacs-collapse-dirs 0 ;; 不 collapse 目录(容易导致路径名过长)。
treemacs-deferred-git-apply-delay 0.5
treemacs-display-in-side-window t
treemacs-eldoc-display 'simple
treemacs-file-event-delay 2000
treemacs-file-follow-delay 0.1
treemacs-follow-after-init t
treemacs-expand-after-init t
treemacs-git-command-pipe ""
treemacs-goto-tag-strategy 'refetch-index
treemacs-indentation 1
treemacs-indentation-string " "
treemacs-is-never-other-window t
treemacs-max-git-entries 100
treemacs-missing-project-action 'ask
treemacs-no-png-images nil ;; 是否显示图标
treemacs-no-delete-other-windows t
treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory)
treemacs-position 'left
treemacs-read-string-input 'from-minibuffer
treemacs-recenter-distance 0.01 ;; 缺省 0.1
treemacs-recenter-after-file-follow 'on-distance
treemacs-recenter-after-tag-follow 'on-distance
treemacs-recenter-after-project-jump 'always
treemacs-recenter-after-project-expand 'on-distance
treemacs-litter-directories '("/node_modules" "/.venv" "/.cask" "/vendor")
treemacs-shownn-cursor t
treemacs-show-hidden-files t
treemacs-silent-filewatch nil
treemacs-silent-refresh nil
treemacs-sorting 'alphabetic-asc
treemacs-select-when-already-in-treemacs 'stay
treemacs-space-between-root-nodes nil
treemacs-tag-follow-cleanup t
treemacs-tag-follow-delay 0.5
treemacs-width 35
treemacs-width-increment 5
treemacs-width-is-initially-locked nil
treemacs-project-follow-cleanup t
treemacs-git-commit-diff-mode t
treemacs-indent-guide-style 'block
imenu-auto-rescan t)
(treemacs-resize-icons 11)
;; 跟随打开当前打开的文件(需要鼠标在文件内点击下,才会跟随)。
(treemacs-follow-mode t)
;;(treemacs-tag-follow-mode t)
;;(treemacs-project-follow-mode t)
(treemacs-filewatch-mode t)
(treemacs-fringe-indicator-mode 'always)
(treemacs-indent-guide-mode -1)
(treemacs-git-mode 'deferred)
(treemacs-hide-gitignored-files-mode nil) ;; nil 开启
;; 为远程 buffer 关闭 treemacs, 避免建立新连接耗时。
(add-hook 'buffer-list-update-hook
(lambda ()
(when (file-remote-p default-directory)
(require 'treemacs)
(if (string-match "visible" (symbol-name (treemacs-current-visibility)))
(delete-window (treemacs-get-local-window))))))
;; 启动 ediff 前关闭 treemacs frame, 否则 Control-Panel 显示异常。
(add-hook 'ediff-before-setup-hook
(lambda ()
(require 'treemacs)
(if (string-match "visible" (symbol-name (treemacs-current-visibility)))
(delete-window (treemacs-get-local-window)) ) ))
;; treemacs-magit 和 treemacs-tab-bar 已经位于 treemacs src/extra 下,被 treemacs 加载,
;; 所以没必要再用 user-package 来安装,否则 treemas-magit 会提示找不到 py 文件。
;;(require 'treemacs-magit)
;;(require 'treemacs-tar-bar)
;;(treemacs-set-scope-type 'Tabs)
:bind
(:map global-map
("s-0" . treemacs-select-window)))
;; (use-package treemacs-all-the-icons
;; :after (treemacs magit)
;; :config
;; (treemacs-load-theme "all-the-icons"))
(use-package treemacs-magit
:after (treemacs magit)
:ensure t)
(use-package deadgrep
:ensure-system-package (rg . ripgrep)
:bind ("<f5>" . deadgrep))
版本 1:
;;安装外置输入法切换工具 [[https://github.com/laishulu/macism#install][macism]],
;;解决 Mac 切换输入法后必须输入一个字符才能生效的问题。同时系统的 “快捷键”->“选
;;择上一个输入法” 快捷键必须要开启,否则 macism
;;[[https://github.com/laishulu/macism/issues/2][会切换失败]]。必须在启用
;;=respect-mode= 之前设置 =sis-prefix-override-keys= 变量,否则变量不生效。
(use-package sis
;; mac 输入法选择
:ensure-system-package (macism . "brew tap laishulu/macism; brew install macism")
:config
(sis-ism-lazyman-config "com.apple.keylayout.ABC" "com.sogou.inputmethod.sogou.pinyin")
;; 自动切换到英文的前缀快捷键
(push "C-;" sis-prefix-override-keys)
(push "M-o" sis-prefix-override-keys)
(push "M-g" sis-prefix-override-keys)
(push "M-s" sis-prefix-override-keys)
(sis-global-context-mode nil)
(sis-global-respect-mode t)
(global-set-key (kbd "C-\\") 'sis-switch))
版本 2:
(use-package sis
:demand
:disabled
:ensure-system-package (macism . "brew tap laishulu/macism; brew install macism")
:bind ("<f9>" . sis-switch)
:config
(add-to-list 'sis-prefix-override-keys "M-s")
(add-to-list 'sis-prefix-override-keys "M-g")
(add-to-list 'sis-prefix-override-keys "M-o")
(sis-ism-lazyman-config "com.apple.keylayout.ABC" "com.sogou.inputmethod.sogou.pinyin")
(setq sis-other-cursor-color "orange")
(sis-global-cursor-color-mode t)
;; 开启 respect mode 模式。
;;(add-to-list 'sis-prefix-override-recap-triggers 'lsp-bridge-ref-mode)
;; (setq sis-prefix-override-buffer-disable-predicates
;; (list 'minibufferp
;; (;; lsp-bridge
;; lambda (buffer)
;; (sis--string-match-p "^\*lsp-bridge.*" (buffer-name buffer)))
;; (;; magit
;; lambda (buffer)
;; (sis--string-match-p "^magit.*:" (buffer-name buffer)))
;; (;; special buffer
;; lambda (buffer)
;; (and (sis--string-match-p "^\*" (buffer-name buffer))
;; (not (sis--string-match-p "^\*new\*"
;; (downcase (buffer-name buffer))))
;; (not (sis--string-match-p "^\*scratch\*"
;; (downcase (buffer-name buffer))))))))
(sis-global-respect-mode t)
;; 开启 inline english 模式。
(setq sis-inline-tighten-head-rule 0)
(setq sis-inline-tighten-tail-rule 0)
(sis-global-inline-mode t)
;; 开启 follow-context 模式。
(sis-global-context-mode t)
;; 上下文探测。
;; (add-to-list 'sis-context-detectors (lambda (&rest _)
;; (when (eq major-mode 'lsp-bridge-ref-mode)
;; 'english)))
)
版本 3:
;; brew tap laishulu/macism && brew install macism
(use-package sis
:config
;;执行 macism 命令查看输入法名称。
(sis-ism-lazyman-config "com.apple.keylayout.ABC" "com.sogou.inputmethod.sogou.pinyin")
;; 添加 emabark 前缀快捷键, 需要在打开 sis-global-xx-mode 之前设置才
;; 生效。
(add-to-list 'sis-prefix-override-keys "C-;")
(add-to-list 'sis-prefix-override-keys "M-s")
(add-to-list 'sis-prefix-override-keys "M-g")
(sis-global-cursor-color-mode t)
(sis-global-respect-mode t)
(sis-global-context-mode t)
(sis-global-inline-mode t)
;; 切换系统输入法。
(global-set-key (kbd "M-\\") 'sis-switch)
(global-set-key (kbd "C-\\") 'sis-switch)
;; 为一些打开 minimibuffer 的命令设置英文输入法。
(add-to-list 'sis-respect-minibuffer-triggers
(cons 'embark-act (lambda () 'other))))
使用 sdcv 替代了 osx-dictionary.
;; OSX 词典。
(use-package osx-dictionary
:bind
(("C-c d i" . osx-dictionary-search-input)
("C-c d w" . osx-dictionary-search-pointer))
:config
(use-package chinese-word-at-point :demand t)
(setq osx-dictionary-use-chinese-text-segmentation t))
;; 隐藏 Emacs 自带的 modeline, 使用 awesome-tray。
(use-package hide-mode-line
:disabled
:config
(global-hide-mode-line-mode))
(use-package awesome-tray
:demand
:disabled
:straight (:repo "manateelazycat/awesome-tray" :host github)
:hook
;; emacs 启动完毕后再启动,这样可以替换掉 doom-theme 的 modeline, 避免显示两个。
(after-init . (lambda () (awesome-tray-mode)))
:config
;; 显示更多的目录结构(默认为 2)。
(setq awesome-tray-file-path-full-dirname-levels 4)
;; modeline 中添加 input-method 显示。
(setq awesome-tray-active-modules '("location" "buffer-read-only" "file-path" "mode-name" "input-method" "date")))
(use-package tree-sitter
:disabled
:config
(global-tree-sitter-mode)
;; 对于支持的语言(tree-sitter-major-mode-language-alist)使用
;; tree-sitter 提供的高亮来取代内置的、基于 font-lock 正则的低效高亮模式。
(add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode))
(use-package tree-sitter-langs :after (tree-sitter))
- 下载和安装各语言的语法 bundle 文件:
M-x tree-sitter-langs-install-grammars
;; 当打开文件权限不足时,自动使用 sudo 打开该文件。
;; 关闭:会造成 tramp 远程编辑文件错误。
(use-package auto-sudoedit)
(add-hook 'find-file-hook #'(lambda () (require 'auto-sudoedit) (auto-sudoedit)))
打开 8MiB 左右 JSON 文件有严重性能问题:
(use-package hideshow
:disabled
:hook
(prog-mode . hs-minor-mode)
:bind
("C-<tab>" . hs-cycle)
("C-<iso-lefttab>" . hs-global-cycle)
("C-S-<tab>" . hs-global-cycle))
;; ref: https://karthinks.com/software/simple-folding-with-hideshow/
(defun hs-cycle (&optional level)
(interactive "p")
(let (message-log-max
(inhibit-message t))
(if (= level 1)
(pcase last-command
('hs-cycle
(hs-hide-level 1)
(setq this-command 'hs-cycle-children))
('hs-cycle-children
;; TODO: Fix this case. `hs-show-block' needs to be
;; called twice to open all folds of the parent
;; block.
(save-excursion (hs-show-block))
(hs-show-block)
(setq this-command 'hs-cycle-subtree))
('hs-cycle-subtree
(hs-hide-block))
(_
(if (not (hs-already-hidden-p))
(hs-hide-block)
(hs-hide-level 1)
(setq this-command 'hs-cycle-children))))
(hs-hide-level level)
(setq this-command 'hs-hide-level))))
(defun hs-global-cycle ()
(interactive)
(pcase last-command
('hs-global-cycle
(save-excursion (hs-show-all))
(setq this-command 'hs-global-show))
(_ (hs-hide-all))))
(use-package symbol-overlay
:diminish
:demand
:functions
(turn-off-symbol-overlay turn-on-symbol-overlay)
:custom-face
(symbol-overlay-default-face ((t (:inherit (region bold)))))
:hook
(((prog-mode yaml-mode) . symbol-overlay-mode)
;; (iedit-mode . turn-off-symbol-overlay)
;; (iedit-mode-end . turn-on-symbol-overlay)
)
:init
(setq symbol-overlay-idle-time 0.1)
;;:config
;; ;; Disable symbol highlighting while selecting
;; (defun turn-off-symbol-overlay (&rest _)
;; "Turn off symbol highlighting."
;; (interactive)
;; (symbol-overlay-mode -1))
;; (advice-add #'set-mark :after #'turn-off-symbol-overlay)
;; (defun turn-on-symbol-overlay (&rest _)
;; "Turn on symbol highlighting."
;; (interactive)
;; (when (derived-mode-p 'prog-mode 'yaml-mode)
;; (symbol-overlay-mode 1)))
;; (advice-add #'deactivate-mark :after #'turn-on-symbol-overlay)
)
;; Symbol Overlay
(global-set-key (kbd "M-i") 'symbol-overlay-put)
(global-set-key (kbd "M-n") 'symbol-overlay-jump-next)
(global-set-key (kbd "M-p") 'symbol-overlay-jump-prev)
(global-set-key (kbd "M-N") 'symbol-overlay-switch-forward)
(global-set-key (kbd "M-P") 'symbol-overlay-switch-backward)
(global-set-key (kbd "M-c") 'symbol-overlay-remove-all)
;; (define-key symbol-overlay-map (kbd "M-n") 'symbol-overlay-jump-next)
;; (define-key symbol-overlay-map (kbd "M-p") 'symbol-overlay-jump-prev)
;; (define-key symbol-overlay-map (kbd "M-N") 'symbol-overlay-switch-forward)
;; (define-key symbol-overlay-map (kbd "M-P") 'symbol-overlay-switch-backward)
;; (define-key symbol-overlay-map (kbd "M-c") 'symbol-overlay-remove-all)
(use-package awesome-pair
:straight(awesome-pair :host github :repo "manateelazycat/awesome-pair")
:config
(dolist (hook (list
'c-mode-common-hook
'c-mode-hook
'emacs-lisp-mode-hook
'lisp-interaction-mode-hook
'lisp-mode-hook
'sh-mode-hook
'makefile-gmake-mode-hook
'python-mode-hook
'js-mode-hook
'go-mode-hook
'css-mode-hook
'minibuffer-inactive-mode-hook
))
(add-hook hook '(lambda () (awesome-pair-mode 1)))))
;; 智能括号。
(use-package smartparens
:disabled
:config
(smartparens-global-mode t)
(show-smartparens-global-mode t))
(use-package smartparens
:disabled
:config
(require 'smartparens-config)
(add-hook 'prog-mode-hook #'smartparens-mode)
;;(smartparens-global-mode t)
;; 不在字符串中插入转义字符(容易错误)。
(setq sp-escape-quotes-after-insert nil)
(show-smartparens-global-mode t))
使用内置的 tab-bar history 来代替:
(use-package winner
:straight (:type built-in)
:commands (winner-undo winner-redo)
:hook (after-init . winner-mode)
:init
(setq winner-boring-buffers
'("*Completions*"
"*Compile-Log*"
"*inferior-lisp*"
"*helpful"
"*lsp-help*"
"*Fuzzy Completions*"
"*Apropos*"
"*Help*"
"*cvs*"
"*Buffer List*"
"*Ibuffer*"
"*esh command on file*")))
(use-package tabspaces
;; use this next line only if you also use straight, otherwise ignore it.
:straight (:type git :host github :repo "mclear-tools/tabspaces")
:hook (after-init . tabspaces-mode) ;; use this only if you want the minor-mode loaded at startup.
:commands (tabspaces-switch-or-create-workspace
tabspaces-open-or-create-project-and-workspace)
:custom
(tabspaces-use-filtered-buffers-as-default t)
(tabspaces-default-tab "Default")
(tabspaces-remove-to-default t)
(tabspaces-include-buffers '("*scratch*"))
;; sessions
(tabspaces-session t)
(tabspaces-session-auto-restore t))
(use-package awesome-tab
:disabled
:config
(setq awesome-tab-label-fixed-length 20)
(setq awesome-tab-height 100)
;; 不显示 tab 序号。
(setq awesome-tab-show-tab-index nil)
;; 不显示 tab 的 buffer 名称, 需要先定义才生效。注意:有的 buffer 名称的 * 前有空格(如 Treemacs)。
(defun awesome-tab-hide-tab (x)
(let ((name (format "%s" x)))
(or
(string-prefix-p "*epc" name)
(string-prefix-p "*helm" name)
(string-prefix-p "*help" name)
(string-prefix-p "*Compile-Log*" name)
(string-prefix-p "*lsp" name)
(string-prefix-p "*gopls" name)
(string-prefix-p "*dashboard" name)
(string-prefix-p "*Warnings" name)
(string-prefix-p "*Messages" name)
(string-prefix-p "*scratch" name)
(string-prefix-p "*Help" name)
(string-prefix-p "*vterm" name)
(string-prefix-p " *lsp-bridge" name) ;; lsp-bridge
(string-prefix-p " *acm-doc" name) ;; lsp-bridge
(string-prefix-p " *Treemacs" name) ;; treemacs
(and (string-prefix-p "magit" name)
(not (file-name-extension name))))))
(awesome-tab-mode t)
(global-set-key (kbd "s-1") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-2") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-3") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-4") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-5") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-6") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-7") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-8") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-9") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-0") 'awesome-tab-select-visible-tab)
(global-set-key (kbd "s-j") 'awesome-tab-ace-jump))
(setq tab-line-new-button-show nil) ;; do not show add-new button
(setq tab-line-close-button-show nil) ;; do not show close button
(setq tab-line-separator "|") ;; set it to empty
;; 切换 major-mode 时获得 bufer 对应的 project 名称。
(defvar buffer-project-name "~")
(make-local-variable 'buffer-project-name)
(defun my/update-buf-proj-name()
(if (project-current)
(setq-local buffer-project-name (file-name-nondirectory
(directory-file-name
(file-name-directory (project-root (project-current))))))
(setq-local buffer-project-name "other")))
(add-hook 'after-change-major-mode-hook 'my/update-buf-proj-name)
(defun my/tab-line-buffer-group (buffer)
(interactive)
(with-current-buffer buffer
(cond
((s-match "scratch" (buffer-name)) "Scratch")
((string= major-mode "eshell-mode") "Eshell")
((string= major-mode "dired-mode") "Dir")
((string= major-mode "vterm-mode") "vTerm")
((s-match "Customize" (buffer-name)) "Customize")
((s-match "magit:" (buffer-name)) "Magit")
((s-match "magit " (buffer-name)) "Magit others")
((s-match "magit-" (buffer-name)) "Magit others")
((string= (s-left 1 (buffer-name)) "*") "Utils")
((string= (s-left 2 (buffer-name)) " *") "Utils")
;; 按照 project 分组。
((boundp 'buffer-project-name) buffer-project-name)
(major-mode (format-mode-line mode-name))
(t "other"))))
(setq tab-line-tabs-buffer-group-function #'my/tab-line-buffer-group)
(setq tab-line-tabs-function #'tab-line-tabs-buffer-groups)
(global-tab-line-mode t)
(global-set-key (kbd "s-<tab>") 'tab-line-switch-to-next-tab)
(global-set-key (kbd "s-S-<tab>") 'tab-line-switch-to-prev-tab)
;; 不显示 tab-line 的 major-mode。
(add-to-list 'tab-line-exclude-modes 'vterm-mode)
(add-to-list 'tab-line-exclude-modes 'magit-mode)
(add-to-list 'tab-line-exclude-modes 'magit-diff-mode)
(add-to-list 'tab-line-exclude-modes 'magit-log-mode)
(add-to-list 'tab-line-exclude-modes 'dashboard-mode)
(add-to-list 'tab-line-exclude-modes 'ediff-mode)
grammatical-edit 基于 tree-sitter 提供结构化编程能力。
(use-package grammatical-edit
:straight (:host github :repo "manateelazycat/grammatical-edit")
:after (treesit)
:disabled
:config
(dolist (hook (list
'c-mode-common-hook
'c-mode-hook
'c++-mode-hook
'java-mode-hook
'emacs-lisp-mode-hook
'lisp-interaction-mode-hook
'lisp-mode-hook
'sh-mode-hook
'makefile-gmake-mode-hook
'python-mode-hook
'js-mode-hook
'go-mode-hook
'css-mode-hook
'rust-mode-hook
'minibuffer-inactive-mode-hook
'typescript-mode-hook
))
(add-hook hook '(lambda () (grammatical-edit-mode 1)))))
;;; grammatical-edit
;; 符号插入
;; (define-key grammatical-edit-mode-map (kbd "(") 'grammatical-edit-open-round) ;;智能 (
;; (define-key grammatical-edit-mode-map (kbd "[") 'grammatical-edit-open-bracket) ;;智能 [
;; (define-key grammatical-edit-mode-map (kbd "{") 'grammatical-edit-open-curly) ;;智能 {
;; (define-key grammatical-edit-mode-map (kbd ")") 'grammatical-edit-close-round) ;;智能 )
;; (define-key grammatical-edit-mode-map (kbd "]") 'grammatical-edit-close-bracket) ;;智能 ]
;; (define-key grammatical-edit-mode-map (kbd "}") 'grammatical-edit-close-curly) ;;智能 }
;; (define-key grammatical-edit-mode-map (kbd "=") 'grammatical-edit-equal) ;;智能 =
;; (define-key grammatical-edit-mode-map (kbd "%") 'grammatical-edit-match-paren) ;; 括号跳转
;; (define-key grammatical-edit-mode-map (kbd "\"") 'grammatical-edit-double-quote) ;;智能 "
;; (define-key grammatical-edit-mode-map (kbd "'") 'grammatical-edit-single-quote) ;;智能 '
;; (define-key grammatical-edit-mode-map (kbd "SPC") 'grammatical-edit-space) ;;智能 space
;; (define-key grammatical-edit-mode-map (kbd "C-j") 'grammatical-edit-newline) ;; 智能 newline
;; ;;(define-key grammatical-edit-mode-map (kbd "RET") 'grammatical-edit-newline) ;; 智能 newline
;; ;; 删除
;; (define-key grammatical-edit-mode-map (kbd "M-S-d") 'grammatical-edit-backward-delete) ;;向后 kill
;; (define-key grammatical-edit-mode-map (kbd "M-d") 'grammatical-edit-forward-delete) ;;向前 delete
;; (define-key grammatical-edit-mode-map (kbd "C-k") 'grammatical-edit-kill) ;;向前 kill
;; ;; 包围
;; (define-key grammatical-edit-mode-map (kbd "M-\"") 'grammatical-edit-wrap-double-quote) ;; 用 " " 包围对象, 或跳出字符串
;; (define-key grammatical-edit-mode-map (kbd "M-'") 'grammatical-edit-wrap-single-quote) ;;用 ' ' 包围对象, 或跳出字符串
;; (define-key grammatical-edit-mode-map (kbd "M-[") 'grammatical-edit-wrap-bracket) ;; 用 [ ] 包围对象
;; (define-key grammatical-edit-mode-map (kbd "M-{") 'grammatical-edit-wrap-curly) ;; 用 { } 包围对象
;; (define-key grammatical-edit-mode-map (kbd "M-(") 'grammatical-edit-wrap-round) ;; 用 ( ) 包围对象
;; (define-key grammatical-edit-mode-map (kbd "M-)") 'grammatical-edit-unwrap) ;; 去掉包围对象
;; ;; 移动
;; (define-key grammatical-edit-mode-map (kbd "M-n") 'grammatical-edit-jump-right) ;; 左侧
;; (define-key grammatical-edit-mode-map (kbd "M-p") 'grammatical-edit-jump-left) ;; 右侧
;; ;; 跳出括号并换行
;; ;;(define-key grammatical-edit-mode-map (kbd "M-:") 'grammatical-edit-jump-out-pair-and-newline)
;; ;; 向父节点跳动
;; (define-key grammatical-edit-mode-map (kbd "M-u") 'grammatical-edit-jump-up)
- 不能与 smartparens 连用。
macos 安装 stardict:
# 安装 sdcv 命令行版本
brew install sdcv
# 下载词典文件
#牛津高阶英汉双解:
wget http://download.huzheng.org/zh_CN/stardict-oald-cn-2.4.2.tar.bz2
#朗道英汉字典:
wget http://download.huzheng.org/zh_CN/stardict-langdao-ec-gb-2.4.2.tar.bz2
#朗道汉英字典:
wget http://download.huzheng.org/zh_CN/stardict-langdao-ce-gb-2.4.2.tar.bz2
#KDic11万英汉词典 - 计算机
wget http://download.huzheng.org/zh_CN/stardict-kdic-ec-11w-2.4.2.tar.bz2
# 解压词典
mkdir ~/.emacs.d/stardict/dic
tar -xzvf stardict-oald-cn-2.4.2.tar.bz2 -C ~/.emacs.d/sync/stardict/dic
tar -xzvf stardict-langdao-ec-gb-2.4.2.tar.bz2 -C ~/.emacs.d/sync/stardict/dic
tar -xzvf stardict-langdao-ce-gb-2.4.2.tar.bz2 -C ~/.emacs.d/sync/stardict/dict
tar -xzvf ~/Downloads/stardict-kdic-ec-11w-2.4.2.tar.bz2 -C ~/.emacs.d/sync/stardict/dic/
# 测试
$ sdcv -2 ~/.emacs.d/sync/stardict/dic/ -l
Dictionary's name Word count
牛津高阶英汉双解 39433
朗道汉英字典5.0 405719
朗道英汉字典5.0 435468
本地翻译:
(use-package sdcv
:straight (:host github :repo "manateelazycat/sdcv")
:ensure-system-package (sdcv . "brew install sdcv")
:config
(setq sdcv-tooltip-timeout 0)
;;say word after translation
(setq sdcv-say-word-p nil)
(setq sdcv-dictionary-data-dir "/Users/zhangjun/.emacs.d/sync/stardict/dic")
;;setup dictionary list for simple search
(setq sdcv-dictionary-simple-list
'("朗道英汉字典5.0"
"朗道汉英字典5.0"
"KDic11万英汉词典"))
;;setup dictionary list for complete search
(setq sdcv-dictionary-complete-list
'("朗道汉英字典5.0"
"朗道英汉字典5.0"
"KDic11万英汉词典"
"牛津高阶英汉双解")))
;;; sdcv
(global-set-key (kbd "C-c d P") #'sdcv-search-pointer) ;; 光标处的单词, buffer 显示。
(global-set-key (kbd "C-c d p") #'sdcv-search-pointer+) ;; 光标处的单词, tooltip 显示。
(global-set-key (kbd "C-c d I") #'sdcv-search-input) ;; 输入的单词, buffer 显示。
(global-set-key (kbd "C-c d i") #'sdcv-search-input+)
(use-package eat
:straight (eat :type git
:repo "https://codeberg.org/akib/emacs-eat.git"
:files ("*.el" ("term" "term/*.el") "*.texi"
"*.ti" ("terminfo/e" "terminfo/e/*")
("terminfo/65" "terminfo/65/*")
("integration" "integration/*")
(:exclude ".dir-locals.el" "*-tests.el")))
:config
(setq eat-term-terminfo-directory "/Users/zhangjun/.emacs.d/straight/build/eat/terminfo/e")
)
(use-package ace-window
:init
;; 使用字母而非数字标记窗口,便于跳转。
(setq aw-keys '(?a ?w ?e ?g ?i ?j ?k ?l ?p))
;; 根据自己的使用习惯来调整快捷键,这里使用大写字母避免与 aw-keys 冲突。
(setq aw-dispatch-alist
'((?0 aw-delete-window "Delete Window")
(?1 delete-other-windows "Delete Other Windows")
(?2 aw-split-window-vert "Split Vert Window")
(?3 aw-split-window-horz "Split Horz Window")
(?F aw-split-window-fair "Split Fair Window")
(?S aw-swap-window "Swap Windows")
(?M aw-move-window "Move Window")
(?C aw-copy-window "Copy Window")
;; 为指定 window 选择新的 Buffer 并切换过去。
(?B aw-switch-buffer-in-window "Select Buffer")
;; 为指定 window 选择新的 Buffer,切换到其它 buffer。
(?O aw-switch-buffer-other-window "Switch Buffer Other Window")
(?N aw-flip-window)
(?? aw-show-dispatch-help)))
:config
(setq aw-scope 'frame)
;; 总是提示窗口选择,进而执行 ace 命令。
;;(setq aw-dispatch-always t)
)
;; https://github.com/manateelazycat/toggle-one-window/blob/main/toggle-one-window.el
(defvar toggle-one-window-window-configuration nil
"The window configuration use for `toggle-one-window'.")
(defun my/toggle-one-window ()
"Toggle between window layout and one window."
(interactive)
(if (equal (length (cl-remove-if #'window-dedicated-p (window-list))) 1)
(if toggle-one-window-window-configuration
(progn
(set-window-configuration toggle-one-window-window-configuration)
(setq toggle-one-window-window-configuration nil))
(message "No other windows exist."))
(setq toggle-one-window-window-configuration (current-window-configuration))
(delete-other-windows)))
;;; ace-window
(global-set-key (kbd "M-o") #'ace-window)
(global-set-key (kbd "s-<f11>") #'my/toggle-one-window)
(use-package hl-todo
:straight (hl-todo :host github :repo "tarsius/hl-todo")
:after (ef-themes)
:config
(global-hl-todo-mode)
(defun my-ef-themes-hl-todo-faces ()
"Configure `hl-todo-keyword-faces' with Ef themes colors. The exact color values are taken from the active Ef theme."
(ef-themes-with-colors
(setq hl-todo-keyword-faces
`(("HOLD" . ,yellow)
("TODO" . ,red)
("NEXT" . ,blue)
("THEM" . ,magenta)
("PROG" . ,cyan-warmer)
("OKAY" . ,green-warmer)
("DONT" . ,yellow-warmer)
("FAIL" . ,red-warmer)
("BUG" . ,red-warmer)
("DONE" . ,green)
("NOTE" . ,blue-warmer)
("KLUDGE" . ,cyan)
("HACK" . ,cyan)
("TEMP" . ,red)
("FIXME" . ,red-warmer)
("FIXME!!!" . ,red-warmer)
("XXX+" . ,red-warmer)
("REVIEW" . ,red)
("DEPRECATED" . ,yellow)))))
(add-hook 'ef-themes-post-load-hook #'my-ef-themes-hl-todo-faces))
(global-set-key (kbd "C-M-<left>") 'shrink-window-horizontally)
(global-set-key (kbd "C-M-<right>") 'enlarge-window-horizontally)
(global-set-key (kbd "C-M-<down>") 'shrink-window)
(global-set-key (kbd "C-M-<up>") 'enlarge-windo)
(use-package elfeed
:demand
:config
(setq elfeed-db-directory (expand-file-name "elfeed" user-emacs-directory))
(setq elfeed-show-entry-switch 'display-buffer)
(setq elfeed-curl-program-name "/usr/local/opt/curl/bin/curl")
(setq elfeed-curl-timeout 30)
(setf url-queue-timeout 40)
(push "-k" elfeed-curl-extra-arguments)
(setq elfeed-search-filter "@1-months-ago +unread")
;; 在同一个 buffer 中显示条目。
(setq elfeed-show-unique-buffers nil)
(setq elfeed-search-title-max-width 150)
(setq elfeed-search-date-format '("%Y-%m-%d %H:%M" 20 :left))
(setq elfeed-log-level 'warn)
;; 支持收藏 feed, 参考:http://pragmaticemacs.com/emacs/star-and-unstar-articles-in-elfeed/
(defalias 'elfeed-toggle-star (elfeed-expose #'elfeed-search-toggle-all 'star))
(eval-after-load 'elfeed-search '(define-key elfeed-search-mode-map (kbd "m") 'elfeed-toggle-star))
(defface elfeed-search-star-title-face '((t :foreground "#f77")) "Marks a starred Elfeed entry.")
(push '(star elfeed-search-star-title-face) elfeed-search-face-alist))
(use-package elfeed-org
:custom ((rmh-elfeed-org-files (list "~/.emacs.d/elfeed.org")))
:hook
((elfeed-dashboard-mode . elfeed-org)
(elfeed-show-mode . elfeed-org))
:config
(progn
(defun my/reload-org-feeds ()
(interactive)
(rmh-elfeed-org-process rmh-elfeed-org-files rmh-elfeed-org-tree-id))
(advice-add 'elfeed-dashboard-update :before #'my/reload-org-feeds)))
(use-package elfeed-dashboard
:config
(global-set-key (kbd "C-c f") 'elfeed-dashboard)
(setq elfeed-dashboard-file "~/.emacs.d/elfeed-dashboard.org")
(advice-add 'elfeed-search-quit-window :after #'elfeed-dashboard-update-links))
(use-package elfeed-score
:config
(progn
(elfeed-score-enable)
(define-key elfeed-search-mode-map "=" elfeed-score-map)))
(use-package elfeed-goodies
:config
(setq elfeed-goodies/entry-pane-position 'bottom)
(setq elfeed-goodies/feed-source-column-width 30)
(setq elfeed-goodies/tag-column-width 30)
(setq elfeed-goodies/powerline-default-separator 'arrow)
(elfeed-goodies/setup))
;; elfeed-goodies 显示日期栏
;;https://github.com/algernon/elfeed-goodies/issues/15#issuecomment-243358901
(defun elfeed-goodies/search-header-draw ()
"Returns the string to be used as the Elfeed header."
(if (zerop (elfeed-db-last-update))
(elfeed-search--intro-header)
(let* ((separator-left (intern (format "powerline-%s-%s"
elfeed-goodies/powerline-default-separator
(car powerline-default-separator-dir))))
(separator-right (intern (format "powerline-%s-%s"
elfeed-goodies/powerline-default-separator
(cdr powerline-default-separator-dir))))
(db-time (seconds-to-time (elfeed-db-last-update)))
(stats (-elfeed/feed-stats))
(search-filter (cond
(elfeed-search-filter-active
"")
(elfeed-search-filter
elfeed-search-filter)
(""))))
(if (>= (window-width) (* (frame-width) elfeed-goodies/wide-threshold))
(search-header/draw-wide separator-left separator-right search-filter stats db-time)
(search-header/draw-tight separator-left separator-right search-filter stats db-time)))))
(defun elfeed-goodies/entry-line-draw (entry)
"Print ENTRY to the buffer."
(let* ((title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
(date (elfeed-search-format-date (elfeed-entry-date entry)))
(title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
(feed (elfeed-entry-feed entry))
(feed-title
(when feed
(or (elfeed-meta feed :title) (elfeed-feed-title feed))))
(tags (mapcar #'symbol-name (elfeed-entry-tags entry)))
(tags-str (concat "[" (mapconcat 'identity tags ",") "]"))
(title-width (- (window-width) elfeed-goodies/feed-source-column-width
elfeed-goodies/tag-column-width 4))
(title-column (elfeed-format-column
title (elfeed-clamp
elfeed-search-title-min-width
title-width
title-width)
:left))
(tag-column (elfeed-format-column
tags-str (elfeed-clamp (length tags-str)
elfeed-goodies/tag-column-width
elfeed-goodies/tag-column-width)
:left))
(feed-column (elfeed-format-column
feed-title (elfeed-clamp elfeed-goodies/feed-source-column-width
elfeed-goodies/feed-source-column-width
elfeed-goodies/feed-source-column-width)
:left)))
(if (>= (window-width) (* (frame-width) elfeed-goodies/wide-threshold))
(progn
(insert (propertize date 'face 'elfeed-search-date-face) " ")
(insert (propertize feed-column 'face 'elfeed-search-feed-face) " ")
(insert (propertize tag-column 'face 'elfeed-search-tag-face) " ")
(insert (propertize title 'face title-faces 'kbd-help title)))
(insert (propertize title 'face title-faces 'kbd-help title)))))
elfeed-score 规则文件(语法参考):
;;; Elfeed score file -*- lisp -*-
(
;; ("title"
;; (:text "opsnull" :value 250 :type S))
;; ("content"
;; (:text "type erasure" :value 500 :type s))
("title-or-content"
;; (:text "emacs" :title-value 150 :content-value 100 :type s)
(:text "opsnull" :title-value 150 :content-value 100 :type w))
("feed"
(:text "Irreal" :value 250 :type S :attr t)
(:text "emacs-news – sacha chua" :value 350 :type S :attr t :comment "Essential!"))
;; ("authors"
;; (:text "opsnull" :value 500 :type s))
;; ("tag"
;; (:tags (t . reddit-question)
;; :value 750
;; :comment "Add 750 points to any entry with a tag of reddit-question"))
(mark -2500))
ctrlf 是 isearch 更好的替代品(搜索结果编号,支持:C-v, M-v, M-<, M->, C-l):
(use-package ctrlf
:disabled
:config
(ctrlf-mode +1)
(add-hook 'pdf-isearch-minor-mode-hook (lambda () (ctrlf-local-mode -1))))
(when (fboundp 'native-compile-async)
(setenv "LIBRARY_PATH"
(concat (getenv "LIBRARY_PATH") "/usr/local/opt/gcc/lib/gcc/12:/usr/local/opt/gcc/lib/gcc/12/gcc/x86_64-apple-darwin21/12"))
(setq native-comp-speed 2)
(setq native-comp-async-jobs-number 4)
;; Emacs 29;
;;(setq inhibit-automatic-native-compilation t)
;;(setq native-comp-async-report-warnings-errors 'silent)
)
;; 关闭 cl 告警。
(require 'cl-lib)
(setq byte-compile-warnings '(cl-functions))
;; 平滑地进行半屏滚动(默认避免滚动后进行 recenter)。
(setq scroll-step 1)
(setq scroll-conservatively 10000)
(setq scroll-margin 2)
(use-package pyim
:straight (pyim :repo "tumashu/pyim")
:disabled
:hook
;; 设置缺省输入法为 pyim。
(emacs-startup . (lambda () (setq default-input-method "pyim")))
:config
(setq-default default-input-method "pyim")
(setq pyim-title "IM")
(setq pyim-indicator-modeline-string '("CN" "EN"))
;; 单字符快捷键,可以实现快速切换标点符号和添加个人生词。
(setq pyim-outcome-trigger "^")
(setq pyim-dcache-directory "~/.emacs.d/sync/pyim/dcache/")
;; 使用全拼。
(pyim-default-scheme 'quanpin)
;; 使用百度云拼音。
(setq pyim-cloudim 'baidu)
;; 关闭代码搜索中文功能(比如拼音,五笔码等), 会导致 isearch 卡死。
(pyim-isearch-mode nil)
;; 中文使用全角标点,英文使用半角标点。
(setq-default pyim-punctuation-translate-p '(auto yes no))
;; posframe 性能更好且显式的较为干净, popup 较慢且容易干扰当前 buffer。
(setq-default pyim-page-tooltip 'posframe)
;; 设置模糊音。
(setq pyim-pinyin-fuzzy-alist '( ("z" "zh") ("c" "ch") ("s" "sh") ("en" "eng") ("in" "ing")))
;; Dictionaries:
;; pyim-greatdict is not recommended. It has too many useless words and slows down pyim.
;;
;; Download pyim-bigdict,
;; curl -L http://tumashu.github.io/pyim-bigdict/pyim-bigdict.pyim.gz | zcat > ~/.eim/pyim-bigdict.pyim
;;
;; Download pyim-tsinghua (recommended),
;; curl -L https://raw.githubusercontent.com/redguardtoo/pyim-tsinghua-dict/master/pyim-tsinghua-dict.pyim > ~/.eim/pyim-tsinghua-dict.pyim
(setq pyim-dicts '(
(:name "tsinghua" :file "~/.emacs.d/straight/repos/pyim-tsinghua-dict/pyim-tsinghua-dict.pyim")
;; (:name "pyim-bigdict" :file "~/.emacs.d/sync/pyim/pyim-bigdict.pyim")
))
;; 使用性能更好的 pyim-dregcache dcache 后端。
;;(setq pyim-dcache-backend 'pyim-dregcache)
;; 设置中英文自动切换。
(setq-default pyim-english-input-switch-functions
'(pyim-probe-program-mode
pyim-probe-auto-english
pyim-probe-dynamic-english
;; pyim-probe-org-structure-template
))
;; 显示候选词数量。
(setq pyim-page-length 8)
;; 输入法切换。
(global-set-key (kbd "C-\\") #'toggle-input-method)
;; 中英文切换。
(global-set-key (kbd "C-.") #'pyim-toggle-input-ascii)
;; 金手指设置,将光标处的拼音字符串转换为中文。
(global-set-key (kbd "M-j") #'pyim-convert-string-at-point))
;; 清华大学开放中文词库。
(use-package pyim-tsinghua-dict
:straight (pyim-tsinghua-dict :host github :repo "redguardtoo/pyim-tsinghua-dict")
:after pyim
:config
(pyim-tsinghua-dict-enable))
;;(global-set-key (kbd "s-h") #'windmove-left)
;;(global-set-key (kbd "s-j") #'windmove-down)
;;(global-set-key (kbd "s-k") #'windmove-up)
;;(global-set-key (kbd "s-l") #'windmove-right)
;;(global-set-key (kbd "s-s") #'save-buffer)
;;(global-set-key (kbd "s-w") #'delete-frame)
;; buffer 窗口移动。
(use-package buffer-move
:straight (:host github :repo "lukhas/buffer-move")
:disabled
:config
(global-set-key (kbd "C-s-k") #'buf-move-up)
(global-set-key (kbd "C-s-j") #'buf-move-down)
(global-set-key (kbd "C-s-h") #'buf-move-left)
(global-set-key (kbd "C-s-l") #'buf-move-right))
company-capf 使用 completion-at-point 机制,所以自动的会使用 orderless。但是 orderless 的过滤 style 貌似不能正 常工作,所以切换到 corfu。
(use-package company
:straight (:host github :repo "company-mode/company-mode")
:disabled
:bind
(:map company-mode-map
([remap completion-at-point] . company-complete)
:map company-active-map
([escape] . company-abort)
("C-p" . company-select-previous)
("C-n" . company-select-next)
("C-s" . company-filter-candidates)
([tab] . company-complete-common-or-cycle)
([backtab] . company-select-previous-or-abort)
:map company-search-map
([escape] . company-search-abort)
("C-p" . company-select-previous)
("C-n" . company-select-next))
:custom
;; allow input string that do not match candidate words
;; 开启后有大量不匹配的候选情况,故关闭。
;;(company-require-match nil)
;; pop up a completion menu by tapping a character
(company-minimum-prefix-length 1) ;; default: 3
;; trigger completion immediately.
(company-idle-delay 0)
(company-echo-delay 0)
(company-global-modes '(not message-mode help-mode eshell-mode comint-mode))
(company-tooltip-limit 14) ;; default 10;
;; tooltip 的 annotations 右对齐。
(company-tooltip-align-annotations t)
(company-dabbrev-minimum-length 2)
;; Only search the current buffer for `company-dabbrev' (a backend that
;; suggests text your open buffers). This prevents Company from causing
;; lag once you have a lot of buffers open.
(company-dabbrev-other-buffers nil)
;; Make `company-dabbrev' fully case-sensitive, to improve UX with
;; domain-specific words with particular casing.
(company-dabbrev-ignore-case nil)
;; Don't downcase the returned candidates.
(company-dabbrev-downcase nil)
(company-files-exclusions '(".git/" ".DS_Store" "vendor/"))
;; (company-transformers '(delete-consecutive-dups
;; company-sort-by-occurrence))
;; 候选框宽度
(company-tooltip-minimum-width 70)
(company-tooltip-maximum-width 100)
;; (setq company-frontends
;; '(company-pseudo-tooltip-unless-just-one-frontend
;; company-preview-frontend
;; company-echo-metadata-frontend))
;; 补全后端
(company-backends '(company-capf
;;company-clang
company-files
company-gtags
;;(company-dabbrev-code company-gtags company-keywords)
;;company-dabbrev
))
:config
;; 不考虑大小写。
(setq completion-ignore-case t)
;; 高亮候选者(orderless 排序)。
(defun just-one-face (fn &rest args)
(let ((orderless-match-faces [completions-common-part]))
(apply fn args)))
(advice-add 'company-capf--candidates :around #'just-one-face)
;; We follow a suggestion by company maintainer u/hvis:
;; https://www.reddit.com/r/emacs/comments/nichkl/comment/gz1jr3s/
(defun company-completion-styles (capf-fn &rest args)
(let ((completion-styles '(basic partial-completion)))
(apply capf-fn args)))
(advice-add 'company-capf :around #'company-completion-styles)
(global-company-mode t))
- C-h: Display a buffer with the documentation for the selected candidate (company-show-doc-buffer).
- C-w: Display a buffer with the definition of the selected candidate (company-show-location).
- C-s: By default, when company-mode is in action, a key binding C-s starts looking for matches to
additionally typed characters among the displayed candidates.
- 由参数 company-search-regexp-function 控制,默认为 regexp-quote, 表示 Exact match;
The full list of the default key bindings is stored in the variables company-active-map and company-search-map 2.
- M-x company-diag: 诊断。
;; 低对比度主题。
(use-package color-theme-sanityinc-tomorrow
:straight (:host github :repo "purcell/color-theme-sanityinc-tomorrow")
)
(use-package zenburn-theme
:straight (:host github :repo "bbatsov/zenburn-emacs")
:config
;; use variable-pitch fonts for some headings and titles
(setq zenburn-use-variable-pitch t)
;; scale headings in org-mode
(setq zenburn-scale-org-headlines t)
;; scale headings in outline-mode
(setq zenburn-scale-outline-headlines t)
(setq zenburn-height-plus-1 1.2)
(setq zenburn-height-plus-2 1.3)
(setq zenburn-height-plus-3 1.4)
(setq zenburn-height-plus-4 1.5)
(load-theme 'zenburn t)
)
doom-themes:
(use-package doom-themes
;; 添加 "extensions/*" 后才支持 visual-bell/treemacs/org 配置。
;;:straight (:files ("*.el" "themes/*" "extensions/*"))
:custom-face
(doom-modeline-buffer-file ((t (:inherit (mode-line bold)))))
:custom
(doom-themes-enable-bold t)
(doom-themes-enable-italic t)
:config
(doom-themes-visual-bell-config)
;;(load-theme 'doom-palenight t)
(doom-themes-org-config)
;; 开启 variable-pitch 模式后,treemacs 字体显示异常,故关闭。
;; 必须在执行 doom-themes-treemacs-config 前设置该变量为 nil, 否则不生效。
(setq doom-themes-treemacs-enable-variable-pitch nil)
(setq doom-themes-treemacs-theme "doom-colors")
(doom-themes-treemacs-config))
doom-themes:
(use-package doom-themes
:demand
;; 添加 "extensions/*" 后才支持 visual-bell/treemacs/org 配置。
;;:straight (:files ("*.el" "themes/*" "extensions/*"))
:custom-face
(doom-modeline-buffer-file ((t (:inherit (mode-line bold)))))
:config
(setq doom-themes-enable-bold t)
(setq doom-themes-enable-italic t)
(doom-themes-visual-bell-config)
(load-theme 'doom-one t)
(doom-themes-org-config)
;; 开启 variable-pitch 模式后,treemacs 字体显示异常,故关闭。
;; 必须在执行 doom-themes-treemacs-config 前设置该变量为 nil, 否则不生效。
(setq doom-themes-treemacs-enable-variable-pitch t)
(setq doom-themes-treemacs-theme "doom-colors")
(doom-themes-treemacs-config)
)
;; 调整 header 字体大小。
;; ef-themes/modus-themes 主题有专门参数用于调整 header 字体,不需要配置如下参数。
;; (dolist (face '((org-level-1 . 1.9)
;; (org-level-2 . 1.8)
;; (org-level-3 . 1.7)
;; (org-level-4 . 1.6)
;; (org-level-5 . 1.5)
;; (org-level-6 . 1.4)
;; (org-level-7 . 1.4)
;; (org-level-8 . 1.2)))
;; (set-face-attribute (car face) nil :height (cdr face)))
ef-themes, modus-themes:
(use-package ef-themes
:disabled
:straight (ef-themes :host github :repo "protesilaos/ef-themes")
:config
;; Disable all other themes to avoid awkward blending:
(mapc #'disable-theme custom-enabled-themes))
;; 高亮光标移动到的行。
(use-package pulsar
:disabled
:straight (pulsar :host github :repo "protesilaos/pulsar")
:config
(setq pulsar-pulse t)
(setq pulsar-delay 2)
(setq pulsar-iterations 10)
(setq pulsar-face 'pulsar-magenta)
(setq pulsar-highlight-face 'pulsar-yellow)
(pulsar-global-mode 1))
(use-package modus-themes
:disabled
:init
(setq modus-themes-italic-constructs t
modus-themes-bold-constructs t
modus-themes-region '(accented no-extend)
modus-themes-hl-line '(underline accented)
modus-themes-paren-match '(intense)
modus-themes-links '(neutral-underline background)
modus-themes-box-buttons '(variable-pitch flat faint 0.9)
modus-themes-prompts '(intense bold)
modus-themes-syntax '(alt-syntax)
modus-themes-mixed-fonts t
modus-themes-org-blocks 'gray-background ;; 'tinted-background
modus-themes-headings '((t . (variable-pitch background overline rainbow semibold)))
modus-themes-scale-1 1.1
modus-themes-scale-2 1.15
modus-themes-scale-3 1.21
modus-themes-scale-4 1.27
modus-themes-scale-title 1.33)
;; 关闭 variable-pitch-ui 和添加 padding, 否则 mode-line 右侧可能溢出。
(setq modus-themes-variable-pitch-ui nil)
(setq modus-themes-mode-line (quote (borderless (padding 4) (height 0.9))))
;; Load the theme files before enabling a theme
(modus-themes-load-themes)
;;:config
;;(modus-themes-load-vivendi) ;; 深色主题
;;(modus-themes-load-operandi) ;; 浅色主题
)
(use-package modus-themes
:ensure t
:config
;; Maybe define some palette overrides, such as by using our presets
;; (setq modus-themes-common-palette-overrides
;; modus-themes-preset-overrides-intense)
(setq modus-themes-common-palette-overrides
'(
;; Make the `tab-bar-mode' mode subtle while keepings its original gray aesthetic.
(bg-tab-bar bg-main)
(bg-tab-current bg-active)
(bg-tab-other bg-dim)
;; Make the fringe invisible
(fringe unspecified)
;; org-mode src block
(bg-prose-block-contents bg-dim) ;; bg-magenta-nuanced
(bg-prose-block-delimiter unspeficied)
(fg-prose-block-delimiter fg-dim)
))
(setq modus-themes-italic-constructs t
modus-themes-bold-constructs t
modus-themes-mixed-fonts t
modus-themes-variable-pitch-ui t
modus-themes-custom-auto-reload t
modus-themes-disable-other-themes t
modus-themes-prompts '(italic bold)
modus-themes-completions '((matches . (extrabold)) (selection . (semibold italic text-also)))
modus-themes-headings
'(
;; title
(0 . (variable-pitch light 2.0))
(1 . (variable-pitch light 1.8))
(2 . (variable-pitch light 1.5))
(3 . (variable-pitch regular 1.6))
(4 . (variable-pitch regular 1.5))
(5 . (variable-pitch 1.4))
(6 . (variable-pitch 1.3))
(7 . (variable-pitch 1.2))
(agenda-date . (1.3))
(agenda-structure . (variable-pitch light 1.8))
(t . (variable-pitch 1.1))))
(load-theme 'modus-vivendi :no-confirm) ;; modus-operandi
)
A minimal ui for completion-in-region。corfu 与 orderless 的匹配性更好,比如可以对候选词使用 orderless 的过滤 方式。但是 company-mode 与 orderless 的匹配性不好,不能使用空格,模糊匹配等特性。
(use-package corfu
:straight (:host github :repo "minad/corfu" :files ("*.el" "extensions/*.el"))
:init
(global-corfu-mode)
;;(corfu-echo-mode)
;;(corfu-history-mode) ;; 需要和 company-mode 联合使用。
(corfu-popupinfo-mode)
;; TAB-and-Go completion
:bind
(:map corfu-map
("TAB" . corfu-next)
([tab] . corfu-next)
("S-TAB" . corfu-previous)
([backtab] . corfu-previous))
:custom
(corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
;;(corfu-auto t) ;; Enable auto completion
(corfu-separator ?\s) ;; Orderless field separator
(corfu-quit-at-boundary nil) ;; Never quit at completion boundary
(corfu-quit-no-match nil) ;; Never quit, even if there is no match
(corfu-preview-current nil) ;; Disable current candidate preview
(corfu-preselect 'prompt) ;; Preselect the prompt
(corfu-on-exact-match nil) ;; Configure handling of exact matches
(corfu-scroll-margin 5) ;; Use scroll margin
;; (corfu-min-width 80)
;; (corfu-max-width corfu-min-width)
;; (corfu-count 14)
;; (corfu-scroll-margin 4)
:hook ((prog-mode . corfu-mode)
(shell-mode . corfu-mode)
(eshell-mode . corfu-mode))
:config
;; Enable `corfu-history-mode' to sort candidates by their history position.
(savehist-mode 1)
(add-to-list 'savehist-additional-variables 'corfu-history)
(defun corfu-enable-always-in-minibuffer ()
(setq-local corfu-auto nil)
(corfu-mode 1))
(add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1))
;; A few more useful configurations...
(use-package emacs
:init
;; 总是在弹出菜单中显示候选者。
;; TAB cycle if there are only few candidates
(setq completion-cycle-threshold nil)
;; 使用 TAB 来 indentation+completion(completion-at-point 默认是 M-TAB) 。
(setq tab-always-indent 'complete))
;; Add extensions
(use-package cape
:straight (:host github :repo "minad/cape")
:disabled
:demand
;; Bind dedicated completion commands
;; Alternative prefix keys: C-c p, M-p, M-+, ...
:bind (("C-c p p" . completion-at-point) ;; capf
("C-c p t" . complete-tag) ;; etags
("C-c p d" . cape-dabbrev) ;; or dabbrev-completion
("C-c p h" . cape-history)
("C-c p f" . cape-file)
("C-c p k" . cape-keyword)
("C-c p s" . cape-symbol)
("C-c p a" . cape-abbrev)
("C-c p l" . cape-line)
("C-c p w" . cape-dict)
("C-c p \\" . cape-tex)
("C-c p _" . cape-tex)
("C-c p ^" . cape-tex)
("C-c p &" . cape-sgml)
("C-c p r" . cape-rfc1345))
:init
;; completion-at-point 使用的函数列表,注意顺序。
(add-to-list 'completion-at-point-functions #'cape-symbol)
;;(add-to-list 'completion-at-point-functions #'cape-keyword)
(add-to-list 'completion-at-point-functions #'cape-file)
;;(add-to-list 'completion-at-point-functions #'cape-dabbrev)
;;(add-to-list 'completion-at-point-functions #'cape-elisp-block)
;;(add-to-list 'completion-at-point-functions #'cape-history)
;;(add-to-list 'completion-at-point-functions #'cape-tex)
;;(add-to-list 'completion-at-point-functions #'cape-sgml)
;;(add-to-list 'completion-at-point-functions #'cape-rfc1345)
;;(add-to-list 'completion-at-point-functions #'cape-abbrev)
;;(add-to-list 'completion-at-point-functions #'cape-dict)
;;(add-to-list 'completion-at-point-functions #'cape-line)
:config
(setq dabbrev-check-other-buffers nil
dabbrev-check-all-buffers nil
cape-dabbrev-min-length 3)
;; 前缀长度达到 3 时才调用 CAPF,避免频繁调用自动补全。
(cape-wrap-prefix-length #'cape-dabbrev 3))
(use-package kind-icon
:straight '(kind-icon :host github :repo "jdtsmith/kind-icon")
:after corfu
:demand
:custom
(kind-icon-default-face 'corfu-default)
:config
(add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
elgot 使用 Emacs 内置的 flymake(而非 flycheck)、xref、eldoc、project。
(use-package eglot
:ensure t
:defer t
:straight (:type built-in)
:bind (:map eglot-mode-map
("C-c C-a" . eglot-code-actions)
;; 如果 buffer 出现错误的诊断消息,可以执行 flymake-start 命令来重新触发诊断。
("C-c C-c" . flymake-start)
("C-c C-d" . eldoc)
("C-c C-f" . eglot-format-buffer)
("C-c C-r" . eglot-rename)
)
:config
(setq eglot-events-buffer-size 0)
;; 将 flymake-no-changes-timeout 设置为 nil 后,eglot 在保存 buffer 内容后,经过 idle time 才会显示 LSP 发送
;; 的诊断消息。
(setq eglot-send-changes-idle-time 0.3)
(add-hook 'prog-mode-hook #'eglot-ensure)
;; 在 eldoc bufer 中只显示帮助文档。
(defun /eglot-managed-mode-initialize ()
;; 不显示 flymake 错误和函数签名,放置后续的 eldoc buffer 内容来回变。
(setq-local
eldoc-documentation-functions
(list
;; 关闭自动在 eldoc 显示 flymake 的错误, 这样 eldoc 只显示函数签名或文档,后续 flymake 的错误单独在
;; echo area 显示。
;;#'flymake-eldoc-function
;;#'eglot-signature-eldoc-function ;; 关闭自动在 eldoc 自动显示函数签名,使用 M-x eldoc 手动显示函数帮助。
#'eglot-hover-eldoc-function))
;; 在单独的 buffer 中显示 eldoc 而非 echo area。
(setq-local
eldoc-display-functions
(list
;;#'eldoc-display-in-echo-area
#'eldoc-display-in-buffer)))
(add-hook 'eglot-managed-mode-hook #'/eglot-managed-mode-initialize))
;; https://go.googlesource.com/tools/+/refs/heads/master/gopls/doc/emacs.md
;; Optional: install eglot-format-buffer as a save hook.
;; The depth of -10 places this before eglot's willSave notification,
;; so that that notification reports the actual contents that will be saved.
(defun eglot-format-buffer-on-save ()
(add-hook 'before-save-hook #'eglot-format-buffer -10 t))
(add-hook 'go-ts-mode-hook #'eglot-format-buffer-on-save)
(add-hook 'go-ts-mode-hook #'eglot-ensure)
(setq-default eglot-workspace-configuration
'((:gopls .
((staticcheck . t)
(matcher . "CaseSensitive")))))
- 更新内置的 elgot: M-x eglot-upgrade-eglot。
- eldoc,eldoc-doc-buffer: C-h-.
consult-eglot 提供 consult-eglot-symbols 函数,可以选择 workspace 中的 symbol:
(use-package consult-eglot
:straight (:host github :repo "mohkale/consult-eglot")
:after (eglot consult))
;; cape 补全融合
(use-package cape)
(defalias #'my/eglot-citre-capf
(cape-super-capf #'eglot-completion-at-point #'citre-completion-at-point))
(defun my/toggle-citre-eglot-capf ()
(if (eglot-managed-p)
(add-to-list 'completion-at-point-functions #'my/eglot-citre-capf)
(setq-local completion-at-point-functions
(delq #'my/eglot-citre-capf completion-at-point-functions))))
(add-hook
'eglot-managed-mode-hook #'my/toggle-citre-eglot-capf)
(defun tab-to-eshell ()
"Open a tab with eshell. If that tab doesn't exist, create it. If
already in that tab, switch to previous tab."
(interactive)
(if (equal (tab-bar-tab-name-current) "*eshell*<43>")
(tab-bar-switch-to-prev-tab)
(eshell 43)))
(add-to-list 'display-buffer-alist
'("\\*eshell\\*<43>"
(display-buffer-in-tab)
(tab-name . "*eshell*<43>")))
(use-package hydra)
;; dump-jump 使用 ag、rg 来实时搜索当前项目文件来进行定位和跳转。相比
;; 使用 TAGS 的 citre(适合静态浏览)以及 lsp 方案,更通用和轻量。
(use-package dumb-jump
:demand
:config
;; xref 默认将 elisp--xref-backend 加到 backend 的最后面,它使用
;; etags 作为数据源。将 dump-jump 加到 xref 后端中,作为其它 backend,
;; 如 citre 的后备。加到 xref 后端后,可以使用 M-. 和 M-? 来跳转。
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
;; dumb-jump 发现支持的语言和项目后,会自动生效。
;;; 将 Go module 文件作为 project root 标识。
(add-to-list 'dumb-jump-project-denoters "go.mod")
(defhydra dumb-jump-hydra (:color blue :columns 3)
"Dumb Jump"
("j" dumb-jump-go "Go")
("o" dumb-jump-go-other-window "Other window")
("e" dumb-jump-go-prefer-external "Go external")
("x" dumb-jump-go-prefer-external-other-window "Go external other window")
("i" dumb-jump-go-prompt "Prompt")
("l" dumb-jump-quick-look "Quick look")
("b" dumb-jump-back "Back"))
(keymap-set dumb-jump-mode-map "C-M-y" #'dumb-jump-hydra/body))
不再使用了,而是直接设置 emacs 的exec-path 和PATH 环境变量。
exec-path-from-shell
将 Shell
环境变量拷贝到 Emacs
:
(use-package exec-path-from-shell
:disabled
:custom
;; 去掉 -l 参数, 加快启动速度。
(exec-path-from-shell-arguments '("-l"))
(exec-path-from-shell-check-startup-files nil)
(exec-path-from-shell-variables '("PATH" "MANPATH" "GOPATH" "GOPROXY" "GOPRIVATE" "GOFLAGS" "GO111MODULE" "PYTHONPATH"))
:config
(when (memq window-system '(mac ns x))
(exec-path-from-shell-initialize)))
lsp-bridge 依赖如下 python packages:
pip3 install epc orjson sexpdata six paramiko
(use-package posframe)
;; dump-jump 支持 GNU Global 的 gtags 跳转。
(use-package dumb-jump
:demand
:init
(setq xref-show-definitions-function #'xref-show-definitions-completing-read)
:config
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
;; 融合 `lsp-bridge' `find-function' 以及 `dumb-jump' 的智能跳转。
(defun lsp-bridge-jump ()
(interactive)
(cond
((eq major-mode 'emacs-lisp-mode)
(let ((symb (function-called-at-point)))
(when symb
(find-function symb))))
(lsp-bridge-mode
(lsp-bridge-find-def))
(t
(require 'dumb-jump)
(dumb-jump-go))))
(defun lsp-bridge-jump-back ()
(interactive)
(cond
(lsp-bridge-mode
(lsp-bridge-return-from-def))
(t
(require 'dumb-jump)
(dumb-jump-back))))
(use-package lsp-bridge
:after (markdown-mode)
;;:no-native-compile t: straight pull 包后不编译为 elc。
:straight (:no-native-compile t :host github :repo "manateelazycat/lsp-bridge" :files ("*" "acm/*"))
:custom
;; 不在 modeline 显示 lsp-bridge 信息。
(lsp-bridge-enable-mode-line nil)
:config
;; 调试模式。
(setq lsp-bridge-enable-log nil)
(setq lsp-bridge-enable-hover-diagnostic t)
(setq lsp-bridge-diagnostic-fetch-idle 1) ;; default 0.5
(setq lsp-bridge-diagnostic-tooltip-border-width 0)
(setq lsp-bridge-diagnostic-display-errors-delay 3)
(setq lsp-bridge-diagnostic-hide-severities '(2 3 4)) ;; 过滤 warnning.
;; 关闭 code action 的 popup-menu。
(setq lsp-bridge-code-action-enable-popup-menu nil)
(setq lsp-bridge-enable-signature-help t)
;; 关闭 word 补全。
(setq acm-enable-search-file-words nil)
(setq lsp-bridge-enable-search-words nil)
;; 自动补全。
(setq acm-backend-lsp-enable-auto-import t)
(setq acm-backend-lsp-candidate-min-length 0)
(setq acm-backend-lsp-candidate-max-length 100)
;; 使用 orderless flex 过滤。
(setq acm-candidate-match-function 'orderless-flex)
;; 显示 incon。
(setq acm-enable-icon t)
;; 关闭不必要的配置。
(setq acm-enable-doc nil)
(setq acm-enable-telega nil)
(setq acm-enable-tabnine nil)
;; 关闭 yas 补全。
(setq acm-enable-yas nil)
;;开启 citre 集成。
(setq acm-enable-citre t)
(add-to-list 'lsp-bridge-org-babel-lang-list "emacs-lisp")
(add-to-list 'lsp-bridge-org-babel-lang-list "sh")
(add-to-list 'lsp-bridge-org-babel-lang-list "shell")
(add-to-list 'lsp-bridge-org-babel-lang-list "go")
(global-lsp-bridge-mode)
;; M-j 被预留给 pyim 使用。
(define-key acm-mode-map (kbd "M-j") nil)
(define-key lsp-bridge-mode-map (kbd "M-.") #'lsp-bridge-find-def)
(define-key lsp-bridge-mode-map (kbd "C-M-.") #'lsp-bridge-find-def-other-window)
(define-key lsp-bridge-mode-map (kbd "M-,") #'lsp-bridge-find-def-return)
(define-key lsp-bridge-mode-map (kbd "M-?") #'lsp-bridge-find-references)
(define-key lsp-bridge-mode-map (kbd "M-d") #'lsp-bridge-popup-documentation)
(define-key lsp-bridge-mode-map (kbd "C-c C-a") #'lsp-bridge-code-action)
(define-key lsp-bridge-mode-map (kbd "C-c C-f") #'lsp-bridge-code-format)
(define-key lsp-bridge-mode-map (kbd "C-s-l") #'lsp-bridge-diagnostic-list)
(define-key lsp-bridge-mode-map (kbd "C-s-n") #'lsp-bridge-diagnostic-jump-next)
(define-key lsp-bridge-mode-map (kbd "C-s-p") #'lsp-bridge-diagnostic-jump-prev))
- acm-backend-search-words, 用来替换 acm-backend-dabbrev, acm-backend-search-words 后端的原理是, Emacs 告诉 Python 进程哪些文件被打开了, Python 进程就开一个子线程在后台计算每个文件的单词, 并对提取的单词进行清洗、去 重和排序。
- M-x lsp-bridge-toggle-english-helper: 快速切换英文单词补全后端;
lsp-bridge 在开启了编译后,大概率会报错,故关闭:
((emacs-lisp-mode . ((no-byte-compile . t))))
由于 lsp-bridge mode map 将 M-./M-,/M-? 等 xref 相关命令绑定到自己的 lsp-bridge 相关函数,但是当 lsp-bridge 的补全、跳转等不可用时,需要使用 citre-mode 的 xref 集成特性的化,就需要使用 M-x xref-xxx 等命令。这时可以在 project root 目录创建一个 .dir-locals.el 文件来为项目所有文件关闭 lsp-mode:
;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
;;; disable lsp-mode and enable ggtags-mode
((nil . ((eval . (lsp-bridge-mode -1)))
))
为了方便输入,可以在 ~/.emacs.d/snippets/prog-mode 下创建一个名为 disable-lsp-bridge 的 snippet,后 续可以使用M-x consult-yasnippet 来快速在 .dir-locals.el 文件中插入 snippet 文件内容。
# -*- mode: snippet -*-
# name: disable-lsp-bridge
# key: dlspb
# --
;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
;;; .dir-locals.el
;;; disable lsp-mode and enable ggtags-mode
((nil . ((eval . (lsp-bridge-mode -1)))
))
通过上面的 .dir-locals.el 机制来在项目级别关闭 lsp-bridge 后,M-./M-,/M-? 恢复绑定到 consult-xref 来 跳转和预览。
修改 gopls 配置:
- 添加
"verboseOutput": true
配置参数将打开 gopls 调试模式; - 去掉配置参数
"env": {"GOFLAGS": "-mod=mod"},
可以防止带有 vendor 目录的项目报错: go: inconsistent vendoring;- 项目如果要使用 vendor, 则可以使用 .envrc 添加项目相关的环境变量:”export GOFLAGS=’-mod=vendor’”
- usePlaceholders 依赖于 yasnippet, 使用时容易出错,故关闭。lsp-bridge 在 echo area 提供了 signature-help 的功 能,也能方便的看到函数签名。
- json 配置必须保存到 straight/repos 目录下(而非 build 目录)下才生效。
{
"name": "gopls",
"languageId": "go",
"command": ["gopls", "-remote=auto"],
"settings":{
"gopls": {
"usePlaceholders": false,
"completeUnimported": true,
"staticcheck": true,
"allowModfileModifications": true,
"allowImplicitNetworkAccess": true
}
}
}
目录变量是只对特定目录及子目录有效的 shell 环境变量,主要使用场景是根据目录变量来指定版本的 python 和 golang。
先安装 direnv 命令 brew install direnv
, 然后在 ~/.bashrc
中添加:
eval "$(direnv hook bash)"
安装 emacs envrc 软件包,它调用 direnv 命令获取当前文件或目录的环境变量,然后更新 emacs 变量
process-environment
和 exec-path
,这样 emacs 后续启动的命令就会继承这些环境变量(direnv 包是全局的,而 envrc
是 buffer-local 的):
which direnv || brew install direnv
(use-package envrc :hook (after-init . envrc-global-mode))
- C-c e a: envrc-allow
- C-c e d: envrc-deny
- C-c e r: envrc-reload
使用步骤:
- 在对应目录创建
.envrc
文件; - 向 .envrc 文件添加 shell 环境变量;
- 执行
direnv allow .
生效环境变量;
如果某些变量未被 lsp 识别,则需要打开 .envrc 所在目录的文件后执行 M-x lsp-workspace-restart
来重启 lsp。
pyenv
和 pyenv-virtualenv
可以为项目或系统指定不同隔离的 python 或 venv 版本。
which pyenv || brew install --HEAD pyenv
which pyenv-virtualenv || brew install --HEAD pyenv-virtualenv
为了在进入项目目录时自动切换到指定 pyenv 或 venv 版本,需要配置 ~/.bashrc
:
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
eval "$(jenv init -)"
pyenv 工作流:
- 列出可以安装的 python 版本:
pyenv install -l
- 安装指定的 python 版本:
pyenv install <version>
- 创建一个 pyenv virtualenv:
pyenv virtualenv [version] <virtualenv-name>
- 为项目指定 python 版本或上一步创建的 virtualenv 名称:
- 在项目根目录执行
pyenv local <version1> <version2>
- 在项目根目录执行
- 指定用户默认使用 3.9.0 python 版本:
- 在家目录执行命令: cd ~ && pyenv local 3.9.0
- 后续家目录下执行 pip 命令安装的包都是 3.x 版本。
- 如果虚拟环境中没有 pip 命令,安装:
python -m ensurepip
高性能的, 使用 tree-sitter 进行展开和隐藏。
- 依赖于非内置的 elisp-tree-sitter, 暂时禁用。
(use-package ts-movement
:disabled
:straight (ts-movement :type git :host github :repo "haritkapadia/ts-movement")
:hook
(bash-ts-mode-hook . ts-movement-mode)
(c++-ts-mode-hook . ts-movement-mode)
(c-ts-mode-hook . ts-movement-mode)
(cmake-ts-mode-hook . ts-movement-mode)
(csharp-ts-mode-hook . ts-movement-mode)
(css-ts-mode-hook . ts-movement-mode)
(dockerfile-ts-mode-hook . ts-movement-mode)
(go-ts-mode-hook . ts-movement-mode)
(java-ts-mode-hook . ts-movement-mode)
(js-ts-mode-hook . ts-movement-mode)
(json-ts-mode-hook . ts-movement-mode)
(python-ts-mode-hook . ts-movement-mode)
(ruby-ts-mode-hook . ts-movement-mode)
(rust-ts-mode-hook . ts-movement-mode)
(toml-ts-mode-hook . ts-movement-mode)
(tsx-ts-mode-hook . ts-movement-mode)
(typescript-ts-mode-hook . ts-movement-mode)
(yaml-ts-mode-hook . ts-movement-mode))
(use-package ts-fold
:straight (ts-fold :host github :repo "emacs-tree-sitter/ts-fold")
:disabled
:config
(global-ts-fold-mode)
(global-set-key (kbd "C-c C-<tab>") 'ts-fold-toggle)
;; indicators 影响性能;
;; (add-hook 'tree-sitter-after-on-hook #'ts-fold-indicators-mode)
)
(use-package yasnippet
:init
(defvar snippet-directory "~/.emacs.d/snippets")
:hook
((prog-mode org-mode vterm-mode) . yas-minor-mode)
:config
(add-to-list 'yas-snippet-dirs snippet-directory)
;; 保留 snippet 的缩进。
(setq yas-indent-line 'fixed)
(yas-global-mode 1))
(use-package consult-yasnippet
:after(consult yasnippet)
:config
(define-key yas-minor-mode-map (kbd "C-c y") #'consult-yasnippet))
;; 避免报错:Symbol’s function definition is void: yasnippet-snippets--fixed-indent
(use-package yasnippet-snippets :after(yasnippet))
- 完全输入
snippet
简写后,按TAB
自动扩展。 - 从 yasnippet-snippets 获取 snippets, 然后放到 snippet-directory 目录下,这样更容易定制。
;; 从各种 Mac 应用(如 finder/浏览器)获取 org-mode 链接。
(use-package org-mac-link
:commands (org-mac-grab-link))
;; 使用 font-lock 来隐藏中文前后的空格。
;; https://emacs-china.org/t/org-mode/22313
(font-lock-add-keywords 'org-mode
'(("\\cc\\( \\)[/+*_=~][^a-zA-Z0-9/+*_=~\n]+?[/+*_=~]\\( \\)?\\cc?"
(1 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))
("\\cc?\\( \\)?[/+*_=~][^a-zA-Z0-9/+*_=~\n]+?[/+*_=~]\\( \\)\\cc"
(2 (prog1 () (compose-region (match-beginning 2) (match-end 2) "")))))
'append)
;; 导出时删除空格。
(with-eval-after-load 'ox
(defun eli-strip-ws-maybe (text _backend _info)
(let* ((text (replace-regexp-in-string
"\\(\\cc\\) *\n *\\(\\cc\\)"
"\\1\\2" text));; remove whitespace from line break
;; remove whitespace from `org-emphasis-alist'
(text (replace-regexp-in-string "\\(\\cc\\) \\(.*?\\) \\(\\cc\\)"
"\\1\\2\\3" text))
;; restore whitespace between English words and Chinese words
(text (replace-regexp-in-string "\\(\\cc\\)\\(\\(?:<[^>]+>\\)?[a-z0-9A-Z-]+\\(?:<[^>]+>\\)?\\)\\(\\cc\\)"
"\\1 \\2 \\3" text)))
text))
(add-to-list 'org-export-filter-paragraph-functions #'eli-strip-ws-maybe))
;; 编辑时显示隐藏的标记。
(use-package org-appear
:config
(add-hook 'org-mode-hook 'org-appear-mode)
;; 删除 * 和 / 类型的标记。
;; (setq org-appear-elements '(underline strike-through verbatim code))
)
;; 为 #+begin_quote 和 #+begin_verse 添加特殊 face 。
(setq org-fontify-quote-and-verse-blocks t)
;; 不自动缩进。
(setq org-src-preserve-indentation t)
(setq org-edit-src-content-indentation 0)
;; 在当前窗口编辑 SRC Block.
;; 2023.04.05 设置为 current-window 后会导致 src window 不退出。
;;(setq org-src-window-setup 'current-window)
;; 修复报错: org-journal-display-entry: Symbol’s value as variable is void: displayed-month
;; https://github.com/bastibe/org-journal/commit/1de9153f2120e92779d95d9e13f249e98ff1ad14
;; (defun org-journal-display-entry (_arg &optional event)
;; "Display journal entry for selected date in another window."
;; (interactive
;; (list current-prefix-arg last-nonmenu-event))
;; (let* ((time (or (ignore-errors (org-journal-calendar-date->time (calendar-cursor-to-date t event)))
;; (org-time-string-to-time (org-read-date nil nil nil "Date:")))))
;; ;; (let* ((time (org-journal--calendar-date->time
;; ;; (calendar-cursor-to-date t event))))
;; (org-journal-read-or-display-entry time t)))
为 org-mode 和 org-journal-mode 设置关键字提示:
(dolist (m '(org-mode org-journal-mode))
(font-lock-add-keywords m ; A bit silly but my headers are now
`(("^\\*+ \\(TODO\\) " ; shorter, and that is nice canceled
(1 (progn (compose-region (match-beginning 1) (match-end 1) "⚑") nil)))
("^\\*+ \\(DOING\\) "
(1 (progn (compose-region (match-beginning 1) (match-end 1) "⚐") nil)))
("^\\*+ \\(CANCELED\\) "
(1 (progn (compose-region (match-beginning 1) (match-end 1) "✘") nil)))
("^\\*+ \\(BLOCKED\\) "
(1 (progn (compose-region (match-beginning 1) (match-end 1) "✋") nil)))
("^\\*+ \\(DONE\\) "
(1 (progn (compose-region (match-beginning 1) (match-end 1) "✔") nil)))
;; Here is my approach for making the initial asterisks for listing items and
;; whatnot, appear as Unicode bullets ;; (without actually affecting the text
;; file or the behavior).
("^ +\\([-*]\\) "
(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•")))))))
(defun my/hugo-newpost (slug title tags)
(interactive "sSlug: \nsTitle: \nsTags: \n")
(let* ((categories (choose-blog-categories))
(now (current-time))
(basename (concat (format-time-string "%Y-%m-%d-" now)
slug ".org"))
(postdir (expand-file-name "content/post" (locate-dominating-file default-directory "config.toml")))
(filename (expand-file-name basename postdir))
(create-date (my/iso-8601-date-string)))
(when (file-exists-p filename)
(error "%s already exists!" filename))
(find-file filename)
(insert
(format "#+TITLE: %s\n#+DATE: %s\n#+LASTMOD: %s\n#+TAGS[]: %s\n#+CATEGORIES[]: %s\n"
title create-date create-date tags categories))
(goto-char (point-max))
(save-buffer)))
(setq org-link-abbrev-alist
'(
("tsfile" . "/Users/zhangjun/org/memacs/filenametimestamps.org_archive::/\*.*%s/")
))
;; kill 所有 magit buffer。
;; (defun my-magit-kill-buffers (&rest _)
;; "Restore window configuration and kill all Magit buffers."
;; (interactive)
;; (magit-restore-window-configuration)
;; (let ((buffers (magit-mode-get-buffers)))
;; (when (eq major-mode 'magit-status-mode)
;; (mapc (lambda (buf)
;; (with-current-buffer buf
;; (if (and magit-this-process
;; (eq (process-status magit-this-process) 'run))
;; (bury-buffer buf)
;; (kill-buffer buf))))
;; buffers))))
;; (setq magit-bury-buffer-function #'my-magit-kill-buffers)
使用更好的 olivetti mode 替换。
内容居中显示:
(defun my/org-mode-visual-fill (fill width)
;; 使用 setq-default 来设置, 否则可能不生效。
(setq-default
;; 自动换行的字符数。
fill-column fill
;; 窗口可视化行宽度,值应该比 fill-column 大,否则超出的字符被隐藏。
visual-fill-column-width width
visual-fill-column-fringes-outside-margins nil
visual-fill-column-center-text t)
(visual-fill-column-mode 1))
(use-package visual-fill-column
:after (org)
:hook
(org-mode . (lambda () (my/org-mode-visual-fill 100 130)))
:config
;; 文字缩放时自动调整 visual-fill-column-width 。
(advice-add 'text-scale-adjust :after #'visual-fill-column-adjust))
- 如果文字居中失效, 可以执行
M-x redraw-display
命令生效。
;; https://github.com/redguardtoo/emacs.d/blob/master/lisp/init-gtags.el
(defun my-gtags-produce-tags-if-needed (directory)
"Product gtags tags file (index file) from DIRECTORY."
(if (not (= 0 (call-process "global" nil nil nil " -p")))
(let ((default-directory directory))
(shell-command "gtags")
(message "Tag file was created by GNU Global."))
;; Tag file already exists; update it
(shell-command "global -u")
(message "Tag file was updated by GNU Global.")))
;; @see http://emacs-fu.blogspot.com.au/2008/01/navigating-through-source-code-using.html
(defun my-gtags-create-or-update ()
"Create or update the gnu global tag file."
(interactive)
(my-gtags-produce-tags-if-needed (read-directory-name
"gtags: top of source tree:" default-directory)))
(defun my-gtags-add-gtagslibpath (libdir)
"Add external library directory LIBDIR to gtags' environment variable.
Gtags scans that directory if needed."
(interactive "DDirectory containing GTAGS:\nP")
(let (sl
(gtags-lib-path (getenv "GTAGSLIBPATH")))
(unless (file-exists-p (concat (file-name-as-directory libdir) "GTAGS"))
;; create tags
(let ((default-directory libdir))
(shell-command "gtags")
(message "tag file is created by GNU Global")))
(setq libdir (directory-file-name libdir)) ;remove final slash
(setq sl (split-string (or gtags-lib-path "") ":" t))
(setq sl (delete libdir sl))
(push libdir sl)
(setenv "GTAGSLIBPATH" (mapconcat 'identity sl ":"))))
(defun my-gtags-print-gtagslibpath ()
"Print the gtags default library path (for debug purpose)."
(interactive)
(message "GTAGSLIBPATH=%s" (getenv "GTAGSLIBPATH")))
;; 创建名为 *tmp-<N>* 的临时 buffer;
(defun create-temp-buffer ()
"Create a new temporary buffer with a specific prefix."
(interactive)
(let ((temp-buffer-prefix "tmp-")
(buffer-counter 1))
(while (get-buffer (format "*%s%d*" temp-buffer-prefix buffer-counter))
(setq buffer-counter (1+ buffer-counter)))
(switch-to-buffer (format "*%s%d*" temp-buffer-prefix buffer-counter))))
(global-set-key (kbd "C-c t") 'create-temp-buffer)
;; https://www.emacswiki.org/emacs/AsciiTable
(defun my/ascii-table ()
"Display basic ASCII table (0 thru 128).
https://ss64.com/ascii.html
https://www.emacswiki.org/emacs/ascii-table.el"
(interactive)
(switch-to-buffer "*ASCII*")
(erase-buffer)
(setq buffer-read-only nil) ;; Not need to edit the content, just read mode (added)
(local-set-key "q" 'bury-buffer) ;; Nice to have the option to bury the buffer (added)
(setq lower32 '("nul" "soh" "stx" "etx" "eot" "enq" "ack" "bel"
"bs" "ht" "nl" "vt" "np" "cr" "so" "si"
"dle" "dc1" "dc2" "dc3" "dc4" "nak" "syn" "etb"
"can" "em" "sub" "esc" "fs" "gs" "rs" "us"
))
(save-excursion (let ((i -1))
(insert "ASCII characters 0 thru 127.\n\n")
(insert " Hex Dec Char| Hex Dec Char| Hex Dec Char| Hex Dec Char\n")
(while (< i 31)
(insert (format "%4x %4d %4s | %4x %4d %4s | %4x %4d %4s | %4x %4d %4s\n"
(setq i (+ 1 i)) i (elt lower32 i)
(setq i (+ 32 i)) i (single-key-description i)
(setq i (+ 32 i)) i (single-key-description i)
(setq i (+ 32 i)) i (single-key-description i)))
(setq i (- i 96))))))
;; 使用 fundamental-mode 打开大文件。
(defun my/large-file-hook ()
(when (or (string-equal (file-name-extension (buffer-file-name)) "json")
(string-equal (file-name-extension (buffer-file-name)) "yaml")
(string-equal (file-name-extension (buffer-file-name)) "yml")
(string-equal (file-name-extension (buffer-file-name)) "log"))
(setq buffer-read-only t)
(font-lock-mode -1)
;;(yas-minor-mode -1)
(smartparens-mode -1)
(show-smartparens-mode -1)
(show-paren-mode -1)
(js2-minor-mode -1)
;;(fira-code-mode -1)
(prettify-symbols-mode -1)
;;(symbol-overlay-mode -1)
;;(lsp-bridge-mode -1)
(display-line-numbers-mode -1)
(highlight-indent-guides-mode -1)
(visual-fill-column-mode -1)
(rainbow-delimiters-mode -1)))
(add-hook 'find-file-hook 'my/large-file-hook)
已被更先进和活跃的 indent-bars 取代。
(use-package highlight-indent-guides
:custom
(highlight-indent-guides-method 'column)
(highlight-indent-guides-responsive 'top)
(highlight-indent-guides-suppress-auto-error t)
:config
(add-hook 'python-mode-hook 'highlight-indent-guides-mode)
(add-hook 'python-ts-mode-hook 'highlight-indent-guides-mode)
(add-hook 'yaml-mode-hook 'highlight-indent-guides-mode)
(add-hook 'yaml-ts-mode-hook 'highlight-indent-guides-mode)
(add-hook 'js-mode-hook 'highlight-indent-guides-mode)
(add-hook 'js-ts-mode-hook 'highlight-indent-guides-mode)
(add-hook 'web-mode-hook 'highlight-indent-guides-mode))
(use-package sort-tab
:demand
:vc (:fetcher github :repo manateelazycat/sort-tab)
;; emacs 启动后再启用 sort-tab 防止显示异常。
:hook (after-init . sort-tab-mode)
:config
;;(sort-tab-mode 1)
(setq sort-tab-show-index-number t)
(setq sort-tab-height 40)
(global-set-key (kbd "s-n") 'sort-tab-select-next-tab)
(global-set-key (kbd "s-p") 'sort-tab-select-prev-tab)
(global-set-key (kbd "s-w") 'sort-tab-close-current-tab)
;; (global-set-key (kbd "s-0") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-1") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-2") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-3") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-4") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-5") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-6") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-7") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-8") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-9") 'sort-tab-select-visible-tab)
;; 设置 tab 颜色,M-x list-colors-display。
(set-face-foreground 'sort-tab-current-tab-face "peru")
;; 不显示背景颜色。
(set-face-background 'sort-tab-current-tab-face nil))
;; Garbage Collector Magic Hack
(use-package gcmh
:init
;;(setq garbage-collection-messages t)
;;(setq gcmh-verbose t)
(setq gcmh-idle-delay 5)
(setq gcmh-high-cons-threshold (* 100 1024 1024))
(gcmh-mode 1)
(gcmh-set-high-threshold))
;; 缺省字体;
(setq +font-family "Iosevka Comfy")
(setq +modeline-font-family "Iosevka Comfy")
(setq +fixed-pitch-family "Iosevka Comfy")
(setq +variable-pitch-family "LXGW WenKai Screen")
(setq +font-unicode-family "LXGW WenKai Screen")
;; 中文字体和英文字体按照 1:1 缩放,在偶数字号的情况下可以实现等宽等高。
(setq face-font-rescale-alist '(("LXGW WenKai Screen" . 1))) ;; 1:1 缩放。
(setq +font-size 14) ;; 偶数字号。
;; 只为缺省字体设置 size, 其它字体都通过 :height 动态伸缩。
(defun +load-base-font ()
(let* ((font-spec (format "%s-%d" +font-family +font-size)))
(set-frame-parameter nil 'font font-spec)
(add-to-list 'default-frame-alist `(font . ,font-spec))))
;; 设置各特定 face 的字体。
(defun +load-face-font (&optional frame)
(let ((font-spec (format "%s" +font-family))
(modeline-font-spec (format "%s" +modeline-font-family))
(variable-pitch-font-spec (format "%s" +variable-pitch-family))
(fixed-pitch-font-spec (format "%s" +fixed-pitch-family)))
(set-face-attribute 'variable-pitch frame :font variable-pitch-font-spec)
(set-face-attribute 'fixed-pitch frame :font fixed-pitch-font-spec)
(set-face-attribute 'fixed-pitch-serif frame :font fixed-pitch-font-spec)
(set-face-attribute 'tab-bar frame :font font-spec)
(set-face-attribute 'mode-line frame :font modeline-font-spec)
(set-face-attribute 'mode-line-inactive frame :font modeline-font-spec)))
;; 设置中文字体。
(defun +load-ext-font ()
(when window-system
(let ((font (frame-parameter nil 'font))
(font-spec (font-spec :family +font-unicode-family)))
(dolist (charset '(kana han hangul cjk-misc bopomofo))
(set-fontset-font font charset font-spec)))))
;; 设置 Emoji 和 Symbol “字体。
(defun +load-emoji-font ()
(when window-system
(setq use-default-font-for-symbols nil)
(set-fontset-font t 'emoji (font-spec :family "Apple Color Emoji")) ;; Noto Color Emoji
(set-fontset-font t 'symbol (font-spec :family "Symbola")))) ;; Apple Symbols vs Symbola
(add-hook 'after-make-frame-functions
( lambda (f)
(+load-face-font)
(+load-ext-font)
(+load-emoji-font)))
(defun +load-font ()
(+load-base-font)
(+load-face-font)
(+load-ext-font)
(+load-emoji-font))
(+load-font)
;; all-the-icons 只能在 GUI 模式下使用。
(when (display-graphic-p)
(use-package all-the-icons :demand))
2023.08.24 常会导致写代码的途中提前缩进, 故关闭。
(use-package aggressive-indent
:config
(global-aggressive-indent-mode 1)
(add-to-list 'aggressive-indent-excluded-modes 'html-mode))
(use-package org-modern-indent
:vc (:fetcher github :repo jdtsmith/org-modern-indent)
:config
(add-hook 'org-mode-hook #'org-modern-indent-mode 90))
emacs-eat 是高性能的终端仿真器。
- 如果提示错误:Error opening terminal: eat-truecolor., 可以执行命令
M-x eat-compile-terminfo
后重 启 eat 来解决。
(use-package eat
:config
;; eshell 集成,使 eshell 也能运行终端命令。
(add-hook 'eshell-load-hook #'eat-eshell-mode)
(add-hook 'eshell-load-hook #'eat-eshell-visual-command-mode))
bash 集成:在 ~/.bashrc
中添加如下内容:
[ -n "$EAT_SHELL_INTEGRATION_DIR" ] && \
source "$EAT_SHELL_INTEGRATION_DIR/bash"
(use-package elfeed
:demand
:config
(setq elfeed-db-directory (expand-file-name "elfeed" user-emacs-directory))
(setq elfeed-show-entry-switch 'display-buffer)
(setq elfeed-curl-max-connections 32)
(setq elfeed-curl-timeout 60)
(setf url-queue-timeout 120)
(push "-k" elfeed-curl-extra-arguments)
(setq elfeed-search-filter "@1-months-ago +unread")
;; 在同一个 buffer 中显示条目。
(setq elfeed-show-unique-buffers nil)
(setq elfeed-search-title-max-width 150)
(setq elfeed-search-date-format '("%Y-%m-%d %H:%M" 20 :left))
(setq elfeed-log-level 'warn)
;; 支持收藏 feed, 参考:http://pragmaticemacs.com/emacs/star-and-unstar-articles-in-elfeed/
(defalias 'elfeed-toggle-star (elfeed-expose #'elfeed-search-toggle-all 'star))
(eval-after-load 'elfeed-search '(define-key elfeed-search-mode-map (kbd "m") 'elfeed-toggle-star))
(defface elfeed-search-star-title-face '((t :foreground "#f77")) "Marks a starred Elfeed entry.")
(push '(star elfeed-search-star-title-face) elfeed-search-face-alist))
(use-package elfeed-org
:custom ((rmh-elfeed-org-files (list "~/.emacs.d/elfeed.org")))
:hook
((elfeed-dashboard-mode . elfeed-org)
(elfeed-show-mode . elfeed-org)))
(use-package elfeed-dashboard
:after (elfeed-org)
:config
;;(global-set-key (kbd "C-c f") 'elfeed-dashboard)
(setq elfeed-dashboard-file "~/.emacs.d/elfeed-dashboard.org")
(advice-add 'elfeed-search-quit-window :after #'elfeed-dashboard-update-links)
(defun my/reload-org-feeds ()
(interactive)
(rmh-elfeed-org-process rmh-elfeed-org-files rmh-elfeed-org-tree-id))
(advice-add 'elfeed-dashboard-update :before #'my/reload-org-feeds))
(use-package elfeed-score
:config
(progn
(elfeed-score-enable)
(define-key elfeed-search-mode-map "=" elfeed-score-map)))
(use-package elfeed-goodies
:config
(setq elfeed-goodies/entry-pane-position 'bottom)
(setq elfeed-goodies/feed-source-column-width 30)
(setq elfeed-goodies/tag-column-width 30)
(setq elfeed-goodies/powerline-default-separator 'arrow)
(elfeed-goodies/setup))
;; elfeed-goodies 显示日期栏
;;https://github.com/algernon/elfeed-goodies/issues/15#issuecomment-243358901
(defun elfeed-goodies/search-header-draw ()
"Returns the string to be used as the Elfeed header."
(if (zerop (elfeed-db-last-update))
(elfeed-search--intro-header)
(let* ((separator-left (intern (format "powerline-%s-%s"
elfeed-goodies/powerline-default-separator
(car powerline-default-separator-dir))))
(separator-right (intern (format "powerline-%s-%s"
elfeed-goodies/powerline-default-separator
(cdr powerline-default-separator-dir))))
(db-time (seconds-to-time (elfeed-db-last-update)))
(stats (-elfeed/feed-stats))
(search-filter (cond
(elfeed-search-filter-active
"")
(elfeed-search-filter
elfeed-search-filter)
(""))))
(if (>= (window-width) (* (frame-width) elfeed-goodies/wide-threshold))
(search-header/draw-wide separator-left separator-right search-filter stats db-time)
(search-header/draw-tight separator-left separator-right search-filter stats db-time)))))
(defun elfeed-goodies/entry-line-draw (entry)
"Print ENTRY to the buffer."
(let* ((title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
(date (elfeed-search-format-date (elfeed-entry-date entry)))
(title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
(feed (elfeed-entry-feed entry))
(feed-title
(when feed
(or (elfeed-meta feed :title) (elfeed-feed-title feed))))
(tags (mapcar #'symbol-name (elfeed-entry-tags entry)))
(tags-str (concat "[" (mapconcat 'identity tags ",") "]"))
(title-width (- (window-width) elfeed-goodies/feed-source-column-width
elfeed-goodies/tag-column-width 4))
(title-column (elfeed-format-column
title (elfeed-clamp
elfeed-search-title-min-width
title-width
title-width)
:left))
(tag-column (elfeed-format-column
tags-str (elfeed-clamp (length tags-str)
elfeed-goodies/tag-column-width
elfeed-goodies/tag-column-width)
:left))
(feed-column (elfeed-format-column
feed-title (elfeed-clamp elfeed-goodies/feed-source-column-width
elfeed-goodies/feed-source-column-width
elfeed-goodies/feed-source-column-width)
:left)))
(if (>= (window-width) (* (frame-width) elfeed-goodies/wide-threshold))
(progn
(insert (propertize date 'face 'elfeed-search-date-face) " ")
(insert (propertize feed-column 'face 'elfeed-search-feed-face) " ")
(insert (propertize tag-column 'face 'elfeed-search-tag-face) " ")
(insert (propertize title 'face title-faces 'kbd-help title)))
(insert (propertize title 'face title-faces 'kbd-help title)))))
elfeed-score 规则文件(语法参考):
;;; Elfeed score file -*- lisp -*-
(
;; ("title"
;; (:text "opsnull" :value 250 :type S))
;; ("content"
;; (:text "type erasure" :value 500 :type s))
("title-or-content"
;; (:text "emacs" :title-value 150 :content-value 100 :type s)
(:text "opsnull" :title-value 150 :content-value 100 :type w))
("feed"
(:text "Irreal" :value 250 :type S :attr t)
(:text "Sacha Chua" :value 350 :type S :attr t :comment "Essential!"))
;; ("authors"
;; (:text "opsnull" :value 500 :type s))
;; ("tag"
;; (:tags (t . reddit-question)
;; :value 750
;; :comment "Add 750 points to any entry with a tag of reddit-question"))
(mark -2500))
(setq org-agenda-time-grid
(quote ((daily today require-timed)
(300 600 900 1200 1500 1800 2100 2400)
"......"
"-----------------------------------------------------"
)))
;; org-agenda 展示的文件。
(setq org-agenda-files
'("~/docs/org/todo.org"
"~/docs/org/capture.org"))
(setq org-agenda-start-day "-7d")
(setq org-agenda-span 21)
(setq org-agenda-include-diary t)
;; use org-journal
;;(setq diary-file "~/docs/orgs/diary")
;;(setq diary-mail-addr "geekard@qq.com")
;; 获取经纬度:https://www.latlong.net/
(setq calendar-latitude +39.904202)
(setq calendar-longitude +116.407394)
(setq calendar-location-name "北京")
(setq calendar-remove-frame-by-deleting t)
;; 每周第一天是周一。
(setq calendar-week-start-day 1)
;; 标记有记录的日期。
(setq mark-diary-entries-in-calendar t)
;; 标记节假日。
(setq mark-holidays-in-calendar nil)
;; 不显示节日列表。
(setq view-calendar-holidays-initially nil)
(setq org-agenda-include-diary t)
;; 除去基督徒、希伯来和伊斯兰教的节日。
(setq christian-holidays nil
hebrew-holidays nil
islamic-holidays nil
solar-holidays nil
bahai-holidays nil)
(setq mark-diary-entries-in-calendar t
appt-issue-message nil
mark-holidays-in-calendar t
view-calendar-holidays-initially nil)
(setq diary-date-forms '((year "/" month "/" day "[^/0-9]"))
calendar-date-display-form '(year "/" month "/" day)
calendar-time-display-form '(24-hours ":" minutes (if time-zone " (") time-zone (if time-zone ")")))
(add-hook 'today-visible-calendar-hook 'calendar-mark-today)
(autoload 'chinese-year "cal-china" "Chinese year data" t)
(setq calendar-load-hook '(lambda ()
(set-face-foreground 'diary-face "skyblue")
(set-face-background 'holiday-face "slate blue")
(set-face-foreground 'holiday-face "white")))
(use-package org-super-agenda
:config
(setq org-super-agenda-groups
'(;; Each group has an implicit boolean OR operator between its selectors.
(:name "Today" ; Optionally specify section name
:time-grid t ; Items that appear on the time grid
:todo "TODAY") ; Items that have this TODO keyword
(:name "Important"
;; Single arguments given alone
:tag "bills"
:priority "A")
;; Set order of multiple groups at once
(:order-multi (2 (:name "Shopping in town"
;; Boolean AND group matches items that match all subgroups
:and (:tag "shopping" :tag "@town"))
(:name "Food-related"
;; Multiple args given in list with implicit OR
:tag ("food" "dinner"))
(:name "Personal"
:habit t
:tag "personal")
(:name "Space-related (non-moon-or-planet-related)"
;; Regexps match case-insensitively on the entire entry
:and (:regexp ("space" "NASA")
;; Boolean NOT also has implicit OR between selectors
:not (:regexp "moon" :tag "planet")))))
;; Groups supply their own section names when none are given
(:todo "WAITING" :order 8) ; Set order of this section
(:todo ("SOMEDAY" "TO-READ" "CHECK" "TO-WATCH" "WATCHING")
;; Show this group at the end of the agenda (since it has the
;; highest number). If you specified this group last, items
;; with these todo keywords that e.g. have priority A would be
;; displayed in that group instead, because items are grouped
;; out in the order the groups are listed.
:order 9)
(:priority<= "B"
;; Show this section after "Today" and "Important", because
;; their order is unspecified, defaulting to 0. Sections
;; are displayed lowest-number-first.
:order 1)
;; After the last group, the agenda will display items that didn't
;; match any of these groups, with the default order position of 99
))
(org-super-agenda-mode))
安装终端提示工具:
which terminal-notifier || brew install terminal-notifiler
倒计时结束通知:
(defvar terminal-notifier-command (executable-find "terminal-notifier") "The path to terminal-notifier.")
(defun terminal-notifier-notify (title message)
(start-process "terminal-notifier"
"terminal-notifier"
terminal-notifier-command
"-title" title
"-sound" "default"
"-message" message
"-activate" "org.gnu.Emacs"))
(defun timed-notification (time msg)
(interactive "sNotification when (e.g: 2 minutes, 60 seconds, 3 days): \nsMessage: ")
(run-at-time time nil (lambda (msg) (terminal-notifier-notify "Emacs" msg)) msg))
;;(terminal-notifier-notify "Emacs notification" "Something amusing happened")
(setq org-show-notification-handler (lambda (msg) (timed-notification nil msg)))
which tsc || npm install -g typescript
which typescript-language-server || npm install -g typescript-language-server
which eslint || npm install -g eslint babel-eslint eslint-plugin-react
which prettier || npm install -g prettier
which importjs || npm install -g import-js
which yaml-language-server || npm install -g yaml-language-server
which vscode-css-language-server &>/dev/null || npm i -g vscode-langservers-extracted
使用 Emacs 内置的 typescript-ts-mode 为 typescript 文件(扩展名为 .ts 和 .tsx) 提供编辑支持(major-mode)。 js-mode/js2-mode 则为 .js/.jsx 文件提供编辑支持。
;; for .ts/.tsx file
;; (use-package typescript-mode
;; :mode "\\.tsx?\\'"
;; :config
;; (setq typescript-indent-level 2))
(setq typescript-ts-mode-indent-offset 2)
在安装 typescript-mode 包的同时,确保已安装 typescript 和 typescript-language-server 包:
npm install -g typescript
: 提供 tsc 和 tsserver 命令。npm install -g typescript-language-server
: 基于 typescript 的 tsserver 实现的语言服务器, 支持以下三种语言的补全:- typescript: 扩展名 .ts/.tsx
- javascript: 扩展名 .js/.jsx
- 其中 .tsx/.jsx 是 React 的语法格式。
eslint:
- 安装 eslint npm 包后,安装语言服务器
M-x lsp-install-server RET eslint RET
。 - 创建 .eslintrc.js:
M-x lsp-eslint-create-default-configuration
, 回答一些问题后自动创建配置文件并安装 eslint plugin。
prettier 提供了 javascript/typescript 的格式化的功能。import-js 则提供了 import 功能。
Emacs 内置的 js-ts-mode 完整支持 .js/.jsx 文件的编辑, 官方建议将 js2 作为 js-ts-mode 的 minor-mode 来一起用, 这样 js2 为 js-ts-mode 提供了更好的 AST 和 JavaScript linting 支持能力。
(use-package js2-mode
:disabled
:init
(add-to-list 'auto-mode-alist '("\\.jsx?\\'" . js-ts-mode))
:config
;; 仍然使用 js-ts-mode 作为 .js/.jsx 的 marjor-mode, 但使用 js2-minor-mode 提供 AST 解析。
(add-hook 'js-ts-mode-hook 'js2-minor-mode)
;; 将 js2-mode 作为 .js/.jsx 的 major-mode
;;(add-to-list 'auto-mode-alist '("\\.jsx?\\'" . js2-mode))
;; 由于 lsp 已经提供了 diagnose 功能,故关闭 js2 自带的错误检查,防止干扰。
(setq js2-mode-show-strict-warnings nil)
(setq js2-mode-show-parse-errors nil)
;; 缩进配置。
(setq javascript-indent-level 2)
(setq js-indent-level 2)
(setq js2-basic-offset 2)
(add-to-list 'interpreter-mode-alist '("node" . js2-mode)))
web-mode 用于编辑 html/css/jinja2/gotmpl/tmpl 等模板文件,不用于编辑 js/jsx/ts/tsx 等类型文件。
(use-package web-mode
:mode "(\\.\\(jinja2\\|j2\\|css\\|vue\\|tmpl\\|gotmpl\\|html?\\|ejs\\)\\'"
:disabled ;; 使用内置的 TypeScript mode
:custom
(css-indent-offset 2)
(web-mode-attr-indent-offset 2)
(web-mode-attr-value-indent-offset 2)
(web-mode-code-indent-offset 2)
(web-mode-css-indent-offset 2)
(web-mode-markup-indent-offset 2)
(web-mode-sql-indent-offset 2)
(web-mode-enable-auto-pairing t)
(web-mode-enable-css-colorization t)
(web-mode-enable-auto-quoting nil)
(web-mode-enable-block-face t)
(web-mode-enable-current-element-highlight t)
:config
;; Emmit.
(setq web-mode-tag-auto-close-style 2) ;; 2 mean auto-close with > and </.
(setq web-mode-markup-indent-offset 2))
vscode-langservers-extracted 提供了如下三个 web 开发相关的 lsp server:
- vscode-html-language-server
- vscode-css-language-server
- vscode-json-language-server
- vscode-eslint-language-server
lsp-bridge 默认在打开 css-mode 时使用 vscode-css-language-server。
- 各语言使用的 language server 参考变量: lsp-bridge-lang-server-mode-list
;; dump-jump 使用 ag、rg 来实时搜索当前项目文件来进行定位和跳转,相比使用 TAGS 的 citre(适合静态浏
;; 览)以及 lsp 方案,更通用和轻量。
(use-package dumb-jump
:after (citre)
:config
;; xref 默认将 elisp--xref-backend 加到 backend 的最后面,它使用 etags 作为数据源。将 dump-jump 加
;; 到 xref 后端中,作为其它 backend,如 citre 的后备。加到 xref 后端后,可以使用 M-. 和 M-? 来跳转。
;; 100 表示加到最后,优先级低于 citre。
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate 100)
;; dumb-jump 发现支持的语言和项目后,会自动生效。
;;; 将 Go module 文件作为 project root 标识。
(add-to-list 'dumb-jump-project-denoters "go.mod"))
(use-package rainbow-csv
:vc (:fetcher github :repo emacs-vs/rainbow-csv)
)
- 切换模式:
M-x rainbow-csv-mode
使用 emacs 内置的 yaml-ts-mode。
(use-package yaml-ts-mode
:mode "\\.ya?ml\\'"
:config
(define-key yaml-ts-mode-map (kbd "\C-m") #'newline-and-indent))
;; https://gitlab.com/skybert/my-little-friends/-/blob/master/emacs/.emacs#L295
(setq compilation-ask-about-save nil
compilation-always-kill t
compile-command "go build")
;; Convert shell escapes to color
(add-hook 'compilation-filter-hook
(lambda () (ansi-color-apply-on-region (point-min) (point-max))))
;; Taken from https://emacs.stackexchange.com/questions/31493/print-elapsed-time-in-compilation-buffer/56130#56130
(make-variable-buffer-local 'my-compilation-start-time)
(add-hook 'compilation-start-hook #'my-compilation-start-hook)
(defun my-compilation-start-hook (proc)
(setq my-compilation-start-time (current-time)))
(add-hook 'compilation-finish-functions #'my-compilation-finish-function)
(defun my-compilation-finish-function (buf why)
(let* ((elapsed (time-subtract nil my-compilation-start-time))
(msg (format "Compilation took: %s" (format-time-string "%T.%N" elapsed t))))
(save-excursion (goto-char (point-max)) (insert msg))
(message "Compilation %s: %s" (string-trim-right why) msg)))
(defun my/goto-compilation()
(interactive)
(switch-to-buffer
(get-buffer-create "*compilation*")))
;; (use-package ibuffer-project
;; :config
;; (add-to-list 'ibuffer-project-root-functions '(file-remote-p . "Remote"))
;; (add-hook
;; 'ibuffer-hook
;; (lambda ()
;; (setq ibuffer-filter-groups (ibuffer-project-generate-filter-groups))
;; (unless (eq ibuffer-sorting-mode 'project-file-relative)
;; (ibuffer-do-sort-by-project-file-relative)))))
(use-package sort-tab
:demand
:vc (:fetcher github :repo manateelazycat/sort-tab)
;; emacs 启动后再启用 sort-tab 防止显示异常。
:hook (after-init . sort-tab-mode)
:config
;;(sort-tab-mode 1)
(setq sort-tab-show-index-number t)
(setq sort-tab-height 40)
(setq sort-tab-name-max-length 15)
(global-set-key (kbd "s-n") 'sort-tab-select-next-tab)
(global-set-key (kbd "s-p") 'sort-tab-select-prev-tab)
(global-set-key (kbd "s-w") 'sort-tab-close-current-tab)
;; (global-set-key (kbd "s-0") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-1") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-2") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-3") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-4") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-5") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-6") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-7") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-8") 'sort-tab-select-visible-tab)
(global-set-key (kbd "s-9") 'sort-tab-select-visible-tab)
;; 设置 tab 颜色,M-x list-colors-display。
(set-face-foreground 'sort-tab-current-tab-face "peru")
;; 不显示背景颜色。
(set-face-background 'sort-tab-current-tab-face nil)
;; 忽略 treemacs-mode 的 buffer显示。
(setq sort-tab-hide-function '(lambda (buf) (with-current-buffer buf (derived-mode-p 'treemacs-mode)))))
2024.03.19 不再使用: 显示的窗口位置不对, 响应慢, 对 macos 支持的不好.
安装:
npm config set registry=https://registry.npmmirror.com
yarn config set registry https://registry.npmmirror.com
npm config set fetch-timeout 600000
npm config set fetch-retry-maxtimeout 600000
npm config set fetch-retry-mintimeout 100000
brew upgrade node
node version
git clone --depth=1 -b master https://github.com/emacs-eaf/emacs-application-framework.git ~/.emacs.d/site-lisp/emacs-application-framework/
./install-eaf.py
./install-eaf.py -i terminal -f
cd app/terminal/
npm install -g npm-check-updates --registry=https://registry.npmmirror.com --disturl=https://npmmirror.com/mirrors/node
npm-check-updates -u
npm install --registry=https://registry.npmmirror.com --disturl=https://npmmirror.com/mirrors/node
# 报错: 解决办法: 注释掉 /Users/zhangjun/Library/Caches/node-gyp/21.7.1/include/node/v8-local-handle.h:253
npm ERR! /Users/zhangjun/Library/Caches/node-gyp/21.7.1/include/node/v8-local-handle.h:253:5: error: static assertion failed due to requirement 'std::is_base_of<v8::Value, v8::Data>::value': type check
npm ERR! static_assert(std::is_base_of<T, S>::value, "type check");
npm ERR! ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
npm ERR! ../../nan/nan_callbacks_12_inl.h:175:20: note: in instantiation of function template specialization 'v8::Local<v8::Value>::Local<v8::Data>' requested here
npm ERR! cbinfo(info, obj->GetInternalField(kDataIndex));
npm ERR! ^
npm ERR! 1 error generated.
(use-package eaf
:load-path "~/.emacs.d/site-lisp/emacs-application-framework"
:custom
; See https://github.com/emacs-eaf/emacs-application-framework/wiki/Customization
(eaf-browser-continue-where-left-off t)
(eaf-browser-enable-adblocker t)
(browse-url-browser-function 'eaf-open-browser)
:config
(defalias 'browse-web #'eaf-open-browser)
(require 'eaf-demo)
(require 'eaf-js-video-player)
(require 'eaf-rss-reader)
(require 'eaf-terminal)
(require 'eaf-image-viewer)
(require 'eaf-vue-demo)
(require 'eaf-pdf-viewer)
(require 'eaf-browser)
(require 'eaf-markdown-previewer)
(require 'eaf-vue-tailwindcss)
(require 'eaf-video-player)
(require 'eaf-pyqterminal)
(eaf-bind-key scroll_up "C-n" eaf-pdf-viewer-keybinding)
(eaf-bind-key scroll_down "C-p" eaf-pdf-viewer-keybinding)
;;(eaf-bind-key take_photo "p" eaf-camera-keybinding)
(eaf-bind-key nil "M-q" eaf-browser-keybinding)) ;; unbind, see more in the Wiki
20240319 不再使用: 使用速度更快的 dired-sidebar.
(use-package neotree
:config
(setq neo-smart-open t) ;; 自动跳转到当前打开的文件。
(setq neo-theme 'nerd) ;; nerd 更简洁。
(setq neo-vc-integration '(face))
(setq neo-window-width 30)
(setq neo-window-fixed-size nil)
(setq neo-theme (if (display-graphic-p) 'icons 'arrow))
:bind
(:map global-map
("s-0" . neotree-toggle)))
(add-hook 'neotree-mode-hook (lambda () (display-line-numbers-mode 0)))
;; 根据项目切换 neotree root 目录。
(defun my-neotree-project-dir-toggle ()
"Open NeoTree using the project root, using projectile, find-file-in-project,
or the current buffer directory."
(interactive)
(require 'neotree)
(let* ((filepath (buffer-file-name))
(project-dir
(with-demoted-errors "neotree-project-dir-toggle error: %S"
(cond
((featurep 'projectile)
(projectile-project-root))
((featurep 'find-file-in-project)
(ffip-project-root))
(t ;; Fall back to version control root.
(if filepath
(vc-call-backend
(vc-responsible-backend filepath) 'root filepath)
nil)))))
(neo-smart-open t))
(if (and (fboundp 'neo-global--window-exists-p)
(neo-global--window-exists-p))
(neotree-hide)
(neotree-show)
(when project-dir
(neotree-dir project-dir))
(when filepath
(neotree-find filepath)))))
(define-key global-map (kbd "M-e") 'my-neotree-project-dir-toggle)
neotree 窗口快捷键:
- U
- 切换到上一级目录;
- g
- 刷新;
- A
- 最大/最小 tree 窗口;
- s/S
- 选择下一个或上一个同级的 sibling 节点;
- D/U
- 选择下一个或上一级节点;
- H
- 显示隐藏文件;
打开文件或目录:
- SPC
- 快速显示光标所在的文件,光标
- TAB
- neo-open-dir
- RET
- neo-open-file 或 neo-open-dir
- |
- neo-open-file-vertical-split
- -
- neo-open-file-horizontal-split
- a
- neo-open-file-ace-window
- d
- neo-open-dired,光标必须位于目录节点才有效。
- O
- neo-open-dir-recursive
- o
- neotree-open-file-in-system-application
节点 CRUD:
- C-c C-p
- copy node
- C-c C-r
- rename node
- C-c C-d
- delete node
- C-c C-n
- create ndoe
- C-c C-a
- neotree-collapse-all
改变 tree 显示的目录:
- C-c C-c
- neotree-chanage-root, 将光标所在的目录设置为 root;
- C-c c
- neotree-dir,将指定的目录设置为 root;
- M-x neotree-show
- 将当前 buffer 所在文件设置为 root
其他:
- M-x neotree-find
- 在 tree 中定位当前文件;
- M-x neotree-copy-filepath-to-yank-ring
- 光标位于 neo 窗口时,复制当前节点的路径;
安装外置输入法切换工具 macism,解决 Mac 切换输入法后必须输入一个字符才能生效的问题。同时系统的 “快捷 键”->“选择上一个输入法” 必须要开启,否则 macism 会切换失败。
which macism || brew tap laishulu/macism && brew install macism
# 切换测试,第一次 mac 会提示权限问题,需要在安全中开启对应权限。
macism com.sogou.inputmethod.sogou.pinyin
Mac 配置:
- 关闭搜狗输入法的状态切换相关的快捷键;高级选项中的“自动切换到英文输入法的应用”去掉 emacs;
- 开启 Mac 自带的 ABC 输入法;
- Mac 系统设置输入法切换快捷键: 键盘-》键盘快捷键 -》输入法 -》“选择上一个输入法”
切换延迟的说明:如果使用 emacs 的 mac-port,自带原生的输入法相关的api,不需要外部的切换工具,那个就很丝滑了。
brew tap railwaycat/emacsmacport
brew install emacs-mac --with-modules --with-rsvg --with-imagemagick --with-natural-title-bar
配置 sis:
(use-package sis
:demand t
:config
;;执行 macism 命令查看输入法名称。
(sis-ism-lazyman-config "com.apple.keylayout.ABC" "com.sogou.inputmethod.sogou.pinyin")
;;(sis-ism-lazyman-config "com.apple.keylayout.ABC" "com.apple.inputmethod.SCIM.ITABC")
;; 降低 macism 命令切换延迟(默认 20000),防止快速切换到中文时第一个字母未当做拼音的问题。具体最小
;; 值是多少,如 2000,需要进行实际切换测试来验证。
;;(setq sis-do-set (lambda(source) (start-process "set-input-source" nil "macism" source "2000")))
;; 自动切换到英文的快捷键前缀, 需要在打开 sis-global-xx-mode 之前设置才生效。
(add-to-list 'sis-prefix-override-keys "C-;") ;; embark
(add-to-list 'sis-prefix-override-keys "M-s") ;; consult
(add-to-list 'sis-prefix-override-keys "M-g") ;; consult
(add-to-list 'sis-prefix-override-keys "M-o")
;; 开启输入法颜色指示:橘色表示当前是中文输入模式。
;; 貌似有性能问题,先关闭。
;;(setq sis-other-cursor-color "orange")
;;(sis-global-cursor-color-mode t)
;; 开启 respect-mode 后才会在 minibuffer、prefix key 等场景自动切换到英文。
(sis-global-respect-mode t)
;; context/inline 模式经常会误判,故不使用,而是使用下面定义的手动切换命令。
;;(sis-global-context-mode t)
;;(sis-global-inline-mode t)
;; 手动切换输入法。
(global-set-key (kbd "C-\\") 'sis-switch)
(global-set-key (kbd "C-.") 'sis-switch)
(global-set-key (kbd "C-,") 'sis-set-english)
;; 为 minimibuffer 命令设置英文输入法。
(add-to-list 'sis-respect-minibuffer-triggers (cons 'embark-act (lambda () 'other)))
;; Helpful 等特殊 Mode 自动切换到英文。
(add-hook 'special-mode-hook #'sis-set-english)
(add-hook 'comint-mode-hook #'sis-set-english)
(add-hook 'prog-mode-hook #'sis-set-english)
(add-hook 'dashboard-mode-hook #'sis-set-english)
)
dash 开发者文档:
;; 开发者文档。
(use-package dash-at-point
:config
;; 可以在搜索输入中指定 docset 名称,例如: spf13/viper: getstring
(global-set-key (kbd "C-c d .") #'dash-at-point)
;; 提示选择 docset;
(global-set-key (kbd "C-c d d") #'dash-at-point-with-docset)
;; 扩展提示可选的 docset 列表, 名称必须与 dash 中定义的一致。
(add-to-list 'dash-at-point-docsets "go")
(add-to-list 'dash-at-point-docsets "viper")
(add-to-list 'dash-at-point-docsets "cobra")
(add-to-list 'dash-at-point-docsets "pflag")
(add-to-list 'dash-at-point-docsets "k8s/api")
(add-to-list 'dash-at-point-docsets "k8s/apimachineary")
(add-to-list 'dash-at-point-docsets "k8s/client-go")
(add-to-list 'dash-at-point-docsets "klog")
(add-to-list 'dash-at-point-docsets "k8s/controller-runtime")
(add-to-list 'dash-at-point-docsets "k8s/componet-base")
(add-to-list 'dash-at-point-docsets "k8s.io/kubernetes"))
(use-package anki-helper
:vc (:fetcher github :repo Elilif/emacs-anki-helper)
:config
(setq anki-helper-media-directory "~/Library/Application Support/Anki2/User 1/collection.media/")
)
- Anki GUI 默认使用的 media-directory 是 “~/Library/Application Support/Anki2/User 1/collection.media/”,需要与 anki-helper-media-directory 变量配置的一致,否则不能显示图片。
- anki 支持启动时通过命令行参数或环境变量来指定数据目录:
https://docs.ankiweb.net/files.html#startup-options
- 数据目录下可以有多个 profile,然后使用 -p <name> 来指定具体的 profile name;
- 安装 anki 插件 AnkiConnect;
- 安装 anki 插件 anki-open-org-note,以支持在 anki 卡片反向链接到 org 文档。
修改 anki-open-org-note 的配置: Tools -> Add-ons -> Open Org Note -> Config, 贴入如下内容(重启anki 生效):
- 在 org-paths 列表中添加 org-mode 文件的上层路径;
- 系统安装 ripgrep 命令,搜索命令更好;
- 修正原始的 note_match 正则表达式(最新 anki-open-org-note 版本已修复);
- Emacs 需要以 server 启动,而且需要关闭 socks5 代理,否则报错 ‘anki-helper–curl-sentinel: End of file while parsing JSON’。
{ "exec": "emacsclient -nce '(progn (select-frame-set-input-focus (selected-frame)) (find-file \"{org_file}\") (goto-char {char_pos_end}) (when (string-equal \"{note_type}\" \"ANKI_NOTE_ID\") (org-back-to-heading t)) (recenter))'", "note_match": "^\\s*(?:#\\+|:)(ANKI_NOTE_ID|ATTR_ID):\\s*?{note_id}\\b", "org-paths": [ "~/org", "~/docs", "~/work/docs", "~/.emacs.d" ], "ripgrep_opts": "rg -ni", "shortcuts": { "open": "Ctrl+O" }, "texts": { "open": "Open Note", "open_btn": "Open", "open_hint": "Open Org Note in Editor", "open_menu": "Org Notes" }, "use_index_cache": true, "use_ripgrep": true }
调试 anki 程序(如打印插件日志):
zj@a:~$ cd /Applications/Anki.app/Contents/
CodeResources Frameworks/ Info.plist MacOS/ Resources/ _CodeSignature/
zj@a:~$ cd /Applications/Anki.app/Contents/MacOS/
zj@a:/Applications/Anki.app/Contents/MacOS$ ls
anki* lib/ libankihelper.dylib
zj@a:/Applications/Anki.app/Contents/MacOS$ ./anki
Anki starting...
Initial setup...
Preparing to run...
Qt debug: doh set to "" -- SystemOnly
Starting main loop...
JS warning /_anki/js/editor.js:438 No version information available for component [tex]/noerrors
JS warning /_anki/js/editor.js:438 No version information available for component [tex]/mathtools
JS warning /_anki/js/editor.js:438 No version information available for component [tex]/mhchem
regex parse error:
^\s*(?:#\+|:)(ANKI_NOTE_ID|ATTR_ID):\s*\"?1703514630494\"?\b
^^
error: unrecognized escape sequence
regex parse error:
^\s*(?:#\+|:)(ANKI_NOTE_ID|ATTR_ID):\s*\"?1703514630494\"?\b
^^
error: unrecognized escape sequence
regex parse error:
^\s*(?:#\+|:)(ANKI_NOTE_ID|ATTR_ID):\s*\"?1703514630494\"?\b
^^
error: unrecognized escape sequence
^Czj@a:/Applications/Anki.app/Contents/MacOS$ ./anki
anki-helpers 支持创建两类卡片:
- 使用光标所在的 org-mode entry:headline 为 front 内容,下面的内容为 back 卡片内容;
- 交互式创建 front 和 back 卡片内容;
也可以通过 entry 匹配的模式来为文档所有 entry 生成卡片,匹配 tags/property/todo 的语法于 agenda tags view 一致:
- 例如下面的 ANKI_MATCH 只会创建两个卡片。
#+ANKI_DECK: Default #+ANKI_MATCH: TODO="TODO"|+DATE="today" #+ANKI_NOTE_TYPE: Basic #+ANKI_TAGS: test * test note 1 back side * TODO test note 2 back side * test note 3 :PROPERTIES: :DATE: today :END: back side * test note 4 back side
使用 anki-helper-set-front-region 和 anki-helper-make-two-sided-card 函数来交互式创建卡片的标题和内 容:
- 先选中内容,然后执行 anki-helper-set-front-region,设置 front 卡片内容;
- 再选中内容,然后执行 anki-helper-make-two-sided-card,设置 back 卡片内容;
anki-helpers 默认使用 org-mode 的强调 *xxx*
来生成填空型卡片。
参考:
eglot 运行时将 xref-backend-functions 设置为 eglot-xref-backend,而忽略已注册的其它 backend. 这里定 义一个一键切换函数,在 eglot 失效的情况下,手动关闭当前 major-mode 的 eglot,从而让 xref-backend-functions 恢复为以前的值,如 citre-xref-backend;
(defun my/toggle-eglot ()
(interactive)
(let ((current-mode major-mode)
(hook (intern (concat (symbol-name major-mode) "-hook"))))
(if (bound-and-true-p eglot--managed-mode)
(progn
(eglot-shutdown-all)
(remove-hook hook 'eglot-ensure)
;; 当打开一个文件时,如果可以找到对应 TAGS 文件则自动开启 citre-mode。开启了 citre-mode 后,
;; 会自动向 xref-backend-functions hook 添加 citre-xref-backend,从而支持于 xref 和 imenu
;; 的集成。
(require 'citre-config)
;; 将 citre 添加到 xref backend 中,-100 表示添加到开头,确保优先使用。
(add-hook 'xref-backend-functions #'citre-xref-backend -100))
(progn
(add-hook hook 'eglot-ensure)
(eglot-ensure)))))
(global-set-key (kbd "s-`") 'my/toggle-eglot)
nyan:modeline 彩虹猫。
(use-package nyan-mode
:config
(setq nyan-animate-nyancat t)
(setq nyan-wavy-trail t)
(nyan-mode)
(nyan-start-animation))
flymake-clippy 为 flymake 添加 flymake-clippy-backend,用于对 rust 代码进行丰富的 linter 规则检查:
- 需要配置 eglot 才能生效,参考 flymake-clippy 文档。
(use-package flymake-clippy
:after (flymake rust-mode)
:hook
(rust-ts-mode . flymake-clippy-setup-backend))
下载 emacs-lsp-booster 可执行程序,然后使用 emacs-lsp-booster 来加速 eglot 的响应性能:
(use-package eglot-booster
:vc (:url "https://github.com/jdtsmith/eglot-booster")
:disabled
:after (eglot)
:config (eglot-booster-mode))
cargo:
;; (use-package cargo
;; :commands cargo-minor-mode
;; :bind (:map cargo-mode-map
;; ("C-c C-c C-y" . cargo-process-clippy))
;; :hook (rust-mode . my-rust-mode-cargo-init)
;; :custom
;; (cargo-process--command-clippy "clippy") ;; rust lints
;; :preface
;; (defun my-update-cargo-path (&rest _args)
;; (setq cargo-process--custom-path-to-bin
;; (executable-find "cargo")))
;; (defun my-cargo-target-dir (path)
;; (replace-regexp-in-string "kadena" "Products" path))
;; (defun my-update-cargo-args (ad-do-it name command &optional last-cmd opens-external)
;; (let* ((cmd (car (split-string command)))
;; (new-args
;; (if (member cmd '("build" "check" "clippy" "doc" "test"))
;; (let ((args
;; (format "--target-dir=%s -j8"
;; (my-cargo-target-dir
;; (replace-regexp-in-string
;; "target" "target--custom"
;; (regexp-quote (getenv "CARGO_TARGET_DIR")))))))
;; (if (member cmd '("build"))
;; (concat "--message-format=short " args)
;; args))
;; ""))
;; (cargo-process--command-flags
;; (pcase (split-string cargo-process--command-flags " -- ")
;; (`(,before ,after)
;; (concat before " " new-args " -- " after))
;; (_ (concat cargo-process--command-flags new-args)))))
;; (funcall ad-do-it name command last-cmd opens-external)))
;; (defun my-rust-mode-cargo-init ()
;; (advice-add 'direnv-update-directory-environment
;; :after #'my-update-cargo-path)
;; (advice-add 'cargo-process--start :around #'my-update-cargo-args)
;; (cargo-minor-mode 1))
;; :config
;; (defadvice cargo-process-clippy
;; (around my-cargo-process-clippy activate)
;; (let ((cargo-process--command-flags
;; (concat cargo-process--command-flags
;; "--all-targets "
;; "--all-features "
;; "-- "
;; "-D warnings "
;; "-D clippy::all "
;; "-D clippy::mem_forget "
;; "-C debug-assertions=off")))
;; ad-do-it))
;; (defun cargo-fix ()
;; (interactive)
;; (async-shell-command
;; (concat "cargo fix"
;; " --clippy --tests --benches --allow-dirty --allow-staged"))))
(global-set-key (kbd "s-v") 'scroll-other-window)
(global-set-key (kbd "C-s-v") 'scroll-other-window-down)
org-tree-slide 被 dslide 取代。
(use-package org-tree-slide
:disabled
:after (org)
:commands org-tree-slide-mode
:hook
((org-tree-slide-play . (lambda ()
(org-fold-hide-block-all)
(setq-default x-stretch-cursor -1)
(redraw-display)
(blink-cursor-mode -1)
(setq cursor-type 'bar)
;;(org-display-inline-images)
;;(hl-line-mode -1)
(text-scale-increase 2)
(setq org-tree-slide-slide-in-waiting 0.01)
(read-only-mode 1)))
(org-tree-slide-stop . (lambda ()
(blink-cursor-mode +1)
(setq-default x-stretch-cursor t)
(setq cursor-type t)
(text-scale-increase 0)
;;(hl-line-mode 1)
(read-only-mode -1))))
:config
(setq org-tree-slide-header t)
(setq org-tree-slide-content-margin-top 0)
(setq org-tree-slide-heading-emphasis nil)
(setq org-tree-slide-slide-in-effect t)
(setq org-tree-slide-activate-message " ")
(setq org-tree-slide-deactivate-message " ")
;;(setq org-tree-slide-modeline-display t)
;;(setq org-tree-slide-breadcrumbs " 👉 ")
(define-key org-mode-map (kbd "<f8>") #'org-tree-slide-mode)
(define-key org-tree-slide-mode-map (kbd "<f9>") #'org-tree-slide-content)
(define-key org-tree-slide-mode-map (kbd "<left>") #'org-tree-slide-move-previous-tree)
(define-key org-tree-slide-mode-map (kbd "<right>") #'org-tree-slide-move-next-tree))
tramp:
(use-package tramp
:config
;; 使用远程主机自己的 PATH(默认是本地的 PATH)
(setq tramp-remote-path '(tramp-default-remote-path "/bin" "/usr/bin" "/sbin" "/usr/sbin" "/usr/local/bin" "/usr/local/sbin"))
;;(add-to-list 'tramp-remote-path 'tramp-own-remote-path)
;; 使用 ~/.ssh/config 中的 ssh 持久化配置。(Emacs 默认复用连接,但不持久化连接)
(setq tramp-use-ssh-controlmaster-options nil)
(setq tramp-ssh-controlmaster-options nil)
;; TRAMP buffers 关闭 version control, 防止卡住。
(setq vc-ignore-dir-regexp (format "\\(%s\\)\\|\\(%s\\)" vc-ignore-dir-regexp tramp-file-name-regexp))
;; 关闭自动保存 ad-hoc proxy 代理配置, 防止为相同 IP 的 VM 配置了错误的 Proxy.
(setq tramp-save-ad-hoc-proxies nil)
;; 调大远程文件名过期时间(默认 10s), 提高查找远程文件性能.
(setq remote-file-name-inhibit-cache 1800)
;; 设置 tramp-verbose 10 打印详细信息。
(setq tramp-verbose 1)
;; 增加压缩传输的文件起始大小(默认 4KB),否则容易出错: “gzip: (stdin): unexpected end of file”
(setq tramp-inline-compress-start-size (* 1024 8))
;; 当文件大小超过 tramp-copy-size-limit 时,用 external methods(如 scp)来传输,从而大大提高拷贝效率。
(setq tramp-copy-size-limit (* 1024 100))
(setq tramp-allow-unsafe-temporary-files t)
;; 本地不保存 tramp 备份文件。
(setq tramp-backup-directory-alist `((".*" . nil)))
;; Backup (file~) disabled and auto-save (#file#) locally to prevent delays in editing remote files
;; https://stackoverflow.com/a/22077775
(add-to-list 'backup-directory-alist (cons tramp-file-name-regexp nil))
;; 临时目录中保存 TRAMP auto-save 文件, 重启后清空,防止启动时 tramp 扫描文件卡住。
(setq tramp-auto-save-directory temporary-file-directory)
;; 连接历史文件。
(setq tramp-persistency-file-name (expand-file-name "tramp-connection-history" user-emacs-directory))
;; 避免在 shell history 中添加过多 vterm 自动执行的命令。
(setq tramp-histfile-override nil)
;; 在整个 Emacs session 期间保存 SSH 密码.
(setq password-cache-expiry nil)
(setq tramp-default-method "ssh")
(setq tramp-default-remote-shell "/bin/bash")
(setq tramp-encoding-shell "/bin/bash")
(setq tramp-default-user "root")
(setq tramp-terminal-type "tramp")
(customize-set-variable 'tramp-encoding-shell "/bin/bash")
(add-to-list 'tramp-connection-properties '("/ssh:" "remote-shell" "/bin/bash"))
(setq tramp-connection-local-default-shell-variables
'((shell-file-name . "/bin/bash")
(shell-command-switch . "-c")))
;; 自定义远程环境变量。
(let ((process-environment tramp-remote-process-environment))
;; 设置远程环境变量 VTERM_TRAMP, 远程机器的 emacs_bashrc 根据这个变量设置 VTERM 参数。
(setenv "VTERM_TRAMP" "true")
(setq tramp-remote-process-environment process-environment)))
;; 切换 Buffer 时设置 VTERM_HOSTNAME 环境变量为多跳的最后一个主机名,并通过 vterm-environment 传递到
;; 远程 vterm shell 环境变量中,这样远程机器 ~/.bashrc 读取并执行的 emacs_bashrc 脚本正确设置 Buffer
;; 名称和 vtem_prompt_end 函数, 从而确保目录跟踪功能正常,以及通过主机名而非 IP 来打开远程 vterm
;; shell, 确保 SSH ProxyJump 功能正常(只能通过主机名而非 IP 访问),以及避免目标 IP 重复时连接复用
;; 错误的问题。
(defvar my/remote-host "")
(add-hook 'buffer-list-update-hook
(lambda ()
(when (file-remote-p default-directory)
(setq my/remote-host (file-remote-p default-directory 'host))
;; 动态计算 ENV=VALUE.
(require 'vterm)
(setq vterm-environment `(,(concat "VTERM_HOSTNAME=" my/remote-host))))))
(use-package consult-tramp
:vc (:url "https://github.com/Ladicle/consult-tramp")
:custom
;; 默认为 scpx 模式,不支持 SSH 多跳 Jump。
(consult-tramp-method "ssh")
;; 打开远程的 /root 目录,而非 ~, 避免 tramp hang。
;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2007-07/msg00006.html
(consult-tramp-path "/root/")
;; 即使 ~/.ssh/config 正确 Include 了 hosts 文件,这里还是需要配置,因为 consult-tramp 不会解析 Include 配置。
(consult-tramp-ssh-config "~/work/proxylist/hosts_config"))
tramp-default-method
缺省值为 scp, 不支持多跳(但拷贝大文件时性能更高),再打开多跳远程文件时每次 都需要修改/- 中的 -为 ssh,较麻烦,所以设置为 ssh。- tramp 打开远程文件时,避免使用 ~ 路径,而应该是绝对路径,防止切换 buffer 时卡住;
- 修改 net/tramp-sh.el 中的 tramp-send-commad, 将 (concat “exec env TERM=’%s’ INSIDE_EMACS=’%s’ ” “ENV=%s %s PROMPT_COMMAND=” PS1=%s PS2=” PS3=” %s %s”) 中最后的 “-i” 去掉, 然后删除同目录下的 tramp-sh.elc 文件;
C/C++/Rust 语言调试: llvm 项目提供了名为 lldb-dap(以前被命名为 lldb-dap) 的 debug adapter, 但相比 codelldb 功能较弱而且和 dape 不兼容(执行失败), 会报类似错误: failed to load objfile …lib/rustlib/x86_64-apple-darwin/lib/libcore-ef02792cbce15279.rlib, 所以需要使用 codelldb 来代替.
# 确保安装了 cmake 和 llvm 包且 /usr/local/opt/llvm/bin 位于 PATH 中, 参考前面的 clang 配置.
# 从 https://github.com/vadimcn/codelldb/releases 下载最新的 vsix 包
$ mkdir -p ~/.emacs.d/debug-adapters
$ unzip codelldb-<platform>-<os>.vsix -d ~/.emacs.d/debug-adapters/codelldb
# dape 后续使用安装的 codelldb 命令来进行调试(debug-adapters/codelldb/extension/adapter/codelldb)
$ ln -s ~/.emacs.d/debug-adapters/codelldb/extension/adapter/codelldb /usr/local/bin/codelldb
# 移除 codelldb 自带的 debugserver 而是使用 xcode-select --install 提供的版本, 否则会导致调试失败报
# 错: Process exited with code -1. 参考:
# https://github.com/eecs280staff/tutorials/issues/177#issue-2151798211
$ mv debug-adapters/codelldb/extension/lldb/bin/debugserver /tmp
Go 语言调试:
# https://github.com/go-delve/delve/tree/master/Documentation/installation
go install github.com/go-delve/delve/cmd/dlv@latest
sudo /usr/sbin/DevToolsSecurity -enable
sudo dscl . append /Groups/_developer GroupMembership $(whoami)
Python 语言调试:
pip install debugpy
配置 emacs dape:
(use-package dape
;; By default dape shares the same keybinding prefix as `gud'
;; If you do not want to use any prefix, set it to nil.
;; :preface
;; (setq dape-key-prefix "\C-x\C-a")
;;
;; May also need to set/change gud (gdb-mi) key prefix
;; (setq gud-key-prefix "\C-x\C-a")
:hook
;; Save breakpoints on quit
(kill-emacs . dape-breakpoint-save)
;; Load breakpoints on startup
;; (after-init . dape-breakpoint-load))
:config
(setq dape-buffer-window-arrangement 'right) ;; 'gud
;; To not display info and/or buffers on startup
;; (remove-hook 'dape-on-start-hooks 'dape-info)
;; (remove-hook 'dape-on-start-hooks 'dape-repl)
;; To display info and/or repl buffers on stopped
;; (add-hook 'dape-on-stopped-hooks 'dape-info)
;; (add-hook 'dape-on-stopped-hooks 'dape-repl)
;; Kill compile buffer on build success
;; (add-hook 'dape-compile-compile-hooks 'kill-buffer)
;; Save buffers on startup, useful for interpreted languages
;; (add-hook 'dape-on-start-hooks (lambda () (save-some-buffers t t)))
)
查看和设置 dape-configs 变量: 例如 rust-ts-mode 使用的 codelldb-rust 配置如下:
- 各参数可以使用 :key value 的形式来配置, 具体参数列表参考: codelldb/MANUAL.md
- :request “launch” 表示 Launching a New Process, 支持的参数也在上面的 MANUAL.md 中有说明.
- 通用配置参数:program/cargo/args/cwd/env/envFile/stdio/terminal/stopOnEntry;
(codelldb-rust
modes (rust-mode rust-ts-mode)
ensure dape-ensure-command
command-cwd dape-command-cwd
command "~/.emacs.d/debug-adapters/codelldb/extension/adapter/codelldb" ;;以前安装的 codelldb 位置.
command-args ("--port" :autoport "--settings" "{\"sourceLanguages\":[\"rust\"]}")
port :autoport
:type "lldb"
:request "launch"
:cwd "."
:program (lambda nil ;; 要调试的二进制可执行程序
(file-name-concat "target" "debug"
(thread-first
(dape-cwd)
(directory-file-name)
(file-name-split)
(last)
(car))))
:args [] ;; 传给可执行程序的参数列表
:stopOnEntry nil)
其他配置参考: Emacs Debugging with Dape
(add-to-list 'dape-configs
'(dlv
modes (go-mode go-ts-mode)
ensure dape-ensure-command
command "dlv"
command-args ("dap" "--listen" "127.0.0.1:5678")
command-cwd dape-cwd-fn
port 5678
:request "launch"
:type "debug"
:cwd dape-cwd-fn
:program dape-cwd-fn))
(setq dape-configs
'((lldb-vscode modes (c-mode c-ts-mode c++-mode c++-ts-mode rust-mode rust-ts-mode)
command "lldb-vscode"
compile "make -j10"
:type "lldb-vscode" ensure dape-ensure-command
:cwd "xxx/build-dbg/xxx/src/"
:program "/usr/bin/python3"
:args ["EdgeCondTests.py"]
:MIMode "lldb"
:setupCommands [
(
:description "Enable pretty-printing for gdb"
:text "-enable-pretty-printing"
:ignoreFailures t
)
]
)))
dape 不使用 launch.json 配置, 而是使用 emacs dir-local 变量, 例如:
;;; Directory Local Variables -*- no-byte-compile: t -*-
;;; For more information see (info "(emacs) Directory Variables")
((c++-mode . ((dape-command . (codelldb-cc
command-cwd "~/C++/test/"
:program "main"
:args ["1"]
compile "g++ -g -o main main.cpp"))))) ;; 需要加 -g 来添加 debug flags
调试 dape: 设置 (setq dape-debug t), 然后查看 dape-repl, dape-connection events buffer 的内容.
调试程序:
- 在源码设置断点:
M-x dape-breakpoint-toggle
; - 调试: M-x dape, Run adapter 后面输入调试命令:
- dape-configs 变量配置了各语言可以使用的 debug adapter 以及安装到的目录.
- 各语言的 debug adapter 默认别安装到
~/.emacs.d/debug-adapters
目录下, 且需要和 dape-configs 配 置的一致. - 按 TAB 自动补全当前源码可以使用的 debug adapter, 同时显示对应的参数提示, 按 TAB 会自动补全, 例如调试 Rust 程序:
codelldb-rust command-cwd "/Users/zhangjun/codes/rust/mydemo/" :program "target/debug/stream" :args ["1", "2"]
- dape-repl 输入 lldb 命令时需要加 ` 前缀, 如 `help, `breakpoint list, lldb 命令参考: Tutorial;
- 结束调试: M-x dape-quit, 自动关闭 dape buffer 窗口.
;; 透明背景
(defun my/toggle-transparency ()
(interactive)
;; 分别为 frame 获得焦点和失去焦点的不透明度。
(set-frame-parameter (selected-frame) 'alpha '(90 . 90))
(add-to-list 'default-frame-alist '(alpha . (90 . 90)))
(add-to-list 'default-frame-alist '(alpha-background . 90)))
自动切换深浅主题:
(defun my/load-theme (appearance)
(interactive)
(pcase appearance
('light (load-theme 'ef-elea-dark t))
('dark (load-theme 'ef-elea-dark t))))
(add-hook 'ns-system-appearance-change-functions 'my/load-theme)
(add-hook 'after-init-hook (lambda () (my/load-theme ns-system-appearance)))
高亮光标移动到的行:
(use-package pulsar
:config
(setq pulsar-pulse t)
(setq pulsar-delay 0.25)
(setq pulsar-iterations 5)
(setq pulsar-face 'pulsar-magenta)
(setq pulsar-highlight-face 'pulsar-yellow)
(pulsar-global-mode 1)
(add-hook 'next-error-hook #'pulsar-pulse-line-red))
org-capture 支持 store-link 和 capture 协议:
- store-link:获取浏览器的 URL 和 Title,然后在 kill-ring 中生成一个链接;
- capture:根据浏览器复制的内容和指定的 capture-template 名称来创建一个 capture 项目。
打开 MAC “脚本编辑器” ,写入如下内容,保存为 “EmacsClient-Org”,文件格式为 “应用程序”,保存到 /Applications 目录。
on open location this_URL
do shell script "/opt/homebrew/bin/emacsclient \"" & this_URL & "\" && open -a Emacs"
end open location
- 如果是自编译的 Emacs 则 emacsclient 位于
/opt/homebrew/bin/
目录下,否则位于/Applications/Emacs
包中。
编辑 “/Applications/EmacsClient-Org.app/Contents/Info.plist” 文件,在 plist->dict 部分添加如下内容:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>org-protocol handler</string>
<key>CFBundleURLSchemes</key>
<array>
<string>org-protocol</string>
</array>
</dict>
</array>
执行命令:
xattr -r -d com.apple.quarantine /Applications/EmacsClient-Org.app
双击刚才保存到应用程序目录中的 EmacsClient-Org 程序图标,激活 org-proto 协议。
保存浏览器链接:新建一个浏览器书签,Location 内容如下,然后点击该书签,确认 Emacs 有反应,在 Emacs 内按 C-c C-l 自动补全 URL 和 Title.
javascript:location.href='org-protocol://store-link?url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)
需要在浏览器分别建立对应的书签后而且 emacs 以 server 模式运行才可以使用:
(require 'org-protocol)
(require 'org-capture)
(setq org-capture-templates
'(("c" "Capture" entry (file+headline "~/docs/org/capture.org" "Capture")
"* %^{Title}\nDate: %U\nSource: %:annotation\nQuote:\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n"
:empty-lines 1)
("t" "Todo" entry (file+headline "~/docs/org/todo.org" "Tasks")
"* TODO %?\n %U %a\n %i"
:empty-lines 1)))
新建一个浏览器书签,内容如下:
- template=c 的 c 与上面定义的 capture template 名称相同。
- 具体参考 https://orgmode.org/manual/The-capture-protocol.html
javascript:location.href='org-protocol://capture?template=c'+'&url='+encodeURIComponent(window.location.href)+'&title='+encodeURIComponent(document.title)+'&body='+encodeURIComponent(window.getSelection())
;; org-capture 集成。
(defun org-journal-find-location ()
(org-journal-new-entry t)
(unless (eq org-journal-file-type 'daily)
(org-narrow-to-subtree))
(goto-char (point-max)))
(setq org-capture-templates
(cons '("j" "Journal" plain (function org-journal-find-location)
"** %(format-time-string org-journal-time-format)%^{Title}\n%i%?"
:jump-to-captured t :immediate-finish t) org-capture-templates))
;; org-agenda 集成。
;; automatically adds the current and all future journal entries to the agenda
;;(setq org-journal-enable-agenda-integration t)
;; When org-journal-file-pattern has the default value, this would be the regex.
(setq org-agenda-file-regexp "\\`\\\([^.].*\\.org\\\|[0-9]\\\{8\\\}\\\(\\.gpg\\\)?\\\)\\'")
(add-to-list 'org-agenda-files org-journal-dir)
- 不开启 org-journal-enable-agenda-integration, 而是向 org-agenda-files 变量添加日志文件的方式。否 则在历史日记被删除的情况下, 可能导致 Dashbard 显示 agenda 时 hang 。
在线预览 LaTex fragments:
- 预览命令:
C-c C-x C-l (org-latex-preview)
,When called with a single prefix argument, clear all images in the current entry. Two prefix arguments produce a preview image for all fragments in the buffer, while three of them clear all the images in that buffer. - 或者添加启动时自动预览:
#+STARTUP: latexpreview
或nolatexpreview
;; 使用 imagemagick 而非默认的 dvipng 来生成 M-x org-latex-preview 在线显式的图片。dvipng 总是报错。
;; 参考:https://orgmode.org/worg/org-tutorials/org-latex-preview.html
(setq latex-run-command "xelatex")
(setq org-latex-create-formula-image-program 'imagemagick)
(setq auth-source-cache-expiry 300)
(setq-default epa-file-cache-passphrase-for-symmetric-encryption t)
(setq bidi-inhibit-bpa t)
(setq bidi-paragraph-direction 'left-to-right)
(setq-default bidi-display-reordering nil)
(setq-default ad-redefinition-action 'accept)
(setq auto-window-vscroll nil)
(setq hscroll-margin 2
hscroll-step 1)
;; doom-modeline
(size-indication-mode t)
(setq display-time-day-and-date t)
;; vertico
;;(require 'vertico-directory)
;; 默认不选中任何候选者,避免不必要的预览。
;;(setq vertico-preselect 'prompt)
;; consult
(setq consult-ripgrep-args
"rg --null --line-buffered --color=never --max-columns=1000 --path-separator / --smart-case --no-heading --with-filename --line-number --search-zip -g !vendor/")
(setq completion-in-region-function #'consult-completion-in-region)
;; eldoc 支持多个 document sources, 默认当它们都 Ready 时才显示, 设置为 compose-eagerly 后会显示
;; 先 Ready 的内容.
;;(setq eldoc-documentation-strategy 'eldoc-documentation-compose-eagerly)
;; 使用 yapf 格式化 python 代码。
;;(use-package yapfify)
快速跳转到注释位置(自定义函数):
(defun my/goto-comment-start ()
(interactive)
(search-forward comment-start))
(define-key prog-mode-map (kbd "C-c C-;") 'my/goto-comment-start)
- cape:多源补全融合;
cape 补全融合:
(use-package cape
:init
;; completion-at-point 使用的函数列表,注意顺序。
(add-to-list 'completion-at-point-functions #'cape-file)
;;(add-to-list 'completion-at-point-functions #'cape-dabbrev)
(add-to-list 'completion-at-point-functions #'cape-elisp-block)
;;(add-to-list 'completion-at-point-functions #'cape-symbol)
;;(add-to-list 'completion-at-point-functions #'cape-keyword)
;;(add-to-list 'completion-at-point-functions #'cape-history)
;;(add-to-list 'completion-at-point-functions #'cape-tex)
;;(add-to-list 'completion-at-point-functions #'cape-sgml)
;;(add-to-list 'completion-at-point-functions #'cape-rfc1345)
;;(add-to-list 'completion-at-point-functions #'cape-abbrev)
;;(add-to-list 'completion-at-point-functions #'cape-dict)
;;(add-to-list 'completion-at-point-functions #'cape-line)
:config
(setq dabbrev-check-other-buffers nil
dabbrev-check-all-buffers nil
cape-dabbrev-min-length 3)
;; 前缀长度达到 3 时才调用 CAPF,避免频繁调用自动补全。
(cape-wrap-prefix-length #'cape-dabbrev 3)
;; 持续刷新候选者(适用于 eglot server 一次没有返回所有候选者情况).
;; profiling 显示影响性能,暂时关闭。
;;(advice-add 'eglot-completion-at-point :around #'cape-wrap-buster)
)
使用更好用的 gptel package.
在 ~/.authinfo.gpg 中添加 api.openai.com key,然后使用本地 socks5h 代理访问 API。
- azure 各 region 的访问速度测试:https://www.azurespeed.com/Azure/Latency
- 切换 prompt:M-x chatgpt-shell-swap-system-prompt
(use-package shell-maker)
(use-package ob-chatgpt-shell :defer t)
(use-package ob-dall-e-shell :defer t)
(use-package chatgpt-shell
:requires shell-maker
:defer t
:config
(setq chatgpt-shell-model-version "gpt-4o")
(setq chatgpt-shell-model-temperature 0.7)
(setq chatgpt-shell-request-timeout 300)
(setq chatgpt-shell-highlight-blocks t)
(setq chatgpt-shell-insert-queries-inline t)
(setq chatgpt-shell-chatgpt-streaming t)
(setq chatgpt-shell-system-prompt 2) ;; 默认使用 Programming prompt
;; (setq chatgpt-shell-system-prompts
;; `(("tl;dr" . "Be as succint but informative as possible and respond in tl;dr form to my queries")
;; ("General" . "You use org-mode format to structure responses. Always show code snippets in org-mode source code block format.")
;; ;; Based on https://github.com/benjamin-asdf/dotfiles/blob/8fd18ff6bd2a1ed2379e53e26282f01dcc397e44/mememacs/.emacs-mememacs.d/init.el#L768
;; ("Programming" . ,(chatgpt-shell--append-system-info
;; "The user is a programmer with very limited time.
;; You treat their time as precious. You do not repeat obvious things, including their query.
;; You are as concise as possible in responses.
;; You never apologize for confusions because it would waste their time.
;; You use emacs org-mode format to structure responses.
;; Always show code snippets in org-mode source code block format.
;; Don't explain code snippets.
;; Whenever you output updated code for the user, only show diffs, instead of entire snippets."))
;; ("Positive Programming" . ,(chatgpt-shell--append-system-info
;; "Your goal is to help the user become an amazing computer programmer.
;; You are positive and encouraging.
;; You love see them learn.
;; You do not repeat obvious things, including their query.
;; You are as concise in responses. You always guide the user go one level deeper and help them see patterns.
;; You never apologize for confusions because it would waste their time.
;; You use markdown liberally to structure responses. Always show code snippets in markdown blocks with language labels.
;; Don't explain code snippets. Whenever you output updated code for the user, only show diffs, instead of entire snippets."))))
(require 'ob-chatgpt-shell)
(ob-chatgpt-shell-setup)
(require 'ob-dall-e-shell)
(ob-dall-e-shell-setup)
;;(setq chatgpt-shell-api-url-base "http://127.0.0.1:1090")
(setq chatgpt-shell-api-url-base "https://westus3ai.openai.azure.com")
(setq chatgpt-shell-api-url-path "/openai/deployments/4fouro/chat/completions?api-version=2024-02-15-preview")
(setq chatgpt-shell-openai-key (auth-source-pick-first-password :host "westus3ai.openai.azure.com"))
;; azure 使用 api-key 而非 openai 的 Authorization: Bearer 认证头部。
(setq chatgpt-shell-auth-header
(lambda ()
(format "api-key: %s" (auth-source-pick-first-password :host "westus3ai.openai.azure.com"))))
)