Compare commits
291 Commits
fix-initia
...
master
Author | SHA1 | Date |
---|---|---|
Jason Tian | 109f429fe4 | |
Jason Tian | eb153feaf8 | |
Jason Tian | 2faac38bfb | |
Jason Tian | a498501990 | |
Jason Tian | d352002533 | |
Jason Tian | a3c250b319 | |
Jason Tian | 3fb9aa54b6 | |
Jason Tian | 40d476fdc3 | |
Jason Tian | 7e7ac60c3e | |
Jason Tian | bdfc09728d | |
Jason Tian | b6d13060ad | |
Jason TIAN | bfea1fef65 | |
Jason Tian | 2cde369f2c | |
Jason Tian | 694387d224 | |
Jason Tian | 9e22603aa1 | |
Jason Tian | 96556d9c43 | |
Jason Tian | b518d119b0 | |
Jason Tian | 6df10cbf69 | |
Jason Tian | c48704f56c | |
Jason TIAN | 476cddf94d | |
Jason Tian | 6d8cbbe550 | |
Jason TIAN | caec912e41 | |
Jason Tian | aae9b067d6 | |
Jason Tian | 5c74a0b844 | |
Jason Tian | c8805d9343 | |
Jason Tian | 2f6282fdbb | |
Jason TIAN | 98901bc571 | |
Jason Tian | 93babf47ea | |
Jason Tian | 088f83b5c4 | |
Jason Tian | ad09d3b400 | |
Jason Tian | 2ae1eae926 | |
Jason Tian | 927557c73d | |
Jason Tian | 48d0fdc68f | |
Jason Tian | 16d135d02e | |
Jason Tian | f5b39933c9 | |
Jason Tian | 5b03714102 | |
Jason Tian | d423fd4b42 | |
Jason TIAN | bcc5f1d479 | |
Jason Tian | 6fde67de92 | |
Jason Tian | 284185713d | |
Jason Tian | 1dd6c472ee | |
Jason Tian | 69c9937c9a | |
Jason Tian | 087119992e | |
Jason TIAN | 7a692390d8 | |
Jason TIAN | e9cf14a52e | |
Jason TIAN | c45d2aa2a8 | |
Jason Tian | 7f504839cd | |
Jason Tian | 02440a1790 | |
Jason TIAN | 412edd8e3a | |
Jason TIAN | d721427451 | |
Jason Tian | d2e1999fe3 | |
Jason Tian | 95cd5b59ee | |
Jason TIAN | 370453498c | |
Jason TIAN | f269336ae9 | |
Jason Tian | 1372adce4a | |
Jason Tian | ce48f33da9 | |
Jason Tian | eed8c68547 | |
Jason Tian | b776f8e933 | |
Jason Tian | 3f19f57372 | |
Jason Tian | f9db842d3e | |
Jason Tian | 527b0bbebe | |
Jason TIAN | 768afe0c9a | |
Jason TIAN | 31b04bde62 | |
Jason Tian | e8c23439d4 | |
Jason Tian | ed967a1b78 | |
Jason Tian | 543cecceaf | |
Jason Tian | 74d10abb3b | |
Jason Tian | 5821170136 | |
Jason Tian | 8f8d46a7fa | |
Jason Tian | ba269d7883 | |
Jason Tian | 2c7017a0bb | |
Jason Tian | 0e0f8ae740 | |
Jason Tian | f1331ca39c | |
Jason Tian | 18855f1cf5 | |
Jason Tian | 8974d10224 | |
Jason Tian | 4e049e7324 | |
Jason TIAN | 4cf43be49a | |
Jason Tian | 54aa9434bb | |
Jason Tian | 4604a04498 | |
Jason Tian | c5cd88c43e | |
Jason Tian | 4f8d93b6eb | |
Jason Tian | dc31dbcfaf | |
Jason TIAN | 1c36f1b5fc | |
Jason TIAN | f90e0e367d | |
Jason TIAN | 6fa61ea31f | |
Jason TIAN | 280162cce6 | |
Jason TIAN | 08d667936a | |
Jason TIAN | 2b35b4977a | |
Jason TIAN | b29f07ca7e | |
Jason TIAN | 47c75a5ca0 | |
Jason TIAN | 7f7e4c5d5b | |
Jason TIAN | b58d654d5c | |
Jason TIAN | fbb1c0f7f8 | |
Jason Tian | d8aca09760 | |
Jason TIAN | 3c032209f9 | |
Jason TIAN | f0d6cd7ec0 | |
Jason TIAN | 65b5bae621 | |
Jason TIAN | a924a0c00c | |
Jason TIAN | e51952cb4c | |
Jason Tian | a8b933f160 | |
Jason Tian | 3241e53445 | |
Jason Tian | eb5f3c8c5b | |
Jason Tian | dbf0c7419f | |
Jason Tian | 7133d7b46f | |
Jason Tian | ad1d0e4f3f | |
Jason Tian | 8e1cb910e8 | |
Jason TIAN | e8951d3497 | |
Jason Tian | 51c0ea7a08 | |
Jason Tian | 2ad7e2dad5 | |
Jason TIAN | 47c9bb2697 | |
Jason TIAN | cac4baac66 | |
Jason TIAN | 2acffc266b | |
Jason TIAN | 8f622305db | |
Jason TIAN | cf61dada53 | |
Jason TIAN | 1dcc5a224d | |
Jason Tian | 76edd01283 | |
Jason Tian | 36502a9922 | |
Jason Tian | 0b1276afc0 | |
Jason Tian | 075419b514 | |
Jason Tian | e47afad2a3 | |
Jason TIAN | f914de5e8b | |
Jason TIAN | de0091efb9 | |
Jason Tian | d98cb2bf85 | |
Jason Tian | 67b481e2fb | |
Jason Tian | 119b31f3be | |
Jason Tian | 053cf8ecc4 | |
Jason Tian | 8a2ea7c11d | |
Jason Tian | 388a03d9f6 | |
Jason TIAN | 53189db3d5 | |
Jason TIAN | dabc465ace | |
Jason Tian | 2ec8a7c20b | |
Jason Tian | 075c923417 | |
Jason TIAN | 123f9ad8d3 | |
Jason TIAN | 2eae453c82 | |
Jason Tian | fad8ba9193 | |
Jason TIAN | 7225adcae7 | |
Jason Tian | d7f8dac6fd | |
Jason Tian | bfab9cc424 | |
Jason Tian | 3caac8534c | |
Jason Tian | 077e134c48 | |
Jason Tian | bd69d77818 | |
Jason Tian | b4c0d646e6 | |
Jason Tian | 0f20dd5180 | |
Jason Tian | f91940cc3c | |
Jason Tian | 2379ac053a | |
Jason TIAN | 32f0c7cdfc | |
Jason TIAN | d9da861a13 | |
Jason TIAN | 2f8913ef99 | |
Jason TIAN | 2faa722940 | |
Jason Tian | 5f9ea80b1c | |
Jason TIAN | d5c89926fa | |
Jason TIAN | 597a8f2277 | |
Jason Tian | 3a5eedbc10 | |
Jason Tian | a714ee74b5 | |
Jason Tian | b1dbd3133b | |
Jason Tian | 685f97533a | |
Jason Tian | 309c05595a | |
Jason TIAN | d47b4ff75e | |
Jason Tian | 2354c4db22 | |
Jason TIAN | d594e2e5cd | |
Jason Tian | 29647f3f05 | |
Jason Tian | b0c73b1309 | |
Jason Tian | 2895e19245 | |
Jason Tian | e19b5d6169 | |
Jason Tian | 586dbd5fea | |
Jason TIAN | 0be75132ce | |
Jason TIAN | 0ad2bf30dc | |
Jason TIAN | 1d3c87b799 | |
Jason TIAN | a0e9e9102e | |
Jason TIAN | 3a6f18c2da | |
Jason Tian | ebacf50268 | |
Jason Tian | 1f5759dd3e | |
Jason Tian | d7209bea78 | |
Jason TIAN | f053793ef3 | |
Jason TIAN | b61e9f4b97 | |
Jason Tian | 0e2c17fc91 | |
Jason TIAN | 0ce5641904 | |
Jason Tian | 715e6a7114 | |
Jason TIAN | 78fec507a8 | |
Jason TIAN | 475d74834d | |
Jason Tian | 0e23f41eff | |
Jason Tian | ddd7841231 | |
Jason Tian | 03c1e7bdca | |
Jason Tian | 66e2c2ec62 | |
Jason Tian | fafec53fde | |
Jason Tian | d07a37fa84 | |
Jason Tian | df5b8e9481 | |
Jason Tian | 552ad698bc | |
Jason Tian | 5c21e564b7 | |
Jason Tian | 4e4d938a70 | |
Jason Tian | a7ba745c8d | |
Jason TIAN | a4a5ce49ea | |
Jason Tian | 84b2b6cdf8 | |
Jason TIAN | f3ada96530 | |
Jason TIAN | f730b0ac90 | |
Jason TIAN | 2d260e2b36 | |
Jason TIAN | 03667dd4df | |
Jason TIAN | 3eb5ab539f | |
Jason TIAN | 4df8fac72b | |
Jason TIAN | 59946ab887 | |
Jason TIAN | f3ff855490 | |
Jason TIAN | bb9dc489d5 | |
Jason Tian | 2ae5378dea | |
Jason Tian | 33775e40f3 | |
Jason Tian | 11a35818e0 | |
Jason TIAN | 66891a7a51 | |
Jason TIAN | 95b874b213 | |
Jason TIAN | 1b683f00f4 | |
Jason Tian | d73030d726 | |
Jason Tian | e2fafc2560 | |
Jason Tian | f6b6bbabf2 | |
Jason TIAN | 9fd4f75146 | |
Jason TIAN | f9e1e0bb90 | |
Jason Tian | 878704546f | |
Jason Tian | d0c51da73a | |
Jason TIAN | 749653e1b0 | |
Jason Tian | 32d7096dd3 | |
Jason TIAN | 15754083d0 | |
Jason TIAN | 267cf7549a | |
Jason TIAN | e5199ee924 | |
Jason TIAN | 4800051a1a | |
Jason TIAN | 48eab3baba | |
Jason TIAN | 2ac92c3e2d | |
Jason Tian | b4363e0963 | |
Jason Tian | bdfa5d7725 | |
Jason Tian | 0616584194 | |
Jason Tian | 2952424d48 | |
Jason Tian | f3b25d0658 | |
Jason Tian | a1120b4510 | |
Jason Tian | a4063b2b90 | |
Jason Tian | 8147166eee | |
Jason TIAN | d00fb6d027 | |
Jason TIAN | 7dfd883e87 | |
Jason Tian | 675647bb58 | |
Jason TIAN | 169a703b39 | |
Jason Tian | 343e062cbe | |
Jason Tian | 536f1d03ea | |
Jason Tian | 311e34199a | |
Jason Tian | ba8c8f413d | |
Jason Tian | affb503699 | |
Jason TIAN | c4b6eae7aa | |
Jason TIAN | 8bd115c9a0 | |
Jason TIAN | 2576788254 | |
Jason TIAN | 79dc8f01a7 | |
Jason Tian | d283298d91 | |
Jason Tian | 200e709bed | |
Jason Tian | 009d1d553f | |
Jason TIAN | a342f0151b | |
Jason TIAN | 62f6a67ed5 | |
Jason TIAN | b1f501f000 | |
Jason Tian | e89b4153e7 | |
Jason Tian | 446df787a6 | |
Jason Tian | 0df04a24c7 | |
Jason Tian | 80df2d987d | |
Jason Tian | 03846ad9d6 | |
Jason TIAN | de523e5e07 | |
Jason TIAN | 67605efb08 | |
Jason Tian | 95966ace0a | |
Jason TIAN | 36f8e00171 | |
Jason TIAN | 0e0ac08270 | |
Jason TIAN | 102201f283 | |
Jason Tian | c0dba97f21 | |
Jason Tian | 652ebbe53e | |
Jason TIAN | f15eae7912 | |
Jason TIAN | b255ed7d4b | |
Jason TIAN | ed6ee59ac8 | |
Jason TIAN | f98ff66754 | |
Jason TIAN | e979c59662 | |
Jason Tian | 844fe445fe | |
Jason Tian | a8ab9dbdec | |
Jason Tian | 4621d64cec | |
Jason TIAN | 92c34d9d85 | |
Jason TIAN | 46646ebcf7 | |
Jason TIAN | e8dafdeacd | |
Jason Tian | 41fbe857f5 | |
Jason Tian | 3104fa845d | |
Jason Tian | 5f3103e950 | |
Jason TIAN | 7526959fd2 | |
Jason TIAN | ba8225b345 | |
Jason Tian | 2cdd54365b | |
Jason TIAN | c20a7cbe84 | |
Jason TIAN | 23190fb58c | |
Jason Tian | df072a0053 | |
Jason Tian | c3a81d71b2 | |
Jason Tian | 218b42db23 | |
Jason Tian | 4ace2bc298 | |
Jason Tian | 07b559fa30 | |
Jason Tian | 8e69b4b0c2 | |
Jason Tian | 798323a740 | |
Jason Tian | 169ca3e94a | |
Jason Tian | 9396661c67 |
12
README.org
12
README.org
|
@ -1,9 +1,11 @@
|
|||
* A Personal Emacs Configuration
|
||||
|
||||
[[https://github.com/jsntn/emacs.d/actions/workflows/test.yml][https://github.com/jsntn/emacs.d/actions/workflows/test.yml/badge.svg]]
|
||||
[[https://github.com/jsntn/emacs.d/actions/workflows/myelpa.yml][https://github.com/jsntn/emacs.d/actions/workflows/myelpa.yml/badge.svg]]
|
||||
|
||||
This is my personal Emacs configuration, continually used and tweaked since
|
||||
2020, and I am always trying to make it same behaviour on my Windows and macOS.
|
||||
Behold, dear visitor, my cherished Emacs configuration, meticulously crafted and
|
||||
refined since the year of 2020. With unwavering dedication, I tirelessly
|
||||
endeavor to harmonize its functioning across the Windows, Arch Linux and macOS
|
||||
operating systems. 🙂
|
||||
|
||||
* Table of Content :noexport:TOC_4:
|
||||
- [[#a-personal-emacs-configuration][A Personal Emacs Configuration]]
|
||||
|
@ -30,6 +32,7 @@ This is my personal Emacs configuration, continually used and tweaked since
|
|||
- [[#plantuml-1][PlantUML]]
|
||||
- [[#winpython][WinPython]]
|
||||
- [[#known-issue][Known Issue]]
|
||||
- [[#read-more][Read more]]
|
||||
|
||||
* Usage
|
||||
TODO
|
||||
|
@ -161,3 +164,6 @@ I use [[https://github.com/jwiegley/use-package][use-package]] to manage package
|
|||
However, it seems the hl-todo and org-bullets settings don't work if they are
|
||||
configured in the init-packages.el, i.e., [[https://github.com/jsntn/emacs.d/commit/1e409e075024d72f2dc7520ada092b04b3012f48#diff-aeac2722d1b94adc236ce40df31d9cb7eb107e43b95c13c6c795e71044ec2c29L119-L138][link 1]] and [[https://github.com/jsntn/emacs.d/commit/1e409e075024d72f2dc7520ada092b04b3012f48#diff-aeac2722d1b94adc236ce40df31d9cb7eb107e43b95c13c6c795e71044ec2c29L150-L152][link 2]], but both of them
|
||||
are effective if I move them to [[https://github.com/jsntn/emacs.d/commit/19e71501432f5b5ba36375ad711eb62a3fbe91d4#diff-54e03c0bf9c47228b3868e00ea21baade79013af33501ff53bbadbd26060a227R32-R35][init-display.el]] and my [[https://github.com/jsntn/emacs.d/blob/1e409e075024d72f2dc7520ada092b04b3012f48/init.el#L98][local-config.el]].
|
||||
|
||||
* Read more
|
||||
- https://github.com/jsntn/emacs-vagrantfile
|
||||
|
|
53
init.el
53
init.el
|
@ -2,7 +2,7 @@
|
|||
|
||||
;; =============================================================================
|
||||
;; hi@jsntn.com
|
||||
;; 2020, 2021, 2022, 2023
|
||||
;; 2020, 2021, 2022, 2023, 2024
|
||||
;; =============================================================================
|
||||
|
||||
;;; Commentary:
|
||||
|
@ -32,13 +32,35 @@
|
|||
(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
|
||||
|
||||
|
||||
(defconst *dependencies-installation-enabled* nil) ; enable with t if you prefer
|
||||
|
||||
|
||||
;; =============================================================================
|
||||
;; require settings
|
||||
;; =============================================================================
|
||||
|
||||
(require 'init-portable) ; portable Emacs settings
|
||||
|
||||
|
||||
|
||||
(when (string-equal (getenv "ELPA") "local")
|
||||
(message "The built-in Org version: %s" (org-version)))
|
||||
|
||||
(when (string-equal (getenv "ELPA") "online")
|
||||
;; use the latest version of Org
|
||||
(add-to-list 'load-path (concat (getenv "GITHUB_WORKSPACE") "/src/org-mode/lisp"))
|
||||
(require 'org)
|
||||
(message "The latest Org version: %s" (org-version)))
|
||||
|
||||
|
||||
|
||||
(require 'init-load-path) ; load-path settings
|
||||
(require 'init-pre) ; pre-startup settings
|
||||
|
||||
(require 'init-messages) ; *Messages* buffer settings
|
||||
(require 'init-utils) ; utils configuration
|
||||
(require 'init-timer-utils) ; timer utils
|
||||
|
||||
(require 'local-var nil 'noerror) ; allow users to provide an optional
|
||||
; "local-var" containing personal variables
|
||||
|
||||
|
@ -62,6 +84,8 @@
|
|||
|
||||
(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
|
||||
("melpa" . "https://melpa.org/packages/")))
|
||||
;; Official MELPA Mirror, in case necessary.
|
||||
;;(add-to-list 'package-archives (cons "melpa-mirror" (concat proto "://www.mirrorservice.org/sites/melpa.org/packages/")) t)
|
||||
|
||||
(when (string-equal (getenv "ELPA") "local")
|
||||
;; when running on GitHub w/ local elpa Actions config, overwrite above `package-archives'
|
||||
|
@ -83,6 +107,13 @@
|
|||
(eval-print-last-sexp)))
|
||||
(load bootstrap-file nil 'nomessage))
|
||||
|
||||
(when (or (string-equal (getenv "ELPA") "local")
|
||||
(string-equal (getenv "STRAIGHT") "freeze"))
|
||||
(let ((source (expand-file-name "straight/versions/my.el" user-emacs-directory))
|
||||
(destination (expand-file-name "straight/versions/default.el" user-emacs-directory)))
|
||||
(when (file-exists-p source)
|
||||
(copy-file source destination t))))
|
||||
|
||||
|
||||
(package-initialize)
|
||||
(unless package-archive-contents
|
||||
|
@ -90,10 +121,6 @@
|
|||
|
||||
|
||||
|
||||
(when (string-equal (getenv "ELPA") "online")
|
||||
;; use the latest version of Org
|
||||
(add-to-list 'load-path (concat (getenv "GITHUB_WORKSPACE") "/src/org-mode/lisp"))
|
||||
(message "Org version: %s" (org-version)))
|
||||
|
||||
|
||||
|
||||
|
@ -104,17 +131,27 @@
|
|||
;; require settings
|
||||
;; =============================================================================
|
||||
|
||||
(require 'init-company) ; company completion related settings
|
||||
(require 'init-dict) ; dict settings
|
||||
(require 'init-display) ; display settings
|
||||
(require 'init-encryption) ; encryption settings
|
||||
(require 'init-evil) ; evil related config
|
||||
(require 'init-font) ; font settings
|
||||
(require 'init-gpg) ; GPG settings
|
||||
|
||||
(require 'init-ibuffer) ; IBuffer mode settings
|
||||
|
||||
(require 'init-org) ; Org-mode settings
|
||||
(require 'init-plantuml) ; PlantUML settings
|
||||
(require 'init-python) ; Python settings
|
||||
(require 'init-reformatter) ; reformatter settings
|
||||
(require 'init-sessions) ; session settings
|
||||
(require 'init-shell) ; Shell settings
|
||||
(require 'init-spelling) ; spelling settings
|
||||
(require 'init-utils) ; utils configuration
|
||||
(require 'init-tags) ; tags related config
|
||||
|
||||
(require 'init-uuid) ; UUID settings
|
||||
(require 'init-veracrypt) ; VeraCrypt/TrueCrypt settings
|
||||
(require 'init-yaml) ; YAML settings
|
||||
|
||||
(require 'init-misc) ; miscellaneous settings
|
||||
|
@ -128,6 +165,10 @@
|
|||
;; move the keybindings to the end of the other settings
|
||||
(require 'init-keybindings) ; keybindings with general.el
|
||||
|
||||
(when *dependencies-installation-enabled*
|
||||
(require 'init-deps) ; dependencies installation
|
||||
)
|
||||
|
||||
|
||||
;; =============================================================================
|
||||
;; footer
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
;;; init-company.el --- company completion related settings -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
|
||||
;; {{ START: PComplete - Context-Sensitive Completion in Emacs
|
||||
;; reference,
|
||||
;; - https://web.archive.org/web/20231102054827/http://www.masteringemacs.org:80/article/pcomplete-context-sensitive-completion-emacs
|
||||
;; - https://web.archive.org/web/20231102145724/https://timmydouglas.com/2020/12/18/eshell-complete.html
|
||||
(defconst pcmpl-git-commands
|
||||
'("add"
|
||||
"bisect" "branch"
|
||||
"checkout" "clone" "commit"
|
||||
"diff"
|
||||
"fetch"
|
||||
"grep"
|
||||
"init"
|
||||
"log"
|
||||
"merge" "mv"
|
||||
"pull" "push"
|
||||
"rebase" "remote" "reset" "restore" "rm"
|
||||
"show"
|
||||
"stash"
|
||||
"status"
|
||||
"submodule"
|
||||
"tag")
|
||||
"List of `git' commands.")
|
||||
|
||||
(defconst pcmpl-git-submodule-commands
|
||||
'(
|
||||
"foreach"
|
||||
"init"
|
||||
"status"
|
||||
"sync"
|
||||
"update"
|
||||
))
|
||||
|
||||
(defconst pcmpl-git-diff-args
|
||||
'(
|
||||
"--cached"
|
||||
"--staged"
|
||||
))
|
||||
|
||||
(defconst pcmpl-git-log-args
|
||||
'(
|
||||
"--oneline"
|
||||
))
|
||||
|
||||
(defconst pcmpl-git-stash-commands
|
||||
'(
|
||||
"clear"
|
||||
"drop"
|
||||
"list"
|
||||
"pop"
|
||||
"save"
|
||||
))
|
||||
|
||||
(defvar pcmpl-git-ref-list-cmd "git for-each-ref refs/ --format='%(refname)'"
|
||||
"The `git' command to run to get a list of refs.")
|
||||
|
||||
(defun pcmpl-git-get-refs (type)
|
||||
"Return a list of `git' refs filtered by TYPE."
|
||||
(with-temp-buffer
|
||||
(insert (shell-command-to-string pcmpl-git-ref-list-cmd))
|
||||
(goto-char (point-min))
|
||||
(let ((ref-list))
|
||||
(while (re-search-forward (concat "^refs/" type "/\\(.+\\)$") nil t)
|
||||
(add-to-list 'ref-list (match-string 1)))
|
||||
ref-list)))
|
||||
|
||||
(defun pcmpl-git-remotes ()
|
||||
"Return a list of remote repositories."
|
||||
(split-string (shell-command-to-string "git remote")))
|
||||
|
||||
(defun pcomplete/git ()
|
||||
"Completion for `git'."
|
||||
;; Completion for the command argument.
|
||||
(pcomplete-here* pcmpl-git-commands)
|
||||
;; complete files/dirs forever if the command is `add' or `rm'
|
||||
(cond
|
||||
((pcomplete-match "help" 1)
|
||||
(pcomplete-here* pcmpl-git-commands))
|
||||
((pcomplete-match (regexp-opt '("pull" "push")) 1)
|
||||
(pcomplete-here (pcmpl-git-remotes)))
|
||||
((pcomplete-match (regexp-opt '("add" "rm")) 1)
|
||||
(while (pcomplete-here (pcomplete-entries))))
|
||||
;; provide branch completion for the command `checkout'.
|
||||
((pcomplete-match "checkout" 1)
|
||||
(pcomplete-here* (append (pcmpl-git-get-refs "heads")
|
||||
(pcmpl-git-get-refs "tags"))))
|
||||
((pcomplete-match "submodule" 1)
|
||||
(pcomplete-here* pcmpl-git-submodule-commands))
|
||||
((pcomplete-match "diff" 1)
|
||||
(pcomplete-here* pcmpl-git-diff-args))
|
||||
((pcomplete-match "log" 1)
|
||||
(pcomplete-here* pcmpl-git-log-args))
|
||||
((pcomplete-match "stash" 1)
|
||||
(pcomplete-here* pcmpl-git-stash-commands))
|
||||
(t
|
||||
(while (pcomplete-here (pcomplete-entries))))
|
||||
))
|
||||
;; END: PComplete - Context-Sensitive Completion in Emacs }}
|
||||
|
||||
|
||||
|
||||
|
||||
;; {{ START: pcomplete company completion
|
||||
;; via https://web.archive.org/web/20231102031110/https://xenodium.com/eshell-pcomplete-company-completion/
|
||||
(defun company-pcomplete--overlap-tail (a b)
|
||||
"When A is \"SomeDev\" and B is \"Developer\", return \"eloper\"."
|
||||
(let ((prefix a)
|
||||
(remaining nil))
|
||||
(while (and (not remaining) (> (length prefix) 0))
|
||||
(when (s-starts-with? prefix b)
|
||||
(setq remaining (substring b (length prefix))))
|
||||
(setq prefix (substring prefix 1)))
|
||||
remaining))
|
||||
|
||||
(defun company-pcomplete--candidates (prefix)
|
||||
"Get candidates for PREFIX company completion using `pcomplete'."
|
||||
;; When prefix is: "~/Down" and completion is "Downloads", need
|
||||
;; to find common string and join into "~/Downloads/".
|
||||
(-map (lambda (item)
|
||||
(if (s-starts-with? prefix item)
|
||||
item
|
||||
(concat prefix (company-pcomplete--overlap-tail prefix item))))
|
||||
(all-completions prefix (pcomplete-completions))))
|
||||
|
||||
(defun company-pcomplete (command &optional arg &rest ignored)
|
||||
"Complete using pcomplete. See `company''s COMMAND ARG and IGNORED for details."
|
||||
(interactive (list 'interactive))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend 'company-pcomplete))
|
||||
(prefix (company-grab-symbol))
|
||||
(candidates
|
||||
(company-pcomplete--candidates arg))))
|
||||
;; END: pcomplete company completion }}
|
||||
|
||||
|
||||
|
||||
(use-package company-tabnine
|
||||
:config
|
||||
(setq company-tabnine-binaries-folder (expand-file-name ".TabNine/" user-emacs-directory))
|
||||
;; (add-to-list 'company-backends #'company-tabnine)
|
||||
)
|
||||
|
||||
(use-package company
|
||||
:delight
|
||||
:init
|
||||
(global-company-mode)
|
||||
:config
|
||||
(setq company-idle-delay 0.2)
|
||||
;; number the candidates (use M-1, M-2 etc to select completions).
|
||||
(setq company-show-numbers t)
|
||||
;; show suggestions after entering 3 character.
|
||||
(setq company-minimum-prefix-length 3)
|
||||
(setq company-dabbrev-downcase nil)
|
||||
(setq company-dabbrev-ignore-case t)
|
||||
(setq company-dabbrev-other-buffers nil)
|
||||
(setq company-tooltip-align-annotations t)
|
||||
;; when the list of suggestions is shown, and you go through the list of
|
||||
;; suggestions and reach the end of the list, the end of the list of
|
||||
;; suggestions does not wrap around to the top of the list again. This is a
|
||||
;; minor inconvenience that can be solved:
|
||||
(setq company-selection-wrap-around t)
|
||||
;; use tab key to cycle through suggestions.
|
||||
;; ('tng' means 'tab and go')
|
||||
(company-tng-configure-default)
|
||||
|
||||
(setq company-transformers '(delete-dups
|
||||
company-sort-by-occurrence))
|
||||
|
||||
(setq company-backends '(
|
||||
(company-capf company-keywords company-dabbrev-code)
|
||||
;; commented below to speed up the completion
|
||||
;; (company-tabnine)
|
||||
company-files)
|
||||
)
|
||||
|
||||
;; add yasnippet support for all company backends.
|
||||
(defvar company-mode/enable-yas t
|
||||
"Enable yasnippet for all backends.")
|
||||
(defun company-mode/backend-with-yas (backend)
|
||||
(if (or (not company-mode/enable-yas) (and (listp backend) (member 'company-yasnippet backend)))
|
||||
backend
|
||||
(append (if (consp backend) backend (list backend))
|
||||
'(:with company-yasnippet))))
|
||||
(setq company-backends (mapcar #'company-mode/backend-with-yas company-backends))
|
||||
|
||||
;; set the backends for writing in text related mode
|
||||
(defun my-company-backends-text-mode-hook ()
|
||||
(setq-local company-backends '(
|
||||
(company-dabbrev company-ispell)
|
||||
;; commented below to speed up the completion
|
||||
;; (company-tabnine)
|
||||
company-files)
|
||||
))
|
||||
(dolist (hook '(
|
||||
markdown-mode-hook
|
||||
org-mode-hook
|
||||
text-mode-hook
|
||||
))
|
||||
(add-hook hook 'my-company-backends-text-mode-hook))
|
||||
|
||||
;; set the backends for shell-mode
|
||||
(defun my-company-backends-shell-mode-hook ()
|
||||
(setq-local company-backends '(
|
||||
(company-capf company-files company-pcomplete)
|
||||
)))
|
||||
(add-hook 'shell-mode-hook 'my-company-backends-shell-mode-hook)
|
||||
|
||||
;; add `company-elisp' backend for elisp.
|
||||
;; (add-hook 'emacs-lisp-mode-hook
|
||||
;; #'(lambda ()
|
||||
;; (require 'company-elisp)
|
||||
;; (push 'company-elisp company-backends)))
|
||||
;; via https://github.com/manateelazycat/lazycat-emacs/blob/8f3dee8a6fe724ec52cd2b17155cfc2cefc8066b/site-lisp/config/init-company-mode.el
|
||||
|
||||
|
||||
|
||||
|
||||
;; { START: company-candidates from abo-abo
|
||||
;; if candidate list was ("var0" "var1" "var2"), then entering 1 means:
|
||||
;; select the first candidate (i.e. "var0"), instead of:
|
||||
;; insert "1", resulting in "var1", i.e. the second candidate
|
||||
;; via,
|
||||
;; - https://oremacs.com/2017/12/27/company-numbers/
|
||||
(defun ora-company-number ()
|
||||
"Forward to `company-complete-number'.
|
||||
Unless the number is potentially part of the candidate.
|
||||
In that case, insert the number."
|
||||
;; via https://github.com/abo-abo/oremacs/blob/d217e22a3b8dc88d10f715b32a7d1facf1f7ae18/modes/ora-company.el#L22-L39
|
||||
(interactive)
|
||||
(let* ((k (this-command-keys))
|
||||
(re (concat "^" company-prefix k)))
|
||||
(if (or (cl-find-if (lambda (s) (string-match re s))
|
||||
company-candidates)
|
||||
(> (string-to-number k)
|
||||
(length company-candidates))
|
||||
(looking-back "[0-9]+\\.[0-9]*" (line-beginning-position)))
|
||||
(self-insert-command 1)
|
||||
(company-complete-number
|
||||
(if (equal k "0")
|
||||
10
|
||||
(string-to-number k))))))
|
||||
|
||||
(let ((map company-active-map))
|
||||
;; via https://github.com/abo-abo/oremacs/blob/d217e22a3b8dc88d10f715b32a7d1facf1f7ae18/modes/ora-company.el#L46-L53
|
||||
(mapc (lambda (x) (define-key map (format "%d" x) 'ora-company-number))
|
||||
(number-sequence 0 9))
|
||||
(define-key map " " (lambda ()
|
||||
(interactive)
|
||||
(company-abort)
|
||||
(self-insert-command 1)))
|
||||
(define-key map (kbd "<return>") nil))
|
||||
;; END: company-candidates from abo-abo }
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
;; use this package to fix tooltip alignment issue below,
|
||||
;; https://github.com/company-mode/company-mode/issues/1388
|
||||
(use-package company-posframe
|
||||
:delight
|
||||
:straight (:type git :host github :repo "tumashu/company-posframe")
|
||||
:config
|
||||
(company-posframe-mode 1)
|
||||
)
|
||||
|
||||
|
||||
|
||||
(provide 'init-company)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-company.el ends here
|
|
@ -3,69 +3,282 @@
|
|||
;;; Code:
|
||||
|
||||
|
||||
(defcustom my-install-deps
|
||||
'((aspell
|
||||
:darwin-command "brew install aspell"
|
||||
|
||||
;;; TODO:
|
||||
;; - dep: shred
|
||||
;; - dep: truecrypt/veracrypt
|
||||
|
||||
|
||||
;;; NOTE: specific package manager is required,
|
||||
;; - macOS: brew
|
||||
;; - Linux: pacman
|
||||
;; - Windows: scoop
|
||||
|
||||
;; (my-check-for-executable "Homebrew (macOS)" "brew")
|
||||
;; (my-check-for-executable "npm (macOS/Linux)" "npm")
|
||||
|
||||
|
||||
(defvar my-install-deps
|
||||
'(
|
||||
;; example,
|
||||
;; (npm ; the executable binary name
|
||||
;; :darwin-command "brew install node" ; the installation command for macOS
|
||||
;; :linux-command "sudo pacman -S --noconfirm nodejs npm" ; the installation command for Linux
|
||||
;; :windows-command "scoop install nodejs" ; the installation command for Windows
|
||||
;; ...... ; if the darwin/linux/windows-command (above) is,
|
||||
;; ...... ; - empty value (like - linux-command: ""), then a reminding message will raise for manual installation
|
||||
;; ...... ; - not exist, then reminding message will show that xxx (executable binary name) is not considered to install on specific OS
|
||||
;; :message nil ; manual set reminding message
|
||||
;; :enabled t) ; to install or not
|
||||
(npm ; install npm first
|
||||
:darwin-command "brew install node"
|
||||
:linux-command "sudo pacman -S --noconfirm nodejs npm"
|
||||
:windows-command "scoop install nodejs"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(ansible-language-server
|
||||
:darwin-command "npm install -g @ansible/ansible-language-server"
|
||||
:linux-command "sudo npm install -g @ansible/ansible-language-server"
|
||||
:windows-command "npm install -g @ansible/ansible-language-server"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(aspell
|
||||
:darwin-command "brew install aspell" ; TODO: is the en dictionary installed automatically by brew?
|
||||
:linux-command "sudo pacman -S --noconfirm aspell aspell-en"
|
||||
:windows-command "scoop install aspell"
|
||||
:message "aspell is needed in this configuration file, check/install it manually."
|
||||
:enabled t)
|
||||
(bash-language-server
|
||||
:darwin-command "npm install -g bash-language-server"
|
||||
:linux-command "sudo npm install -g bash-language-server"
|
||||
:windows-command "npm install -g bash-language-server"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(ctags
|
||||
:darwin-command "brew install universal-ctags"
|
||||
:linux-command "sudo pacman -S --noconfirm ctags"
|
||||
:windows-command "scoop install universal-ctags"
|
||||
:message "ctags is needed in this configuration file, check/install it manually."
|
||||
:enabled t)
|
||||
;; (dovecot
|
||||
;; :linux-command "sudo pacman -S --noconfirm dovecot"
|
||||
;; :message nil
|
||||
;; :enabled t)
|
||||
(fzf
|
||||
:linux-command "sudo pacman -S --noconfirm fzf"
|
||||
:message nil
|
||||
:enabled t)
|
||||
;; (global ; https://www.gnu.org/software/global/
|
||||
;; :linux-command "sudo pacman -S --noconfirm global"
|
||||
;; :message nil
|
||||
;; :enabled t)
|
||||
(grammarly-languageserver
|
||||
:darwin-command "npm install -g @emacs-grammarly/grammarly-languageserver"
|
||||
:linux-command "sudo npm install -g @emacs-grammarly/grammarly-languageserver"
|
||||
:windows-command "npm install -g @emacs-grammarly/grammarly-languageserver"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(languagetool
|
||||
:darwin-command "brew install languagetool"
|
||||
:linux-command "sudo pacman -S --noconfirm languagetool"
|
||||
;; :windows-command ""
|
||||
:message nil
|
||||
:enabled t)
|
||||
(less
|
||||
:darwin-command "brew install less"
|
||||
:linux-command "sudo pacman -S --noconfirm less"
|
||||
:windows-command "scoop install less"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(marksman ; https://github.com/artempyanykh/marksman
|
||||
:darwin-command "brew install marksman"
|
||||
:linux-command "sudo pacman -S --noconfirm marksman"
|
||||
;; :windows-command ""
|
||||
:message nil
|
||||
:enabled t)
|
||||
(mbsync ; https://isync.sourceforge.io
|
||||
:linux-command "sudo pacman -S --noconfirm isync"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(notmuch
|
||||
;; it is recommended to install the specific version that come the same as
|
||||
;; Emacs `notmuch` package (in my `site-lisp`)
|
||||
;; currently, the notmuch version on my Arch Linux is 0.38.3-1,
|
||||
;; $ notmuch --version
|
||||
;; notmuch 0.38.3
|
||||
:linux-command "sudo pacman -S --noconfirm notmuch"
|
||||
:message nil
|
||||
:enabled t)
|
||||
;; (offlineimap
|
||||
;; :linux-command "sudo pacman -S --noconfirm offlineimap"
|
||||
;; :message nil
|
||||
;; :enabled t)
|
||||
(rsync
|
||||
:linux-command "sudo pacman -S --noconfirm rsync"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(sbcl
|
||||
:darwin-command "brew install sbcl"
|
||||
:linux-command "sudo pacman -S --noconfirm sbcl"
|
||||
:windows-command "scoop install sbcl"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(shellcheck
|
||||
:darwin-command "brew install shellcheck"
|
||||
:linux-command "sudo pacman -S --noconfirm shellcheck"
|
||||
:windows-command "scoop install shellcheck"
|
||||
:message "shellcheck is needed in this configuration file, check/install it manually."
|
||||
:enabled t)
|
||||
(shfmt
|
||||
:darwin-command "brew install shfmt"
|
||||
;; :linux-command "sudo snap install shfmt"
|
||||
:linux-command "sudo pacman -S --noconfirm shfmt"
|
||||
:windows-command "scoop install shfmt"
|
||||
:message "shfmt is needed in this configuration file, check/install it manually."
|
||||
:enabled t)
|
||||
(sqlite3
|
||||
:darwin-command "brew install sqlite"
|
||||
:linux-command "sudo pacman -S --noconfirm sqlite"
|
||||
:windows-command "scoop install sqlite"
|
||||
:message "sqlite3 is needed in this configuration file, check/install it manually."
|
||||
:enabled t)
|
||||
(ripgrep
|
||||
(stardict
|
||||
:darwin-command "brew install stardict"
|
||||
:linux-command "sudo pacman -S --noconfirm stardict"
|
||||
;; :windows-command ""
|
||||
:message nil
|
||||
:enabled t)
|
||||
(sdcv
|
||||
:darwin-command "brew install sdcv"
|
||||
:linux-command "sudo pacman -S --noconfirm sdcv"
|
||||
;; :windows-command ""
|
||||
:message nil
|
||||
:enabled t)
|
||||
(rg
|
||||
:darwin-command "brew install ripgrep"
|
||||
:message "ripgrep is needed in this configuration file, check/install it manually."
|
||||
:linux-command "sudo pacman -S --noconfirm ripgrep"
|
||||
:windows-command "scoop install ripgrep"
|
||||
:message "ripgrep (rg) is needed in this configuration file, check/install it manually."
|
||||
:enabled t)
|
||||
(js-yaml
|
||||
:darwin-command "npm install -g js-yaml"
|
||||
:linux-command "sudo npm install -g js-yaml"
|
||||
:windows-command "npm install -g js-yaml"
|
||||
:message nil ;; No message needed for js-yaml
|
||||
:enabled t)
|
||||
(pyright
|
||||
:linux-command "sudo npm install -g pyright"
|
||||
:darwin-command "brew install pyright"
|
||||
;; :linux-command "pipx install pyright"
|
||||
:linux-command "sudo pacman -S --noconfirm pyright"
|
||||
:windows-command "npm install -g pyright"
|
||||
:message nil ;; No message needed for pyright
|
||||
:enabled t)
|
||||
(prettier
|
||||
:linux-command "sudo npm install -g prettier"
|
||||
:darwin-command "brew install prettier"
|
||||
:linux-command "sudo pacman -S --noconfirm prettier"
|
||||
:windows-command "npm install -g prettier"
|
||||
:message nil ;; No message needed for prettier
|
||||
:enabled t)))
|
||||
:enabled t)
|
||||
(trash-list ;; check trash-list command as the trash-cli is not a valid command for trash-cli
|
||||
:linux-command "sudo pacman -S --noconfirm trash-cli"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(tmux
|
||||
:linux-command "sudo pacman -S --noconfirm tmux"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(unzip ; for nov.el package
|
||||
:linux-command "sudo pacman -S --noconfirm unzip"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(vi ; recommended as it is needed by `git merge` by default
|
||||
:linux-command "sudo pacman -S --noconfirm vi"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(vim
|
||||
:linux-command "sudo pacman -S --noconfirm vim"
|
||||
:message nil
|
||||
:enabled t)
|
||||
(yaml-language-server
|
||||
:darwin-command "npm install -g yaml-language-server"
|
||||
:linux-command "sudo npm install -g yaml-language-server"
|
||||
:windows-command "npm install -g yaml-language-server"
|
||||
:message nil
|
||||
:enabled t)
|
||||
))
|
||||
|
||||
(defun my-install-dependency (name command)
|
||||
"Install a dependency NAME using COMMAND if it is not already installed.
|
||||
Display the MESSAGE if installation is skipped."
|
||||
(unless (executable-find name)
|
||||
(if (y-or-n-p (format "Install %s? " name))
|
||||
(progn
|
||||
(message (format "Installing %s..." name))
|
||||
(shell-command command))
|
||||
(progn
|
||||
(message (format "Installing %s..." name))
|
||||
(shell-command command))
|
||||
(message "Skipping %s installation." name))))
|
||||
|
||||
(defun my-install-all-deps ()
|
||||
"Install enabled Emacs dependencies."
|
||||
(interactive)
|
||||
(dolist (dep my-install-deps)
|
||||
(let* ((name (car dep))
|
||||
(command-darwin (plist-get dep :darwin-command))
|
||||
(command-linux (plist-get dep :linux-command))
|
||||
(command (plist-get dep :command))
|
||||
(message (plist-get dep :message))
|
||||
(enabled (plist-get dep :enabled)))
|
||||
(let* ((name (symbol-name (car dep)))
|
||||
(value (cdr dep))
|
||||
(command-darwin (plist-get value :darwin-command))
|
||||
(command-linux (plist-get value :linux-command))
|
||||
(command-windows (plist-get value :windows-command))
|
||||
(msg (plist-get value :message))
|
||||
(enabled (plist-get value :enabled)))
|
||||
(when enabled
|
||||
(cond
|
||||
((and command-darwin (eq system-type 'darwin))
|
||||
(my-install-dependency name command-darwin))
|
||||
((and command-linux (eq system-type 'gnu/linux))
|
||||
(my-install-dependency name command-linux))
|
||||
(unless (and command-darwin command-linux)
|
||||
(when message
|
||||
(message message))))))))
|
||||
(unless (executable-find name)
|
||||
(cond
|
||||
;; macOS condition
|
||||
((and (eq system-type 'darwin)
|
||||
command-darwin
|
||||
(not (string-empty-p command-darwin)))
|
||||
(my-install-dependency name command-darwin))
|
||||
((and (eq system-type 'darwin)
|
||||
(eq command-darwin nil))
|
||||
(message "%s is not considered to install on macOS." name))
|
||||
;; Linux condition
|
||||
((and (eq system-type 'gnu/linux)
|
||||
command-linux
|
||||
(not (string-empty-p command-linux)))
|
||||
(my-install-dependency name command-linux))
|
||||
((and (eq system-type 'gnu/linux)
|
||||
(eq command-linux nil))
|
||||
(message "%s is not considered to install on Linux." name))
|
||||
;; Windows condition
|
||||
((and (eq system-type 'windows-nt)
|
||||
command-windows
|
||||
(not (string-empty-p command-windows)))
|
||||
(my-install-dependency name
|
||||
(format
|
||||
"powershell -Command \"%s\""
|
||||
command-windows)))
|
||||
((and (eq system-type 'windows-nt)
|
||||
(eq command-windows nil))
|
||||
(message "%s is not considered to install on Windows." name))
|
||||
;; for any other condition
|
||||
(t
|
||||
(let* ((msg-content
|
||||
(if msg
|
||||
msg
|
||||
(format "%s executable is needed in this configuration file,
|
||||
check/install it manually." name)))
|
||||
(prompt-msg (concat msg-content " Press ENTER to continue.")))
|
||||
(when (string= (read-string prompt-msg) "")
|
||||
(message "Continuing..."))
|
||||
))))))))
|
||||
|
||||
|
||||
(progn
|
||||
(when *is-win*
|
||||
(my-check-for-executable "Scoop (Windows)" "scoop"))
|
||||
(when *is-mac*
|
||||
(my-check-for-executable "Homebrew (macOS)" "brew"))
|
||||
(when *is-linux*
|
||||
(my-check-for-executable "npm (macOS/Linux)" "npm"))
|
||||
(my-install-all-deps))
|
||||
|
||||
|
||||
(provide 'init-deps)
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
;;; init-dict.el --- dict settings -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
|
||||
;; {{ START: Look up text at-point/marked by GoldenDict in Emacs
|
||||
;; https://github.com/jsntn/goldendict-emacs/tree/my
|
||||
(defun my-look-up-dict (word)
|
||||
(let ((goldendict-executable (executable-find "goldendict")))
|
||||
(if goldendict-executable
|
||||
(start-process "goldendict" nil goldendict-executable word)
|
||||
(message "GoldenDict executable not found. Please make sure it is installed and in your PATH."))))
|
||||
|
||||
(defun my/look-up-dict ()
|
||||
"Look up text at-point/marked by GoldenDict in Emacs.
|
||||
|
||||
Version 2023-08-03"
|
||||
(interactive)
|
||||
(let ((word ""))
|
||||
(if (and (bound-and-true-p mark-active) (not (equal (point) (mark))))
|
||||
(setq word (buffer-substring (region-beginning) (region-end)))
|
||||
(setq word (thing-at-point 'word)))
|
||||
(if (not (string-blank-p word))
|
||||
(my-look-up-dict word)
|
||||
(message "No word found at point or in the marked region."))))
|
||||
|
||||
(global-set-key (kbd "C-c g") 'my/look-up-dict)
|
||||
;; END }}
|
||||
|
||||
|
||||
(use-package company-english-helper
|
||||
:straight (:host github :repo "jsntn/company-english-helper" :branch "my"))
|
||||
|
||||
|
||||
(if *is-linux*
|
||||
(use-package sdcv
|
||||
:straight (:host github :repo "manateelazycat/sdcv")
|
||||
:config
|
||||
(setq sdcv-dictionary-data-dir "/usr/share/stardict/dic/")
|
||||
(global-set-key (kbd "C-c d") 'sdcv-search-pointer)
|
||||
|
||||
;; extract my dictionaries of ~/misc/*.bz2 files to stardict dictionary folder
|
||||
;; note: the extraction will not happen if ~/misc/extracted.txt exists
|
||||
(defun my-extract-stardict-bz2-files-on-linux ()
|
||||
(interactive)
|
||||
(let* ((dir (expand-file-name "misc" (getenv "HOME")))
|
||||
(extracted-file (concat dir "/extracted.txt"))
|
||||
(files
|
||||
(if (file-directory-p dir)
|
||||
(directory-files dir nil "\\.bz2\\'")
|
||||
nil)))
|
||||
(if (and files (not (null files)))
|
||||
(if (or (not (file-exists-p extracted-file))
|
||||
(not (my-file-contains-p extracted-file files)))
|
||||
(progn
|
||||
(dolist (file files)
|
||||
(let ((abs-file (concat dir "/" file)))
|
||||
(shell-command
|
||||
(format "sudo tar -xjvf %s -C /usr/share/stardict/dic" abs-file)))
|
||||
(with-temp-buffer
|
||||
(set-buffer-file-coding-system 'utf-8-unix)
|
||||
(insert file)
|
||||
(insert "\n")
|
||||
(append-to-file (point-min) (point-max) extracted-file)
|
||||
))
|
||||
(my-merge-duplicated-lines-in-file extracted-file)
|
||||
(message "StarDict dictionaries extraction completed."))
|
||||
(message "All StarDict dictionaries have already been extracted."))
|
||||
(message "The folder (misc) does not exist or does not contain any .bz2 files.")
|
||||
)))
|
||||
(my-extract-stardict-bz2-files-on-linux)
|
||||
|
||||
))
|
||||
|
||||
|
||||
|
||||
(provide 'init-dict)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-dict.el ends here
|
|
@ -3,12 +3,109 @@
|
|||
;;; Code:
|
||||
|
||||
|
||||
|
||||
(use-package delight)
|
||||
|
||||
(use-package diminish)
|
||||
|
||||
(use-package doom-themes
|
||||
:config
|
||||
;; global settings (defaults)
|
||||
(setq doom-themes-enable-bold t) ; if nil, bold is universally disabled
|
||||
;; corrects (and improves) org-mode's native fontification
|
||||
;; update: disable this as it is not compatible with org-modern horizontal
|
||||
;; line, see https://github.com/jsntn/emacs.d/issues/13
|
||||
;; (doom-themes-org-config)
|
||||
|
||||
;; theme does not load correctly in daemon mode, see,
|
||||
;; - https://stackoverflow.com/a/23668935/4274775
|
||||
;; - https://github.com/cpaulik/emacs-material-theme/issues/45#issuecomment-385247309
|
||||
;; - https://github.com/nordtheme/emacs/issues/59
|
||||
;; customization on doom-monokai-classic
|
||||
(defun my-load-theme ()
|
||||
(load-theme 'doom-monokai-classic t)
|
||||
(custom-set-faces
|
||||
`(mode-line ((t (:background ,(doom-color 'dark-violet)))))
|
||||
`(font-lock-comment-face ((t (:foreground ,(doom-color 'base6)))))
|
||||
`(default ((t (:background "black"))))))
|
||||
(if (daemonp)
|
||||
(add-hook 'after-make-frame-functions
|
||||
(lambda (frame)
|
||||
(with-selected-frame frame
|
||||
(my-load-theme))))
|
||||
(my-load-theme))
|
||||
)
|
||||
|
||||
|
||||
;; { START: hide list of minor modes in mode-line
|
||||
;; from https://emacs.stackexchange.com/a/3928/29715
|
||||
(defvar my-hidden-minor-modes
|
||||
'(abbrev-mode
|
||||
auto-capitalize-mode
|
||||
auto-fill-function
|
||||
auto-revert-mode
|
||||
dired-async-mode
|
||||
flycheck-mode
|
||||
flyspell-mode
|
||||
;; haskell-indent-mode
|
||||
;; haskell-doc-mode
|
||||
;; inf-haskell-mode
|
||||
org-roam-mode
|
||||
pangu-spacing-mode
|
||||
projectile-mode
|
||||
pyim-isearch-mode
|
||||
;; smooth-scroll-mode
|
||||
undo-tree-mode
|
||||
which-key-mode
|
||||
evil-collection-unimpaired-mode
|
||||
hs-minor-mode
|
||||
org-remark-global-tracking-mode
|
||||
yas-minor-mode
|
||||
eldoc-mode
|
||||
org-indent-mode
|
||||
))
|
||||
|
||||
(defun my/purge-minor-modes ()
|
||||
(interactive)
|
||||
(dolist (x my-hidden-minor-modes nil)
|
||||
(let ((trg (cdr (assoc x minor-mode-alist))))
|
||||
(when trg
|
||||
(setcar trg "")))))
|
||||
|
||||
(add-hook 'after-change-major-mode-hook 'my/purge-minor-modes)
|
||||
;; END: hide list of minor modes in mode-line }
|
||||
|
||||
|
||||
;; { -- START: display time in mode line --
|
||||
;; reference:
|
||||
;; ... https://www.reddit.com/r/emacs/comments/6ftm3x/share_your_modeline_customization/dil4x5z/?utm_source=reddit&utm_medium=web2x&context=3
|
||||
;; ... http://emacs.1067599.n8.nabble.com/Week-number-td89988.html
|
||||
(setq display-time-string-forms
|
||||
'((propertize
|
||||
;; %W and %V
|
||||
;; http://emacs.1067599.n8.nabble.com/Week-number-tp89988p89991.html
|
||||
(format-time-string "[%V] %H:%M:%S" now) ;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Time-Parsing.html
|
||||
;; 'face 'modeline-display-time
|
||||
'help-echo (format-time-string "[%V] %H:%M:%S" now))))
|
||||
(display-time-mode 1)
|
||||
(defun my-update-time ()
|
||||
"Update the time string in the mode line every second."
|
||||
(display-time-mode 1))
|
||||
(run-with-timer 0 1 'my-update-time)
|
||||
;; -- END: display time in mode line -- }
|
||||
|
||||
|
||||
(add-hook 'emacs-lisp-mode-hook 'show-paren-mode) ; highlight matching
|
||||
; parenthesis
|
||||
(global-hl-line-mode 1) ; highlight current line
|
||||
|
||||
(setq display-line-numbers-width-start t)
|
||||
|
||||
;; use below to fix slow scrolling issue
|
||||
(global-display-line-numbers-mode 1)
|
||||
;; via https://www.reddit.com/r/orgmode/comments/e7pq7k/linummode_very_slow_for_large_org_files/
|
||||
;; there is display issue on citre-peek, see,
|
||||
;; https://github.com/universal-ctags/citre/issues/161
|
||||
|
||||
(setq column-number-mode t) ; turn on column numbers
|
||||
|
||||
;; wrap lines at 80 characters
|
||||
|
@ -31,32 +128,9 @@
|
|||
;; https://stackoverflow.com/questions/50417/how-do-i-get-list-of-recent-files-in-gnu-emacs/50422#50422
|
||||
(recentf-mode 1)
|
||||
|
||||
;; FIXME: to be fixed (GitHub Actions Pipeline). See error below,
|
||||
;; Debugger entered--Lisp error: (void-function set-fontset-font)
|
||||
;; (progn
|
||||
;; ;; set font for emoji (if before emacs 28, should come after setting
|
||||
;; ;; symbols. emacs 28 now has 'emoji . before, emoji is part of 'symbol)
|
||||
;; ;; http://xahlee.info/emacs/emacs/emacs_set_font_emoji.html
|
||||
;; (set-fontset-font
|
||||
;; t
|
||||
;; (if (version< emacs-version "28.1")
|
||||
;; '(#x1f300 . #x1fad0)
|
||||
;; 'emoji
|
||||
;; )
|
||||
;; (cond
|
||||
;; ((member "Apple Color Emoji" (font-family-list)) "Apple Color Emoji")
|
||||
;; ((member "Noto Color Emoji" (font-family-list)) "Noto Color Emoji")
|
||||
;; ;;<2022-10-31 NotoColorEmoji uses the CBDT/CBLC color font format, which is
|
||||
;; ;; supported by Android and Chrome/Chromium OS. Windows supports it starting
|
||||
;; ;; with Windows 10 Anniversary Update in Chrome and Edge.
|
||||
;; ;; Via https://github.com/googlefonts/noto-emoji/blob/f826707b28355f6cd1593f504427ca2b1f6c4c19/README.md#using-notocoloremoji
|
||||
;; ((member "Noto Emoji" (font-family-list)) "Noto Emoji")
|
||||
;; ((member "Segoe UI Emoji" (font-family-list)) "Segoe UI Emoji")
|
||||
;; ((member "Symbola" (font-family-list)) "Symbola"))) ; http://xahlee.info/comp/unicode_font_download.html
|
||||
;; )
|
||||
|
||||
(when (display-graphic-p)
|
||||
(my-check-for-font "Symbola" "Symbola font is not installed, however, it is recommended to install for proper emoji display. Press ENTER to continue."))
|
||||
|
||||
|
||||
|
||||
;; the default split-screen direction
|
||||
;; https://stackoverflow.com/a/7998271
|
||||
|
@ -139,8 +213,7 @@ Version 2017-03-12"
|
|||
(hs-hide-block)
|
||||
(hs-show-block)))
|
||||
|
||||
;; { -- START --
|
||||
;; default inline image background in Org-mode
|
||||
;; { -- START: default inline image background in Org-mode
|
||||
;; https://emacs.stackexchange.com/a/37927/29715
|
||||
;; note: restart Emacs to make this change effective
|
||||
(defcustom org-inline-image-background nil
|
||||
|
@ -149,7 +222,11 @@ When nil, use the default face background."
|
|||
:group 'org
|
||||
:type '(choice color (const nil)))
|
||||
|
||||
(defun create-image-with-background-color (args)
|
||||
(defvar my-bg-color-to-create-image 'transparent
|
||||
"Variable to track the current advice for create-image.
|
||||
Possible values: 'transparent or 'white.")
|
||||
|
||||
(defun my-create-image-with-white-background-color (args)
|
||||
"Specify background color of Org-mode inline image through modify `ARGS'."
|
||||
(let* ((file (car args))
|
||||
(type (cadr args))
|
||||
|
@ -160,9 +237,39 @@ When nil, use the default face background."
|
|||
(list :background "white")
|
||||
props)))
|
||||
|
||||
(defun my-create-image-with-transparent-background-color (args)
|
||||
"Specify background color of Org-mode inline image through modify `ARGS'."
|
||||
(let* ((file (car args))
|
||||
(type (cadr args))
|
||||
(data-p (caddr args))
|
||||
(props (cdddr args)))
|
||||
;; Get this return result style from `create-image'.
|
||||
(append (list file type data-p)
|
||||
(list :background "transparent")
|
||||
props)))
|
||||
|
||||
(advice-add 'create-image :filter-args
|
||||
#'create-image-with-background-color)
|
||||
;; -- END -- }
|
||||
#'my-create-image-with-transparent-background-color)
|
||||
|
||||
(defun my/toggle-bg-color-to-create-image ()
|
||||
"Toggle between transparent and white background color advice for create-image."
|
||||
(interactive)
|
||||
(advice-remove 'create-image 'my-create-image-with-transparent-background-color)
|
||||
(advice-remove 'create-image 'my-create-image-with-white-background-color)
|
||||
(if (eq my-bg-color-to-create-image 'transparent)
|
||||
(progn
|
||||
(advice-add 'create-image :filter-args
|
||||
#'my-create-image-with-white-background-color)
|
||||
(setq my-bg-color-to-create-image 'white)
|
||||
(message "Switched background color for create-image to white.")
|
||||
)
|
||||
(progn
|
||||
(advice-add 'create-image :filter-args
|
||||
#'my-create-image-with-transparent-background-color)
|
||||
(setq my-bg-color-to-create-image 'transparent)
|
||||
(message "Switched background color for create-image to transparent.")
|
||||
)))
|
||||
;; -- END: default inline image background in Org-mode }
|
||||
|
||||
|
||||
(provide 'init-display)
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
;;; init-evil.el --- evil related config -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
;; evil-collection assumes evil-want-keybinding is set to nil and
|
||||
;; evil-want-integration is set to t before loading evil and evil-collection.
|
||||
(setq evil-want-integration t)
|
||||
(setq evil-want-keybinding nil)
|
||||
|
||||
(use-package evil
|
||||
:init
|
||||
(unless (display-graphic-p)
|
||||
(setq evil-want-C-i-jump nil)
|
||||
)
|
||||
:after undo-tree
|
||||
:config
|
||||
(evil-set-undo-system 'undo-tree) ; https://github.com/emacs-evil/evil/issues/1372#issuecomment-712611291
|
||||
(global-undo-tree-mode)
|
||||
(evil-mode 1)
|
||||
;; change the cursor color in terms of evil mode
|
||||
(setq evil-emacs-state-cursor '("red" box))
|
||||
(setq evil-normal-state-cursor '("green" box))
|
||||
(setq evil-visual-state-cursor '("orange" box))
|
||||
(setq evil-insert-state-cursor '("red" bar))
|
||||
(setq evil-replace-state-cursor '("red" bar))
|
||||
(setq evil-operator-state-cursor '("red" hollow))
|
||||
)
|
||||
|
||||
(use-package evil-collection
|
||||
:after evil
|
||||
:config
|
||||
(evil-collection-init)
|
||||
)
|
||||
|
||||
(use-package evil-leader
|
||||
:config
|
||||
(global-evil-leader-mode)
|
||||
)
|
||||
|
||||
(use-package evil-surround
|
||||
:config
|
||||
(global-evil-surround-mode 1)
|
||||
)
|
||||
|
||||
(use-package evil-visualstar
|
||||
:config
|
||||
(global-evil-visualstar-mode)
|
||||
)
|
||||
|
||||
|
||||
|
||||
(provide 'init-evil)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-evil.el ends here
|
|
@ -5,6 +5,99 @@
|
|||
|
||||
(setq inhibit-compacting-font-caches t) ; don't compact font caches during GC.
|
||||
|
||||
(use-package cnfonts
|
||||
:if window-system ; only load this package when in graphical Emacs
|
||||
:config
|
||||
(cnfonts-mode 1)
|
||||
(setq cnfonts-profiles
|
||||
'("program" "org-mode" "read-book"))
|
||||
(setq cnfonts-use-system-type t) ; save profile config across different system-type
|
||||
)
|
||||
|
||||
|
||||
;; {{ START: display the emojis
|
||||
;; reference,
|
||||
;; https://github.com/doomemacs/doomemacs/issues/3298
|
||||
;; https://www.reddit.com/r/emacs/comments/4v7tcj/does_emacs_have_a_hook_for_when_the_theme_changes/
|
||||
|
||||
;; (defvar after-load-theme-hook nil
|
||||
;; "Hook run after a color theme is loaded using `load-theme'.")
|
||||
;; (defadvice load-theme (after run-after-load-theme-hook activate)
|
||||
;; "Run `after-load-theme-hook'."
|
||||
;; (run-hooks 'after-load-theme-hook))
|
||||
;; ...
|
||||
;; (add-hook 'after-load-theme-hook #'my-set-emoji-font)
|
||||
|
||||
;; for debugging,
|
||||
;; (set-fontset-font t 'emoji nil)
|
||||
;; :smile:
|
||||
;; 😄
|
||||
|
||||
(defun my-emoji-can-display ()
|
||||
(if (char-displayable-p ?😄)
|
||||
t
|
||||
nil))
|
||||
|
||||
;; 2023/08/29 enable this and this needs further investigation...
|
||||
;; FIXME: to be fixed (GitHub Actions Pipeline). See error below,
|
||||
;; Debugger entered--Lisp error: (void-function set-fontset-font)
|
||||
(defun my-set-emoji-font ()
|
||||
(if (functionp 'set-fontset-font)
|
||||
(progn
|
||||
;; set font for emoji (if before emacs 28, should come after setting
|
||||
;; symbols. emacs 28 now has 'emoji . before, emoji is part of 'symbol)
|
||||
;; http://xahlee.info/emacs/emacs/emacs_set_font_emoji.html
|
||||
(set-fontset-font
|
||||
t
|
||||
(if (version< emacs-version "28.1")
|
||||
'(#x1f300 . #x1fad0)
|
||||
'emoji
|
||||
)
|
||||
(cond
|
||||
((member "Apple Color Emoji" (font-family-list)) "Apple Color Emoji")
|
||||
((member "Noto Color Emoji" (font-family-list)) "Noto Color Emoji")
|
||||
;; 2022-10-31 NotoColorEmoji uses the CBDT/CBLC color font format, which is
|
||||
;; supported by Android and Chrome/Chromium OS. Windows supports it starting
|
||||
;; with Windows 10 Anniversary Update in Chrome and Edge.
|
||||
;; Via https://github.com/googlefonts/noto-emoji/blob/f826707b28355f6cd1593f504427ca2b1f6c4c19/README.md#using-notocoloremoji
|
||||
((member "Noto Emoji" (font-family-list)) "Noto Emoji")
|
||||
((member "Segoe UI Emoji" (font-family-list)) "Segoe UI Emoji")
|
||||
((member "Symbola" (font-family-list)) "Symbola")
|
||||
((message "No emoji font found."))
|
||||
)) ; http://xahlee.info/comp/unicode_font_download.html
|
||||
)
|
||||
(message "set-fontset-font is not available in current %s" emacs-version))
|
||||
;; (remove-hook 'focus-in-hook #'my-set-emoji-font)
|
||||
)
|
||||
|
||||
;; (my-set-emoji-font)
|
||||
|
||||
;; https://www.reddit.com/r/emacs/comments/6lxf9b/question_emacsclient_and_connection_hooks/
|
||||
;; (add-hook 'focus-in-hook #'my-set-emoji-font)
|
||||
|
||||
(defun my-advice-cnfonts-set-font (&rest _)
|
||||
"Advice function to set emoji font when cnfonts-mode is activated."
|
||||
(my-set-emoji-font))
|
||||
|
||||
;; Advising cnfonts-set-font to include setting emoji font
|
||||
(advice-add 'cnfonts-set-font :after 'my-advice-cnfonts-set-font)
|
||||
|
||||
(when (display-graphic-p)
|
||||
(unless (my-emoji-can-display)
|
||||
(my-check-for-font "Symbola" "Symbola font is not installed, however, it is recommended to install for proper emoji display. Press ENTER to continue.")
|
||||
))
|
||||
|
||||
|
||||
(use-package emojify
|
||||
:init
|
||||
(global-emojify-mode)
|
||||
:config
|
||||
(setq emojify-company-tooltips-p t)
|
||||
(setq emojify-display-style 'unicode)
|
||||
)
|
||||
;; END: display the emojis }}
|
||||
|
||||
|
||||
|
||||
(provide 'init-font)
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
;;; init-gpg.el --- GPG settings -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
|
||||
;; {{ START: decrypt link at point
|
||||
(require 'epa)
|
||||
(require 'org-element)
|
||||
|
||||
(defun my/decrypt-gpg-link-at-point ()
|
||||
"Decrypt GPG link at point.
|
||||
|
||||
Version 2023-08-04"
|
||||
(interactive)
|
||||
(let* ((link-info (org-element-context))
|
||||
(path (org-element-property :path link-info))
|
||||
(abs-path (if (string-prefix-p "file:" path)
|
||||
(file-truename (replace-regexp-in-string "^file:" "" path))
|
||||
(file-truename path)))
|
||||
(default-decrypt-path (concat abs-path ".clear")))
|
||||
(if (file-exists-p abs-path)
|
||||
(let ((decrypt-path (read-file-name
|
||||
(format "Enter target path (default %s): " default-decrypt-path)
|
||||
nil nil nil default-decrypt-path)))
|
||||
(epa-decrypt-file abs-path decrypt-path))
|
||||
(message "File not found: %s" abs-path))))
|
||||
;; END }}
|
||||
|
||||
|
||||
|
||||
(provide 'init-gpg)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-gpg.el ends here
|
|
@ -3,6 +3,9 @@
|
|||
;;; Code:
|
||||
|
||||
|
||||
;; highlight current line for all programming major modes
|
||||
(add-hook 'prog-mode-hook #'hl-line-mode)
|
||||
|
||||
(defun my/hs-hide-all ()
|
||||
(hs-minor-mode 1)
|
||||
(hs-hide-all)
|
||||
|
|
|
@ -9,6 +9,19 @@
|
|||
(ibuffer-jump-to-buffer (buffer-name (cadr (buffer-list)))))
|
||||
(add-hook 'ibuffer-hook #'ibuffer-jump-to-last-buffer)
|
||||
|
||||
(with-eval-after-load 'ibuffer
|
||||
;; use human readable size column instead of original one
|
||||
(define-ibuffer-column size-h
|
||||
(:name "Size" :inline t)
|
||||
(file-size-human-readable (buffer-size))))
|
||||
|
||||
(setq ibuffer-formats
|
||||
'((mark modified read-only " "
|
||||
(name 35 35 :left :nil) " "
|
||||
(size-h 9 -1 :right) " "
|
||||
(mode 12 12 :left :elide) " "
|
||||
filename-and-process)))
|
||||
|
||||
|
||||
(provide 'init-ibuffer)
|
||||
|
||||
|
|
|
@ -7,9 +7,24 @@
|
|||
;; https://github.com/noctuid/general.el
|
||||
|
||||
|
||||
;; C-x Keybindings:
|
||||
;; Commands starting with C-x are often used for operations related to files,
|
||||
;; buffers, windows, and frames.
|
||||
;; For example, C-x C-f is used to open a file, C-x C-s is used to save a
|
||||
;; buffer, and C-x 2 is used to split the current window into two vertical
|
||||
;; windows.
|
||||
|
||||
;; C-c Keybindings:
|
||||
;; Commands starting with C-c are typically user-defined or related to major and
|
||||
;; minor modes specific to a particular programming language or context.
|
||||
;; Users and mode developers can define their own keybindings under this prefix
|
||||
;; to tailor Emacs to their specific needs.
|
||||
;; For example, in a programming mode like Python mode, C-c C-r can be bound to
|
||||
;; run a Python script.
|
||||
|
||||
|
||||
;; global keybindings
|
||||
(general-define-key
|
||||
"M-x" 'helm-M-x
|
||||
"C-s" 'swiper ; having own history variable allows to get more use of M-p, M-n
|
||||
; and C-r.
|
||||
"C-=" 'er/expand-region
|
||||
|
@ -21,6 +36,7 @@
|
|||
"C-M-<up>" 'enlarge-window
|
||||
"M-i" 'pyim-convert-string-at-point ; <<pyim-csap>>
|
||||
"C-;" 'pyim-delete-word-from-personal-buffer
|
||||
"M-," 'citre-jump-back
|
||||
;; ...
|
||||
)
|
||||
|
||||
|
@ -58,9 +74,9 @@
|
|||
:states '(normal visual)
|
||||
"ff" 'evil-scroll-page-down ; <<page down>>
|
||||
"bb" 'evil-scroll-page-up ; <<page-up>>
|
||||
"be" 'ibuffer
|
||||
"br" 'ibuffer
|
||||
"SPC" 'my/toggle-hideshow-block
|
||||
"C-]" 'counsel-etags-find-tag-at-point ; <<ftap>>
|
||||
"C-]" 'citre-jump
|
||||
;; ...
|
||||
)
|
||||
|
||||
|
@ -105,6 +121,10 @@
|
|||
"b" 'counsel-bookmark
|
||||
"c" 'org-capture
|
||||
"f" 'ace-jump-char-mode ;; <<ajm-1>>
|
||||
"]" 'citre-peek
|
||||
"[" 'citre-jump-back
|
||||
"C-/" 'company-files
|
||||
"C-b" 'company-tabnine
|
||||
;; ...
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
;;; init-messages.el --- *Messages* buffer settings -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
(setq messages-buffer-max-lines 10000)
|
||||
|
||||
;; {{ START: add a timestamp to each entry in Emacs' *Messages* buffer
|
||||
;; via https://emacs.stackexchange.com/a/33523
|
||||
(defun sh/current-time-microseconds ()
|
||||
"Return the current time formatted to include microseconds."
|
||||
(let* ((nowtime (current-time))
|
||||
(now-ms (format "%.3s" (nth 2 nowtime)))) ; use "%.3s" to limit to 3 characters
|
||||
(concat (format-time-string "[%Y-%m-%dT%T" nowtime) (format ".%s]" now-ms))))
|
||||
|
||||
(defun sh/ad-timestamp-message (FORMAT-STRING &rest args)
|
||||
"Advice to run before `message' that prepends a timestamp to each message.
|
||||
|
||||
Activate this advice with:
|
||||
(advice-add 'message :before 'sh/ad-timestamp-message)"
|
||||
(unless (string-equal FORMAT-STRING "%s%s")
|
||||
(let ((deactivate-mark nil)
|
||||
(inhibit-read-only t))
|
||||
(with-current-buffer "*Messages*"
|
||||
(goto-char (point-max))
|
||||
(if (not (bolp))
|
||||
(newline))
|
||||
(insert (sh/current-time-microseconds) " ")))))
|
||||
|
||||
(advice-add 'message :before 'sh/ad-timestamp-message)
|
||||
;; END: add a timestamp to each entry in Emacs' *Messages* buffer }}
|
||||
|
||||
|
||||
(provide 'init-messages)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-messages.el ends here
|
|
@ -14,8 +14,6 @@
|
|||
(interactive)
|
||||
(find-file (symbol-value 'user-init-file)))
|
||||
|
||||
(save-place-mode 1)
|
||||
|
||||
(fset 'yes-or-no-p 'y-or-n-p) ; use 'y/n' instead of 'yes/no'
|
||||
|
||||
(setq confirm-kill-emacs
|
||||
|
@ -25,344 +23,152 @@
|
|||
;; to prevent kill and yank commands from accessing the clipboard
|
||||
(setq x-select-enable-clipboard nil)
|
||||
|
||||
(defun my/review-random-function ()
|
||||
"Review a random function defined in my Emacs configuration."
|
||||
(interactive)
|
||||
(let* ((config-functions '())
|
||||
(config-files (directory-files-recursively user-emacs-directory "\\.el$")))
|
||||
(dolist (file config-files)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "(defun \\([^ ]+\\)" nil t)
|
||||
(push (match-string 1) config-functions))))
|
||||
(let* ((command (nth (random (length config-functions)) config-functions)))
|
||||
(describe-function (intern command)))))
|
||||
|
||||
(defun my/review-random-my-function ()
|
||||
"Review a random function that starts with 'my/' in my Emacs configuration."
|
||||
(interactive)
|
||||
(let* ((config-functions '())
|
||||
(config-files (directory-files-recursively user-emacs-directory "\\.el$")))
|
||||
(dolist (file config-files)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "(defun my/\\([^ ]+\\)" nil t)
|
||||
(push (match-string 1) config-functions))))
|
||||
(let* ((command (nth (random (length config-functions)) config-functions)))
|
||||
(describe-function (intern (concat "my/" command))))))
|
||||
|
||||
|
||||
;; {{ START: my/open-link-at-point-as-gpg
|
||||
(defun my/securely-delete-file (&optional filename)
|
||||
"Securely delete the specified file interactively or by providing FILENAME.
|
||||
If secure deletion failed, then continue with the normal deletion."
|
||||
(interactive (list (when current-prefix-arg
|
||||
(read-file-name "Choose file to securely delete: "))))
|
||||
(if filename
|
||||
(progn
|
||||
(message "Securely deleting %s..." (shell-quote-argument filename))
|
||||
(cond
|
||||
((eq system-type 'windows-nt)
|
||||
;; https://learn.microsoft.com/en-us/sysinternals/downloads/sdelete
|
||||
(my-check-for-executable "SDelete" "sdelete")
|
||||
(shell-command (concat "sdelete -p 3 " (shell-quote-argument filename))))
|
||||
((eq system-type 'gnu/linux)
|
||||
(my-check-for-executable "shred" "shred")
|
||||
(shell-command (concat "shred -v -z -u -n 10 " (shell-quote-argument filename))))
|
||||
((eq system-type 'darwin)
|
||||
(my-check-for-executable "shred" "gshred")
|
||||
(shell-command (concat "gshred -v -z -u -n 10 " (shell-quote-argument filename)))))
|
||||
(when (file-exists-p (shell-quote-argument filename))
|
||||
(message "Securely deleting %s failed, and continue with the normal deletion." (shell-quote-argument filename))
|
||||
(delete-file filename)))
|
||||
(user-error "No file specified for secure deletion.")))
|
||||
|
||||
(defun my/open-link-at-point-as-gpg ()
|
||||
"Open the link at point using Emacs epa in a temporary buffer,
|
||||
and the decrypted file will be securely deleted after opening in buffer."
|
||||
(interactive)
|
||||
(require 'epa)
|
||||
(let* ((link-info (org-element-context))
|
||||
(path (org-element-property :path link-info))
|
||||
(abs-path (if (string-prefix-p "file:" path)
|
||||
(file-truename (replace-regexp-in-string ":" "" path))
|
||||
(file-truename path)))
|
||||
(decrypted-file (concat abs-path ".clear")))
|
||||
(if (file-exists-p abs-path)
|
||||
(progn
|
||||
(epa-decrypt-file abs-path decrypted-file)
|
||||
(find-file decrypted-file)
|
||||
(when (file-exists-p decrypted-file)
|
||||
(my/securely-delete-file decrypted-file)))
|
||||
(message "File does not exist: %s" abs-path))))
|
||||
;; END: my/open-link-at-point-as-gpg }}
|
||||
|
||||
|
||||
;; {{ START: my/check-orphaned-org-ids-in-directory
|
||||
(require 'org-element) ; this should be here before `org-add-link-type'
|
||||
(require 'cl-lib)
|
||||
|
||||
;; From ChatGPT,
|
||||
;; The message "Created id link." is printed by the `org-add-link-type` function
|
||||
;; each time it is called.
|
||||
;; Since you have the line `(org-add-link-type "id" #'my-org-id-link-follow)` in
|
||||
;; your code, this function is called every time you load or reload your Emacs
|
||||
;; configuration. It registers a new link type called `"id"` that is handled by
|
||||
;; the `my-org-id-link-follow` function.
|
||||
(defun my-monitor-clipboard-and-write-to-file (output-file-path x-seconds)
|
||||
"Monitor system clipboard and write new content to the specified file."
|
||||
(defun my-clipboard-monitor-task ()
|
||||
(let ((current-clipboard
|
||||
(or (x-get-selection 'CLIPBOARD) "")))
|
||||
(unless (equal current-clipboard my-clipboard-text)
|
||||
(setq my-clipboard-text current-clipboard)
|
||||
(with-temp-file output-file-path
|
||||
(insert current-clipboard)))))
|
||||
|
||||
;; register new link type called "id"
|
||||
(org-add-link-type "id" #'my-org-id-link-follow)
|
||||
(setq my-clipboard-text nil)
|
||||
(my-schedule-task-every-x-secs x-seconds 'my-clipboard-monitor-task))
|
||||
;; Call the function with the desired output file path
|
||||
;; (my-monitor-clipboard-and-write-to-file "c:/x-clipboard.txt" 1)
|
||||
|
||||
(defun my-org-id-link-follow (id)
|
||||
"Follow an `id' link."
|
||||
(message "Link ID: %s" id))
|
||||
(defun my-monitor-kill-and-write-to-file (register-name output-file-path x-seconds)
|
||||
"Monitor the specified kill ring for changes and write its content to a specified file.
|
||||
|
||||
(defun my-org-id-links-in-buffer ()
|
||||
"Return a list of Org ID links in the current buffer."
|
||||
(let (org-id-links) ; creates a local variable called `org-id-links` with an
|
||||
; initial value of `nil` that is only visible within the
|
||||
; `let` block
|
||||
(org-element-map (org-element-parse-buffer) 'link
|
||||
(lambda (link)
|
||||
(when (string= (org-element-property :type link) "id")
|
||||
(push (org-element-property :path link) org-id-links)
|
||||
)))
|
||||
org-id-links))
|
||||
Example usage:
|
||||
(my-monitor-kill-and-write-to-file ?0 \"c:/emacs-clipboard.txt\" 1)
|
||||
|
||||
(defun my-list-org-id-links-in-directory (directory)
|
||||
"Search all .org files in DIRECTORY for Org ID links, and return a list of unique IDs found."
|
||||
(interactive "DDirectory: ")
|
||||
(let (org-ids)
|
||||
(dolist (file (directory-files-recursively directory "\\.org$") org-ids)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(setq org-ids (append org-ids (my-org-id-links-in-buffer)))
|
||||
))
|
||||
(delete-dups org-ids)
|
||||
))
|
||||
Version: 2023
|
||||
Updated: 2024-04-24"
|
||||
(defun my-kill-monitor-task ()
|
||||
(condition-case nil
|
||||
(let ((current-contents (get-register register-name)))
|
||||
(unless (equal current-contents my-previous-kill-contents)
|
||||
(setq my-previous-kill-contents current-contents)
|
||||
(with-temp-file output-file-path
|
||||
(insert current-contents))))
|
||||
(error)))
|
||||
|
||||
(defun my-list-org-ids-in-directory (directory)
|
||||
"List all org-ids in org-files in the given DIRECTORY and return them as a list."
|
||||
(interactive "DDirectory: ")
|
||||
(let ((org-files (directory-files-recursively directory "\\.org$"))
|
||||
(org-ids '()))
|
||||
(dolist (file org-files)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(org-mode)
|
||||
(org-element-map (org-element-parse-buffer) 'headline
|
||||
(lambda (headline)
|
||||
(when-let ((id (org-element-property :ID headline)))
|
||||
(push id org-ids))))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^:ID:\\s-+\\(\\S-+\\)" nil t)
|
||||
(push (match-string 1) org-ids))))
|
||||
org-ids))
|
||||
|
||||
(defun my/check-orphaned-org-ids-in-directory (dir)
|
||||
"Find the difference between org-ids obtained by `my-list-org-ids-in-directory'
|
||||
and org-ids obtained by `my-list-org-id-links-in-directory'.
|
||||
DIRECTORY is the directory where the org files are located."
|
||||
(interactive "DDirectory: ")
|
||||
(let ((org-ids (my-list-org-ids-in-directory dir))
|
||||
(id-links (my-list-org-id-links-in-directory dir)))
|
||||
(let ((not-linked (cl-set-difference org-ids id-links :test #'string=))
|
||||
(invalid-links (cl-set-difference id-links org-ids :test #'string=)))
|
||||
(message "%d not-linked org-ids: %s"
|
||||
(length not-linked)
|
||||
(format "%s" not-linked))
|
||||
(message "%d invalid org-id links: %s"
|
||||
(length invalid-links)
|
||||
(format "%s" invalid-links)))))
|
||||
;; END: my/check-orphaned-org-ids-in-directory }}
|
||||
(setq my-previous-kill-contents "")
|
||||
(let ((task-name (concat "my-kill-monitor-task_from-"
|
||||
(string register-name)
|
||||
"-to-"
|
||||
(my-remove-file-suffix (file-name-nondirectory output-file-path)))))
|
||||
(fset (intern task-name) #'my-kill-monitor-task)
|
||||
(my-schedule-task-every-x-secs x-seconds (intern task-name))))
|
||||
;; (current-kill 0)
|
||||
;; (get-register ?0)
|
||||
;; (my-monitor-kill-and-write-to-file ?0 "c:/emacs-clipboard.txt" 1)
|
||||
|
||||
|
||||
(defun my/org-list-entries-without-id-property ()
|
||||
"List all entries in the current buffer that don't have an ID property."
|
||||
(interactive)
|
||||
(with-output-to-temp-buffer "*Org Entries Without ID*"
|
||||
(let ((results nil))
|
||||
(org-map-entries
|
||||
(lambda ()
|
||||
(unless (org-id-get)
|
||||
(push (format "** LINE #%d:\n%s"
|
||||
(line-number-at-pos)
|
||||
(buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position)))
|
||||
results)))
|
||||
nil nil t)
|
||||
(princ (concat "#+TITLE: Org Entries Without ID\n\n"))
|
||||
(princ (concat "#+OPTIONS: toc:nil\n\n"))
|
||||
(princ (concat "* Entries without ID\n\n"))
|
||||
(dolist (result (nreverse results))
|
||||
(princ (concat result "\n\n")))))
|
||||
(with-current-buffer "*Org Entries Without ID*"
|
||||
(org-mode)))
|
||||
(defun my-remove-file-suffix (filename)
|
||||
"Remove the file suffix from FILENAME."
|
||||
(if (string-match "\\(.*\\)\\..*" filename)
|
||||
(match-string 1 filename)
|
||||
filename))
|
||||
;; (my-remove-file-suffix "abc.txt")
|
||||
;; (file-name-nondirectory "/temp/abc.txt")
|
||||
|
||||
(defun my-monitor-file-and-copy-to-register (file-path register-name x-seconds)
|
||||
"Monitor the specified file for changes and copy its content to a specified register.
|
||||
|
||||
Example usage:
|
||||
(my-monitor-file-and-copy-to-register \"c:/x-clipboard.txt\" ?a 1)
|
||||
|
||||
Version: 2023
|
||||
Updated: 2024-04-24"
|
||||
(let ((previous-contents-alist ()))
|
||||
|
||||
(defun my-file-monitor-task ()
|
||||
(let* ((base-filename
|
||||
(my-remove-file-suffix (file-name-nondirectory file-path)))
|
||||
(current-contents (when (file-readable-p file-path)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file-path)
|
||||
(buffer-string))))
|
||||
(previous-contents (assoc base-filename previous-contents-alist)))
|
||||
|
||||
(unless (equal current-contents (cdr previous-contents))
|
||||
(set-register register-name current-contents)
|
||||
(setq previous-contents-alist
|
||||
(cons
|
||||
(cons base-filename current-contents)
|
||||
(delq
|
||||
(assoc base-filename previous-contents-alist)
|
||||
previous-contents-alist)
|
||||
)))))
|
||||
|
||||
(let ((task-name (concat "my-file-monitor-task_from-"
|
||||
(my-remove-file-suffix (file-name-nondirectory file-path))
|
||||
"-to-"
|
||||
(string register-name))))
|
||||
(fset (intern task-name) #'my-file-monitor-task)
|
||||
(my-schedule-task-every-x-secs x-seconds (intern task-name)))))
|
||||
;; Example usage:
|
||||
;; (my-monitor-file-and-copy-to-register "c:/x-clipboard.txt" ?a 1)
|
||||
;; (my-monitor-file-and-copy-to-register "c:/emacs-clipboard.txt" ?b 1)
|
||||
;; Testing:
|
||||
;; (get-register ?a)
|
||||
;; (get-register ?b)
|
||||
;; (w32-set-clipboard-data "Your content goes here")
|
||||
|
||||
(defun my-monitor-file-and-copy-to-w32-clipboard (file-path x-seconds)
|
||||
"Monitor the specified file for changes and copy its content to Windows clipboard.
|
||||
|
||||
Example usage:
|
||||
(my-monitor-file-and-copy-to-w32-clipboard \"c:/emacs-clipboard.txt\" 1)
|
||||
|
||||
Version: 2023
|
||||
Updated: 2024-04-24"
|
||||
(if *is-win*
|
||||
(let ((previous-contents-alist ()))
|
||||
|
||||
(defun my-w32-file-monitor-task ()
|
||||
(let* ((base-filename
|
||||
(my-remove-file-suffix (file-name-nondirectory file-path)))
|
||||
(current-contents (when (file-readable-p file-path)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file-path)
|
||||
(buffer-string))))
|
||||
(previous-contents (assoc base-filename previous-contents-alist)))
|
||||
|
||||
(unless (equal current-contents (cdr previous-contents))
|
||||
(w32-set-clipboard-data current-contents)
|
||||
(setq previous-contents-alist
|
||||
(cons
|
||||
(cons base-filename current-contents)
|
||||
(delq
|
||||
(assoc base-filename previous-contents-alist)
|
||||
previous-contents-alist)
|
||||
)))))
|
||||
|
||||
(let ((task-name (concat "my-w32-file-monitor-task_from-"
|
||||
(my-remove-file-suffix (file-name-nondirectory file-path))
|
||||
"-to-w32-clipboard")))
|
||||
(fset (intern task-name) #'my-w32-file-monitor-task)
|
||||
(my-schedule-task-every-x-secs x-seconds (intern task-name))))
|
||||
(message "Only Windows system is supported.")))
|
||||
|
||||
;; (my-monitor-file-and-copy-to-w32-clipboard "c:/emacs-clipboard.txt" 1)
|
||||
|
||||
|
||||
(defun my/list-packages-and-versions ()
|
||||
(interactive)
|
||||
(package-initialize)
|
||||
(let ((pkgs (mapcar 'car package-alist)))
|
||||
(dolist (pkg pkgs)
|
||||
(message "%s - %s"
|
||||
pkg (package-desc-version (cadr (assq pkg package-alist)))))))
|
||||
|
||||
(defun my/copy-org-id-at-point ()
|
||||
"Copy the ID property of the heading at point to the kill-ring."
|
||||
(interactive)
|
||||
(let ((id (org-entry-get nil "ID")))
|
||||
(when id
|
||||
(kill-new id)
|
||||
(message "Copied ID: %s" id))))
|
||||
|
||||
(defun my-get-heading-from-org-id-db (org-id)
|
||||
"Retrieve the heading title associated with an Org ID from the
|
||||
current buffer's Org mode database."
|
||||
(org-with-point-at (org-id-find org-id 'marker)
|
||||
(org-get-heading)))
|
||||
|
||||
(defun my/insert-org-id-from-kill-ring ()
|
||||
"Insert a link to an Org ID from the kill-ring with a user-defined description.
|
||||
The user is prompted to enter a description for the link.
|
||||
|
||||
If description is empty, retrieve the heading from the org-id
|
||||
database using `my-get-heading-from-org-id-db` function."
|
||||
(interactive)
|
||||
(let ((id (current-kill 0)))
|
||||
(when id
|
||||
(let* ((org-id (replace-regexp-in-string "^id:" "" id))
|
||||
(description (read-string "Description: " nil 'my-history)))
|
||||
(if (string-empty-p description)
|
||||
(setq description (my-get-heading-from-org-id-db org-id)))
|
||||
(org-insert-link nil (concat "id:" org-id) description)))))
|
||||
|
||||
|
||||
(defun my/link-selected-text-with-org-id-from-kill-ring ()
|
||||
"Create an Org-mode link using the selected text and an Org ID from the kill ring.
|
||||
Version 2023-04-28
|
||||
|
||||
The selected text is replaced with,
|
||||
[[id:<Org ID unique identifier>][<selected text>]].
|
||||
|
||||
Usage: Select the text that you want to link to an Org ID, then
|
||||
run `M-x my/link-selected-text-with-org-id-from-kill-ring`. The
|
||||
function will take the Org ID from the kill ring, and create an
|
||||
Org-mode link with the selected text and the Org ID. The link
|
||||
will be inserted at the cursor position, replacing the selected
|
||||
text."
|
||||
(interactive)
|
||||
(let* ((org-id (substring-no-properties (current-kill 0)))
|
||||
(text (buffer-substring-no-properties (region-beginning) (region-end)))
|
||||
(link (concat "[[id:" org-id "][" text "]]")))
|
||||
(delete-region (region-beginning) (region-end))
|
||||
(insert link)))
|
||||
|
||||
|
||||
(defun my-parse-link-id (link)
|
||||
"Parse the ID from an org-mode link of the form `id:xxxxxxxxxxxx'."
|
||||
(when (string-match "id:\\(.+\\)" link)
|
||||
(match-string 1 link)))
|
||||
|
||||
(defun my/org-link-goto-at-point ()
|
||||
"Check if link at point is a file link or an ID link, and jump to
|
||||
the appropriate location."
|
||||
(interactive)
|
||||
(if-let ((link (org-element-property :raw-link (org-element-context))))
|
||||
(cond ((string-prefix-p "file:" link)
|
||||
(org-open-at-point))
|
||||
((string-prefix-p "id:" link)
|
||||
(org-id-goto (my-parse-link-id link))))
|
||||
(message "No link at point.")))
|
||||
|
||||
|
||||
(defun my/switch-opened-org-files-to-org-mode ()
|
||||
"Switch all open buffers that end with .org to org-mode,
|
||||
skipping buffers that are already in org-mode.
|
||||
Version 2023-05-06"
|
||||
;; See, https://stackoverflow.com/a/76187210/4274775
|
||||
(interactive)
|
||||
(dolist (buffer (buffer-list))
|
||||
(with-current-buffer buffer
|
||||
(when (and (buffer-file-name)
|
||||
(string= (file-name-extension (buffer-file-name)) "org")
|
||||
(not (eq major-mode 'org-mode)))
|
||||
(org-mode)
|
||||
(message "Switched %s to org-mode." (buffer-name))))))
|
||||
|
||||
(defun my/strikethrough-current-line ()
|
||||
"Strikethrough the current line using +<striked text>+"
|
||||
(interactive)
|
||||
(back-to-indentation)
|
||||
(insert "+")
|
||||
(move-end-of-line nil)
|
||||
;; skips over any consecutive space or tab characters immediately before the
|
||||
;; end of the line, effectively moving the cursor to the last non-blank
|
||||
;; character on the line, rather than after any trailing whitespace. see,
|
||||
(skip-chars-backward " \t")
|
||||
(insert "+"))
|
||||
|
||||
(defun my/readonly-files ()
|
||||
"Check for a '.readonly' file in the directory of the current
|
||||
buffer, and set the read-only status of any listed buffers. The
|
||||
'.readonly' file should contain a list of buffer names, one per
|
||||
line, that should be set to read-only. Any buffers not listed in
|
||||
the file will remain unaffected.
|
||||
Version 2023-05-04
|
||||
|
||||
This function is intended to be used as a hook to automatically
|
||||
set the read-only status of buffers when they are opened or
|
||||
saved, based on the contents of the '.readonly' file. To use this
|
||||
function as a hook, add it to the appropriate hook list, such as
|
||||
'find-file-hook', 'after-save-hook' or 'switch-buffer-hook'."
|
||||
;; (add-hook 'find-file-hook 'my/readonly-files)
|
||||
;; (add-hook 'after-save-hook 'my/readonly-files)
|
||||
;; (add-hook 'switch-buffer-hook 'my/readonly-files)
|
||||
(interactive)
|
||||
(let ((readonly-file (concat (file-name-directory (buffer-file-name)) ".readonly")))
|
||||
(when (file-exists-p readonly-file)
|
||||
(let ((readonly-bufs (split-string (with-temp-buffer
|
||||
(insert-file-contents readonly-file)
|
||||
(buffer-string))
|
||||
"\n" t)))
|
||||
(message "read-only files list: %s" readonly-bufs)
|
||||
(dolist (buf readonly-bufs)
|
||||
(message "%s is read-only now" buf)
|
||||
(let ((buf (find-buffer-visiting buf)))
|
||||
(when buf
|
||||
(with-current-buffer buf
|
||||
(toggle-read-only t)))))))))
|
||||
|
||||
(defun my/revert-all-file-buffers ()
|
||||
"Refresh all open file buffers without confirmation.
|
||||
Buffers in modified (not yet saved) state in emacs will not be reverted. They
|
||||
will be reverted though if they were modified outside emacs.
|
||||
Buffers visiting files which do not exist any more or are no longer readable
|
||||
will be killed."
|
||||
;; via https://emacs.stackexchange.com/a/24461/29715
|
||||
(interactive)
|
||||
(dolist (buf (buffer-list))
|
||||
(let ((filename (buffer-file-name buf)))
|
||||
;; Revert only buffers containing files, which are not modified;
|
||||
;; do not try to revert non-file buffers like *Messages*.
|
||||
(when (and filename
|
||||
(not (buffer-modified-p buf)))
|
||||
(if (file-readable-p filename)
|
||||
;; If the file exists and is readable, revert the buffer.
|
||||
(with-current-buffer buf
|
||||
(revert-buffer :ignore-auto :noconfirm :preserve-modes))
|
||||
;; Otherwise, kill the buffer.
|
||||
(let (kill-buffer-query-functions) ; No query done when killing buffer
|
||||
(kill-buffer buf)
|
||||
(message "Killed non-existing/unreadable file buffer: %s" filename))))))
|
||||
(message "Finished reverting buffers containing unmodified files."))
|
||||
|
||||
;; via https://emacs.stackexchange.com/questions/13080/reloading-directory-local-variables
|
||||
(defun my/reload-dir-locals-for-current-buffer ()
|
||||
"reload dir locals for the current buffer"
|
||||
"Reload dir locals for the current buffer"
|
||||
(interactive)
|
||||
(let ((enable-local-variables :all))
|
||||
(hack-dir-local-variables-non-file-buffer)))
|
||||
|
@ -387,7 +193,7 @@ current buffer's, reload dir-locals."
|
|||
nil t))))
|
||||
|
||||
(defun eh-org-clean-space (text backend info)
|
||||
"remove the space between chinese characters during exporting
|
||||
"Remove the space between chinese characters during exporting
|
||||
to HTML files."
|
||||
;; https://github.com/hick/emacs-chinese#%E4%B8%AD%E6%96%87%E6%96%AD%E8%A1%8C
|
||||
(when (org-export-derived-backend-p backend 'html)
|
||||
|
@ -415,148 +221,15 @@ to HTML files."
|
|||
(add-to-list 'org-export-filter-paragraph-functions 'eh-org-clean-space)
|
||||
)
|
||||
|
||||
(defun my/create-TAGS (&optional sudo dir-name tag-relative)
|
||||
"Create a TAGS file with absolute or relative paths recorded inside. With a
|
||||
prefix argument SUDO, run the command with sudo privilege. With a prefix
|
||||
argument TAG-RELATIVE, create the TAGS file with relative paths recorded inside.
|
||||
|
||||
When called interactively, prompt the user for the directory name to create the
|
||||
TAGS file. If no input is given, use the current working directory.
|
||||
|
||||
The `ctags` command is executed with the `--tag-relative` option set to `yes` if
|
||||
the `tag-relative` prefix argument is set to 'y', or 'never' otherwise. The `*`
|
||||
wildcard is included in the `ctags` command to create TAGS for all files in the
|
||||
directory.
|
||||
|
||||
Example usage:
|
||||
- To create a TAGS file for the current directory:
|
||||
M-x my/create-TAGS RET RET
|
||||
- To create a TAGS file for a specific directory with relative paths recorded:
|
||||
M-x my/create-TAGS RET /path/to/directory RET y RET
|
||||
- To create a TAGS file for a specific directory with absolute paths recorded,
|
||||
using sudo privilege:
|
||||
C-u M-x my/create-TAGS RET /path/to/directory RET RET"
|
||||
|
||||
;; This function is improved by ChatGPT and Claude :)
|
||||
(interactive "P\nDEnter the directory to create TAGS file: \nMCreate TAGS file with relative paths (y/n):")
|
||||
|
||||
(let* ((target-dir (if (string= "" dir-name)
|
||||
default-directory
|
||||
(expand-file-name dir-name)))
|
||||
(tags-path (if (string= tag-relative 'y)
|
||||
nil
|
||||
(read-file-name "Enter the path for TAGS file: ")))
|
||||
(ctags-cmd (format "cd %s && ctags --options=%s -e -R --tag-relative=%s -f %s *"
|
||||
target-dir
|
||||
(expand-file-name ".ctags" user-emacs-directory)
|
||||
(if (string-equal tag-relative 'y) "yes" "never")
|
||||
(or tags-path (expand-file-name "TAGS" target-dir)))))
|
||||
(let ((command (if sudo
|
||||
(concat "sudo sh -c '"
|
||||
ctags-cmd
|
||||
"'")
|
||||
ctags-cmd)))
|
||||
(start-process-shell-command "create TAGS" nil command))))
|
||||
|
||||
(defun my/find-tags-file ()
|
||||
"recursively searches each parent directory for a file named
|
||||
'TAGS' and returns the path to that file or nil if a tags file is
|
||||
not found. Returns nil if the buffer is not visiting a file"
|
||||
(progn
|
||||
(defun find-tags-file-r (path)
|
||||
"find the tags file from the parent directories"
|
||||
(let* ((parent (file-name-directory path))
|
||||
(possible-tags-file (concat parent "TAGS")))
|
||||
(cond
|
||||
((file-exists-p possible-tags-file)
|
||||
(throw 'found-it possible-tags-file))
|
||||
((string= "/TAGS" possible-tags-file)
|
||||
(error "no tags file found"))
|
||||
(t (find-tags-file-r (directory-file-name parent))))))
|
||||
|
||||
(if (buffer-file-name)
|
||||
(catch 'found-it
|
||||
(find-tags-file-r (buffer-file-name)))
|
||||
(error "buffer is not visiting a file"))))
|
||||
|
||||
(defun my/file ()
|
||||
"prompt user to enter a file name, with completion and history
|
||||
support."
|
||||
;; http://xahlee.info/emacs/emacs/elisp_idioms_prompting_input.html
|
||||
(interactive)
|
||||
(setq my-file-value (read-file-name "Input file name: "))
|
||||
(message "my-file-value is %s" my-file-value)
|
||||
)
|
||||
|
||||
;; { START: config for counsel-etags and company-ctags
|
||||
;; <<config-ce-cc>>
|
||||
(defun my-tags-file (&optional select)
|
||||
"If SELECT is non-nil, set the value of `my-tags-file` to the user-selected file path
|
||||
after prompting for it through `my/file`.
|
||||
Otherwise, set `my-tags-file` to the value returned by `my/find-tags-file`.
|
||||
-- generated by ChatGPT :)"
|
||||
(if select
|
||||
(progn (my/file)
|
||||
(setq my-tags-file my-file-value))
|
||||
(setq my-tags-file (my/find-tags-file)))
|
||||
)
|
||||
|
||||
(defun my-set-extra-tags-files (my-tags-table-list)
|
||||
(setq counsel-etags-extra-tags-files my-tags-table-list)
|
||||
(setq company-ctags-extra-tags-files my-tags-table-list)
|
||||
(message "tags-table list for counsel-etags/company-ctags:\n%s"
|
||||
my-tags-table-list)
|
||||
)
|
||||
|
||||
(defun my/insert-into-my-tags-table-list(&optional select)
|
||||
"automatically insert the TAGS file or select TAGS file to
|
||||
insert(C-u), into `my-tags-table-list',
|
||||
`counsel-etags-extra-tags-files' and
|
||||
`company-ctags-extra-tags-files'."
|
||||
(interactive "P")
|
||||
(unless (boundp 'my-tags-table-list)
|
||||
;; if `my-tags-table-list' is void, then set it to empty list
|
||||
(setq my-tags-table-list '()))
|
||||
(setq existing-my-tags-table-list my-tags-table-list)
|
||||
(setq my-tags-table-list '()) ; initiate empty list
|
||||
(my-tags-file select)
|
||||
(setq my-tags-table-list
|
||||
(delq nil (delete-dups ; delete nil and duplicates
|
||||
(cons (symbol-value 'my-tags-file)
|
||||
(symbol-value 'existing-my-tags-table-list)))))
|
||||
(my-set-extra-tags-files my-tags-table-list)
|
||||
)
|
||||
|
||||
(defun my/delete-from-my-tags-table-list (&optional select)
|
||||
"automatically delete the TAGS file or select TAGS file to
|
||||
delete(C-u), from `my-tags-table-list',
|
||||
`counsel-etags-extra-tags-files' and
|
||||
`company-ctags-extra-tags-files'."
|
||||
(interactive "P")
|
||||
(my-tags-file select)
|
||||
(setq my-tags-table-list
|
||||
(delete (symbol-value 'my-tags-file) my-tags-table-list))
|
||||
(my-set-extra-tags-files my-tags-table-list)
|
||||
)
|
||||
|
||||
;; keybinding -> [[./init-keybindings.el::m-ftf]]
|
||||
(defun my/set-tags-table-list (&optional del)
|
||||
"calls `my/find-tags-file' to recursively search up the directory
|
||||
tree to find a file named 'TAGS'. If found, add/delete(C-u) it
|
||||
to/from 'counsel-etags-extra-tags-files' and
|
||||
'company-ctags-extra-tags-files'."
|
||||
(interactive "P")
|
||||
(if del (my/delete-from-my-tags-table-list)
|
||||
(my/insert-into-my-tags-table-list))
|
||||
)
|
||||
|
||||
(defun my/tags-table-list ()
|
||||
"check and display my tags-table list through message."
|
||||
(interactive)
|
||||
(message "tags-table list for counsel-etags/company-ctags:\n%s"
|
||||
my-tags-table-list)
|
||||
)
|
||||
;; END: config for counsel-etags and company-ctags }
|
||||
|
||||
|
||||
(provide 'init-misc)
|
||||
|
|
|
@ -3,22 +3,6 @@
|
|||
;;; Code:
|
||||
|
||||
|
||||
(when (not (file-directory-p org-directory))
|
||||
(if noninteractive
|
||||
(message "The org-directory is not defined, will set it to .emacs.d folder to avoid 'No such file org directory' warning.")
|
||||
(read-string "The org-directory is not defined, will set it to .emacs.d folder to avoid 'No such file org directory' warning. Press ENTER to continue."))
|
||||
;; [[./init-org.el::od-1]]
|
||||
;; [[./init-org.el::od-2]]
|
||||
(setq org-directory (symbol-value 'user-emacs-directory))
|
||||
)
|
||||
(when (not (boundp 'org-mobile-directory))
|
||||
(if noninteractive
|
||||
(message "The org-mobile-directory is not defined, will set it to .emacs.d folder to avoid void-variable error.")
|
||||
(read-string "The org-mobile-directory is not defined, will set it to .emacs.d folder to avoid void-variable error. Press ENTER to continue."))
|
||||
;; [[./init-org.el::omd]]
|
||||
(setq org-mobile-directory (symbol-value 'user-emacs-directory))
|
||||
)
|
||||
|
||||
(setq org-startup-indented t) ; enable org-indent mode
|
||||
|
||||
(setq org-log-done 'time) ; keep track of when a certain TODO item was marked as
|
||||
|
@ -26,6 +10,8 @@
|
|||
|
||||
(setq org-log-done 'note) ; record a note along with the timestamp
|
||||
|
||||
(setq org-log-into-drawer t) ; log into LOGBOOK drawer
|
||||
|
||||
(setq org-src-fontify-natively t) ; highlight the code in Org-mode
|
||||
|
||||
;; { START: temp WA to fix bug #52587
|
||||
|
@ -71,23 +57,24 @@
|
|||
|
||||
(setq org-todo-keywords
|
||||
;; '((sequence "☛ TODO(t)" "➼ IN-PROGRESS" "⚑ WAIT(w@/!)" "|" "✔ DONE(d!)" "✘ CANCELED(c@)")
|
||||
'((sequence "TODO(t)" "IN-PROGRESS" "WAIT(w@/!)" "|" "DONE(d!)" "CANCELED(c@)")
|
||||
'((sequence "TODO(t)" "IN-PROGRESS" "WAIT(w@/!)" "|" "DONE(d!)" "CANCELED(c@)" "CLOSED")
|
||||
(sequence "REPORT(r)" "BUG(b)" "KNOWNCAUSE(k)" "IMPROVEMENT(m)" "ENHANCEMENT(e)" "FEATURE(a)" "|" "FIXED(f)")
|
||||
))
|
||||
|
||||
(setf org-todo-keyword-faces '(
|
||||
("CANCELED" . (:foreground "white" :background "#95A5A6"))
|
||||
("DONE" . (:foreground "white" :background "#2E8B57"))
|
||||
("CLOSED" . (:foreground "white" :background "#2E8B57"))
|
||||
("WAIT" . (:foreground "white" :background "#F9BC41"))
|
||||
("IN-PROGRESS" . (:foreground "white" :background "#3498DB"))
|
||||
("TODO" . (:foreground "white" :background "#5F87FF"))
|
||||
("REPORT" (:foreground "#C0C0C0" :background "#308014" :box t))
|
||||
("BUG" (:foreground "#E6DB74" :background "black" :box t))
|
||||
("KNOWNCAUSE" (:foreground "#9C91E4" :background "black" :box t))
|
||||
("IMPROVEMENT" (:foreground "#FF9900" :background "black" :box t))
|
||||
("ENHANCEMENT" (:foreground "#9900ff" :background "black" :box t))
|
||||
("FEATURE" (:foreground "#38761d" :background "black" :box t))
|
||||
("FIXED" (:foreground "#4B5556" :strike-through t :box t))
|
||||
("REPORT" (:foreground "#C0C0C0" :background "#308014" :box (:line-width (-1 . -1))))
|
||||
("BUG" (:foreground "#E6DB74" :background "black" :box (:line-width (-1 . -1))))
|
||||
("KNOWNCAUSE" (:foreground "#9C91E4" :background "black" :box (:line-width (-1 . -1))))
|
||||
("IMPROVEMENT" (:foreground "#FF9900" :background "black" :box (:line-width (-1 . -1))))
|
||||
("ENHANCEMENT" (:foreground "#9900ff" :background "black" :box (:line-width (-1 . -1))))
|
||||
("FEATURE" (:foreground "#38761d" :background "black" :box (:line-width (-1 . -1))))
|
||||
("FIXED" (:foreground "#4B5556" :strike-through t :box (:line-width (-1 . -1))))
|
||||
))
|
||||
|
||||
(defun my/modify-org-done-face (&optional disable)
|
||||
|
@ -165,26 +152,27 @@
|
|||
org-agenda-time-grid
|
||||
'((daily today require-timed)
|
||||
(800 1000 1200 1400 1600 1800 2000)
|
||||
" ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄")
|
||||
" ..... "
|
||||
"---------------")
|
||||
org-agenda-current-time-string
|
||||
"⭠ now ─────────────────────────────────────────────────"
|
||||
"<- now -------------------------------------------------"
|
||||
)
|
||||
|
||||
(add-hook 'org-agenda-finalize-hook 'place-agenda-tags)
|
||||
(defun place-agenda-tags ()
|
||||
"Put the agenda tags by the right border of the agenda window."
|
||||
;; http://lists.gnu.org/archive/html/emacs-orgmode//2010-12/msg00410.html
|
||||
(setq org-agenda-tags-column (- 10 (window-width)))
|
||||
(org-agenda-align-tags)
|
||||
)
|
||||
(add-hook 'org-agenda-finalize-hook 'place-agenda-tags)
|
||||
|
||||
;; { -- BEGIN -- org-agenda custom commands
|
||||
;; https://www.reddit.com/r/orgmode/comments/6ybjjw/aligned_agenda_view_anyway_to_make_this_more/
|
||||
(setq org-agenda-prefix-format ; the display format
|
||||
;; http://doc.endlessparentheses.com/Var/org-agenda-prefix-format.html
|
||||
(quote
|
||||
((agenda . "%5e %12s %12t")
|
||||
(timeline . " % s")
|
||||
((agenda . "%5e %27s %12t")
|
||||
;; (timeline . " % s")
|
||||
(todo . " %12t")
|
||||
(tags . " %12t")
|
||||
(search . " %12t"))
|
||||
|
@ -203,10 +191,15 @@
|
|||
|
||||
;; teach Org where to look for all of the files you wish to include in your agenda
|
||||
;; https://stackoverflow.com/a/41969519/4274775
|
||||
(setq org-agenda-files
|
||||
(directory-files-recursively org-directory "\\.org$")) ; <<od-1>>
|
||||
(when (and (file-directory-p org-directory) (not (string= org-directory "")))
|
||||
(setq org-agenda-files
|
||||
(directory-files-recursively org-directory "\\.org$")))
|
||||
;; (length org-agenda-files)
|
||||
|
||||
;; fix issue like below,
|
||||
;; Non-existent agenda file /path/to/.#xxx.org. [R]emove from list or [A]bort?
|
||||
(setq org-agenda-skip-unavailable-files t)
|
||||
|
||||
;; how to truncate the long task name in the agenda custom view?
|
||||
;; https://stackoverflow.com/a/16285673/4274775
|
||||
(defun my-org-agenda-mode-hook ()
|
||||
|
@ -215,17 +208,37 @@
|
|||
(add-hook 'org-agenda-mode-hook
|
||||
'my-org-agenda-mode-hook)
|
||||
|
||||
|
||||
|
||||
(defun my-normalized-paths-list (file-paths-list)
|
||||
"Normalize a list of file paths and remove duplicates.
|
||||
|
||||
Version: 2023/09/27"
|
||||
(let ((normalized-paths (mapcar (lambda (path) (expand-file-name path)) file-paths-list)))
|
||||
(delete-duplicates normalized-paths :test 'string=)))
|
||||
;; Example usage:
|
||||
;; (setq org-agenda-files-list '("~/abc.org" "/Users/jason/abc.org" "~/xyz.org"))
|
||||
;; (setq normalized-list (my-normalized-paths-list org-agenda-files-list))
|
||||
|
||||
|
||||
|
||||
(add-hook 'org-agenda-mode-hook (lambda ()
|
||||
|
||||
;; https://orgmode.org/list/loom.20111014T204701-149@post.gmane.org/
|
||||
(setq org-agenda-files
|
||||
(delete-dups (append org-agenda-files
|
||||
;; <<od-2>>
|
||||
(directory-files-recursively org-directory "\\.org$"))))
|
||||
(when (and org-directory (not (string= org-directory "")))
|
||||
(directory-files-recursively org-directory "\\.org$")))))
|
||||
(setq org-agenda-files
|
||||
(delete-dups
|
||||
(append org-agenda-files
|
||||
(directory-files-recursively org-mobile-directory "\\.org$")))) ; <<omd>>
|
||||
(when (and org-mobile-directory (not (string= org-mobile-directory "")))
|
||||
(directory-files-recursively org-mobile-directory "\\.org$")))))
|
||||
|
||||
;; remove the duplicates like below,
|
||||
;; ("~/abc.org" "/Users/jason/abc.org")
|
||||
(setq org-agenda-files (my-normalized-paths-list org-agenda-files))
|
||||
|
||||
|
||||
;; { -- START --
|
||||
;; <<4osa-start>> | the link anchor to the end: [[./init-org.el::4osa-end]]
|
||||
|
@ -327,6 +340,11 @@
|
|||
))
|
||||
|
||||
|
||||
(use-package org-task-scheduler
|
||||
:straight (:host github :repo "jsntn/org-task-scheduler.el"))
|
||||
|
||||
|
||||
|
||||
(provide 'init-org)
|
||||
|
||||
;; Local Variables:
|
||||
|
|
|
@ -11,12 +11,6 @@
|
|||
;; reference
|
||||
;; https://github.com/domtronn/all-the-icons.el/issues/120
|
||||
(when (display-graphic-p) ; if not in terminal Emacs
|
||||
;; if on Windows and all-the-icons is not installed
|
||||
(when (equal system-type 'windows-nt)
|
||||
(unless (member "all-the-icons" (font-family-list))
|
||||
(yes-or-no-p "The 'all-the-icons' fonts are recommended for this configuration with lsp-mode package. Continue and install it later?")
|
||||
)
|
||||
)
|
||||
;; if not on Windows and all-the-icons is not installed
|
||||
(unless (equal system-type 'windows-nt)
|
||||
(unless (member "all-the-icons" (font-family-list))
|
||||
|
@ -36,62 +30,15 @@
|
|||
|
||||
;; jump to Chinese character by pinyin with `avy' or `ace-jump-mode'
|
||||
(use-package ace-pinyin
|
||||
:delight
|
||||
:config
|
||||
(setq ace-pinyin-use-avy nil) ; use `ace-jump-mode'
|
||||
(ace-pinyin-global-mode +1)
|
||||
)
|
||||
|
||||
(use-package annotate
|
||||
:config
|
||||
(custom-set-faces
|
||||
'(annotate-annotation ((t (:background "#ff7f4f" :foreground "white"))))
|
||||
'(annotate-annotation-secondary ((t (:background "#ff7f4f" :foreground "white"))))
|
||||
'(annotate-highlight ((t (:underline "white"))))
|
||||
'(annotate-highlight-secondary ((t (:underline "white"))))
|
||||
)
|
||||
;; { START: my-annotate-mode-hook
|
||||
(defun my-set-default-annotation-file (annotate-mode-status)
|
||||
"set my default annotation-file, which is used in case the
|
||||
`.annotations' in the directory of the current buffer does not
|
||||
exist."
|
||||
(interactive)
|
||||
(setq annotate-file
|
||||
(expand-file-name ".annotations" user-emacs-directory))
|
||||
(when (eq annotate-mode-status 'on)
|
||||
(annotate-load-annotations))
|
||||
(message "annotate-mode is %s, and the annotate-file is set to %s.annotations"
|
||||
annotate-mode-status user-emacs-directory)
|
||||
)
|
||||
(defun my-annotate-mode-hook ()
|
||||
"my annotate-mode hook to check if `.annotations' exists in the
|
||||
directory of the current buffer then use it as the
|
||||
`annotate-file', otherwise call the
|
||||
`my-set-default-annotate-file'."
|
||||
(interactive)
|
||||
(if (bound-and-true-p annotate-mode); if annotate-mode is on
|
||||
(if (file-exists-p ".annotations") ; if .annotations file exists
|
||||
(progn (setq-local annotate-file ".annotations")
|
||||
(annotate-load-annotations)
|
||||
(message "annotate-mode is on, and the annotate-file is %s.annotations"
|
||||
(file-name-directory (buffer-file-name))))
|
||||
(my-set-default-annotation-file 'on) ; if .annotations file does not exist
|
||||
)
|
||||
(my-set-default-annotation-file 'off) ; if annotate-mode is off
|
||||
))
|
||||
(add-hook 'annotate-mode-hook 'my-annotate-mode-hook)
|
||||
;; END: my-annotate-mode-hook }
|
||||
)
|
||||
(use-package annotate)
|
||||
|
||||
|
||||
(use-package auto-capitalize
|
||||
:straight (:host github :repo "yuutayamada/auto-capitalize-el")
|
||||
:config
|
||||
(setq auto-capitalize-words `("I" "English"))
|
||||
;; this configuration adds capitalized words of .aspell.en.pws
|
||||
(setq auto-capitalize-aspell-file (expand-file-name "misc/aspell.en.pws" user-emacs-directory))
|
||||
(auto-capitalize-setup)
|
||||
;; (add-hook 'after-change-major-mode-hook 'auto-capitalize-mode)
|
||||
:hook (org-mode . auto-capitalize-mode)
|
||||
)
|
||||
|
||||
(use-package benchmark-init
|
||||
:config
|
||||
|
@ -112,151 +59,17 @@ directory of the current buffer then use it as the
|
|||
)
|
||||
)
|
||||
|
||||
(use-package cnfonts
|
||||
:if window-system ; only load this package when in graphical Emacs
|
||||
:config
|
||||
(cnfonts-mode 1)
|
||||
(setq cnfonts-profiles
|
||||
'("program" "org-mode" "read-book"))
|
||||
(setq cnfonts-use-system-type t) ; save profile config across different system-type
|
||||
)
|
||||
|
||||
(use-package company
|
||||
:init
|
||||
(global-company-mode)
|
||||
:config
|
||||
(setq company-idle-delay 0.2)
|
||||
;; number the candidates (use M-1, M-2 etc to select completions).
|
||||
(setq company-show-numbers t)
|
||||
;; show suggestions after entering 3 character.
|
||||
(setq company-minimum-prefix-length 3)
|
||||
;; when the list of suggestions is shown, and you go through the list of
|
||||
;; suggestions and reach the end of the list, the end of the list of
|
||||
;; suggestions does not wrap around to the top of the list again. This is a
|
||||
;; minor inconvenience that can be solved:
|
||||
(setq company-selection-wrap-around t)
|
||||
;; use tab key to cycle through suggestions.
|
||||
;; ('tng' means 'tab and go')
|
||||
(company-tng-configure-default)
|
||||
|
||||
;; { START: company-candidates from abo-abo
|
||||
;; if candidate list was ("var0" "var1" "var2"), then entering 1 means:
|
||||
;; select the first candidate (i.e. "var0"), instead of:
|
||||
;; insert "1", resulting in "var1", i.e. the second candidate
|
||||
;; via,
|
||||
;; - https://oremacs.com/2017/12/27/company-numbers/
|
||||
(defun ora-company-number ()
|
||||
"Forward to `company-complete-number'.
|
||||
Unless the number is potentially part of the candidate.
|
||||
In that case, insert the number."
|
||||
;; via https://github.com/abo-abo/oremacs/blob/d217e22a3b8dc88d10f715b32a7d1facf1f7ae18/modes/ora-company.el#L22-L39
|
||||
(interactive)
|
||||
(let* ((k (this-command-keys))
|
||||
(re (concat "^" company-prefix k)))
|
||||
(if (or (cl-find-if (lambda (s) (string-match re s))
|
||||
company-candidates)
|
||||
(> (string-to-number k)
|
||||
(length company-candidates))
|
||||
(looking-back "[0-9]+\\.[0-9]*" (line-beginning-position)))
|
||||
(self-insert-command 1)
|
||||
(company-complete-number
|
||||
(if (equal k "0")
|
||||
10
|
||||
(string-to-number k))))))
|
||||
|
||||
(let ((map company-active-map))
|
||||
;; via https://github.com/abo-abo/oremacs/blob/d217e22a3b8dc88d10f715b32a7d1facf1f7ae18/modes/ora-company.el#L46-L53
|
||||
(mapc (lambda (x) (define-key map (format "%d" x) 'ora-company-number))
|
||||
(number-sequence 0 9))
|
||||
(define-key map " " (lambda ()
|
||||
(interactive)
|
||||
(company-abort)
|
||||
(self-insert-command 1)))
|
||||
(define-key map (kbd "<return>") nil))
|
||||
;; END: company-candidates from abo-abo }
|
||||
)
|
||||
|
||||
(use-package company-ctags
|
||||
:config
|
||||
(with-eval-after-load 'company
|
||||
(company-ctags-auto-setup))
|
||||
;; my config -> [[./init-misc.el::config-ce-cc]]
|
||||
)
|
||||
|
||||
(use-package company-english-helper
|
||||
:straight (:host github :repo "manateelazycat/company-english-helper")
|
||||
)
|
||||
|
||||
(use-package counsel)
|
||||
|
||||
;; { START: counsel-etags
|
||||
(unless (executable-find "ctags")
|
||||
(when (eq system-type 'darwin)
|
||||
(shell-command "brew install universal-ctags"))
|
||||
(when (string= (which-linux-release-info "distributor") "Ubuntu")
|
||||
(call-process "/bin/bash"
|
||||
(expand-file-name "scripts/ctags.sh" user-emacs-directory)))
|
||||
(yes-or-no-p "Please be informed the ctags is started to install in the background...
|
||||
The installation result can be checked later manually with ctags command. Continue?")
|
||||
)
|
||||
(use-package counsel-etags
|
||||
;; ctags should be installed first, the Universal Ctags is recommended,
|
||||
;; https://github.com/universal-ctags/ctags
|
||||
;; with Exuberant Ctags or Universal Ctags, this package works out of box.
|
||||
;; instructions,
|
||||
;; `counsel-etags-scan-code' to create tags file
|
||||
;; `counsel-etags-find-tag-at-point' to navigate. This command will also
|
||||
;; run `counsel-etags-scan-code' AUTOMATICALLY if tags file does not exist.
|
||||
;; it also calls `counsel-etags-fallback-grep-function' if no tag is found.
|
||||
|
||||
;; keybinding -> [[./init-keybindings.el::ftap]]
|
||||
|
||||
;; update the TAGS file automatically on file saves
|
||||
:init
|
||||
(add-hook 'prog-mode-hook
|
||||
(lambda ()
|
||||
(add-hook 'after-save-hook
|
||||
'counsel-etags-virtual-update-tags 'append 'local)))
|
||||
|
||||
:config
|
||||
(setq counsel-etags-update-interval 60)
|
||||
(push "build" counsel-etags-ignore-directories)
|
||||
|
||||
;; create TAGS with the absolute recorded file paths
|
||||
(setq counsel-etags-update-tags-backend
|
||||
(lambda (src-dir)
|
||||
(shell-command
|
||||
;; relative path is used by default by ctags
|
||||
;; relative path is more portable and uses less memory (this package
|
||||
;; reads the tags file's content into memory)
|
||||
;; https://github.com/redguardtoo/counsel-etags/pull/88
|
||||
(format "ctags --options=%s -e -R"
|
||||
(expand-file-name ".ctags" user-emacs-directory)))))
|
||||
;; my config -> [[./init-misc.el::config-ce-cc]]
|
||||
)
|
||||
(when (or (eq system-type 'darwin) (eq system-type 'windows-nt))
|
||||
(unless (executable-find "ctags")
|
||||
(yes-or-no-p "Please be informed the ctags is used in this configuration file, but the executable file is not found.
|
||||
You need to install it manually. Continue?")
|
||||
))
|
||||
;; END: counsel-etags }
|
||||
|
||||
(use-package doom-themes
|
||||
:config
|
||||
;; global settings (defaults)
|
||||
(setq doom-themes-enable-bold t) ; if nil, bold is universally disabled
|
||||
;; corrects (and improves) org-mode's native fontification
|
||||
;; (doom-themes-org-config) ; disable this as it is not compatible with
|
||||
; org-modern horizontal line, see,
|
||||
; https://github.com/jsntn/emacs.d/issues/13
|
||||
;; personal modified version of doom-monokai-classic
|
||||
(add-to-list 'custom-theme-load-path (expand-file-name "themes/" user-emacs-directory))
|
||||
(load-theme 'doom-monokai-classic t)
|
||||
(set-background-color "black")
|
||||
(custom-set-faces
|
||||
`(mode-line ((t (:background ,(doom-color 'dark-violet)))))
|
||||
`(font-lock-comment-face ((t (:foreground ,(doom-color 'base6))))))
|
||||
)
|
||||
(use-package eglot)
|
||||
|
||||
;; M-x elgrep: search a single directory
|
||||
;; C-u M-x elgrep: search the directory recursively
|
||||
|
@ -276,50 +89,7 @@ You need to install it manually. Continue?")
|
|||
;; (add-hook 'elpy-mode-hook 'flycheck-mode))
|
||||
;; )
|
||||
|
||||
;; evil-collection assumes evil-want-keybinding is set to nil and
|
||||
;; evil-want-integration is set to t before loading evil and evil-collection.
|
||||
(setq evil-want-integration t)
|
||||
(setq evil-want-keybinding nil)
|
||||
|
||||
(use-package evil
|
||||
:init
|
||||
(unless (display-graphic-p)
|
||||
(setq evil-want-C-i-jump nil)
|
||||
)
|
||||
:after undo-tree
|
||||
:config
|
||||
(evil-set-undo-system 'undo-tree) ; https://github.com/emacs-evil/evil/issues/1372#issuecomment-712611291
|
||||
(global-undo-tree-mode)
|
||||
(evil-mode 1)
|
||||
;; change the cursor color in terms of evil mode
|
||||
(setq evil-emacs-state-cursor '("red" box))
|
||||
(setq evil-normal-state-cursor '("green" box))
|
||||
(setq evil-visual-state-cursor '("orange" box))
|
||||
(setq evil-insert-state-cursor '("red" bar))
|
||||
(setq evil-replace-state-cursor '("red" bar))
|
||||
(setq evil-operator-state-cursor '("red" hollow))
|
||||
)
|
||||
|
||||
(use-package evil-collection
|
||||
:after evil
|
||||
:config
|
||||
(evil-collection-init)
|
||||
)
|
||||
|
||||
(use-package evil-leader
|
||||
:init
|
||||
(global-evil-leader-mode)
|
||||
)
|
||||
|
||||
(use-package evil-surround
|
||||
:config
|
||||
(global-evil-surround-mode 1)
|
||||
)
|
||||
|
||||
(use-package evil-visualstar
|
||||
:config
|
||||
(global-evil-visualstar-mode)
|
||||
)
|
||||
|
||||
(when (memq window-system '(mac ns))
|
||||
(use-package exec-path-from-shell
|
||||
|
@ -336,7 +106,8 @@ You need to install it manually. Continue?")
|
|||
|
||||
(use-package git-messenger)
|
||||
|
||||
(use-package helm)
|
||||
(use-package git-timemachine)
|
||||
|
||||
|
||||
;; { -- START --
|
||||
;; use helm-dash and language-detection
|
||||
|
@ -440,6 +211,7 @@ You need to install it manually. Continue?")
|
|||
)
|
||||
|
||||
(use-package highlight-parentheses
|
||||
:delight
|
||||
:config
|
||||
(add-hook 'prog-mode-hook 'highlight-parentheses-mode)
|
||||
(setq highlight-parentheses-colors
|
||||
|
@ -449,6 +221,7 @@ You need to install it manually. Continue?")
|
|||
;; automatic and manual symbol highlighting
|
||||
;; cycle through the locations of any symbol at point
|
||||
(use-package highlight-symbol
|
||||
:delight
|
||||
:config
|
||||
(add-hook 'prog-mode-hook 'highlight-symbol-mode)
|
||||
(add-hook 'prog-mode-hook 'highlight-symbol-nav-mode)
|
||||
|
@ -462,26 +235,39 @@ You need to install it manually. Continue?")
|
|||
(setq hl-todo-highlight-punctuation ":"
|
||||
hl-todo-keyword-faces
|
||||
`(
|
||||
;; align with the org-todo-keyword-faces
|
||||
("TODO" :foreground "white" :background "#5F87FF")
|
||||
("DONE" :foreground "white" :background "#2E8B57")
|
||||
("CLOSED" :foreground "white" :background "#2E8B57")
|
||||
("CANCELED" :foreground "white" :background "#95A5A6")
|
||||
("WAIT" :foreground "white" :background "#F9BC41")
|
||||
("IN-PROGRESS" :foreground "white" :background "#3498DB")
|
||||
("REPORT" :foreground "#C0C0C0" :background "#308014" :box (:line-width (-1 . -1)))
|
||||
("BUG" :foreground "#E6DB74" :background "black" :box (:line-width (-1 . -1)))
|
||||
("KNOWNCAUSE" :foreground "#9C91E4" :background "black" :box (:line-width (-1 . -1)))
|
||||
("IMPROVEMENT" :foreground "#FF9900" :background "black" :box (:line-width (-1 . -1)))
|
||||
("ENHANCEMENT" :foreground "#9900ff" :background "black" :box (:line-width (-1 . -1)))
|
||||
("FEATURE" :foreground "#38761d" :background "black" :box (:line-width (-1 . -1)))
|
||||
("FIXED" :foreground "#4B5556" :strike-through t :box (:line-width (-1 . -1)))
|
||||
;; my own highlight keywords
|
||||
("FIXME" :foreground "white" :background "red")
|
||||
("DEBUG" :foreground "#E6DB74" :background "black" :box t)
|
||||
("HACK" :foreground "#9C91E4" :background "black" :box t)
|
||||
("REVIEW" :foreground "#F02660" :background "black" :box t)
|
||||
("NOTE" :foreground "#C0C0C0" :background "#308014" :box t)
|
||||
("DEPRECATED" font-lock-doc-face :strike-through t :box t)
|
||||
("FOLLOWUP" :foreground "white" :background "#808A87" :box t)
|
||||
("ANSWER" :foreground "white" :background "#808A87" :box t)
|
||||
("MARK" :foreground "black" :background "#FFFFFF" :box t)
|
||||
("IMPROVEMENT" :foreground "white" :background "#FF9900" :box t)
|
||||
("ENHANCEMENT" :foreground "white" :background "#9900FF" :box t)
|
||||
("FEATURE" :foreground "white" :background "#38761d" :box t)
|
||||
("DEBUG" :foreground "#E6DB74" :background "black" :box (:line-width (-1 . -1)))
|
||||
("HACK" :foreground "#9C91E4" :background "black" :box (:line-width (-1 . -1)))
|
||||
("REVIEW" :foreground "#F02660" :background "black" :box (:line-width (-1 . -1)))
|
||||
("NOTE" :foreground "#C0C0C0" :background "#308014" :box (:line-width (-1 . -1)))
|
||||
("DEPRECATED" font-lock-doc-face :strike-through t :box (:line-width (-1 . -1)))
|
||||
("FOLLOWUP" :foreground "white" :background "#808A87" :box (:line-width (-1 . -1)))
|
||||
("ANSWER" :foreground "white" :background "#808A87" :box (:line-width (-1 . -1)))
|
||||
("MARK" :foreground "black" :background "#FFFFFF" :box (:line-width (-1 . -1)))
|
||||
("IMPROVEMENT" :foreground "white" :background "#FF9900" :box (:line-width (-1 . -1)))
|
||||
("ENHANCEMENT" :foreground "white" :background "#9900FF" :box (:line-width (-1 . -1)))
|
||||
("FEATURE" :foreground "white" :background "#38761d" :box (:line-width (-1 . -1)))
|
||||
("Linode" :foreground "white" :background "#999DF7")
|
||||
("GitHub" :foreground "black" :background "#FFFFFF")
|
||||
("via" :foreground "#5F87FF" :background "black" :box t)
|
||||
("Via" :foreground "#5F87FF" :background "black" :box t)
|
||||
("VIA" :foreground "#5F87FF" :background "black" :box t)
|
||||
("Jason" :foreground "white" :background "#38761d" :box t)
|
||||
("via" :foreground "#5F87FF" :background "black" :box (:line-width (-1 . -1)))
|
||||
("Via" :foreground "#5F87FF" :background "black" :box (:line-width (-1 . -1)))
|
||||
("VIA" :foreground "#5F87FF" :background "black" :box (:line-width (-1 . -1)))
|
||||
("Jason" :foreground "white" :background "#38761d" :box (:line-width (-1 . -1)))
|
||||
("ChatGPT" :foreground "white" :background "#19C37D")
|
||||
)
|
||||
)
|
||||
|
@ -505,24 +291,15 @@ You need to install it manually. Continue?")
|
|||
(setq keyfreq-file-lock (expand-file-name ".emacs.keyfreq.lock" user-emacs-directory))
|
||||
)
|
||||
|
||||
(use-package lsp-mode
|
||||
(use-package marginalia
|
||||
:init
|
||||
(marginalia-mode)
|
||||
:config
|
||||
(setq lsp-headerline-breadcrumb-enable nil)
|
||||
:hook
|
||||
(lsp-mode . lsp-enable-which-key-integration) ; which-key integration
|
||||
(setq marginalia-field-width 9999999) ; maximize the width of marginalia field
|
||||
)
|
||||
|
||||
(use-package lsp-pyright
|
||||
:config
|
||||
(my-check-for-executable "pyright" "pyright")
|
||||
:hook (python-mode . (lambda ()
|
||||
(require 'lsp-pyright)
|
||||
(lsp)))) ; or lsp-deferred
|
||||
|
||||
(use-package lsp-ui
|
||||
:config
|
||||
(setq lsp-ui-doc-position 'top)
|
||||
)
|
||||
(straight-use-package
|
||||
'(mr-poker :type git :host github :repo "jsntn/mr-poker.el"))
|
||||
|
||||
(use-package neotree
|
||||
:config
|
||||
|
@ -530,6 +307,11 @@ You need to install it manually. Continue?")
|
|||
(setq neo-window-fixed-size nil)
|
||||
)
|
||||
|
||||
(use-package orderless
|
||||
:custom
|
||||
(completion-styles '(orderless basic))
|
||||
)
|
||||
|
||||
(use-package org-bullets
|
||||
:config
|
||||
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))
|
||||
|
@ -541,6 +323,7 @@ You need to install it manually. Continue?")
|
|||
;; make all agenda files with any archive files associated with them as the
|
||||
;; source of items for drill sessions(scope)
|
||||
(setq org-drill-scope 'agenda-with-archives)
|
||||
(setq org-drill-leech-method "warn")
|
||||
)
|
||||
|
||||
(use-package org-modern
|
||||
|
@ -622,13 +405,11 @@ You need to install it manually. Continue?")
|
|||
)
|
||||
|
||||
(use-package orglink
|
||||
:delight
|
||||
:config
|
||||
(global-orglink-mode))
|
||||
|
||||
(use-package org-drill
|
||||
:config
|
||||
(setq org-drill-leech-method "warn")
|
||||
)
|
||||
|
||||
|
||||
(use-package ox-hugo
|
||||
:after ox
|
||||
|
@ -654,23 +435,29 @@ You need to install it manually. Continue?")
|
|||
)
|
||||
|
||||
(use-package org-roam
|
||||
:if window-system ; for graphical Emacs
|
||||
;; :if window-system ; for graphical Emacs
|
||||
:after emacsql-sqlite3
|
||||
:config
|
||||
(org-roam-db-autosync-mode)
|
||||
(add-hook 'emacs-startup-hook #'my-activate-org-roam-db-autosync)
|
||||
(setq org-roam-database-connector 'sqlite3)
|
||||
(setq org-roam-mode-sections
|
||||
(list #'org-roam-backlinks-section
|
||||
#'org-roam-reflinks-section
|
||||
;; ripgrep (rg) is used for unlinked references below - (executable-find "rg")
|
||||
#'org-roam-unlinked-references-section
|
||||
;; #'org-roam-unlinked-references-section
|
||||
))
|
||||
)
|
||||
|
||||
(defun my-activate-org-roam-db-autosync ()
|
||||
"Activate `org-roam-db-autosync-mode` after a delay"
|
||||
(run-with-idle-timer 7 nil 'org-roam-db-autosync-mode))
|
||||
|
||||
|
||||
(my-check-for-executable "ripgrep (rg)" "rg")
|
||||
;; END: Org-roam }
|
||||
|
||||
(use-package org-roam-ui
|
||||
:delight
|
||||
:if window-system ; for graphical Emacs
|
||||
:after org-roam
|
||||
:config
|
||||
|
@ -832,62 +619,28 @@ You need to install it manually. Continue?")
|
|||
(set-cursor-color "red"))))
|
||||
)
|
||||
|
||||
(use-package pyvenv
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(use-package smooth-scroll
|
||||
:delight
|
||||
:straight (:type git :host github :repo "k-talo/smooth-scroll.el")
|
||||
:config
|
||||
;; (pyvenv-mode t)
|
||||
|
||||
;; set correct Python interpreter
|
||||
(setq pyvenv-post-activate-hooks
|
||||
(list (lambda ()
|
||||
(if (equal system-type 'windows-nt)
|
||||
(setq python-shell-interpreter (concat pyvenv-virtual-env "Scripts/python"))
|
||||
(setq python-shell-interpreter (concat pyvenv-virtual-env "bin/python"))
|
||||
)
|
||||
)))
|
||||
(setq pyvenv-post-deactivate-hooks
|
||||
(list (lambda ()
|
||||
(setq python-shell-interpreter "python")
|
||||
)))
|
||||
(smooth-scroll-mode t)
|
||||
(global-set-key [next] #'smooth-scroll/scroll-up)
|
||||
(global-set-key [prior] #'smooth-scroll/scroll-down)
|
||||
)
|
||||
|
||||
;; START: reformatter config
|
||||
(unless (executable-find "shfmt")
|
||||
(when (eq system-type 'gnu/linux)
|
||||
(shell-command "sudo snap install shfmt")
|
||||
)
|
||||
)
|
||||
(use-package reformatter
|
||||
(use-package smooth-scrolling
|
||||
:config
|
||||
(reformatter-define css-yaml-format
|
||||
:program "prettier"
|
||||
:args (list "--write" buffer-file-name)
|
||||
;; https://emacs.stackexchange.com/questions/24298/can-i-eval-a-value-in-quote
|
||||
)
|
||||
(reformatter-define sh-format
|
||||
:program "shfmt"
|
||||
:args (list "-l" "-w" "-i" "4" buffer-file-name)
|
||||
;; 4 spaces as indent, read more https://github.com/mvdan/sh/blob/master/cmd/shfmt/shfmt.1.scd
|
||||
;; https://emacs.stackexchange.com/questions/24298/can-i-eval-a-value-in-quote
|
||||
)
|
||||
)
|
||||
(my-check-for-executable "Prettier" "prettier")
|
||||
(my-check-for-executable "shfmt" "shfmt")
|
||||
;; END: reformatter config
|
||||
(smooth-scrolling-mode 1))
|
||||
|
||||
(use-package savehist
|
||||
;; from https://emacs-china.org/t/emacs/17606/9
|
||||
:hook (after-init . savehist-mode)
|
||||
:init (setq enable-recursive-minibuffers t ; allow commands in minibuffers
|
||||
history-length 1000
|
||||
savehist-additional-variables '(mark-ring
|
||||
global-mark-ring
|
||||
search-ring
|
||||
regexp-search-ring
|
||||
extended-command-history)
|
||||
savehist-autosave-interval 300)
|
||||
)
|
||||
|
||||
(use-package super-save
|
||||
:delight
|
||||
:config
|
||||
(super-save-mode +1)
|
||||
(setq super-save-auto-save-when-idle t)
|
||||
|
@ -899,11 +652,8 @@ You need to install it manually. Continue?")
|
|||
|
||||
;; { -- start: if emacs is running in a terminal
|
||||
(unless (display-graphic-p)
|
||||
|
||||
(add-to-list 'package-archives
|
||||
'("cselpa" . "https://elpa.thecybershadow.net/packages/"))
|
||||
|
||||
(use-package term-keys
|
||||
:straight (:type git :host github :repo "CyberShadow/term-keys")
|
||||
:config
|
||||
(term-keys-mode t)
|
||||
;; to configure alacritty for term-keys, use term-keys/alacritty-config to generate a alacritty.yml fragment:
|
||||
|
@ -914,20 +664,6 @@ You need to install it manually. Continue?")
|
|||
;; then, add the output to your main alacritty.yml file.
|
||||
;; via https://github.com/CyberShadow/term-keys#alacritty
|
||||
)
|
||||
|
||||
(setq package-archives (delete '("cselpa" . "https://elpa.thecybershadow.net/packages/") package-archives))
|
||||
|
||||
(defun term-keys-reminder-messages ()
|
||||
(yes-or-no-p "term-keys is used to handle keyboard input involving any combination of keys and modifiers in emacs through supported terminal emulator(Alacritty is recommended on Windows), refer to term-keys README for configuration. Continue?")
|
||||
)
|
||||
|
||||
(unless noninteractive
|
||||
(if (boundp 'term-keys-reminder)
|
||||
(when (symbol-value 'term-keys-reminder) (term-keys-reminder-messages))
|
||||
(term-keys-reminder-messages)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
;; -- end: if emacs is running in a terminal }
|
||||
|
||||
|
@ -938,6 +674,10 @@ You need to install it manually. Continue?")
|
|||
(global-undo-tree-mode)
|
||||
)
|
||||
|
||||
(use-package vertico
|
||||
:init
|
||||
(vertico-mode))
|
||||
|
||||
(use-package vlf
|
||||
:config
|
||||
(require 'vlf-setup)
|
||||
|
|
167
lisp/init-pre.el
167
lisp/init-pre.el
|
@ -3,6 +3,29 @@
|
|||
;;; Code:
|
||||
|
||||
|
||||
;; pre settings: needed by other configurations
|
||||
|
||||
(defconst *is-mac* (eq system-type 'darwin))
|
||||
(defconst *is-win* (eq system-type 'windows-nt))
|
||||
(defconst *is-linux* (or (eq system-type 'gnu/linux) (eq system-type 'linux)) )
|
||||
|
||||
|
||||
|
||||
(defun my-require (feature)
|
||||
"Custom require function to prevent recursive loading."
|
||||
(unless (featurep feature)
|
||||
(require feature)))
|
||||
|
||||
(defun my-require-maybe (feature &optional file)
|
||||
"Try to require FEATURE, but don't signal an error if `require' fails."
|
||||
(require feature file 'noerror))
|
||||
|
||||
(defun my-when-available (func foo)
|
||||
"Do something if FUNCTION is available."
|
||||
(when (fboundp func) (funcall foo)))
|
||||
|
||||
|
||||
|
||||
|
||||
;; { -- START --
|
||||
;; check Linux distribution
|
||||
|
@ -34,6 +57,67 @@
|
|||
|
||||
|
||||
|
||||
|
||||
(defun my/set-windows-paths (custom-paths-list)
|
||||
"Set the PATH and exec-path in sync for Windows-NT system type
|
||||
based on the given list of paths.
|
||||
|
||||
Version: 2023-09-27"
|
||||
(when (eq system-type 'windows-nt)
|
||||
(let ((xPaths custom-paths-list))
|
||||
(setenv "PATH" (mapconcat 'identity xPaths ";"))
|
||||
(setq exec-path (append xPaths (list "." exec-directory))))))
|
||||
;; Example usage,
|
||||
;; (setq my-windows-paths
|
||||
;; `(
|
||||
;; ,(format "%s%s%s" "C:/Users/" (symbol-value 'user-login-name) "/scoop/apps/nodejs/current/bin")
|
||||
;; ,(format "%s%s%s" "C:/Users/" (symbol-value 'user-login-name) "/scoop/apps/nodejs/current")
|
||||
;; ,(format "%s%s%s" "C:/Users/" (symbol-value 'user-login-name) "/scoop/shims")
|
||||
;; "C:/Windows/System32/"
|
||||
;; "C:/Windows/system32/WindowsPowerShell/v1.0/"
|
||||
;; "C:/msys64/mingw64/bin/"
|
||||
;; ,(symbol-value 'windows-portable-bin-directory)
|
||||
;; ))
|
||||
;; (my/set-windows-paths my-windows-paths)
|
||||
;; (getenv "PATH")
|
||||
|
||||
;; Difference between exec-path and PATH
|
||||
;; The value of environment variable "PATH" is used by emacs when you are trying
|
||||
;; to call a linux command from a shell in emacs.
|
||||
;; The exec-path is used by emacs itself to find programs it needs for its
|
||||
;; features, such as spell checking, file compression, compiling, grep, diff,
|
||||
;; etc.
|
||||
;; The value of (getenv "PATH") and exec-path do not need to be the same.
|
||||
;; http://xahlee.info/emacs/emacs/emacs_env_var_paths.html
|
||||
|
||||
|
||||
|
||||
(defun my/set-var (var &rest values)
|
||||
"Set VAR based on the operating system using a list of
|
||||
VALUES. VALUES should be a list of pairs where the car is the
|
||||
operating system identifier ('win', 'mac', 'linux') and the cdr
|
||||
is the value associated with that operating system.
|
||||
|
||||
Version: 2023-09-24
|
||||
Updated: 2023-09-26"
|
||||
(require 'cl-lib) ; to avoid the cl-loop void definition
|
||||
(let* ((os (cond ((eq system-type 'windows-nt) 'win)
|
||||
((eq system-type 'gnu/linux) 'linux)
|
||||
((eq system-type 'darwin) 'mac)
|
||||
(t (error "Unsupported operating system")))))
|
||||
(cl-loop for (os-id . value) in values
|
||||
when (eq os os-id)
|
||||
do (set var (if (stringp value)
|
||||
value
|
||||
(eval value))))))
|
||||
;; Example usage to set org-directory based on OS with my/set-var
|
||||
;; (my/set-var 'org-directory
|
||||
;; '(win . "c:/org-directory")
|
||||
;; '(mac . "~/org-directory")
|
||||
;; '(linux . "~/org-directory"))
|
||||
;; (symbol-value 'org-directory)
|
||||
|
||||
|
||||
(defun my-check-for-executable (executable-name executable-file &optional message)
|
||||
"Check if the given EXECUTABLE-FILE is available. If it's not found,
|
||||
prompt the user with the optional MESSAGE (or a default message) to install it."
|
||||
|
@ -47,7 +131,7 @@ but the %s executable file is not found. You need to install it manually."
|
|||
(unless (executable-find executable-file)
|
||||
(if noninteractive
|
||||
(message noninteractive-msg)
|
||||
(unless (string= (read-string prompt-msg) "")
|
||||
(when (string= (read-string prompt-msg) "")
|
||||
(message "Continuing..."))))))
|
||||
|
||||
|
||||
|
@ -60,11 +144,90 @@ but the %s executable file is not found. You need to install it manually."
|
|||
Press ENTER to continue." font-name))
|
||||
(prompt-msg (or message default-message)))
|
||||
(unless (member font-name (font-family-list))
|
||||
(unless (string= (read-string prompt-msg) "")
|
||||
(when (string= (read-string prompt-msg) "")
|
||||
(message "Continuing...")))))
|
||||
|
||||
|
||||
|
||||
(defun my-async-shell-command-with-unique-buffer-name (command)
|
||||
"Execute an asynchronous shell command and display its output in a unique buffer.
|
||||
|
||||
This function prompts the user for a shell command and then executes it
|
||||
asynchronously. The output of the command is displayed in a buffer with a
|
||||
unique name, incorporating the provided command and a timestamp. The buffer
|
||||
name is of the form '*Async Command - COMMAND - TIMESTAMP*', where COMMAND is
|
||||
the entered shell command and TIMESTAMP is the current date and time in the
|
||||
format 'YYYY-MM-DD HH:MM:SS:NNN'.
|
||||
|
||||
Version: 2023-08-16"
|
||||
(interactive "sShell command: ")
|
||||
(let ((buffer-name
|
||||
(concat "*Async Command - " command " - "
|
||||
(format-time-string "%Y-%m-%d %H:%M:%S:%3N") "*")))
|
||||
(async-shell-command command buffer-name)))
|
||||
|
||||
|
||||
(defun my-file-contains-p (file content)
|
||||
"Check if FILE contains all items in CONTENT list."
|
||||
(when (file-exists-p file)
|
||||
(with-temp-buffer
|
||||
(set-buffer-file-coding-system 'utf-8-unix)
|
||||
(insert-file-contents file)
|
||||
(seq-every-p (lambda (item) (string-match-p (regexp-quote item) (buffer-string))) content))))
|
||||
|
||||
|
||||
(defun my-insert-newline-at-end-of-file (file-path)
|
||||
"Inserts a new line at the end of the file specified by FILE-PATH."
|
||||
(with-current-buffer (find-file-noselect file-path)
|
||||
(goto-char (point-max))
|
||||
(newline)
|
||||
(save-buffer)
|
||||
(kill-buffer)))
|
||||
|
||||
|
||||
(defun my-write-to-file (content file &optional append sudo)
|
||||
"Write CONTENT to FILE. If APPEND is true, append the content to the file; otherwise, overwrite the file.
|
||||
If SUDO is provided and non-nil, execute the write operation with sudo."
|
||||
(let* ((tee-command (if append "tee -a" "tee"))
|
||||
(sudo-command (if sudo (concat "sudo " tee-command) tee-command))
|
||||
(cmd (concat "echo " (shell-quote-argument content) " | " sudo-command " " (shell-quote-argument file))))
|
||||
(if sudo
|
||||
(if (executable-find "tee")
|
||||
(shell-command cmd)
|
||||
(message "Not executed due to tee executable not found.
|
||||
The tee executable is required for the sudo execution."))
|
||||
(with-temp-buffer
|
||||
(insert content)
|
||||
(write-region (point-min) (point-max) file append)))
|
||||
))
|
||||
|
||||
|
||||
(defun my-merge-duplicated-lines-in-file (file &optional sudo)
|
||||
"Merge duplicated lines in FILE.
|
||||
If SUDO is provided and non-nil, execute the merge operation with sudo."
|
||||
(interactive "f")
|
||||
(with-temp-buffer
|
||||
;; fix "\r\n" and "\n" on different systems
|
||||
;; "\n" will be used as utf-8-unix for Unix-like systems
|
||||
(set-buffer-file-coding-system 'utf-8-unix)
|
||||
(insert-file-contents file)
|
||||
(let* ((newline-str "\n")
|
||||
(lines (split-string (buffer-string) newline-str t))
|
||||
;; reverse the list so that the first one will be kept after delete-dups
|
||||
(lines (delete-dups (reverse lines)))
|
||||
;; (lines (sort lines 'string>)) ;; sort the lines
|
||||
)
|
||||
(erase-buffer)
|
||||
(insert (mapconcat 'identity (reverse lines) newline-str)))
|
||||
(if sudo
|
||||
(let* ((sudo-command (concat "sudo tee " (shell-quote-argument file)))
|
||||
(cmd (concat "echo " (shell-quote-argument (buffer-string)) " | " sudo-command)))
|
||||
(if (executable-find "tee")
|
||||
(shell-command cmd)
|
||||
(message "Not executed due to tee executable not found.
|
||||
The tee executable is required for the sudo execution.")))
|
||||
(write-region (point-min) (point-max) file))))
|
||||
|
||||
|
||||
(provide 'init-pre)
|
||||
|
||||
|
|
|
@ -3,6 +3,27 @@
|
|||
;;; Code:
|
||||
|
||||
|
||||
|
||||
|
||||
(use-package pyvenv
|
||||
:config
|
||||
;; (pyvenv-mode t)
|
||||
|
||||
;; set correct Python interpreter
|
||||
(setq pyvenv-post-activate-hooks
|
||||
(list (lambda ()
|
||||
(if (equal system-type 'windows-nt)
|
||||
(setq python-shell-interpreter (concat pyvenv-virtual-env "Scripts/python"))
|
||||
(setq python-shell-interpreter (concat pyvenv-virtual-env "bin/python"))
|
||||
)
|
||||
)))
|
||||
(setq pyvenv-post-deactivate-hooks
|
||||
(list (lambda ()
|
||||
(setq python-shell-interpreter "python")
|
||||
)))
|
||||
)
|
||||
|
||||
|
||||
(defun my/python-mode-config ()
|
||||
(setq python-indent-offset 4
|
||||
python-indent 4
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
;;; init-reformatter.el --- reformatter settings -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
;; START: reformatter config
|
||||
(use-package reformatter
|
||||
:config
|
||||
(reformatter-define css-yaml-format
|
||||
:program "prettier"
|
||||
:args (list "--write" buffer-file-name)
|
||||
;; https://emacs.stackexchange.com/questions/24298/can-i-eval-a-value-in-quote
|
||||
)
|
||||
(reformatter-define sh-format
|
||||
:program "shfmt"
|
||||
:args (list "-l" "-w" "-i" "4" buffer-file-name)
|
||||
;; 4 spaces as indent, read more https://github.com/mvdan/sh/blob/master/cmd/shfmt/shfmt.1.scd
|
||||
;; https://emacs.stackexchange.com/questions/24298/can-i-eval-a-value-in-quote
|
||||
)
|
||||
)
|
||||
(my-check-for-executable "Prettier" "prettier")
|
||||
(my-check-for-executable "shfmt" "shfmt")
|
||||
;; END: reformatter config
|
||||
|
||||
|
||||
(provide 'init-reformatter)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-reformatter.el ends here
|
|
@ -3,10 +3,14 @@
|
|||
;;; Code:
|
||||
|
||||
|
||||
;; https://web.archive.org/web/20240509043743/http://xahlee.info/emacs/emacs/emacs_save_restore_opened_files.html
|
||||
|
||||
;; save a list of open files in ~/.emacs.d/.emacs.desktop
|
||||
(setq desktop-path (list user-emacs-directory)
|
||||
desktop-auto-save-timeout 600
|
||||
desktop-auto-save-timeout 6
|
||||
desktop-save t
|
||||
desktop-load-locked-desktop t ; no ask if crashed
|
||||
desktop-restore-frames t
|
||||
desktop-restore-eager 10 ; the maximum number of 10 buffers to restore
|
||||
; immediately, and the remaining buffers are
|
||||
; restored lazily, when Emacs is idle.
|
||||
|
@ -44,6 +48,27 @@
|
|||
tags-table-list))
|
||||
|
||||
|
||||
;; make Emacs remember cursor position,
|
||||
;; by default, the position records are saved to ~/.emacs.d/places
|
||||
;; via https://web.archive.org/web/20240509044026/http://xahlee.info/emacs/emacs/emacs_save_cursor_position.html
|
||||
(save-place-mode 1)
|
||||
|
||||
;; https://web.archive.org/web/20240509044606/http://xahlee.info/emacs/emacs/emacs_save_command_history.html
|
||||
(use-package savehist
|
||||
;; from https://web.archive.org/web/20240509044708/https://emacs-china.org/t/emacs/17606/9
|
||||
:init (setq enable-recursive-minibuffers t ; allow commands in minibuffers
|
||||
history-length 1000
|
||||
savehist-additional-variables '(mark-ring
|
||||
global-mark-ring
|
||||
search-ring
|
||||
regexp-search-ring
|
||||
extended-command-history)
|
||||
savehist-autosave-interval 6)
|
||||
:config
|
||||
;; by default, the command histories are saved to ~/.emacs.d/history
|
||||
(savehist-mode 1))
|
||||
|
||||
|
||||
(provide 'init-sessions)
|
||||
|
||||
;; Local Variables:
|
||||
|
|
|
@ -8,16 +8,16 @@
|
|||
;; https://github.com/doomemacs/doomemacs/issues/3108#issuecomment-627537230
|
||||
;; (setq garbage-collection-messages t)
|
||||
|
||||
;; http://bling.github.io/blog/2016/01/18/why-are-you-changing-gc-cons-threshold
|
||||
;; https://web.archive.org/web/20231109122321/http://bling.github.io/blog/2016/01/18/why-are-you-changing-gc-cons-threshold/
|
||||
(defun my/minibuffer-setup-hook ()
|
||||
;; http://clhs.lisp.se/Body/v_most_p.htm
|
||||
;; https://web.archive.org/web/20231109123003/http://clhs.lisp.se/Body/v_most_p.htm
|
||||
(setq gc-cons-threshold most-positive-fixnum)
|
||||
)
|
||||
|
||||
(defun my/minibuffer-exit-hook ()
|
||||
;; defer it (1sec) so that commands launched immediately after will enjoy the
|
||||
;; benefits.
|
||||
;; https://github.com/doomemacs/doomemacs/blob/develop/docs/faq.org#how-does-doom-start-up-so-quickly
|
||||
;; https://github.com/doomemacs/doomemacs/blob/35865ef5e89442e3809b8095199977053dd4210f/docs/faq.org#how-does-doom-start-up-so-quickly
|
||||
(run-at-time 1 nil
|
||||
(lambda () (setq gc-cons-threshold 16777216)) ; 16mb
|
||||
)
|
||||
|
@ -27,6 +27,32 @@
|
|||
(add-hook 'minibuffer-exit-hook #'my/minibuffer-exit-hook)
|
||||
|
||||
|
||||
;; { START: 优化 Emacs 的垃圾搜集行为
|
||||
;; https://web.archive.org/web/20231109123542/https://raw.githubusercontent.com/lujun9972/lujun9972.github.com/81a7933b05495155a601b9b57991ca32d12c95a5/Emacs%E4%B9%8B%E6%80%92/%E4%BC%98%E5%8C%96Emacs%E7%9A%84%E5%9E%83%E5%9C%BE%E6%90%9C%E9%9B%86%E8%A1%8C%E4%B8%BA.org
|
||||
;; original link https://web.archive.org/web/20231109123354/https://akrl.sdf.org/
|
||||
|
||||
;; (setq garbage-collection-messages t)
|
||||
;; (setq garbage-collection-messages nil)
|
||||
|
||||
;; This macro measures the time it takes to evaluate a body of code.
|
||||
(defmacro measure-time (&rest body)
|
||||
"Measure and return the time it takes evaluating BODY."
|
||||
`(let ((start-time (current-time)))
|
||||
,@body
|
||||
(float-time (time-since start-time))))
|
||||
|
||||
;; This variable sets a timer to run the garbage collector after 15 seconds
|
||||
;; of idling.
|
||||
(defvar gc-timer
|
||||
(run-with-idle-timer 15 t
|
||||
(lambda ()
|
||||
(message "Garbage collector has run for %.06f seconds"
|
||||
(measure-time (garbage-collect))))))
|
||||
|
||||
;; END: 优化 Emacs 的垃圾搜集行为 }
|
||||
|
||||
|
||||
|
||||
(provide 'init-speed-up)
|
||||
|
||||
;; Local Variables:
|
||||
|
|
|
@ -14,6 +14,29 @@
|
|||
)
|
||||
|
||||
|
||||
(use-package auto-capitalize
|
||||
:straight (:host github :repo "yuutayamada/auto-capitalize-el")
|
||||
:config
|
||||
(setq auto-capitalize-words `("I" "English"))
|
||||
;; this configuration adds capitalized words of .aspell.en.pws
|
||||
(setq auto-capitalize-aspell-file (expand-file-name "misc/aspell.en.pws" user-emacs-directory))
|
||||
(auto-capitalize-setup)
|
||||
;; (add-hook 'after-change-major-mode-hook 'auto-capitalize-mode)
|
||||
:hook (org-mode . auto-capitalize-mode)
|
||||
)
|
||||
|
||||
|
||||
(use-package ta
|
||||
:delight
|
||||
;; :config
|
||||
;; (mapc (lambda (mode-hook) (add-hook mode-hook 'ta-mode))
|
||||
;; '(org-mode-hook
|
||||
;; markdown-mode-hook
|
||||
;; rst-mode-hook))
|
||||
;; (define-key ta-mode-map (kbd "M-o") 'ta-next-homophony)
|
||||
)
|
||||
|
||||
|
||||
(provide 'init-spelling)
|
||||
|
||||
;; Local Variables:
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
;;; init-tags.el --- tags related config -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
;; a tags file is an index to source-code definitions of functions, variables, and any other interesting syntactic feature.
|
||||
|
||||
|
||||
;; { START: citre
|
||||
(unless (executable-find "ctags")
|
||||
(when (string= (which-linux-release-info "distributor") "Ubuntu")
|
||||
(call-process "/bin/bash"
|
||||
(expand-file-name "scripts/ctags.sh" user-emacs-directory)))
|
||||
(yes-or-no-p "Please be informed the ctags is started to install in the background...
|
||||
The installation result can be checked later manually with ctags command. Continue?")
|
||||
)
|
||||
|
||||
(use-package citre
|
||||
:delight
|
||||
;; ctags should be installed first, the Universal Ctags is recommended,
|
||||
;; https://github.com/universal-ctags/ctags
|
||||
:defer t
|
||||
:init
|
||||
;; This is needed in `:init' block for lazy load to work.
|
||||
(require 'citre-config)
|
||||
:config
|
||||
;; see https://github.com/universal-ctags/citre/issues/161
|
||||
(setq citre-peek-fill-fringe nil)
|
||||
(setq citre-peek-use-dashes-as-horizontal-border t))
|
||||
|
||||
(when (or (eq system-type 'darwin) (eq system-type 'windows-nt))
|
||||
(my-check-for-executable "ctags" "ctags"))
|
||||
;; END: citre }
|
||||
|
||||
|
||||
(defun my/create-tags
|
||||
(dir-name tags-format tag-relative tags-filename
|
||||
&optional tags-path append sudo process-name)
|
||||
"Create a tags file with absolute or relative symbols recorded inside. With a
|
||||
prefix argument SUDO, run the command with sudo privilege.
|
||||
|
||||
When called interactively, prompt the user for the directory name to create the
|
||||
tags file. If no input is given, use the current working directory.
|
||||
|
||||
The `ctags` command is executed with the `--tag-relative` option
|
||||
set to `yes` if the `tag-relative` is set to 'y', or 'n'
|
||||
indicates 'never'. The `*` wildcard is included in the `ctags`
|
||||
command to create tags for all files in the directory.
|
||||
|
||||
Version: 2023-03-17
|
||||
Updated: 2023-10-20"
|
||||
|
||||
;; This function is improved by ChatGPT and Claude :)
|
||||
(interactive
|
||||
(let* ((tags-format (completing-read "ctags or etags format? (ctags/etags)\n(Note: omit input indicates etags format) "
|
||||
'("ctags" "etags")
|
||||
nil t nil nil "etags"))
|
||||
(tag-relative (completing-read "Create tags index file with relative symbols? (y/n)\n(Note: omit input indicates absolute symbols) "
|
||||
'("y" "n")
|
||||
nil t nil nil "n"))
|
||||
(tags-filename (if (string-equal tags-format "etags")
|
||||
(if (string-equal tag-relative "y") "TAGS" "TAGS_ABS")
|
||||
(if (string-equal tag-relative "y") "tags" "tags_abs"))))
|
||||
(list (read-directory-name "Enter the directory for creating tags file: ")
|
||||
tags-format
|
||||
tag-relative
|
||||
(read-string "Enter the desired tags filename: " tags-filename)
|
||||
(if (boundp 'tags-path) tags-path nil)
|
||||
(completing-read "Append the tags to existing tags index file? (y/n)\n(Note: omit input indicates creating) "
|
||||
'("y" "n")
|
||||
nil t nil nil "n")
|
||||
(if (boundp 'sudo)
|
||||
sudo
|
||||
(if current-prefix-arg t nil) ; if universal argument (sudo)
|
||||
)
|
||||
(if (boundp 'process-name) process-name "create tags"))))
|
||||
|
||||
(let* ((target-dir-value (if (string= "" dir-name)
|
||||
default-directory
|
||||
(if (eq system-type 'windows-nt)
|
||||
;; if the dir-name already start with "/d", just use it
|
||||
(if (string-prefix-p "/d" dir-name)
|
||||
dir-name
|
||||
;; fix changing dir across different drives issue on Windows
|
||||
(concat "/d " dir-name))
|
||||
(expand-file-name dir-name))))
|
||||
|
||||
(tags-format-value (if (string-equal tags-format 'ctags) "" "-e"))
|
||||
|
||||
(tag-relative-value (if (string-equal tag-relative 'y) "yes" "never"))
|
||||
;; yes - relative symbols
|
||||
;; never - absolute symbols
|
||||
|
||||
(append-t-or-not (if (string-equal append 'y) t nil))
|
||||
(append-or-create (if (string-equal append 'y) "- APPEND: " "- CREATE: "))
|
||||
(append-or-not (if (string-equal append 'y) "--append=yes" ""))
|
||||
|
||||
(tags-path-value
|
||||
(if (string= tag-relative 'y)
|
||||
(expand-file-name tags-filename target-dir-value)
|
||||
(or tags-path
|
||||
(expand-file-name tags-filename
|
||||
(read-directory-name
|
||||
"Enter the path to store the tags file: "
|
||||
nil default-directory)))))
|
||||
|
||||
(command-process-name process-name)
|
||||
|
||||
(ctags-cmd (format "cd %s && ctags --options=%s %s -R --tag-relative=%s %s -f %s *"
|
||||
target-dir-value
|
||||
(expand-file-name ".ctags" user-emacs-directory)
|
||||
tags-format-value
|
||||
tag-relative-value
|
||||
append-or-not
|
||||
tags-path-value))
|
||||
(command (if sudo
|
||||
(concat "sudo sh -c '"
|
||||
ctags-cmd
|
||||
"'")
|
||||
ctags-cmd)))
|
||||
|
||||
|
||||
(if (get-process command-process-name)
|
||||
(message "Process (%s) already running..." command-process-name)
|
||||
(progn
|
||||
(start-process-shell-command command-process-name
|
||||
(format "*%s*" command-process-name)
|
||||
command)
|
||||
(message "Creating tags...")
|
||||
|
||||
(when append-t-or-not
|
||||
(my-insert-newline-at-end-of-file
|
||||
(concat tags-path-value ".commands")))
|
||||
|
||||
(my-write-to-file
|
||||
(format-time-string "%Y-%m-%d %H:%M:%S")
|
||||
(concat tags-path-value ".commands")
|
||||
append-t-or-not
|
||||
sudo)
|
||||
|
||||
(my-insert-newline-at-end-of-file
|
||||
(concat tags-path-value ".commands"))
|
||||
|
||||
(my-write-to-file
|
||||
(concat append-or-create command)
|
||||
(concat tags-path-value ".commands")
|
||||
t
|
||||
sudo)
|
||||
|
||||
(my-insert-newline-at-end-of-file
|
||||
(concat tags-path-value ".commands"))
|
||||
|
||||
(my-write-to-file
|
||||
(concat append-or-create
|
||||
(format "(my/create-tags \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" %s \"%s\")"
|
||||
target-dir-value
|
||||
tags-format
|
||||
tag-relative
|
||||
tags-filename
|
||||
tags-path-value
|
||||
append
|
||||
sudo
|
||||
process-name
|
||||
))
|
||||
(concat tags-path-value ".commands")
|
||||
t
|
||||
sudo)
|
||||
|
||||
(my-insert-newline-at-end-of-file
|
||||
(concat tags-path-value ".commands"))
|
||||
|
||||
(my-merge-duplicated-lines-in-file
|
||||
(concat tags-path-value ".commands")
|
||||
sudo)
|
||||
|
||||
|
||||
))
|
||||
))
|
||||
|
||||
(defvar my/default-tags-file-name "TAGS"
|
||||
"The default name of the tags file to search for.")
|
||||
|
||||
(defun my/find-tags-file (&optional ask tags-file-name)
|
||||
"Recursively search for a 'TAGS' file in parent directories and return its path.
|
||||
|
||||
Optional arguments:
|
||||
ASK (default nil): If t, prompt for entering a custom tags file
|
||||
name.
|
||||
TAGS-FILE-NAME (default 'TAGS'): The name of the tags file to
|
||||
search for.
|
||||
|
||||
This function searches for a 'TAGS' file by recursively examining
|
||||
parent directories starting from the directory of the currently
|
||||
visited file (if any). If a 'TAGS' file is found, its full path
|
||||
is returned. If no 'TAGS' file is found, or if the current buffer
|
||||
is not visiting a file, it returns nil.
|
||||
|
||||
Usage examples:
|
||||
(my/find-tags-file) ; Search for default 'TAGS' file in parent
|
||||
directories
|
||||
(my/find-tags-file t) ; Prompt for a custom tags file name
|
||||
(my/find-tags-file nil \"TAGS_ABS\") ; Search for a 'TAGS_ABS' file
|
||||
(my/find-tags-file nil \"TAGS\") ; Search for the default 'TAGS' file
|
||||
|
||||
Version: 2023-08-23"
|
||||
(progn
|
||||
(unless tags-file-name
|
||||
(setq tags-file-name my/default-tags-file-name))
|
||||
(defun find-tags-file-r (path tags-file prev-parent)
|
||||
"Find the tags file from the parent directories"
|
||||
(let* ((parent (file-name-directory path))
|
||||
(possible-tags-file (concat parent tags-file)))
|
||||
(message "Found tags file: %s" possible-tags-file)
|
||||
(cond
|
||||
((file-exists-p possible-tags-file)
|
||||
(throw 'found-it possible-tags-file)
|
||||
(message "Found tags file: %s" possible-tags-file))
|
||||
((equal parent prev-parent)
|
||||
(error "No tags file found")) ; stop if no progress is made
|
||||
(t (message "Checking %s" possible-tags-file)
|
||||
(find-tags-file-r (directory-file-name parent) tags-file parent)))))
|
||||
|
||||
(if (buffer-file-name)
|
||||
(catch 'found-it
|
||||
(if ask
|
||||
(let ((tags-file-name-input
|
||||
(read-from-minibuffer
|
||||
"Enter tags file name: " tags-file-name)))
|
||||
(find-tags-file-r (buffer-file-name) tags-file-name-input nil))
|
||||
(find-tags-file-r (buffer-file-name) tags-file-name nil)))
|
||||
(error "Buffer is not visiting a file"))))
|
||||
|
||||
(defun my/file ()
|
||||
"prompt user to enter a file name, with completion and history
|
||||
support."
|
||||
;; http://xahlee.info/emacs/emacs/elisp_idioms_prompting_input.html
|
||||
(interactive)
|
||||
(setq my-file-value (read-file-name "Input file name: "))
|
||||
(message "my-file-value is %s" my-file-value)
|
||||
)
|
||||
|
||||
;; { START: config for counsel-etags and company-ctags
|
||||
;; <<config-ce-cc>>
|
||||
(defun my-tags-file (&optional select tags-file)
|
||||
"If SELECT is non-nil, set the value of `my-tags-file` to
|
||||
TAG-FILE. If TAGS-FILE is nil, use the user-selected file path
|
||||
after prompting for it through `my/file`.
|
||||
Otherwise, set `my-tags-file` to the value returned by
|
||||
`my/find-tags-file`. -- generated by ChatGPT :)
|
||||
|
||||
Updated: 2023-08-20"
|
||||
(if select
|
||||
(if tags-file
|
||||
(setq my-tags-file tags-file)
|
||||
(progn (my/file)
|
||||
(setq my-tags-file my-file-value)))
|
||||
(setq my-tags-file (my/find-tags-file t)))
|
||||
)
|
||||
|
||||
(defun my-set-extra-tags-files (my-tags-table-list)
|
||||
(setq counsel-etags-extra-tags-files my-tags-table-list)
|
||||
(setq company-ctags-extra-tags-files my-tags-table-list)
|
||||
(message "tags-table list for counsel-etags/company-ctags:\n%s\n\nNote:
|
||||
files in counsel-etags-extra-tags-files should have symbols with
|
||||
absolute path only." my-tags-table-list)
|
||||
)
|
||||
|
||||
(defun my/insert-into-my-tags-table-list(&optional select tags-file)
|
||||
"automatically insert the TAGS file or select TAGS file to
|
||||
insert(C-u), into `my-tags-table-list',
|
||||
`counsel-etags-extra-tags-files' and
|
||||
`company-ctags-extra-tags-files'.
|
||||
|
||||
Updated: 2023-08-20"
|
||||
(interactive "P")
|
||||
(unless (boundp 'my-tags-table-list)
|
||||
;; if `my-tags-table-list' is void, then set it to empty list
|
||||
(setq my-tags-table-list '()))
|
||||
(setq existing-my-tags-table-list my-tags-table-list)
|
||||
(setq my-tags-table-list '()) ; initiate empty list
|
||||
(my-tags-file select tags-file)
|
||||
(setq my-tags-table-list
|
||||
(delq nil (delete-dups ; delete nil and duplicates
|
||||
(cons (symbol-value 'my-tags-file)
|
||||
(symbol-value 'existing-my-tags-table-list)))))
|
||||
(my-set-extra-tags-files my-tags-table-list)
|
||||
)
|
||||
|
||||
(defun my/delete-from-my-tags-table-list (&optional select tags-file)
|
||||
"automatically delete the TAGS file or select TAGS file to
|
||||
delete(C-u), from `my-tags-table-list',
|
||||
`counsel-etags-extra-tags-files' and
|
||||
`company-ctags-extra-tags-files'.
|
||||
|
||||
Updated: 2023-08-20"
|
||||
(interactive "P")
|
||||
(my-tags-file select tags-file)
|
||||
(setq my-tags-table-list
|
||||
(delete (symbol-value 'my-tags-file) my-tags-table-list))
|
||||
(my-set-extra-tags-files my-tags-table-list)
|
||||
)
|
||||
|
||||
;; keybinding -> [[./init-keybindings.el::m-ftf]]
|
||||
(defun my/set-tags-table-list (&optional del)
|
||||
"calls `my/find-tags-file' to recursively search up the directory
|
||||
tree to find a file named 'TAGS'. If found, add/delete(C-u) it
|
||||
to/from 'counsel-etags-extra-tags-files' and
|
||||
'company-ctags-extra-tags-files'."
|
||||
(interactive "P")
|
||||
(if del (my/delete-from-my-tags-table-list)
|
||||
(my/insert-into-my-tags-table-list))
|
||||
)
|
||||
|
||||
(defun my/tags-table-list ()
|
||||
"check and display my tags-table list through message."
|
||||
(interactive)
|
||||
(message "tags-table list for counsel-etags/company-ctags:\n%s\n\nNote:
|
||||
files in counsel-etags-extra-tags-files should have symbols with
|
||||
absolute path only." my-tags-table-list)
|
||||
)
|
||||
;; END: config for counsel-etags and company-ctags }
|
||||
|
||||
(defun my/sync-tags-table-list ()
|
||||
"sync `tags-table-list' with `my-tags-table-list'.
|
||||
|
||||
Read more,
|
||||
https://www.gnu.org/software/emacs/manual/html_node/emacs/Select-Tags-Table.html
|
||||
|
||||
Some commands for checking the values:
|
||||
(symbol-value 'tags-table-list)
|
||||
(symbol-value 'tags-file-name)
|
||||
|
||||
Version: 2023-08-30"
|
||||
(interactive)
|
||||
(setq tags-table-list my-tags-table-list)
|
||||
(message "tags-table-list is set to %s" tags-table-list))
|
||||
|
||||
|
||||
|
||||
(provide 'init-tags)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-tags.el ends here
|
|
@ -0,0 +1,271 @@
|
|||
;;; init-timer-utils.el --- timer utils -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
|
||||
(defun my-cancel-existing-timer (timer-function)
|
||||
"Cancel an existing active timer with the given TIMER-FUNCTION.
|
||||
|
||||
Version: 2023-08-15"
|
||||
(dolist (timer timer-list)
|
||||
(when (equal (timer--function timer) timer-function)
|
||||
(cancel-timer timer)
|
||||
(setq timer-list (delq timer timer-list)))))
|
||||
|
||||
|
||||
(defun my-calculate-tomorrow-date ()
|
||||
"Calculate tomorrow's date and return it as a list of (day month year).
|
||||
|
||||
Version: 2023-09-05"
|
||||
(interactive)
|
||||
(let* ((current-time (decode-time (current-time)))
|
||||
(current-day (nth 3 current-time))
|
||||
(current-month (nth 4 current-time))
|
||||
(current-year (nth 5 current-time))
|
||||
(tomorrow-day (1+ current-day))
|
||||
(tomorrow-month (if (> tomorrow-day
|
||||
(calendar-last-day-of-month current-month current-year))
|
||||
(1+ current-month)
|
||||
current-month))
|
||||
(tomorrow-year (if (> tomorrow-month 12)
|
||||
(1+ current-year)
|
||||
current-year)))
|
||||
(setq tomorrow-day (if (> tomorrow-day
|
||||
(calendar-last-day-of-month current-month current-year))
|
||||
1
|
||||
tomorrow-day))
|
||||
;; (message "Tomorrow's date is: %d-%02d-%02d" tomorrow-year tomorrow-month tomorrow-day)
|
||||
(list tomorrow-day tomorrow-month tomorrow-year)))
|
||||
|
||||
(defun my-current-time-in-minutes ()
|
||||
"Return the current time in minutes since midnight.
|
||||
|
||||
Version: 2023-09-05"
|
||||
(let* ((current-time (decode-time (current-time)))
|
||||
(current-hour (nth 2 current-time))
|
||||
(current-minute (nth 1 current-time))
|
||||
(current-time-in-minutes (+ (* current-hour 60) current-minute)))
|
||||
current-time-in-minutes))
|
||||
|
||||
|
||||
|
||||
(defun my-schedule-task-every-day (hour minute task-function)
|
||||
"Schedule a task to run every day at a specific time.
|
||||
|
||||
This function schedules the given task function to run at the specified hour
|
||||
and minute every day.
|
||||
|
||||
Args:
|
||||
hour (integer): The hour of the day (0 to 23) at which the task should run.
|
||||
minute (integer): The minute of the hour (0 to 59) at which the task should run.
|
||||
task-function (function): The function to be executed when the scheduled time is reached.
|
||||
|
||||
Returns:
|
||||
None: The task is scheduled to run using the 'run-at-time' function.
|
||||
|
||||
Note:
|
||||
The 'run-at-time' function is used to schedule the task, and it may not guarantee
|
||||
exact timing due to various factors such as system load and other scheduled tasks.
|
||||
|
||||
Example:
|
||||
(my-schedule-task-every-day 15 30 'my-task-function)
|
||||
This will schedule 'my-task-function' to run every day at 3:30 PM.
|
||||
|
||||
Version: 2023-08-15"
|
||||
(my-cancel-existing-timer task-function)
|
||||
|
||||
(let* ((current-time-in-minutes (my-current-time-in-minutes))
|
||||
(scheduled-time-in-minutes (+ (* hour 60) minute)))
|
||||
(if (<= scheduled-time-in-minutes current-time-in-minutes)
|
||||
;; If the scheduled time is before or equal to the current time, set schedule for tomorrow
|
||||
(let* ((tomorrow-date (my-calculate-tomorrow-date))
|
||||
(tomorrow-time
|
||||
(encode-time 0 minute hour
|
||||
(car tomorrow-date)
|
||||
(cadr tomorrow-date)
|
||||
(caddr tomorrow-date))))
|
||||
(run-at-time tomorrow-time
|
||||
(* 60 60 24)
|
||||
task-function))
|
||||
(setq task-function-timer
|
||||
(run-at-time (format "%02d:%02d" hour minute)
|
||||
(* 60 60 24)
|
||||
task-function)))))
|
||||
|
||||
|
||||
|
||||
|
||||
(defun my-schedule-task-every-x-secs (seconds task-function)
|
||||
"Schedule a task to run every x seconds.
|
||||
(my-cancel-existing-timer task-function)
|
||||
|
||||
Version: 2023-08-28"
|
||||
(my-cancel-existing-timer task-function)
|
||||
|
||||
(setq task-function-timer
|
||||
(run-at-time 0 ; the task should start immediately
|
||||
seconds
|
||||
task-function)))
|
||||
|
||||
|
||||
(defun my-schedule-task-every-x-mins (minutes task-function)
|
||||
"Schedule a task to run every x mins.
|
||||
|
||||
Version: 2023-08-19"
|
||||
(my-cancel-existing-timer task-function)
|
||||
|
||||
(setq task-function-timer
|
||||
(run-at-time 0 ; the task should start immediately
|
||||
(* minutes 60)
|
||||
task-function)))
|
||||
|
||||
|
||||
|
||||
|
||||
(defun my-schedule-task-on-day-of-week (day-of-week hour minute task-function)
|
||||
"Schedule a task to run at a specific time on a particular day of the week.
|
||||
|
||||
This function calculates the next occurrence of the specified day of the week
|
||||
and schedules the given task function to run at the specified hour and minute
|
||||
of that day.
|
||||
|
||||
Args:
|
||||
day-of-week (integer): The desired day of the week (0 = Sunday, 1 = Monday, ..., 6 = Saturday).
|
||||
hour (integer): The hour of the day (0 to 23) at which the task should run.
|
||||
minute (integer): The minute of the hour (0 to 59) at which the task should run.
|
||||
task-function (function): The function to be executed when the scheduled time is reached.
|
||||
|
||||
Returns:
|
||||
None: The task is scheduled to run using the 'run-at-time' function.
|
||||
|
||||
Note:
|
||||
This function calculates the next occurrence of the specified day of the week
|
||||
based on the current date and time and then schedules the task to run on that
|
||||
calculated date and time.
|
||||
|
||||
It is important to note that the 'run-at-time' function is used to schedule the task,
|
||||
and it may not guarantee exact timing due to various factors such as system load
|
||||
and other scheduled tasks.
|
||||
|
||||
Example:
|
||||
(my-schedule-task-on-day-of-week 3 15 30 'my-task-function)
|
||||
This will schedule 'my-task-function' to run every Wednesday at 3:30 PM.
|
||||
|
||||
Version 2023-08-15"
|
||||
(let* ((current-time (current-time))
|
||||
(decoded-time (decode-time current-time))
|
||||
(current-day-of-week (nth 6 decoded-time))
|
||||
(days-until-desired-day (mod (+ day-of-week (- current-day-of-week)) 7))
|
||||
(desired-date (decode-time
|
||||
(time-add current-time (seconds-to-time
|
||||
(* days-until-desired-day 24 60 60)))))
|
||||
(desired-day (nth 3 desired-date))
|
||||
(desired-month (nth 4 desired-date))
|
||||
(desired-year (nth 5 desired-date)))
|
||||
|
||||
(my-cancel-existing-timer task-function)
|
||||
|
||||
(run-at-time
|
||||
(encode-time 0 minute hour desired-day desired-month desired-year)
|
||||
(* 7 24 60 60)
|
||||
task-function)))
|
||||
|
||||
|
||||
|
||||
|
||||
(defun my-schedule-task-in-x-mins (minutes task-function)
|
||||
"Schedule a task to run in x mins.
|
||||
|
||||
Version: 2023-08-19"
|
||||
(my-cancel-existing-timer task-function)
|
||||
|
||||
(setq task-function-timer
|
||||
(run-at-time (format "%s min" minutes)
|
||||
nil
|
||||
task-function)))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(defun my-schedule-task-at-specific-min-between-hour
|
||||
(start-hour end-hour specific-minutes base-task-function)
|
||||
"Schedule tasks to run at specific minutes between two hours.
|
||||
|
||||
This function schedules tasks with different specific minutes between the given
|
||||
start and end hours. The task names will have the format 'base-task-function_hour-min'.
|
||||
|
||||
Args:
|
||||
start-hour (integer): The starting hour (0 to 23) of the time range.
|
||||
end-hour (integer): The ending hour (0 to 23) of the time range.
|
||||
specific-minutes (list): A list of specific minutes (0 to 59) at which the tasks should run.
|
||||
base-task-function (function): The base function to be executed when the tasks are scheduled.
|
||||
|
||||
Returns:
|
||||
None: The tasks are scheduled to run using the 'run-at-time' function.
|
||||
|
||||
Note:
|
||||
The 'run-at-time' function is used to schedule the tasks, and it may not guarantee
|
||||
exact timing due to various factors such as system load and other scheduled tasks.
|
||||
|
||||
Example:
|
||||
(my-schedule-task-at-specific-min-between-hour 10 17 '(0 15 30 45) 'my-task-function)
|
||||
This will schedule tasks to execute 'my-task-function' at minutes 0, 15, 30, and 45
|
||||
between 10:00 AM and 5:00 PM.
|
||||
|
||||
Version: 2023-09-04
|
||||
Updated: 2023-09-05"
|
||||
;; Define the task name format
|
||||
(let ((task-name-format
|
||||
(format "%s_%%02d-%%02d" (symbol-name base-task-function))))
|
||||
;; Get the current time in minutes since midnight
|
||||
(let* ((current-time-in-minutes (my-current-time-in-minutes)))
|
||||
;; Loop through hours and minutes to schedule tasks
|
||||
(dotimes (hour-counter (- end-hour start-hour))
|
||||
(let ((current-hour (+ start-hour hour-counter)))
|
||||
(dolist (minute specific-minutes)
|
||||
;; Calculate the scheduled task time in minutes since midnight
|
||||
(let ((scheduled-time-in-minutes (+ (* current-hour 60) minute)))
|
||||
;; Construct the full task name with hour and minute
|
||||
(let ((task-name (format task-name-format current-hour minute)))
|
||||
;; Cancel existing timer with the same task name
|
||||
(my-cancel-existing-timer (intern task-name))
|
||||
;; Check if the scheduled time is ahead of the current time
|
||||
;; Define the new task function using defalias
|
||||
(defalias (intern task-name)
|
||||
`(lambda ()
|
||||
,(format "Scheduled task: %s" (symbol-name base-task-function))
|
||||
(funcall ',base-task-function)))
|
||||
(if (<= scheduled-time-in-minutes current-time-in-minutes)
|
||||
;; If the scheduled time is before or equal to the current time, set schedule for tomorrow
|
||||
(let* ((tomorrow-date (my-calculate-tomorrow-date))
|
||||
(tomorrow-time
|
||||
(encode-time 0 minute current-hour
|
||||
(car tomorrow-date)
|
||||
(cadr tomorrow-date)
|
||||
(caddr tomorrow-date))))
|
||||
(run-at-time tomorrow-time
|
||||
(* 60 60 24)
|
||||
(intern task-name)))
|
||||
;; Schedule the new task
|
||||
(run-at-time (format "%02d:%02d" current-hour minute)
|
||||
(* 60 60 24)
|
||||
(intern task-name)))))))))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(provide 'init-timer-utils)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-timer-utils.el ends here
|
|
@ -3,6 +3,25 @@
|
|||
;;; Code:
|
||||
|
||||
|
||||
;; the my/xxx utils config - yet to be placed in dedicated init-xxx.el files
|
||||
|
||||
|
||||
|
||||
(defun my/highlight-selected-text (start end &optional color)
|
||||
"Highlight the selected region temporarily with the specified color.
|
||||
If color is not provided, the default color is #5F87FF.
|
||||
|
||||
Version 2023-10-18"
|
||||
(interactive "r\nsEnter color (e.g., 'red', press ENTER for default #5F87FF): ")
|
||||
(let* ((overlay (make-overlay start end))
|
||||
(color (if (string= color "") "#5F87FF" color))
|
||||
(text-color (if (or (string= color "black") (string= color "#5F87FF"))
|
||||
"white"
|
||||
"black")))
|
||||
(overlay-put overlay 'face `((:background ,color :foreground ,text-color)))
|
||||
(add-hook 'before-revert-hook (lambda () (delete-overlay overlay)))))
|
||||
|
||||
|
||||
(defun my/random-org-item ()
|
||||
"Go to a random org heading from all org files in `org-directory`."
|
||||
(interactive)
|
||||
|
@ -102,7 +121,7 @@
|
|||
"insert a `SRC-CODE-TYPE' type source code block in org-mode."
|
||||
(interactive
|
||||
(let ((src-code-types
|
||||
'("emacs-lisp" "python" "C" "sh" "java" "js" "clojure" "C++" "css"
|
||||
'("emacs-lisp" "python" "C" "shell" "java" "js" "clojure" "C++" "css"
|
||||
"calc" "asymptote" "dot" "gnuplot" "ledger" "lilypond" "mscgen"
|
||||
"octave" "oz" "plantuml" "R" "sass" "screen" "sql" "awk" "ditaa"
|
||||
"haskell" "latex" "lisp" "matlab" "ocaml" "org" "perl" "ruby"
|
||||
|
@ -131,6 +150,409 @@
|
|||
(while (search-forward "\r" nil t) (replace-match "")))
|
||||
|
||||
|
||||
(defun my/generate-current-time-string (&optional universal-arg silent)
|
||||
"Generate a string representing the current date and time in specific format.
|
||||
(e.g., 230725192607 for July 25th, 2023 at 19:26:07).
|
||||
|
||||
When UNIVERSAL-ARG (C-u) is provided, copy the time string to the kill ring.
|
||||
|
||||
Usage:
|
||||
M-x my/generate-current-time-string
|
||||
C-u M-x my/generate-current-time-string
|
||||
|
||||
Version 2023-07-25"
|
||||
(interactive "P")
|
||||
(let* ((now (current-time))
|
||||
(time-string (concat (substring (format-time-string "%Y" now) -2)
|
||||
(format-time-string "%m%d%H%M%S" now))))
|
||||
(unless silent
|
||||
(insert time-string))
|
||||
(when universal-arg
|
||||
(kill-new time-string)
|
||||
(message "%s is copied." time-string))
|
||||
(message "Current time string generated: %s" time-string)
|
||||
time-string))
|
||||
|
||||
|
||||
(defun my/review-random-function ()
|
||||
"Review a random function defined in my Emacs configuration."
|
||||
(interactive)
|
||||
(let* ((config-functions '())
|
||||
(config-files (directory-files-recursively user-emacs-directory "\\.el$")))
|
||||
(dolist (file config-files)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "(defun \\([^ ]+\\)" nil t)
|
||||
(push (match-string 1) config-functions))))
|
||||
(let* ((command (nth (random (length config-functions)) config-functions)))
|
||||
(describe-function (intern command)))))
|
||||
|
||||
(defun my/review-random-my-function ()
|
||||
"Review a random function that starts with 'my/' in my Emacs configuration."
|
||||
(interactive)
|
||||
(let* ((config-functions '())
|
||||
(config-files (directory-files-recursively user-emacs-directory "\\.el$")))
|
||||
(dolist (file config-files)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "(defun my/\\([^ ]+\\)" nil t)
|
||||
(push (match-string 1) config-functions))))
|
||||
(let* ((command (nth (random (length config-functions)) config-functions)))
|
||||
(describe-function (intern (concat "my/" command))))))
|
||||
|
||||
|
||||
|
||||
;; {{ START: my/open-link-at-point-as-gpg
|
||||
(defun my/securely-delete-file (&optional filename)
|
||||
"Securely delete the specified file interactively or by providing FILENAME.
|
||||
If secure deletion failed, then continue with the normal deletion."
|
||||
(interactive (list (when current-prefix-arg
|
||||
(read-file-name "Choose file to securely delete: "))))
|
||||
(if filename
|
||||
(progn
|
||||
(message "Securely deleting %s..." (shell-quote-argument filename))
|
||||
(cond
|
||||
((eq system-type 'windows-nt)
|
||||
;; https://learn.microsoft.com/en-us/sysinternals/downloads/sdelete
|
||||
(my-check-for-executable "SDelete" "sdelete")
|
||||
(shell-command (concat "sdelete -p 3 " (shell-quote-argument filename))))
|
||||
((eq system-type 'gnu/linux)
|
||||
(my-check-for-executable "shred" "shred")
|
||||
(shell-command (concat "shred -v -z -u -n 10 " (shell-quote-argument filename))))
|
||||
((eq system-type 'darwin)
|
||||
(my-check-for-executable "shred" "gshred")
|
||||
(shell-command (concat "gshred -v -z -u -n 10 " (shell-quote-argument filename)))))
|
||||
(when (file-exists-p (shell-quote-argument filename))
|
||||
(message "Securely deleting %s failed, and continue with the normal deletion." (shell-quote-argument filename))
|
||||
(delete-file filename)))
|
||||
(user-error "No file specified for secure deletion.")))
|
||||
|
||||
(defun my/open-link-at-point-as-gpg ()
|
||||
"Open the link at point using Emacs epa in a temporary buffer,
|
||||
and the decrypted file will be securely deleted after opening in buffer."
|
||||
(interactive)
|
||||
(require 'epa)
|
||||
(let* ((link-info (org-element-context))
|
||||
(path (org-element-property :path link-info))
|
||||
(abs-path (if (string-prefix-p "file:" path)
|
||||
(file-truename (replace-regexp-in-string ":" "" path))
|
||||
(file-truename path)))
|
||||
(decrypted-file (concat abs-path ".clear")))
|
||||
(if (file-exists-p abs-path)
|
||||
(progn
|
||||
(epa-decrypt-file abs-path decrypted-file)
|
||||
(find-file decrypted-file)
|
||||
(when (file-exists-p decrypted-file)
|
||||
(my/securely-delete-file decrypted-file)))
|
||||
(message "File does not exist: %s" abs-path))))
|
||||
;; END: my/open-link-at-point-as-gpg }}
|
||||
|
||||
|
||||
;; {{ START: my/check-orphaned-org-ids-in-directory
|
||||
(defun my-org-id-link-pre ()
|
||||
"The precondition config to my org id link settings"
|
||||
(my-require 'org-element) ; this should be here before `org-add-link-type'
|
||||
(my-require 'cl-lib)
|
||||
|
||||
;; From ChatGPT,
|
||||
;; The message "Created id link." is printed by the `org-add-link-type` function
|
||||
;; each time it is called.
|
||||
;; Since you have the line `(org-add-link-type "id" #'my-org-id-link-follow)` in
|
||||
;; your code, this function is called every time you load or reload your Emacs
|
||||
;; configuration. It registers a new link type called `"id"` that is handled by
|
||||
;; the `my-org-id-link-follow` function.
|
||||
|
||||
;; register new link type called "id"
|
||||
(org-add-link-type "id" #'my-org-id-link-follow))
|
||||
|
||||
(defun my-org-id-link-follow (id)
|
||||
"Follow an `id' link."
|
||||
(message "Link ID: %s" id))
|
||||
|
||||
(defun my-org-id-links-in-buffer ()
|
||||
"Return a list of Org ID links in the current buffer."
|
||||
(my-org-id-link-pre)
|
||||
(let (org-id-links) ; creates a local variable called `org-id-links` with an
|
||||
; initial value of `nil` that is only visible within the
|
||||
; `let` block
|
||||
(org-element-map (org-element-parse-buffer) 'link
|
||||
(lambda (link)
|
||||
(when (string= (org-element-property :type link) "id")
|
||||
(push (org-element-property :path link) org-id-links)
|
||||
)))
|
||||
org-id-links))
|
||||
|
||||
(defun my-list-org-id-links-in-directory (directory)
|
||||
"Search all .org files in DIRECTORY for Org ID links, and return a list of unique IDs found."
|
||||
(interactive "DDirectory: ")
|
||||
(let (org-ids)
|
||||
(dolist (file (directory-files-recursively directory "\\.org$") org-ids)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(setq org-ids (append org-ids (my-org-id-links-in-buffer)))
|
||||
))
|
||||
(delete-dups org-ids)
|
||||
))
|
||||
|
||||
(defun my-list-org-ids-in-directory (directory)
|
||||
"List all org-ids in org-files in the given DIRECTORY and return them as a list."
|
||||
(interactive "DDirectory: ")
|
||||
(my-org-id-link-pre)
|
||||
(let ((org-files (directory-files-recursively directory "\\.org$"))
|
||||
(org-ids '()))
|
||||
(dolist (file org-files)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(org-mode)
|
||||
(org-element-map (org-element-parse-buffer) 'headline
|
||||
(lambda (headline)
|
||||
(when-let ((id (org-element-property :ID headline)))
|
||||
(push id org-ids))))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^:ID:\\s-+\\(\\S-+\\)" nil t)
|
||||
(push (match-string 1) org-ids))))
|
||||
org-ids))
|
||||
|
||||
(defun my/check-orphaned-org-ids-in-directory (dir)
|
||||
"Find the difference between org-ids obtained by `my-list-org-ids-in-directory'
|
||||
and org-ids obtained by `my-list-org-id-links-in-directory'.
|
||||
DIRECTORY is the directory where the org files are located."
|
||||
(interactive "DDirectory: ")
|
||||
(let ((org-ids (my-list-org-ids-in-directory dir))
|
||||
(id-links (my-list-org-id-links-in-directory dir)))
|
||||
(let ((not-linked (cl-set-difference org-ids id-links :test #'string=))
|
||||
(invalid-links (cl-set-difference id-links org-ids :test #'string=)))
|
||||
(message "%d not-linked org-ids: %s"
|
||||
(length not-linked)
|
||||
(format "%s" not-linked))
|
||||
(message "%d invalid org-id links: %s"
|
||||
(length invalid-links)
|
||||
(format "%s" invalid-links)))))
|
||||
;; END: my/check-orphaned-org-ids-in-directory }}
|
||||
|
||||
|
||||
(defun my/org-list-entries-without-id-property ()
|
||||
"List all entries in the current buffer that don't have an ID property."
|
||||
(interactive)
|
||||
(with-output-to-temp-buffer "*Org Entries Without ID*"
|
||||
(let ((results nil))
|
||||
(org-map-entries
|
||||
(lambda ()
|
||||
(unless (org-id-get)
|
||||
(push (format "** LINE #%d:\n%s"
|
||||
(line-number-at-pos)
|
||||
(buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position)))
|
||||
results)))
|
||||
nil nil t)
|
||||
(princ (concat "#+TITLE: Org Entries Without ID\n\n"))
|
||||
(princ (concat "#+OPTIONS: toc:nil\n\n"))
|
||||
(princ (concat "* Entries without ID\n\n"))
|
||||
(dolist (result (nreverse results))
|
||||
(princ (concat result "\n\n")))))
|
||||
(with-current-buffer "*Org Entries Without ID*"
|
||||
(org-mode)))
|
||||
|
||||
|
||||
(defun my/list-packages-and-versions ()
|
||||
(interactive)
|
||||
(package-initialize)
|
||||
(let ((pkgs (mapcar 'car package-alist)))
|
||||
(dolist (pkg pkgs)
|
||||
(message "%s - %s"
|
||||
pkg (package-desc-version (cadr (assq pkg package-alist)))))))
|
||||
|
||||
|
||||
(defun my/copy-org-id-at-point ()
|
||||
"Copy the ID property of the heading at point to the kill-ring."
|
||||
(interactive)
|
||||
(let ((id (org-entry-get nil "ID")))
|
||||
(when id
|
||||
(kill-new id)
|
||||
(message "Copied ID: %s" id))))
|
||||
|
||||
(defun my-get-heading-from-org-id-db (org-id)
|
||||
"Retrieve the heading title associated with an Org ID from the
|
||||
current buffer's Org mode database."
|
||||
(org-with-point-at (org-id-find org-id 'marker)
|
||||
(org-get-heading)))
|
||||
|
||||
(defun my/insert-org-id-from-kill-ring ()
|
||||
"Insert a link to an Org ID from the kill-ring with a user-defined description.
|
||||
The user is prompted to enter a description for the link.
|
||||
|
||||
If description is empty, retrieve the heading from the org-id
|
||||
database using `my-get-heading-from-org-id-db` function."
|
||||
(interactive)
|
||||
(let ((id (current-kill 0)))
|
||||
(when id
|
||||
(let* ((org-id (replace-regexp-in-string "^id:" "" id))
|
||||
(description (read-string "Description: " nil 'my-history)))
|
||||
(if (string-empty-p description)
|
||||
(setq description (my-get-heading-from-org-id-db org-id)))
|
||||
(org-insert-link nil (concat "id:" org-id) description)))))
|
||||
|
||||
|
||||
(defun my/link-selected-text-with-org-id-from-kill-ring ()
|
||||
"Create an Org-mode link using the selected text and an Org ID from the kill ring.
|
||||
Version 2023-04-28
|
||||
|
||||
The selected text is replaced with,
|
||||
[[id:<Org ID unique identifier>][<selected text>]].
|
||||
|
||||
Usage: Select the text that you want to link to an Org ID, then
|
||||
run `M-x my/link-selected-text-with-org-id-from-kill-ring`. The
|
||||
function will take the Org ID from the kill ring, and create an
|
||||
Org-mode link with the selected text and the Org ID. The link
|
||||
will be inserted at the cursor position, replacing the selected
|
||||
text."
|
||||
(interactive)
|
||||
(let* ((org-id (substring-no-properties (current-kill 0)))
|
||||
(text (buffer-substring-no-properties (region-beginning) (region-end)))
|
||||
(link (concat "[[id:" org-id "][" text "]]")))
|
||||
(delete-region (region-beginning) (region-end))
|
||||
(insert link)))
|
||||
|
||||
|
||||
(defun my-parse-link-id (link)
|
||||
"Parse the ID from an org-mode link of the form `id:xxxxxxxxxxxx'."
|
||||
(when (string-match "id:\\(.+\\)" link)
|
||||
(match-string 1 link)))
|
||||
|
||||
(defun my/org-link-goto-at-point ()
|
||||
"Check if link at point is a file link or an ID link, and jump to
|
||||
the appropriate location."
|
||||
(interactive)
|
||||
(if-let ((link (org-element-property :raw-link (org-element-context))))
|
||||
(cond ((string-prefix-p "file:" link)
|
||||
(org-open-at-point))
|
||||
((string-prefix-p "id:" link)
|
||||
(org-id-goto (my-parse-link-id link))))
|
||||
(message "No link at point.")))
|
||||
|
||||
|
||||
(defun my/switch-opened-org-files-to-org-mode ()
|
||||
"Switch all open buffers that end with .org to org-mode,
|
||||
skipping buffers that are already in org-mode.
|
||||
Version 2023-05-06"
|
||||
;; See, https://stackoverflow.com/a/76187210/4274775
|
||||
(interactive)
|
||||
(dolist (buffer (buffer-list))
|
||||
(with-current-buffer buffer
|
||||
(when (and (buffer-file-name)
|
||||
(string= (file-name-extension (buffer-file-name)) "org")
|
||||
(not (eq major-mode 'org-mode)))
|
||||
(org-mode)
|
||||
(message "Switched %s to org-mode." (buffer-name))))))
|
||||
|
||||
|
||||
(defun my/strikethrough-current-line ()
|
||||
"Strikethrough the current line using +<striked text>+"
|
||||
(interactive)
|
||||
(back-to-indentation)
|
||||
(insert "+")
|
||||
(move-end-of-line nil)
|
||||
;; skips over any consecutive space or tab characters immediately before the
|
||||
;; end of the line, effectively moving the cursor to the last non-blank
|
||||
;; character on the line, rather than after any trailing whitespace. see,
|
||||
(skip-chars-backward " \t")
|
||||
(insert "+"))
|
||||
|
||||
|
||||
(defun my/readonly-files ()
|
||||
"Check for a '.readonly' file in the directory of the current
|
||||
buffer, and set the read-only status of any listed buffers. The
|
||||
'.readonly' file should contain a list of buffer names, one per
|
||||
line, that should be set to read-only. Any buffers not listed in
|
||||
the file will remain unaffected.
|
||||
Version 2023-05-04
|
||||
|
||||
This function is intended to be used as a hook to automatically
|
||||
set the read-only status of buffers when they are opened or
|
||||
saved, based on the contents of the '.readonly' file. To use this
|
||||
function as a hook, add it to the appropriate hook list, such as
|
||||
'find-file-hook', 'after-save-hook' or 'switch-buffer-hook'."
|
||||
;; (add-hook 'find-file-hook 'my/readonly-files)
|
||||
;; (add-hook 'after-save-hook 'my/readonly-files)
|
||||
;; (add-hook 'switch-buffer-hook 'my/readonly-files)
|
||||
(interactive)
|
||||
(let ((readonly-file (concat (file-name-directory (buffer-file-name)) ".readonly")))
|
||||
(when (file-exists-p readonly-file)
|
||||
(let ((readonly-bufs (split-string (with-temp-buffer
|
||||
(insert-file-contents readonly-file)
|
||||
(buffer-string))
|
||||
"\n" t)))
|
||||
(message "read-only files list: %s" readonly-bufs)
|
||||
(dolist (buf readonly-bufs)
|
||||
(message "%s is read-only now" buf)
|
||||
(let ((buf (find-buffer-visiting buf)))
|
||||
(when buf
|
||||
(with-current-buffer buf
|
||||
(toggle-read-only t)))))))))
|
||||
|
||||
|
||||
(defun my/revert-all-file-buffers ()
|
||||
"Refresh all open file buffers without confirmation.
|
||||
Buffers in modified (not yet saved) state in emacs will not be reverted. They
|
||||
will be reverted though if they were modified outside emacs.
|
||||
Buffers visiting files which do not exist any more or are no longer readable
|
||||
will be killed."
|
||||
;; via https://emacs.stackexchange.com/a/24461/29715
|
||||
(interactive)
|
||||
(dolist (buf (buffer-list))
|
||||
(let ((filename (buffer-file-name buf)))
|
||||
;; Revert only buffers containing files, which are not modified;
|
||||
;; do not try to revert non-file buffers like *Messages*.
|
||||
(when (and filename
|
||||
(not (buffer-modified-p buf)))
|
||||
(if (file-readable-p filename)
|
||||
;; If the file exists and is readable, revert the buffer.
|
||||
(with-current-buffer buf
|
||||
(revert-buffer :ignore-auto :noconfirm :preserve-modes))
|
||||
;; Otherwise, kill the buffer.
|
||||
(let (kill-buffer-query-functions) ; No query done when killing buffer
|
||||
(kill-buffer buf)
|
||||
(message "Killed non-existing/unreadable file buffer: %s" filename))))))
|
||||
(message "Finished reverting buffers containing unmodified files."))
|
||||
|
||||
|
||||
(defun my/copy-current-buffer-to-another-buffer (target-buffer)
|
||||
"Copy the content of the current buffer to another buffer.
|
||||
If the target buffer does not exist, it will be created.
|
||||
If the target buffer exists, the content will be appended.
|
||||
|
||||
Version: 2023-08-31"
|
||||
(interactive "BTarget Buffer: ")
|
||||
(let ((source-buffer (current-buffer))
|
||||
(existing-buffer (get-buffer-create target-buffer)))
|
||||
(with-current-buffer existing-buffer
|
||||
(goto-char (point-max)) ; move to the end of the existing buffer
|
||||
(insert-buffer-substring source-buffer)
|
||||
(pop-to-buffer existing-buffer))))
|
||||
|
||||
|
||||
(defun my/kill-buffers-by-pattern (pattern)
|
||||
"Kill buffers whose names match the specified pattern.
|
||||
|
||||
This function interactively prompts the user for a pattern and then searches
|
||||
through the list of all buffers. Buffers whose names match the given pattern
|
||||
are killed, effectively closing them. The pattern is a regular expression that
|
||||
is compared against buffer names using 'string-match-p'.
|
||||
|
||||
Version: 2023-08-16"
|
||||
(interactive "sEnter a pattern: ")
|
||||
(dolist (buffer (buffer-list))
|
||||
(let ((buffer-name (buffer-name buffer)))
|
||||
(message "Processing buffer: %s" buffer-name)
|
||||
(when (string-match-p pattern buffer-name)
|
||||
(kill-buffer buffer)
|
||||
(message "Killed buffer '%s'" buffer-name)))))
|
||||
|
||||
|
||||
|
||||
(provide 'init-utils)
|
||||
|
||||
;; Local Variables:
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
;;; init-uuid.el --- UUID settings -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
|
||||
|
||||
;; <2023-04-18 Tue 17:55> now below functions are verified to work, but the
|
||||
;; arbitrary position to other file is not working...
|
||||
|
||||
(defun my/quick-generate-uuid (&optional universal-arg)
|
||||
"Generate a UUID, insert at point, and copy to kill ring when UNIVERSAL-ARG (C-u) is provided.
|
||||
|
||||
Usage:
|
||||
M-x my/quick-generate-uuid
|
||||
C-u M-x my/quick-generate-uuid
|
||||
|
||||
Version 2023-04-18
|
||||
Updated 2023-07-25"
|
||||
(interactive "P")
|
||||
(let* ((time (current-time))
|
||||
(random-bytes (make-vector 16 0))
|
||||
(hash (secure-hash 'sha256 (concat (format "%s" time)
|
||||
(format "%s" random-bytes))))
|
||||
(uuid (format "%08x-%04x-%04x-%04x-%012x"
|
||||
(string-to-number (substring hash 0 8) 16)
|
||||
(string-to-number (substring hash 8 12) 16)
|
||||
(string-to-number (substring hash 12 16) 16)
|
||||
(logior (string-to-number (substring hash 16 20) 16) #b01000000)
|
||||
(string-to-number (substring hash 20 32) 16))))
|
||||
(insert uuid)
|
||||
(when universal-arg
|
||||
(kill-new uuid)
|
||||
(message "%s is copied." uuid))
|
||||
(message "UUID generated: %s" uuid)))
|
||||
|
||||
;; { -- START: for my/quick-insert-uuid-link
|
||||
;; From ChatGPT,
|
||||
|
||||
;; The concat function in (let ((uuid (concat "id:" (current-kill 0)))) adds the
|
||||
;; "id:" prefix only once, so there will be no situation where the link is
|
||||
;; duplicated.
|
||||
|
||||
;; In addition, the org-insert-link function also checks for existing "id:"
|
||||
;; prefixes, so it will not add another one if the UUID already starts with
|
||||
;; "id:".
|
||||
|
||||
;; Here's the relevant code from the org-insert-link function:
|
||||
|
||||
;; (cond ((string-match-p (rx bos "id:") link)
|
||||
;; (concat "[" desc "]" link))
|
||||
;; ((string-match-p (rx bos "attachment:") link)
|
||||
;; (concat "[" desc "]" link))
|
||||
;; (t
|
||||
;; (concat "[" desc "]" (org-make-link-string link type link))))
|
||||
|
||||
;; As you can see, if the link already starts with "id:", it simply concatenates
|
||||
;; the description and the link and returns it. Otherwise, it constructs a link
|
||||
;; string using org-make-link-string.
|
||||
|
||||
(defun my/quick-insert-uuid-link (&optional universal-arg)
|
||||
"Insert a link to the UUID on the kill ring.
|
||||
Prompt for a link name if UNIVERSAL-ARG is non-nil.
|
||||
|
||||
Usage:
|
||||
M-x my/quick-insert-uuid-link Insert anonymous link
|
||||
C-u M-x my/quick-insert-uuid-link Prompt for link name
|
||||
|
||||
Version 2023-04-18"
|
||||
(interactive "P")
|
||||
(let ((uuid (concat "id:" (current-kill 0))))
|
||||
(if universal-arg
|
||||
(org-insert-link "id" uuid (read-string "Link name: "))
|
||||
(org-insert-link "id" uuid nil))))
|
||||
|
||||
;; -- END: for my/quick-insert-uuid-link }
|
||||
|
||||
|
||||
(provide 'init-uuid)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-uuid.el ends here
|
|
@ -0,0 +1,203 @@
|
|||
;;; init-veracrypt.el --- VeraCrypt/TrueCrypt settings -*- lexical-binding: t -*-
|
||||
;;; Commentary:
|
||||
;;; Code:
|
||||
|
||||
|
||||
|
||||
|
||||
;; {{ START: VeraCrypt Volume mounting
|
||||
(require 'org-element)
|
||||
|
||||
(defun my-drive-letter-in-use-p (drive-letter)
|
||||
"Check if a Drive Letter is in use on Windows."
|
||||
(let ((result (shell-command-to-string
|
||||
(concat
|
||||
"powershell -command \"Test-Path '" drive-letter":' -IsValid\""))))
|
||||
(if (string-match-p "True" result)
|
||||
t
|
||||
nil)))
|
||||
|
||||
(defun my-find-available-drive-letter ()
|
||||
"Find an available Drive Letter on Windows."
|
||||
(catch 'drive-letter-found
|
||||
(mapc (lambda (code)
|
||||
(let ((drive-letter (format "%c" code)))
|
||||
(message "Checking Drive %s -> %s"
|
||||
drive-letter (my-drive-letter-in-use-p drive-letter))
|
||||
(unless (my-drive-letter-in-use-p drive-letter)
|
||||
(throw 'drive-letter-found drive-letter))))
|
||||
(number-sequence ?D ?Z)))) ; A, B, C are not available on TrueCrypt
|
||||
; and also they are reserved for floppy,
|
||||
; primary, secondary
|
||||
|
||||
(defun my/mount-veracrypt-volume (mode drive-option
|
||||
&optional volume-path drive-letter use-point-link truecrypt
|
||||
passphrase keyfiles)
|
||||
"Mount a VeraCrypt or TrueCrypt volume in the selected MODE and assign DRIVE-LETTER automatically.
|
||||
If USE-POINT-LINK is non-nil, use the volume link at point instead of prompting.
|
||||
|
||||
MODE can be 'read-only' or 'write'.
|
||||
DRIVE-OPTION can be 'auto' or 'specify'.
|
||||
VOLUME-PATH is the path to the VeraCrypt/TrueCrypt volume.
|
||||
DRIVE-LETTER is the desired drive letter (e.g., 'D').
|
||||
USE-POINT-LINK is non-nil to use the volume link at point.
|
||||
TRUECRYPT is non-nil to use TrueCrypt instead of VeraCrypt.
|
||||
PASSPHRASE is the passphrase for the volume.
|
||||
KEYFILES is a list of keyfiles for the volume.
|
||||
|
||||
Version 2023-08-07
|
||||
Updated 2023-08-15"
|
||||
|
||||
;; Usage:
|
||||
;; (my/mount-veracrypt-volume "read-only" "auto" "/path/to/volume")
|
||||
;; (my/mount-veracrypt-volume "read-only" "specify" "/path/to/volume" "D")
|
||||
;; (my/mount-veracrypt-volume "read-only" "auto" "/path/to/volume" nil nil t)
|
||||
;; (my/mount-veracrypt-volume "read-only" "auto" "/path/to/volume" nil nil t "test" '("/path/to/keyfile"))
|
||||
;; (my/mount-veracrypt-volume "read-only" "auto" "/path/to/volume" nil nil t "test" '("/path/to/keyfile1" "/path/to/keyfile2"))
|
||||
|
||||
(interactive
|
||||
(let ((universal-arg current-prefix-arg)
|
||||
(mode (completing-read "Select mode: " '("read-only" "write")))
|
||||
(drive-option
|
||||
(completing-read "Select Drive Letter assignment option: "
|
||||
'("auto" "specify"))))
|
||||
|
||||
(if (equal '(4) universal-arg) ; Check for universal argument
|
||||
(list mode drive-option
|
||||
nil ; volume-path
|
||||
;; drive-letter
|
||||
(if (equal drive-option "specify")
|
||||
(read-string "Specify the Drive Letter: ")
|
||||
nil)
|
||||
t ; pass t for use-point-link
|
||||
)
|
||||
(list mode drive-option
|
||||
;; volume-path
|
||||
(read-file-name "Enter the path to the VeraCrypt/TrueCrypt volume: ")
|
||||
;; drive-letter
|
||||
(if (equal drive-option "specify")
|
||||
(read-string "Specify the Drive Letter: ")
|
||||
nil)
|
||||
nil ; use-point-link
|
||||
))))
|
||||
|
||||
(if (or (executable-find "veracrypt") (executable-find "truecrypt"))
|
||||
(let* ((is-windows (eq system-type 'windows-nt))
|
||||
(is-macos (eq system-type 'darwin))
|
||||
(is-linux (eq system-type 'gnu/linux))
|
||||
|
||||
(mode-abbrev (if (equal mode "read-only") "ro"
|
||||
(if (equal mode "write") "ts"
|
||||
(error "Invalid mode"))))
|
||||
|
||||
(abs-volume-path (if use-point-link
|
||||
(let* ((link-info (org-element-context))
|
||||
(path (org-element-property :path link-info))
|
||||
(abs-path (if (string-prefix-p "file:" path)
|
||||
(file-truename
|
||||
(replace-regexp-in-string "^file:" "" path))
|
||||
(file-truename path))))
|
||||
(if (and abs-path (file-exists-p abs-path))
|
||||
abs-path
|
||||
(user-error "Invalid or non-existent link at point")))
|
||||
volume-path))
|
||||
|
||||
(final-drive-letter (if (equal drive-option "specify")
|
||||
(when is-windows
|
||||
(if (string-match-p "[D-Z]" drive-letter)
|
||||
drive-letter
|
||||
(user-error "Invalid Drive Letter format")))
|
||||
(if (equal drive-option "auto")
|
||||
(my-find-available-drive-letter)
|
||||
"auto")))
|
||||
|
||||
(vera-or-true (if truecrypt "truecrypt" "veracrypt")))
|
||||
|
||||
(if (and abs-volume-path (file-exists-p abs-volume-path))
|
||||
(let ((command (if is-windows
|
||||
(format "%s /q /m %s /v \"%s\" %s %s %s"
|
||||
vera-or-true
|
||||
mode-abbrev
|
||||
;; fix the path for Windows
|
||||
(subst-char-in-string ?/ ?\\ abs-volume-path)
|
||||
(if (not (equal final-drive-letter "auto"))
|
||||
(format "/a /l %s" final-drive-letter)
|
||||
(format "/a /l %s" (my-find-available-drive-letter)))
|
||||
(if passphrase (format "/p %s" passphrase) "")
|
||||
(if keyfiles
|
||||
(mapconcat (lambda (file)
|
||||
(format "/k \"%s\""
|
||||
;; fix the path for Windows
|
||||
(subst-char-in-string ?/ ?\\ file)))
|
||||
keyfiles
|
||||
" ")
|
||||
""))
|
||||
(if (or is-macos is-linux)
|
||||
(format "%s -q -m %s -v \"%s\" %s %s %s"
|
||||
vera-or-true
|
||||
mode-abbrev
|
||||
abs-volume-path
|
||||
(if (not (equal final-drive-letter "auto"))
|
||||
(format "-a -l %s" final-drive-letter)
|
||||
"-a")
|
||||
(if passphrase (format "-p %s" passphrase) "")
|
||||
(if keyfiles
|
||||
(mapconcat (lambda (file)
|
||||
(format "-k \"%s\"" keyfiles))
|
||||
keyfiles
|
||||
" ")
|
||||
""))
|
||||
(user-error "Unknown platform")))))
|
||||
(message command)
|
||||
(my-async-shell-command-with-unique-buffer-name command))
|
||||
(user-error "Volume does not exist")))
|
||||
(user-error "Neither VeraCrypt nor TrueCrypt is installed")))
|
||||
;; END: VeraCrypt Volume mounting }}
|
||||
|
||||
|
||||
|
||||
|
||||
(defun my/dismount-tc-vc-volume (volume-path &optional use-truecrypt)
|
||||
"Dismount a TrueCrypt or VeraCrypt volume at the given Drive Letter.
|
||||
If called with a universal argument (C-u), TrueCrypt will be used; otherwise, VeraCrypt will be used.
|
||||
|
||||
Version 2023-08-10"
|
||||
|
||||
;; Usage:
|
||||
;; (my/dismount-tc-vc-volume "A")
|
||||
;; (my/dismount-tc-vc-volume "K" t)
|
||||
|
||||
(interactive
|
||||
(let* ((use-truecrypt (if current-prefix-arg t nil))
|
||||
(prompt (if use-truecrypt "TrueCrypt" "VeraCrypt"))
|
||||
(volume-prompt (format "Enter the Drive Letter to the %s Volume to dismount: " prompt)))
|
||||
(list
|
||||
(read-string volume-prompt)
|
||||
use-truecrypt)))
|
||||
|
||||
(let* ((crypt-command (if use-truecrypt "truecrypt" "veracrypt"))
|
||||
(command ""))
|
||||
|
||||
(cond
|
||||
((eq system-type 'windows-nt)
|
||||
(setq command (format "%s /q /d %s" crypt-command volume-path)))
|
||||
((or (eq system-type 'darwin)
|
||||
(eq system-type 'gnu/linux))
|
||||
(setq command (format "%s -q -d %s" crypt-command volume-path)))
|
||||
(t
|
||||
(message "Unsupported system type")))
|
||||
|
||||
(my-async-shell-command-with-unique-buffer-name command)))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(provide 'init-veracrypt)
|
||||
|
||||
;; Local Variables:
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; init-veracrypt.el ends here
|
|
@ -1 +1 @@
|
|||
Subproject commit 77945e002f11440eae72d8730d3de218163d551e
|
||||
Subproject commit a6e856418d2ebd053b34e0ab2fda328abeba731c
|
|
@ -1,181 +0,0 @@
|
|||
;; doom-monokai-classic-theme.el --- inspired by Textmate's Monokai -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
(require 'doom-themes)
|
||||
|
||||
;;
|
||||
(defgroup doom-monokai-classic-theme nil
|
||||
"Options for doom-molokai."
|
||||
:group 'doom-themes)
|
||||
|
||||
(defcustom doom-monokai-classic-brighter-comments nil
|
||||
"If non-nil, comments will be highlighted in more vivid colors."
|
||||
:group 'doom-monokai-classic-theme
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom doom-monokai-classic-comment-bg doom-monokai-classic-brighter-comments
|
||||
"If non-nil, comments will have a subtle, darker background. Enhancing their
|
||||
legibility."
|
||||
:group 'doom-monokai-classic-theme
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom doom-monokai-classic-padded-modeline doom-themes-padded-modeline
|
||||
"If non-nil, adds a 4px padding to the mode-line. Can be an integer to
|
||||
determine the exact padding."
|
||||
:group 'doom-monokai-classic-theme
|
||||
:type '(choice integer boolean))
|
||||
|
||||
;;
|
||||
(def-doom-theme doom-monokai-classic
|
||||
"A dark, vibrant theme inspired by Textmate's Monokai."
|
||||
|
||||
;; name gui 256 16
|
||||
;; ((bg '("#272822" nil nil ))
|
||||
;; (bg-alt '("#1D1E19" nil nil ))
|
||||
((bg '("#1B1D1F" nil nil ))
|
||||
(bg-alt '("#283639" nil nil ))
|
||||
(base0 '("#1B2229" "black" "black" ))
|
||||
(base1 '("#161613" "#101010" "brightblack"))
|
||||
(base2 '("#1D1F20" "#191919" "brightblack"))
|
||||
(base3 '("#2D2E2E" "#252525" "brightblack"))
|
||||
(base4 '("#4E4E4E" "#454545" "brightblack"))
|
||||
(base5 '("#555556" "#6B6B6B" "brightblack"))
|
||||
(base6 '("#767679" "#7B7B7B" "brightblack"))
|
||||
(base7 '("#CFC0C5" "#C1C1C1" "brightblack"))
|
||||
(base8 '("#FFFFFF" "#FFFFFF" "brightwhite"))
|
||||
(fg '("#F8F8F2" "#DFDFDF" "brightwhite"))
|
||||
(fg-alt '("#556172" "#4D4D4D" "white"))
|
||||
|
||||
(grey '("#525254" "#525254" "brightblack"))
|
||||
(red '("#E74C3C" "#E74C3C" "red"))
|
||||
(orange '("#FD971F" "#FD971F" "brightred"))
|
||||
(green '("#A6E22E" "#A6E22E" "green"))
|
||||
(teal green)
|
||||
(yellow '("#E6DB74" "#E6DB74" "yellow"))
|
||||
(blue '("#268bd2" "#268bd2" "brightblue"))
|
||||
(dark-blue '("#727280" "#727280" "blue"))
|
||||
(magenta '("#F92660" "#F92660" "magenta"))
|
||||
(violet '("#9C91E4" "#9C91E4" "brightmagenta"))
|
||||
(cyan '("#66D9EF" "#66D9EF" "brightcyan"))
|
||||
(dark-cyan '("#8FA1B3" "#8FA1B3" "cyan"))
|
||||
|
||||
;; face categories
|
||||
(highlight orange)
|
||||
(vertical-bar (doom-lighten bg 0.1))
|
||||
(selection base5)
|
||||
(builtin orange)
|
||||
(comments (if doom-monokai-classic-brighter-comments violet base5))
|
||||
(doc-comments (if doom-monokai-classic-brighter-comments (doom-lighten violet 0.1) (doom-lighten base5 0.25)))
|
||||
(constants violet)
|
||||
(functions green)
|
||||
(keywords magenta)
|
||||
(methods green)
|
||||
(operators magenta)
|
||||
(type cyan)
|
||||
(strings yellow)
|
||||
(variables fg)
|
||||
(numbers violet)
|
||||
(region base4)
|
||||
(error red)
|
||||
(warning yellow)
|
||||
(success green)
|
||||
(vc-modified cyan)
|
||||
(vc-added (doom-darken green 0.15))
|
||||
(vc-deleted red)
|
||||
|
||||
;; custom categories
|
||||
(hidden `(,(car bg) "black" "black"))
|
||||
(-modeline-pad
|
||||
(when doom-monokai-classic-padded-modeline
|
||||
(if (integerp doom-monokai-classic-padded-modeline) doom-monokai-classic-padded-modeline 4)))
|
||||
|
||||
(modeline-fg nil)
|
||||
(modeline-fg-alt base4)
|
||||
|
||||
(modeline-bg base1)
|
||||
(modeline-bg-inactive (doom-darken base2 0.2))
|
||||
|
||||
(org-quote `(,(doom-lighten (car bg) 0.05) "#1f1f1f")))
|
||||
|
||||
|
||||
;;;; Base theme face overrides
|
||||
((cursor :background magenta)
|
||||
((font-lock-comment-face &override) :slant 'italic)
|
||||
((font-lock-type-face &override) :slant 'italic)
|
||||
(lazy-highlight :background violet :foreground base0 :distant-foreground base0 :bold bold)
|
||||
((line-number &override) :foreground base5 :distant-foreground nil)
|
||||
((line-number-current-line &override) :foreground base7 :distant-foreground nil)
|
||||
(mode-line
|
||||
:background modeline-bg :foreground modeline-fg
|
||||
:box (if -modeline-pad `(:line-width ,-modeline-pad :color modeline-bg)))
|
||||
(mode-line-inactive
|
||||
:background modeline-bg-inactive :foreground modeline-fg-alt
|
||||
:box (if -modeline-pad `(:line-width ,-modeline-pad :color modeline-bg-inactive)))
|
||||
|
||||
;;;; centaur-tabs
|
||||
(centaur-tabs-selected-modified :inherit 'centaur-tabs-selected
|
||||
:background bg
|
||||
:foreground yellow)
|
||||
(centaur-tabs-unselected-modified :inherit 'centaur-tabs-unselected
|
||||
:background bg-alt
|
||||
:foreground yellow)
|
||||
(centaur-tabs-active-bar-face :background yellow)
|
||||
(centaur-tabs-modified-marker-selected :inherit 'centaur-tabs-selected :foreground fg)
|
||||
(centaur-tabs-modified-marker-unselected :inherit 'centaur-tabs-unselected :foreground fg)
|
||||
;;;; css-mode <built-in> / scss-mode
|
||||
(css-proprietary-property :foreground keywords)
|
||||
;;;; doom-modeline
|
||||
(doom-modeline-bar :background yellow)
|
||||
(doom-modeline-buffer-file :inherit 'mode-line-buffer-id :weight 'bold)
|
||||
(doom-modeline-buffer-path :inherit 'bold :foreground green)
|
||||
(doom-modeline-buffer-project-root :foreground green :weight 'bold)
|
||||
(doom-modeline-buffer-modified :inherit 'bold :foreground orange)
|
||||
|
||||
|
||||
(isearch :foreground base0 :background green)
|
||||
;;;; ediff <built-in>
|
||||
(ediff-fine-diff-A :background (doom-blend magenta bg 0.3) :weight 'bold)
|
||||
;;;; evil
|
||||
(evil-search-highlight-persist-highlight-face :background violet)
|
||||
;;;; evil-snipe
|
||||
(evil-snipe-first-match-face :foreground base0 :background green)
|
||||
(evil-snipe-matches-face :foreground green :underline t)
|
||||
;;;; flycheck
|
||||
(flycheck-error :underline `(:style wave :color ,red) :background base3)
|
||||
(flycheck-warning :underline `(:style wave :color ,yellow) :background base3)
|
||||
(flycheck-info :underline `(:style wave :color ,green) :background base3)
|
||||
;;;; helm
|
||||
(helm-swoop-target-line-face :foreground magenta :inverse-video t)
|
||||
;;;; ivy
|
||||
(ivy-current-match :background base3)
|
||||
(ivy-minibuffer-match-face-1 :background base1 :foreground base4)
|
||||
;;;; markdown-mode
|
||||
(markdown-blockquote-face :inherit 'italic :foreground dark-blue)
|
||||
(markdown-list-face :foreground magenta)
|
||||
(markdown-pre-face :foreground cyan)
|
||||
(markdown-link-face :inherit 'bold :foreground blue)
|
||||
((markdown-code-face &override) :background (doom-lighten base2 0.045))
|
||||
;;;; neotree
|
||||
(neo-dir-link-face :foreground cyan)
|
||||
(neo-expand-btn-face :foreground magenta)
|
||||
;;;; outline <built-in>
|
||||
((outline-1 &override) :foreground magenta)
|
||||
((outline-2 &override) :foreground orange)
|
||||
;;;; org <built-in>
|
||||
(org-ellipsis :foreground orange)
|
||||
(org-tag :foreground yellow :bold nil)
|
||||
((org-quote &override) :inherit 'italic :foreground base7 :background org-quote)
|
||||
(org-todo :foreground yellow :bold 'inherit)
|
||||
(org-list-dt :foreground yellow)
|
||||
;;;; rainbow-delimiters
|
||||
(rainbow-delimiters-depth-1-face :foreground magenta)
|
||||
(rainbow-delimiters-depth-2-face :foreground orange)
|
||||
(rainbow-delimiters-depth-3-face :foreground green)
|
||||
(rainbow-delimiters-depth-4-face :foreground cyan)
|
||||
(rainbow-delimiters-depth-5-face :foreground magenta)
|
||||
(rainbow-delimiters-depth-6-face :foreground orange)
|
||||
(rainbow-delimiters-depth-7-face :foreground green))
|
||||
|
||||
;;;; Base theme variable overrides
|
||||
;; ()
|
||||
)
|
||||
|
||||
;;; doom-monokai-classic-theme.el ends here
|
Loading…
Reference in New Issue