├── etc ├── eshell │ └── aliases ├── abbrev.el ├── yasnippet │ └── snippets │ │ ├── c++-ts-mode │ │ └── .yas-parents │ │ ├── rust-ts-mode │ │ └── .yas-parents │ │ ├── rustic-mode │ │ └── .yas-parents │ │ ├── yaml-ts-mode │ │ └── .yas-parents │ │ ├── python-ts-mode │ │ └── .yas-parents │ │ ├── coq-mode │ │ ├── fun │ │ ├── def │ │ ├── ind │ │ ├── match │ │ ├── lem │ │ └── thm │ │ ├── latex-mode │ │ ├── kai │ │ ├── textit │ │ ├── textsc │ │ ├── texttt │ │ └── fig │ │ ├── haskell-mode │ │ ├── import │ │ ├── pragma │ │ ├── todo │ │ ├── module │ │ ├── undefined │ │ ├── quasiquoter │ │ ├── language │ │ ├── newtype │ │ ├── data │ │ ├── deriving_instance │ │ ├── import-qualified │ │ ├── map │ │ ├── set │ │ ├── seq │ │ ├── vector │ │ ├── function │ │ ├── bytestring │ │ ├── lazybytestring │ │ ├── text │ │ ├── bytestring-char8 │ │ ├── lazybytestring-char8 │ │ ├── stack │ │ ├── warnings │ │ └── script │ │ ├── c++-mode │ │ ├── ri │ │ ├── include-brace │ │ ├── include-quote │ │ ├── gcd │ │ ├── main │ │ ├── invmod │ │ ├── exgcd │ │ ├── qpow │ │ ├── acm │ │ ├── cf │ │ └── hungarian │ │ ├── emacs-lisp-mode │ │ ├── vc │ │ └── lexical-binding │ │ ├── python-mode │ │ ├── todo │ │ └── main │ │ ├── haskell-interactive-mode │ │ └── package │ │ ├── js2-mode │ │ └── thisAssignProp │ │ ├── bibtex-mode │ │ └── website │ │ ├── nix-mode │ │ ├── shell │ │ └── deriv │ │ ├── cc-mode │ │ └── bolt │ │ ├── rust-mode │ │ └── bolt │ │ ├── haskell-cabal-mode │ │ ├── warn │ │ └── extensions │ │ └── lua-mode │ │ └── rime-lua-filter └── transient │ └── levels.el ├── lisp ├── pest-mode.el ├── fluidsynth.el ├── clipmgr.el ├── region-mark.el ├── apple-docs.el ├── bionic-reading.el ├── clipboard.el ├── fish-protector.el ├── flygpt.el ├── octo.el ├── lojban.el ├── smtlib2-mode.el ├── hou.el ├── treefold.el └── vc-use-package.el ├── volatile ├── 10-filesets.el.disabled ├── misc.el ├── 00-postdump.el ├── 50-tabnine.el.disabled ├── 50-erc.el.disabled ├── 99-telega.el.disabled ├── 00-ui-hacks.el.disabled ├── mac.el ├── 99-eww.el.disabled ├── 20-modal.el.disabled ├── 99-uptime.el ├── animation.el ├── ytdl.el.disabled ├── fish-protector.el.disabled ├── 00-scratch.el.disabled ├── 99-pile.el.disabled └── 20-highlight-tail.el.disabled ├── modules ├── prelude-lang-idris.el ├── prelude-lang-swift.el ├── prelude-lang-zig.el ├── prelude-lang-lua.el ├── prelude-benchmark.el ├── prelude-lang-lean.el ├── prelude-lang-js.el ├── prelude-lang-ml.el ├── prelude-lang-tlaplus.el ├── prelude-lang-web.el ├── prelude-lang-agda.el ├── prelude-irc.el ├── prelude-package.el ├── prelude-nix.el ├── prelude-search.el ├── prelude-lang-python.el ├── prelude-blog.el ├── prelude-common.el ├── prelude-lang-lisp.el ├── prelude-lang-coq.el ├── prelude-lang-cc.el ├── prelude-dired.el ├── prelude-ibuffer.el ├── prelude-iload.el ├── prelude-tex.el ├── prelude-workspace.el ├── prelude-git.el ├── prelude-lang-rust.el ├── prelude-evil.el ├── prelude-os.el ├── prelude-term.el ├── prelude-mail.el ├── prelude-help.el ├── prelude-lang-haskell.el ├── prelude-chinese.el ├── prelude-apps.el ├── prelude-completion.el ├── prelude-project.el └── prelude-prog.el ├── make-tarball.sh ├── dump.el ├── .gitmodules ├── .gitignore ├── early-init.el ├── init.el └── README.org /etc/eshell/aliases: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/abbrev.el: -------------------------------------------------------------------------------- 1 | ;;-*-coding: utf-8;-*- 2 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-ts-mode/.yas-parents: -------------------------------------------------------------------------------- 1 | c++-mode -------------------------------------------------------------------------------- /etc/yasnippet/snippets/rust-ts-mode/.yas-parents: -------------------------------------------------------------------------------- 1 | rust-mode -------------------------------------------------------------------------------- /etc/yasnippet/snippets/rustic-mode/.yas-parents: -------------------------------------------------------------------------------- 1 | rust-mode -------------------------------------------------------------------------------- /etc/yasnippet/snippets/yaml-ts-mode/.yas-parents: -------------------------------------------------------------------------------- 1 | yaml-mode -------------------------------------------------------------------------------- /etc/yasnippet/snippets/python-ts-mode/.yas-parents: -------------------------------------------------------------------------------- 1 | python-mode -------------------------------------------------------------------------------- /etc/transient/levels.el: -------------------------------------------------------------------------------- 1 | ((magit-file-dispatch 2 | (t . 5))) 3 | -------------------------------------------------------------------------------- /lisp/pest-mode.el: -------------------------------------------------------------------------------- 1 | ;; Moved to https://github.com/ksqsf/pest-mode 2 | -------------------------------------------------------------------------------- /volatile/10-filesets.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (filesets-init) 4 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/coq-mode/fun: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: fun 3 | # key: fun 4 | # -- 5 | fun $1 => -------------------------------------------------------------------------------- /etc/yasnippet/snippets/latex-mode/kai: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: kai 3 | # key: k 4 | # -- 5 | \kai{$1} -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/import: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: import 3 | # key: i 4 | # -- 5 | import -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/pragma: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: pragma 3 | # key: p 4 | # -- 5 | {-# $1 #-} -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/todo: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: todo 3 | # key: todo 4 | # -- 5 | error "TODO" -------------------------------------------------------------------------------- /etc/yasnippet/snippets/latex-mode/textit: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: textit 3 | # key: i 4 | # -- 5 | \textit{$1} -------------------------------------------------------------------------------- /etc/yasnippet/snippets/latex-mode/textsc: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: textsc 3 | # key: sc 4 | # -- 5 | \textsc{$1} -------------------------------------------------------------------------------- /etc/yasnippet/snippets/latex-mode/texttt: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: texttt 3 | # key: t 4 | # -- 5 | \texttt{$1} -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/ri: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: ri 3 | # key: ri 4 | # -- 5 | int $1; scanf("%d", &$1); -------------------------------------------------------------------------------- /etc/yasnippet/snippets/coq-mode/def: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: def 3 | # key: def 4 | # -- 5 | Definition $1 : $2 := $3. -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/module: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: module 3 | # key: m 4 | # -- 5 | module $1 where -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/undefined: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: undefined 3 | # key: u 4 | # -- 5 | undefined -------------------------------------------------------------------------------- /etc/yasnippet/snippets/coq-mode/ind: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: ind 3 | # key: ind 4 | # -- 5 | Inductive $1 : Type := 6 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/emacs-lisp-mode/vc: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: vc 3 | # key: vc 4 | # -- 5 | :vc (:fetcher $1 :repo $2) -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/quasiquoter: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: quasiquoter 3 | # key: qq 4 | # -- 5 | [$1|$2] -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/include-brace: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: include-brace 3 | # key: inc 4 | # -- 5 | #include <$1>$2 -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/include-quote: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: include-quote 3 | # key: inc 4 | # -- 5 | #include "$1"$2 -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/language: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: language 3 | # key: l 4 | # -- 5 | {-# LANGUAGE $1 #-} -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/newtype: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: newtype 3 | # key: nt 4 | # -- 5 | newtype $1 = $2 6 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/python-mode/todo: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: todo 3 | # key: todo 4 | # -- 5 | raise NotImplementedError() -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-interactive-mode/package: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: package 3 | # key: p 4 | # -- 5 | :set -package -------------------------------------------------------------------------------- /etc/yasnippet/snippets/coq-mode/match: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: match 3 | # key: match 4 | # -- 5 | match $1 with 6 | | $2 => $3 7 | end -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/data: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: data 3 | # key: d 4 | # -- 5 | data $1 = $2 6 | deriving (Show) 7 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/js2-mode/thisAssignProp: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: thisAssignProp 3 | # key: this 4 | # -- 5 | this.$1 = props.$1; -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/deriving_instance: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: deriving_instance 3 | # key: di 4 | # -- 5 | deriving instance -------------------------------------------------------------------------------- /volatile/misc.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (use-package keyfreq 4 | :config 5 | (keyfreq-mode 1) 6 | (keyfreq-autosave-mode 1)) 7 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/gcd: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: gcd 3 | # key: gcd 4 | # -- 5 | ll gcd(ll a, ll b) { return b==0? a: gcd(b, a % b); } -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/import-qualified: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: import-qualified 3 | # key: iq 4 | # -- 5 | import qualified $1 as -------------------------------------------------------------------------------- /etc/yasnippet/snippets/emacs-lisp-mode/lexical-binding: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: lexical-binding 3 | # key: lex 4 | # -- 5 | -*- lexical-binding: t; -*- -------------------------------------------------------------------------------- /etc/yasnippet/snippets/coq-mode/lem: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: lem 3 | # key: lem 4 | # -- 5 | Lemma $1 : 6 | $2 7 | Proof. 8 | $3 9 | Qed. 10 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/coq-mode/thm: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: thm 3 | # key: thm 4 | # -- 5 | Theorem $1 : 6 | $2 7 | Proof. 8 | $3 9 | Qed. 10 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/map: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: map 3 | # key: map 4 | # -- 5 | import Data.Map (Map) 6 | import qualified Data.Map as Map -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/set: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: set 3 | # key: set 4 | # -- 5 | import Data.Set (Set) 6 | import qualified Data.Set as Set -------------------------------------------------------------------------------- /modules/prelude-lang-idris.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (use-package idris-mode 4 | :mode ("\\.idr\\'")) 5 | 6 | (provide 'prelude-lang-idris) 7 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/main: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: main 3 | # key: main 4 | # -- 5 | int main() 6 | { 7 | $0 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /volatile/00-postdump.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (when (boundp '+saved-load-path-during-dump) 4 | (global-font-lock-mode +1) 5 | (transient-mark-mode +1)) 6 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/seq: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: seq 3 | # key: seq 4 | # -- 5 | import Data.Sequence (Seq(..)) 6 | import qualified Data.Sequence as Seq -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/vector: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: vector 3 | # key: vec 4 | # -- 5 | import qualified Data.Vector as Vector 6 | import Data.Vector (Vector) -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/invmod: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: invmod 3 | # key: invmod 4 | # -- 5 | template 6 | T invmod(T a, T p) { return qpow(a, p-2, p); } 7 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/bibtex-mode/website: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: website 3 | # key: web 4 | # -- 5 | @misc{ 6 | title = {$1}, 7 | howpublished = {$2}, 8 | year = {$3} 9 | } -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/function: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: function 3 | # key: f 4 | # expand-env: ((yas-indent-line 'fixed)) 5 | # -- 6 | $1 :: $2 7 | $1 = ${3:undefined} -------------------------------------------------------------------------------- /volatile/50-tabnine.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (use-package company-tabnine 4 | :after company 5 | :config 6 | (add-to-list 'company-backends #'company-tabnine)) 7 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/bytestring: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: bytestring 3 | # key: bs 4 | # -- 5 | import Data.ByteString (ByteString) 6 | import qualified Data.ByteString as BS 7 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/nix-mode/shell: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: shell 3 | # key: shell 4 | # -- 5 | { pkgs ? import {} }: 6 | with pkgs; mkShell { 7 | buildInputs = [ $1 ]; 8 | } -------------------------------------------------------------------------------- /etc/yasnippet/snippets/cc-mode/bolt: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: bolt 3 | # key: bolt 4 | # -- 5 | // Local Variables: 6 | // rmsbolt-command: "g++ -O2" 7 | // rmsbolt-disassemble: nil 8 | // End: 9 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/lazybytestring: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: lazybytestring 3 | # key: lbs 4 | # -- 5 | import Data.ByteString.Lazy (ByteString) 6 | import qualified Data.ByteString.Lazy as LBS -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/text: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: text 3 | # key: text 4 | # -- 5 | import Data.Text (Text) 6 | import qualified Data.Text as Text 7 | import qualified Data.Text.IO as Text -------------------------------------------------------------------------------- /make-tarball.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Make a tarball that I can copy everywhere 4 | # The output is mostly self-contained 5 | 6 | tar cjvf ksqsf-emacs.tar.bz2 early-init.el init.el modules elpa etc custom.el lisp 7 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/bytestring-char8: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: bytestring-char8 3 | # key: bs8 4 | # -- 5 | import Data.ByteString.Char8 (ByteString) 6 | import qualified Data.ByteString.Char8 as BS -------------------------------------------------------------------------------- /etc/yasnippet/snippets/rust-mode/bolt: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: bolt 3 | # key: bolt 4 | # -- 5 | // Local Variables: 6 | // rmsbolt-command: "rustc -C opt-level=1" 7 | // rmsbolt-disassemble: nil 8 | // End: 9 | -------------------------------------------------------------------------------- /modules/prelude-lang-swift.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (use-package swift-mode 4 | :mode ((".swift\\'" . swift-mode)) 5 | :custom 6 | (swift-mode:basic-offset 2)) 7 | 8 | (provide 'prelude-lang-swift) 9 | -------------------------------------------------------------------------------- /modules/prelude-lang-zig.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (use-package zig-mode 4 | :mode ("\\.zig\\'") 5 | :hook (zig-mode . k|lsp-ensure) 6 | ;; lsp server: zls. 7 | ) 8 | 9 | (provide 'prelude-lang-zig) 10 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/lazybytestring-char8: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: lazybytestring-char8 3 | # key: lbs8 4 | # -- 5 | import Data.ByteString.Lazy.Char8 (ByteString) 6 | import qualified Data.ByteString.Lazy.Char8 as LBS -------------------------------------------------------------------------------- /volatile/50-erc.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (setq erc-nick "ksqsf") 4 | (setq erc-autojoin-channels-alist 5 | '(("irc.freenode.net" . ("#haskell" "##rust")) 6 | ("irc.mozilla.org" . ("#servo" "#rust")))) 7 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/latex-mode/fig: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: fig 3 | # key: fig 4 | # -- 5 | \begin{figure}[htb] 6 | \centering 7 | \includegraphics[width=\textwidth]{$1.png} 8 | \caption{$2} 9 | \label{fig:$3} 10 | \end{figure} -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-cabal-mode/warn: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: warn 3 | # key: warn 4 | # -- 5 | -Wall -Wcompat -Wincomplete-record-updates -Wredundant-constraints -Wno-unused-do-bind -Wno-orphans -Wno-name-shadowing -Wno-partial-type-signatures -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/stack: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: stack 3 | # key: stack 4 | # expand-env: ((yas-indent-line 'fixed)) 5 | # -- 6 | #!/usr/bin/env stack 7 | {- stack --resolver `(prelude--get-latest-stackage-lts-version)` script $1-} 8 | $0 9 | -------------------------------------------------------------------------------- /modules/prelude-lang-lua.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (use-package lua-mode 4 | :bind (:map lua-mode-map 5 | ("C-c C-z" . run-lua) 6 | ;; ("C-c C-c" . ) 7 | ) 8 | ) 9 | 10 | (provide 'prelude-lang-lua) 11 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/nix-mode/deriv: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: deriv 3 | # key: deriv 4 | # -- 5 | { pkgs ? import {} }: 6 | with pkgs; stdenv.mkDerivation { 7 | pname = "$1"; 8 | version = "$2"; 9 | src = ./.; 10 | buildInputs = [ $3 ]; 11 | } 12 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/warnings: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: warnings 3 | # key: warnings 4 | # -- 5 | {-# OPTIONS_GHC -Wall -Wcompat -Wincomplete-record-updates -Wredundant-constraints -Wno-unused-do-bind -Wno-orphans -Wno-name-shadowing -Wno-partial-type-signatures #-} -------------------------------------------------------------------------------- /volatile/99-telega.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | ;; (with-eval-after-load 'telega 4 | ;; (add-to-list 'telega-proxies '(:server "127.0.0.1" :port 7890 :enable t :type (:@type "proxyTypeHttp"))) 5 | ;; (require 'telega-adblock) 6 | ;; (telega-adblock-mode 1)) 7 | 8 | -------------------------------------------------------------------------------- /volatile/00-ui-hacks.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | ;; Only works on GNOME 4 | ;; Prefer dark variant 5 | 6 | (call-process-shell-command (concat "xprop -f _GTK_THEME_VARIANT 8u -set _GTK_THEME_VARIANT dark -name \"" 7 | (cdr (assoc 'name (frame-parameters))) 8 | "\"")) 9 | -------------------------------------------------------------------------------- /volatile/mac.el: -------------------------------------------------------------------------------- 1 | (defun +set-transparent-background () 2 | (interactive) 3 | (set-frame-parameter nil 'alpha-background 0.7)) 4 | 5 | ;; Depends on a third-party patch "Blur background" 6 | (when k|mac 7 | (add-to-list 'default-frame-alist '(alpha-background . 0.7)) 8 | (+set-transparent-background)) 9 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-mode/script: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: script 3 | # key: s 4 | # expand-env: ((yas-indent-line 'fixed)) 5 | # -- 6 | #!/usr/bin/env runghc 7 | {-# LANGUAGE PackageImports, OverloadedStrings #-} 8 | import "turtle" Turtle 9 | main = do 10 | ${1:echo "it works"} 11 | -------------------------------------------------------------------------------- /modules/prelude-benchmark.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;; run benchmark-init/show-durations-tree after init 3 | 4 | (use-package benchmark-init 5 | :demand t 6 | :config (benchmark-init/activate) 7 | :hook (after-init . benchmark-init/deactivate)) 8 | 9 | (provide 'prelude-benchmark) 10 | -------------------------------------------------------------------------------- /dump.el: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env emacs -x 2 | 3 | (require 'package) 4 | (package-initialize) 5 | 6 | ;; add some large packages here 7 | (require 'org) 8 | (require 'magit) 9 | (require 'org-protocol) 10 | (require 'telega) 11 | 12 | (setq +saved-load-path-during-dump load-path) 13 | (dump-emacs-portable "core.pdmp") 14 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/exgcd: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: exgcd 3 | # key: exgcd 4 | # -- 5 | template 6 | T exgcd(T a, T b, T& x, T& y) { 7 | if (b == 0) { 8 | x = 1, y = 0; 9 | return a; 10 | } 11 | T g = exgcd(b, a % b, y, x); 12 | y -= a / b * x; 13 | return g; 14 | } -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/qpow: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: qpow 3 | # key: qpow 4 | # -- 5 | template 6 | T qpow(T a, T b, T p) { 7 | T ans = 1; 8 | a = (a % p + p) % p; 9 | for (; b; b >>= 1) { 10 | if (b & 1) ans = (a * ans) % p; 11 | a = (a * a) % p; 12 | } 13 | return ans; 14 | } -------------------------------------------------------------------------------- /modules/prelude-lang-lean.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (use-package lean4-mode 4 | :mode ("\\.lean\\'" . lean4-mode) 5 | :vc (:fetcher github :repo "leanprover/lean4-mode") 6 | :config 7 | ;; disable company in lean4 mode because we use corfu 8 | (add-hook 'lean4-mode #'(lambda () (company-mode -1)))) 9 | 10 | (provide 'prelude-lang-lean) 11 | -------------------------------------------------------------------------------- /volatile/99-eww.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | ;;; EMACScript! 4 | 5 | (with-eval-after-load 'eww 6 | (defun EMACScript (script) 7 | (when (equal (dom-attr script 'type) "text/emacscript") 8 | (eval (read (dom-text script)) 'lexical))) 9 | (add-to-list 'shr-external-rendering-functions 10 | '(script . EMACScript))) 11 | -------------------------------------------------------------------------------- /volatile/20-modal.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (use-package xah-fly-keys 4 | :disabled 5 | :config 6 | (xah-fly-keys-set-layout "qwerty") 7 | (xah-fly-keys 1) 8 | (global-set-key (kbd "") 'xah-fly-command-mode-activate)) 9 | 10 | (use-package boon 11 | :disabled 12 | :config 13 | (require 'boon-qwerty) 14 | (boon-mode)) 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lisp/pest-mode"] 2 | path = lisp/pest-mode 3 | url = git@github.com:ksqsf/pest-mode.git 4 | [submodule "lisp/rime-regexp"] 5 | path = lisp/rime-regexp 6 | url = https://github.com/colawithsauce/rime-regexp.el 7 | [submodule "lisp/emt"] 8 | path = lisp/emt 9 | url = git@github.com:roife/emt.git 10 | [submodule "lisp/spookfox"] 11 | path = lisp/spookfox 12 | url = https://github.com/bitspook/spookfox 13 | -------------------------------------------------------------------------------- /modules/prelude-lang-js.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (use-package js2-mode 4 | :mode (("\\.js\\'" . js2-mode) 5 | ("\\.jsx\\'" . js2-jsx-mode)) 6 | :init 7 | (add-to-list 'interpreter-mode-alist '("node" . js2-mode))) 8 | 9 | (defun run-node () 10 | (interactive) 11 | (setenv "NODE_NO_READLINE" "1") 12 | (pop-to-buffer (make-comint "node-repl" "node" nil "--interactive"))) 13 | 14 | (provide 'prelude-lang-js) 15 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/acm: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: acm 3 | # key: acm 4 | # -- 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | using namespace std; 18 | 19 | int main() 20 | { 21 | $0 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/python-mode/main: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: main 3 | # key: main 4 | # -- 5 | import argparse 6 | 7 | 8 | def main(args): 9 | pass 10 | 11 | 12 | if __name__ == '__main__': 13 | parser = argparse.ArgumentParser() 14 | parser.add_argument('path') 15 | parser.add_argument('--version', '-v', 16 | action='store_true', default=None, 17 | help='Print version information') 18 | args = parser.parse_args() 19 | main(args) 20 | -------------------------------------------------------------------------------- /modules/prelude-lang-ml.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;; Tuareg for OCaml 3 | 4 | (use-package tuareg 5 | :mode ("\\.ml[4ilpy]?$" . tuareg-mode)) 6 | 7 | (use-package dune :after tuareg) 8 | 9 | ;; Requires: opam install utop 10 | (use-package utop 11 | :after tuareg 12 | :bind (:map tuareg-mode-map) 13 | :when (executable-find "utop")) 14 | 15 | ;; Requires: opam install merlin 16 | (use-package merlin 17 | :hook (tuareg-mode . merlin-mode) 18 | :when (executable-find "ocamlmerlin")) 19 | 20 | (provide 'prelude-lang-ml) 21 | -------------------------------------------------------------------------------- /volatile/99-uptime.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (defvar k|emacs-uptime-log-file 4 | (expand-file-name "var/uptime.org" user-emacs-directory)) 5 | 6 | (defun k|emacs-record-uptime () 7 | (with-temp-buffer 8 | (insert 9 | "|" 10 | (format-time-string "%FT%T%z" before-init-time) 11 | " | " 12 | (format-time-string "%FT%T%z" (current-time)) 13 | " | " 14 | (emacs-uptime) 15 | " |" 16 | "\n") 17 | (append-to-file nil nil k|emacs-uptime-log-file))) 18 | 19 | (add-hook 'kill-emacs-hook 'k|emacs-record-uptime) 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local customizations or private files 2 | .ercrc.el 3 | custom.el 4 | games/ 5 | mind-wave/ 6 | newsrc.eld 7 | org-roam.db 8 | triples.db 9 | volatile/*-priv.el 10 | 11 | # Caches 12 | *.elc 13 | .cache/ 14 | .emacs.desktop 15 | SessionDesktop.el 16 | ede-projects.el 17 | eln-cache/ 18 | elpa/ 19 | elpaca/ 20 | filesets-cache.el 21 | session.* 22 | var/ 23 | 24 | # Locks 25 | .emacs.desktop.lock 26 | 27 | # Backup or swap files 28 | *~ 29 | \#*\# 30 | backups 31 | 32 | # OS-specific 33 | .DS_Store 34 | 35 | # Other binary blobs 36 | codeium/ 37 | dump/ 38 | librime/ 39 | tree-sitter/ 40 | -------------------------------------------------------------------------------- /modules/prelude-lang-tlaplus.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (defun tla-insert-separator () 4 | "Insert dashes as separator. 5 | 6 | The number of dashes is determined by the MOUDLE line." 7 | (interactive) 8 | (let ((width (save-excursion 9 | (goto-char (point-min)) 10 | (re-search-forward "-+ ?MODULE") 11 | (- (pos-eol) (pos-bol))))) 12 | (insert (make-string width ?-)))) 13 | 14 | ;; NOTE: this does not work... 15 | (use-package tla-tools 16 | :vc (:fetcher github :repo "mrc/tla-tools")) 17 | 18 | (provide 'prelude-lang-tlaplus) 19 | -------------------------------------------------------------------------------- /modules/prelude-lang-web.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (use-package web-mode 4 | :mode (("\\.html?\\'" . web-mode) 5 | ("\\.phtml\\'" . web-mode) 6 | ("\\.tpl\\.php\\'" . web-mode) 7 | ("\\.[agj]sp\\'" . web-mode) 8 | ("\\.as[cp]x\\'" . web-mode) 9 | ("\\.erb\\'" . web-mode) 10 | ("\\.mustache\\'" . web-mode) 11 | ("\\.djhtml\\'" . web-mode) 12 | ("\\.vue\\'" . web-mode) 13 | ("\\.ts\\'" . web-mode)) 14 | :config 15 | (setq web-mode-engines-alist 16 | '(("jinja" . "\\.html\\'")))) 17 | 18 | (provide 'prelude-lang-web) 19 | -------------------------------------------------------------------------------- /volatile/animation.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (defun frame-appear-animation (&optional frame from to inc) 4 | (interactive) 5 | (setq from (or from 0)) 6 | (setq to (or to 100)) 7 | (setq inc (or inc 1)) 8 | (dolist (i (number-sequence from to inc)) 9 | (let ((alpha (* i 0.01))) 10 | (set-frame-parameter frame 'alpha alpha) 11 | (sit-for 0.001)))) 12 | 13 | (defun frame-disappear-animation (&optional frame from to dec) 14 | (setq dec (or dec 1)) 15 | (frame-appear-animation frame from to (- 0 dec))) 16 | 17 | ;; (progn (frame-disappear-animation nil 97 10) (frame-appear-animation nil 10 97)) 18 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/lua-mode/rime-lua-filter: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: rime-lua-filter 3 | # key: filter 4 | # -- 5 | local moran = require("moran") 6 | 7 | ---@param env table 8 | local function init(env) 9 | env.enabled = true 10 | end 11 | 12 | ---@param env table 13 | local function fini(env) 14 | end 15 | 16 | ---@param t_input Translation 17 | ---@param env table 18 | local function func(t_input, env) 19 | local iter = moran.iter_translation(t_input) 20 | if not env.enabled then 21 | moran.yield_all(iter) 22 | else 23 | $1 24 | end 25 | end 26 | 27 | return { init = init, fini = fini, func = func } 28 | -------------------------------------------------------------------------------- /lisp/fluidsynth.el: -------------------------------------------------------------------------------- 1 | ;;; NOTE: PLEASE DO NOT USE THIS FILE AND DON'T BELIEVE ANY WORDS IN 2 | ;;; IT. This file does not contain any usable code. 3 | 4 | ;;; fluidsynth.el --- An interface to fluidsynth -*- lexical-binding: t; -*- 5 | 6 | (defun make-fluidsynth (soundfont-path) 7 | (let ((proc (make-process :name "fluidsynth" 8 | :buffer " *fluidsynth*" 9 | :command `("fluidsynth" ,soundfont-path)))) 10 | (make-record ))) 11 | 12 | (defun fluidsynth-proc (fs) 13 | (aref fs 0)) 14 | 15 | (defun fluidsynth-sheet (fs) 16 | (aref fs 1)) 17 | 18 | (defun fluidsynth-shee) 19 | 20 | (provide 'fluidsynth) 21 | ;;; fluidsynth.el ends here 22 | -------------------------------------------------------------------------------- /volatile/ytdl.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (defvar ytdl-dest-dir (expand-file-name "~/Movies/")) 4 | (defvar ytdl-command "youtube-dl") 5 | (defvar ytdl-cookies-file (expand-file-name "~/Movies/ytdl-cookies.txt")) 6 | 7 | (defun youtube-dl (url) 8 | "Run youtube-dl to download Internet videos." 9 | (interactive "sVideo URL to download: ") 10 | (let ((ytdl (executable-find ytdl-command nil)) 11 | (default-directory ytdl-dest-dir)) 12 | (unless ytdl (error "youtube-dl not found in exec-path")) 13 | (start-process "youtube-dl" "*youtube-dl*" 14 | ytdl url "--no-progress" "--cookies" ytdl-cookies-file) 15 | (pop-to-buffer "*youtube-dl*"))) 16 | -------------------------------------------------------------------------------- /modules/prelude-lang-agda.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (defcustom prelude-agda-mode-path nil 4 | "Specify the path to the executable 'agda-mode'." 5 | :group 'prelude 6 | :type 'string) 7 | 8 | 9 | (defun load-agda-mode () 10 | ;; Load agda-mode only when 'agda-mode' can be found. 11 | (when-let ((agda-mode-path (or prelude-agda-mode-path 12 | (executable-find "agda-mode")))) 13 | (load-file (let ((coding-system-for-read 'utf-8)) 14 | (shell-command-to-string (concat agda-mode-path " locate"))))) 15 | (if (featurep 'agda2) 16 | (agda2-mode) 17 | (fundamental-mode))) 18 | 19 | (add-to-list 'auto-mode-alist '("\\.agda2?\\'" . load-agda-mode)) 20 | 21 | (provide 'prelude-lang-agda) 22 | 23 | -------------------------------------------------------------------------------- /volatile/fish-protector.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (with-eval-after-load 'telega 4 | (require 'fish-protector) 5 | 6 | (setq fish-protector-alist 7 | '((:telega . (or (mode telega-root-mode) 8 | (mode telega-chat-mode))))) 9 | 10 | (fish-protector-add-reach-limit 11 | :telega 12 | (* 30 60) ;; 30 minutes 13 | (lambda (cur-sec) 14 | (if (> cur-sec (* 60 60)) ;; hard limit: 60 minutes 15 | (progn 16 | (kill-buffer "*Telega Root*") 17 | (fish-protector-alert "Hard limit of Telega reached!")) 18 | (when (= 0 (% cur-sec (* 5 60))) 19 | (fish-protector-alert "Soft limit of Telega reached! Please go away from Telega!!"))))) 20 | 21 | (fish-protector-start)) 22 | -------------------------------------------------------------------------------- /modules/prelude-irc.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | ;;; ERC 4 | 5 | (use-package rcirc 6 | :ensure nil 7 | :defer t 8 | :custom 9 | (rcirc-server-alist '(("irc.libera.chat" 10 | :channels ("#emacs" "#haskell")))) 11 | (rcirc-default-nick "ksqsf") 12 | (rcirc-default-user-name "ksqsf") 13 | (rcirc-default-full-name "Curious Minds Want to Know")) 14 | 15 | (use-package erc 16 | :ensure nil 17 | :defer t 18 | :custom 19 | (erc-nick "ksqsf") 20 | (erc-server "irc.libera.chat") 21 | (erc-autojoin-channels-alist '(("libera.chat" "#haskell" "#emacs"))) 22 | (erc-sasl-user "ksqsf") 23 | (erc-sasl-auth-source-function 'erc-sasl-auth-source-password-as-host) 24 | :config 25 | (add-to-list 'erc-modules 'sasl) 26 | (defalias 'erc 'erc-tls)) 27 | 28 | (provide 'prelude-irc) 29 | -------------------------------------------------------------------------------- /modules/prelude-package.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;;; Packages. 3 | ;;; 4 | ;;; package.el: 5 | ;;; * No version pinning 6 | 7 | ;; Mirrors moved to early-init.el 8 | 9 | ;; use-package 10 | (require 'use-package) 11 | 12 | ;; Set up use-package for user config 13 | (setq use-package-always-ensure t) ; All packages used have to be installed 14 | 15 | ;; Marry package-vc with use-package. Quelpa is too heavy. 16 | (require 'vc-use-package) 17 | 18 | ;; Mask package-quickstart before doing batch operations 19 | (advice-add 'package-menu-execute :around 20 | (lambda (oldfun &rest args) 21 | (let ((package-quickstart nil)) 22 | (apply oldfun args)) 23 | (when package-quickstart 24 | (package-quickstart-refresh)))) 25 | 26 | (provide 'prelude-package) 27 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/cf: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: cf 3 | # key: cf 4 | # -- 5 | //! title: $1 6 | //! link: $2 7 | //! solved: $3 8 | //! tags: $4 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | using namespace std; 24 | 25 | using ll = long long; 26 | using ld = long double; 27 | using uint = unsigned int; 28 | using ull = unsigned long long; 29 | using pii = pair; 30 | const ld PI = acos(-1.0); 31 | 32 | #define all(x) (x).begin(),(x).end() 33 | #define car first 34 | #define cdr second 35 | #define forn(i,n) for(int i = 0; i < (n); ++i) 36 | #define clr(x) memset(x,0,sizeof(x)) 37 | 38 | int main() 39 | { 40 | return 0; 41 | } -------------------------------------------------------------------------------- /volatile/00-scratch.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (defun refresh-scratch () 4 | "Refresh the *scratch* buffer with a fortune cookie." 5 | (interactive) 6 | (make-process :name "fortune" 7 | ;; https://github.com/ksqsf/fortune-monika 8 | :command '("fortune" "monika") 9 | :filter 10 | (lambda (process output) 11 | (with-current-buffer "*scratch*" 12 | (fundamental-mode) 13 | (erase-buffer) 14 | (insert output) 15 | (insert "\nHappy Hacking!\nDon't forget to check your calendar (C-c a)\n\n") 16 | (lisp-interaction-mode) 17 | (comment-region (point-min) (point-max)) 18 | (require 'ansi-color) 19 | (ansi-color-apply-on-region (point-min) (point-max)))))) 20 | 21 | (add-hook 'after-init-hook #'refresh-scratch) 22 | -------------------------------------------------------------------------------- /volatile/99-pile.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (use-package w 4 | :vc (:fetcher github :repo "lepisma/w.el")) 5 | (use-package pile 6 | :vc (:fetcher github :repo "lepisma/pile")) 7 | 8 | (setq wiki (pile-project-wiki 9 | :name "Ksqsf's Wiki" 10 | :root-url "https://wiki.ksqsf.moe/" 11 | :base-url "https://wiki.ksqsf.moe/" 12 | :input-dir "/tmp/wiki/src" 13 | :output-dir "/tmp/wiki/output" 14 | :postamble "Yo yo yo!" 15 | :preamble "Yeah yeah yeah!")) 16 | 17 | (setq blog (pile-project-blog 18 | :name "Ksqsf's Blog" 19 | :root-url "https://blog.ksqsf.moe/" 20 | :base-url "https://blog.ksqsf.moe/" 21 | :input-dir "/tmp/blog/src" 22 | :output-dir "/tmp/blog/output" 23 | :postamble "what?" 24 | :preamble "so that")) 25 | 26 | (setq pile-projects (list blog wiki)) 27 | (pile-setup) 28 | -------------------------------------------------------------------------------- /modules/prelude-nix.el: -------------------------------------------------------------------------------- 1 | (use-package nix-mode 2 | :mode (".nix\\'" . nix-mode)) 3 | 4 | (defun nix-shell-vterm () 5 | "Start nix-shell here." 6 | (interactive) 7 | (let ((vterm (vterm "*nix-shell*"))) 8 | (with-current-buffer vterm 9 | (vterm-send-string "nix-shell") 10 | (vterm-send-return)))) 11 | 12 | (defvar k|nix t 13 | "Indicate that the system is managed by Nix, so that we can leverage Nix power. 14 | 15 | If it's enabled, some programming environments may benefit from nix-shell.") 16 | 17 | (use-package nix-sandbox 18 | :commands (nix-shell-command nix-shell nix-compile nix-find-sandbox nix-current-sandbox nix-executable-find)) 19 | 20 | (defun k|nix-refresh () 21 | (interactive) 22 | (save-buffer) 23 | (get-buffer-create "*nix*") 24 | (with-current-buffer "*nix*" 25 | (delete-region (point-min) (point-max)) 26 | (start-process "nix" "*nix*" "nix-env" "-iA" "nixpkgs.myMac") 27 | (display-buffer "*nix*") 28 | (goto-char (point-min)))) 29 | 30 | (provide 'prelude-nix) 31 | -------------------------------------------------------------------------------- /modules/prelude-search.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | ;; Prefer regular expression-based searches 4 | (global-set-key [remap isearch-forward] #'isearch-forward-regexp) 5 | (global-set-key [remap query-replace] #'query-replace-regexp) 6 | 7 | ;; See also occur (built-in), embark, consult-line, etc. 8 | (use-package wgrep 9 | :defer t 10 | :bind 11 | (:map grep-mode-map 12 | ;; occur-style keybinding 13 | ("e" . wgrep-change-to-wgrep-mode) 14 | ;; dired-style keybinding 15 | ("C-x C-q" . wgrep-change-to-wgrep-mode))) 16 | 17 | ;; ripgrep 18 | (use-package rg 19 | :bind ("M-s M-s" . rg-menu)) 20 | 21 | (with-eval-after-load 'grep 22 | (push ".tags" grep-find-ignored-files) 23 | (push ".git" grep-find-ignored-files)) 24 | 25 | (with-eval-after-load 'vterm 26 | (keymap-set vterm-mode-map "M-s" search-map)) 27 | (with-eval-after-load 'eat 28 | (keymap-set eat-semi-char-mode-map "M-s" search-map)) 29 | 30 | (use-package ast-grep 31 | :commands (ast-grep-search ast-grep-project ast-grep-directory)) 32 | 33 | (provide 'prelude-search) 34 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/haskell-cabal-mode/extensions: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: extensions 3 | # key: ext 4 | # -- 5 | default-extensions: 6 | ApplicativeDo 7 | BangPatterns 8 | ConstraintKinds 9 | DataKinds 10 | DefaultSignatures 11 | DeriveFoldable 12 | DeriveFunctor 13 | DeriveGeneric 14 | DeriveLift 15 | DeriveTraversable 16 | DerivingStrategies 17 | EmptyCase 18 | ExistentialQuantification 19 | FlexibleContexts 20 | FlexibleInstances 21 | FunctionalDependencies 22 | GADTs 23 | GeneralizedNewtypeDeriving 24 | InstanceSigs 25 | KindSignatures 26 | LambdaCase 27 | MultiParamTypeClasses 28 | MultiWayIf 29 | NamedFieldPuns 30 | NumericUnderscores 31 | OverloadedStrings 32 | PartialTypeSignatures 33 | PatternSynonyms 34 | RankNTypes 35 | RecordWildCards 36 | ScopedTypeVariables 37 | StandaloneDeriving 38 | TupleSections 39 | TypeApplications 40 | TypeFamilies 41 | TypeFamilyDependencies 42 | TypeOperators -------------------------------------------------------------------------------- /modules/prelude-lang-python.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (use-package python 4 | :ensure nil 5 | :defer t 6 | :init 7 | (setq doom-modeline-env-python-executable "python3") 8 | (setq python-shell-interpreter "python3") 9 | (setq python-shell-interpreter-args "-i") 10 | (setq python-shell-dedicated 'buffer) 11 | (setq gud-pdb-command-name "python3 -m pdb") 12 | :hook 13 | (python-mode . k|lsp-ensure) 14 | (inferior-python-mode . (lambda () (comint-use-persistent-input-history "~/.python_history"))) 15 | :bind (:map python-mode-map 16 | ("C-c C-p" . run-python) 17 | ("C-c C-c" . python-shell-send-buffer) 18 | ("C-x C-e" . python-shell-send-statement)) 19 | :config 20 | ;; (add-hook 'inferior-python-mode-hook 21 | ;; (lambda () 22 | ;; (company-mode t))) 23 | (when (eq prelude-lsp-client 'lsp-mode) 24 | (use-package lsp-pyright))) 25 | 26 | ;; Integrate with uv 27 | (use-package uv-mode 28 | :defer t 29 | :hook (python-mode . uv-mode-auto-activate-hook)) 30 | 31 | ;; (use-package jupyter :defer t) 32 | 33 | (provide 'prelude-lang-python) 34 | -------------------------------------------------------------------------------- /modules/prelude-blog.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | (defcustom blog-posts-dir (expand-file-name "~/Site/jekyll/_posts/") 3 | "The directory for your post Markdown files." 4 | :group 'prelude 5 | :type '(string)) 6 | 7 | (defun blog--header (layout title time) 8 | (let ((datetime (format-time-string "%Y-%m-%d %H:%M:%S" time))) 9 | (format "--- 10 | layout: %s 11 | title: \"%s\" 12 | date: %s 13 | --- 14 | " layout (replace-regexp-in-string "\"" "\\\\\"" title) datetime))) 15 | 16 | (defun blog-new-post (title permalink) 17 | (interactive "sTitle: \nsPermalink for post '%s': \n") 18 | (let* ((time (current-time)) 19 | (date (format-time-string "%Y-%m-%d" time)) 20 | (filename (format "%s-%s.md" date permalink)) 21 | (header (blog--header "post" title time))) 22 | (find-file (expand-file-name filename blog-posts-dir)) 23 | (insert header) 24 | (newline))) 25 | 26 | (defun find-blog-post () 27 | "Find one of your blog posts." 28 | (interactive) 29 | (let ((files (reverse (directory-files blog-posts-dir 30 | nil 31 | "^[^\\.].*\\.md$")))) 32 | (find-file (completing-read "Post: " files)))) 33 | 34 | (provide 'prelude-blog) 35 | -------------------------------------------------------------------------------- /etc/yasnippet/snippets/c++-mode/hungarian: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: hungarian 3 | # key: hungarian 4 | # -- 5 | template 6 | T hungarian(const vector>& cost) { 7 | const T INF = numeric_limits::max(); 8 | int n = cost.size(), m = cost[0].size(); 9 | vector u(n + 1), v(m + 1), dist(m + 1); 10 | vector p(m + 1), way(m + 1), used(m + 1); 11 | for (int i = 1; i <= n; ++i) { 12 | p[0] = i; 13 | int j0 = 0; 14 | fill(dist.begin(), dist.end(), INF); 15 | do { 16 | used[j0] = i; 17 | int i0 = p[j0], j1 = -1; 18 | T delta = INF; 19 | for (int j = 1; j <= m; ++j) if (used[j] != i) { 20 | T cur = cost[i0 - 1][j - 1] - u[i0] - v[j]; 21 | if (cur < dist[j]) dist[j] = cur, way[j] = j0; 22 | if (dist[j] < delta) delta = dist[j], j1 = j; 23 | } 24 | forn(j, m + 1) { 25 | if (used[j] == i) u[p[j]] += delta, v[j] -= delta; 26 | else dist[j] -= delta; 27 | } 28 | j0 = j1; 29 | } while (p[j0] != 0); 30 | for (int j1; j0; j0 = j1) 31 | p[j0] = p[j1 = way[j0]]; 32 | } 33 | 34 | return -v[0]; 35 | } 36 | -------------------------------------------------------------------------------- /early-init.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | ;; Restore dumped load-path 4 | (when (boundp '+saved-load-path-during-dump) 5 | (message "Starting from a dump file...") 6 | (setq load-path +saved-load-path-during-dump)) 7 | 8 | ;; Set package archives. Possibly set mirrors. 9 | (defconst +i-am-in-china+ nil) 10 | (with-eval-after-load 'package 11 | (if +i-am-in-china+ 12 | (setq package-archives '(("gnu" . "https://mirrors.ustc.edu.cn/elpa/gnu/") 13 | ("nongnu" . "https://mirrors.ustc.edu.cn/elpa/nongnu/") 14 | ("melpa" . "https://mirrors.ustc.edu.cn/elpa/melpa/"))) 15 | (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")))) 16 | 17 | (setq package-quickstart t) 18 | (setq package-quickstart-file (expand-file-name "var/package-quickstart.el" user-emacs-directory)) 19 | 20 | ;; Set frame parameters early to prevent flickering. 21 | (setq default-frame-alist 22 | '((height . 50) 23 | (width . 120) 24 | (vertical-scroll-bars . nil) 25 | (tool-bar-mode . nil))) 26 | 27 | ;; Must be set before loading use-package 28 | (setq use-package-enable-imenu-support t) 29 | 30 | ;; Customization file 31 | (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) 32 | -------------------------------------------------------------------------------- /modules/prelude-common.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;; Supporting functions that every module might want to use. 3 | ;; NOTE: This file should NOT depend on any third-party packages. 4 | 5 | (defmacro k|with-suppressed-message (&rest body) 6 | "Suppress new messages temporarily in the echo area and the 7 | *Messages* buffer while BODY is evaluated." 8 | (declare (indent 0)) 9 | (let ((message-log-max nil)) 10 | `(with-temp-message (or (current-message) "") ,@body))) 11 | 12 | (defconst k|mac (eq system-type 'darwin)) 13 | (defconst k|windows (eq system-type 'windows-nt)) 14 | (defconst k|wsl (string-match "WSL2" operating-system-release)) 15 | 16 | (defmacro k|double-tap-to-insert (to-char) 17 | "Create a function suitable for key binding. It replaces 18 | double consecutive occurrences of that character with TO-CHAR." 19 | `(lambda (cnt raw) 20 | (interactive "p\nP") 21 | (if (and (eq (preceding-char) last-command-event) 22 | (not raw)) 23 | (progn 24 | (backward-delete-char 1) 25 | (insert ,to-char)) 26 | (self-insert-command cnt)))) 27 | 28 | (defconst k|default-opener 29 | (cond (k|mac "open") 30 | (k|windows "start") 31 | (k|wsl "wslview") 32 | (t "xdg-open")) 33 | "The default file opener on the current system. (No Windows support.)") 34 | 35 | (require 'cl-lib) 36 | 37 | (provide 'prelude-common) 38 | 39 | -------------------------------------------------------------------------------- /modules/prelude-lang-lisp.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (use-package paredit 4 | :hook (emacs-lisp . paredit-mode)) 5 | 6 | ;; 7 | ;; Emacs Lisp 8 | ;; 9 | (define-key emacs-lisp-mode-map (kbd "C-c C-c") #'eval-buffer) 10 | (define-key emacs-lisp-mode-map (kbd "M-") #'raise-sexp) 11 | (add-to-list 'auto-mode-alist '("\\.el\\.disabled\\'" . emacs-lisp-mode)) 12 | 13 | 14 | ;; 15 | ;; Common Lisp 16 | ;; 17 | 18 | (setq inferior-lisp-program "sbcl") 19 | 20 | (use-package sly 21 | :commands (sly sly-mode) 22 | :mode (("\\.lisp\\'" . lisp-mode)) 23 | :config 24 | (remove-hook 'lisp-mode-hook 'sly-editing-mode)) 25 | 26 | ;; 27 | ;; Scheme 28 | ;; 29 | (eval-after-load 'scheme 30 | (setq scheme-program-name "guile")) 31 | 32 | (use-package geiser 33 | :mode (("\\.ss\\'" . scheme-mode)) 34 | :hook ((scheme-mode . turn-on-geiser-mode) 35 | (geiser-repl-mode . electric-pair-local-mode)) 36 | :custom 37 | (geiser-default-implementation nil) 38 | :config 39 | (use-package geiser-guile) 40 | (use-package geiser-racket)) 41 | 42 | ;; 43 | ;; Racket 44 | ;; 45 | (use-package racket-mode 46 | :mode (("\\.rkt\\'" . racket-mode)) 47 | :hook (racket-mode . racket-xp-mode)) 48 | 49 | 50 | ;; 51 | ;; My own SMTLib mode 52 | ;; 53 | (use-package smtlib2-mode 54 | :ensure nil 55 | :mode (("\\.smt2?\\'" . smtlib2-mode) 56 | ("\\.sygus2?\\'" . smtlib2-mode))) 57 | 58 | ;; Clojure 59 | (use-package cider 60 | :defer t) 61 | 62 | (provide 'prelude-lang-lisp) 63 | -------------------------------------------------------------------------------- /modules/prelude-lang-coq.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (use-package proof-general 4 | :commands (coq-mode) 5 | :mode ("\\.v\\'" . coq-mode) 6 | :custom 7 | (proof-splash-enable nil)) 8 | 9 | (use-package company-coq 10 | :hook (coq-mode . company-coq-mode) 11 | :config 12 | (setq company-coq-live-on-the-edge t)) 13 | 14 | 15 | ;; 16 | ;; The following code is historic. 17 | ;; 18 | 19 | ;; Resolve Coq and Verilog 20 | (defvar coq-or-verilog-mode--regexp "\\(?:\\(Theorem\\|Ltac\\|Example\\|Lemma\\|Axiom\\)[ ]\\).+:") 21 | 22 | (defun coq-or-verilog-mode () 23 | "Analyze buffer and enable either Coq or Verilog mode. 24 | 25 | Coq uses .v extension for Coq files, which is also the one used 26 | for Verilog files. This makes matching on file name insufficient 27 | for detecting major mode that should be used. 28 | 29 | This function attempts to use file contents to determine whether 30 | the code is Coq or Verilog and based on that chooses whether to 31 | enable `coq-mode' or `verilog-mode'. 32 | 33 | The testing procedure and criteria are not sufficiently enough, 34 | but works well enough for distinguishing source files in Software 35 | Foundations and typical Verilog files." 36 | (if (save-excursion 37 | (save-restriction 38 | (save-match-data 39 | (widen) 40 | (goto-char (point-min)) 41 | (re-search-forward coq-or-verilog-mode--regexp 42 | (point-max) t)))) 43 | (coq-mode) 44 | (verilog-mode))) 45 | 46 | ;; I don't write Verilog anymore!! 47 | ;; (add-to-list 'auto-mode-alist '("\\.v\\'" . coq-or-verilog-mode)) 48 | 49 | (provide 'prelude-lang-coq) 50 | -------------------------------------------------------------------------------- /lisp/clipmgr.el: -------------------------------------------------------------------------------- 1 | (require 'ring) 2 | (require 'ivy) 3 | 4 | (defvar clip-ring-size 100) 5 | (defvar clip-ring (make-ring clip-ring-size)) 6 | (defvar clip-timer nil) 7 | 8 | ;;;###autoload 9 | (defun clipmgr-start () 10 | (interactive) 11 | (setq clip-timer 12 | (run-with-timer nil 1 'clipmgr--maybe-insert))) 13 | 14 | ;;;###autoload 15 | (defun clipmgr-stop () 16 | (interactive) 17 | (when clip-timer 18 | (cancel-timer clip-timer))) 19 | 20 | ;;;###autoload 21 | (defun clipmgr-select () 22 | (interactive) 23 | (when (ring-empty-p clip-ring) 24 | (error "The clip ring is empty.")) 25 | (ivy-read "Select a clip to restore: " 26 | (cl-loop for it in (ring-elements clip-ring) 27 | collect (cons (cdr (assoc 'STRING it)) it)) 28 | :action (lambda (selection) 29 | (let ((clip (cdr selection))) 30 | (clipmgr--restore clip))))) 31 | 32 | (defun clipmgr--current-clip () 33 | (let ((targets (gui-get-selection 'CLIPBOARD 'TARGETS))) 34 | (if (not (vectorp targets)) 35 | nil 36 | (cl-loop for it in (seq-into targets 'list) 37 | collect (cons it (gui-get-selection 'CLIPBOARD it)))))) 38 | 39 | (defun clipmgr--maybe-insert () 40 | (let ((cur-clip (clipmgr--current-clip))) 41 | (when cur-clip 42 | (if (ring-empty-p clip-ring) 43 | (ring-insert clip-ring cur-clip) 44 | (if (equal (ring-ref clip-ring 0) cur-clip) 45 | nil 46 | (ring-insert clip-ring cur-clip)))))) 47 | 48 | ;; TODO: This does not work! 49 | (defun clipmgr--restore (clip) 50 | (let ((result (cdr (assoc 'STRING clip)))) 51 | (dolist (pair clip) 52 | (let ((key (car pair)) 53 | (value (cdr pair))) 54 | (unless (eq key 'STRING) 55 | (setq result (propertize result key value))))) 56 | (gui-set-selection 'CLIPBOARD result) 57 | (gui-set-selection 'PRIMARY result))) 58 | 59 | (provide 'clipmgr) 60 | -------------------------------------------------------------------------------- /lisp/region-mark.el: -------------------------------------------------------------------------------- 1 | ;;; region-mark.el --- Obtain pointers of a selected region -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 ksqsf 4 | 5 | ;; Author: ksqsf 6 | ;; Keywords: convenience 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; When hacking Emacs, I often feel the need to use a portion of 24 | ;; buffer text for testing. This package fulfills this need. 25 | 26 | ;;; Code: 27 | 28 | (defvar-local rm-overlay nil 29 | "The overlay used by region-mark.") 30 | 31 | (defun rm-left () 32 | "The pointer to the beginning of the marked region." 33 | (if rm-overlay 34 | (overlay-start rm-overlay) 35 | (user-error "No region marked."))) 36 | 37 | (defun rm-right () 38 | "The pointer to the end of the marked region." 39 | (if rm-overlay 40 | (overlay-end rm-overlay) 41 | (user-error "No region marked."))) 42 | 43 | ;;;###autoload 44 | (defun rm-set (beg end) 45 | "Mark a region for quick references to both pointers of the region. 46 | 47 | Only one region can be marked in a buffer." 48 | (interactive "r") 49 | (unless rm-overlay 50 | (rm-clear)) 51 | (setq rm-overlay (make-overlay beg end)) 52 | (setq rm-left beg) 53 | (setq rm-right end) 54 | (overlay-put rm-overlay 'face 'highlight)) 55 | 56 | ;;;###autoload 57 | (defun rm-clear () 58 | "Clear the marked region." 59 | (interactive) 60 | (when rm-overlay 61 | (delete-overlay rm-overlay) 62 | (setq rm-overlay nil 63 | rm-left nil 64 | rm-right nil))) 65 | 66 | (provide 'region-mark) 67 | ;;; region-mark.el ends here 68 | -------------------------------------------------------------------------------- /modules/prelude-lang-cc.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;;; Config for C/C++/Java/... 3 | 4 | (defvar +c-newline-and-indent-regexp "\\s)") 5 | 6 | (defun +c-newline-and-indent () 7 | "Open one more line when the next char is a closing paren. 8 | 9 | The exact behavior can be controlled by 10 | `c-newline-and-indent-regexp'. 11 | 12 | This command relies on `c-indent-line' so it only works in CC 13 | Mode." 14 | (interactive) 15 | (newline) 16 | (save-excursion 17 | (when (looking-at-p +c-newline-and-indent-regexp) 18 | (newline) 19 | (c-indent-line))) 20 | (c-indent-line)) 21 | 22 | (use-package cc-mode 23 | :ensure nil 24 | :defer t 25 | :config 26 | 27 | (setq c-default-style '((java-mode . "java") 28 | (awk-mode . "awk") 29 | (c-mode . "k&r") 30 | (c++-mode . "stroustrup") 31 | (other . "gnu"))) 32 | (add-hook 'c-mode-common-hook #'k|lsp-ensure) 33 | ;; (add-hook 'c-mode-common-hook #'company-mode) 34 | (add-hook 'c-mode-common-hook #'subword-mode) 35 | ;; (add-hook 'c-mode-common-hook #'smartparens-mode) 36 | (add-hook 'c-mode-common-hook #'(lambda () (rainbow-mode 0))) 37 | (define-key c-mode-map (kbd "") #'compile) 38 | (define-key c++-mode-map (kbd "") #'compile) 39 | (define-key c-mode-base-map (kbd "`") (k|double-tap-to-insert ?\")) 40 | (define-key c-mode-base-map (kbd "RET") #'+c-newline-and-indent) 41 | 42 | (use-package ccls 43 | :disabled ;; default to clangd 44 | :config 45 | (setq ccls-sem-highlight-method 'font-lock) 46 | (with-eval-after-load 'eglot 47 | (add-to-list 'eglot-server-programs '(c++-mode . ("ccls"))) 48 | (add-to-list 'eglot-server-programs '(c-mode . ("ccls")))))) 49 | 50 | (use-package modern-cpp-font-lock 51 | :hook ((c++-mode . modern-c++-font-lock-mode))) 52 | 53 | ;;; Styles 54 | (with-eval-after-load 'cc-mode 55 | (c-add-style "rime" 56 | '("gnu" 57 | (c-offsets-alist 58 | (innamespace . 0) 59 | (access-label . -1) 60 | (inclass . 2))))) 61 | 62 | ;;; ObjC++ 63 | ;;; There seems to be no dedicated major mode for ObjC++. Just use objc-mode. 64 | (add-to-list 'auto-mode-alist '("\\.mm\\'" . objc-mode)) 65 | 66 | (provide 'prelude-lang-cc) 67 | -------------------------------------------------------------------------------- /modules/prelude-dired.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (global-set-key (kbd "C-x C-j") #'dired-jump) 4 | 5 | ;; Display human readable file sizes. 6 | (setq dired-listing-switches "-alh") 7 | 8 | (with-eval-after-load 'dired 9 | ;; Since Emacs 29, Dired supports file DnD on X windows, and NS. 10 | (setq dired-mouse-drag-files t) 11 | 12 | ;; Search file names when point is at a file name; Search unlimitedly 13 | ;; otherwise. 14 | (setq dired-isearch-filenames 'dwim) 15 | 16 | ;; Intelligently guess the target directory. 17 | (setq dired-dwim-target t) 18 | 19 | (setq dired-guess-shell-alist-user 20 | '(("\\.pdf\\'" k|default-opener) 21 | ("\\.png\\'" k|default-opener) 22 | ("\\.jpg\\'" k|default-opener))) 23 | 24 | (when k|mac 25 | (setq dired-omit-files "\\`[.]?#\\|\\`[.][.]?\\'\\|\\.DS_Store\\|\\.localized")) 26 | 27 | ;; revisiting an existing Dired buffer always reverts it 28 | (setq dired-auto-revert-buffer t)) 29 | 30 | (use-package dired-filter 31 | :after (dired) 32 | :custom 33 | (dired-filter-verbose nil) 34 | :hook (;; dired-filter-group-mode causes dired batch operations to 35 | ;; behave strangely, so we disable it for now. 36 | ;; (dired-mode . dired-filter-group-mode) 37 | (dired-mode . dired-filter-mode)) 38 | :config 39 | (define-key dired-mode-map "/" dired-filter-map) 40 | (setq dired-filter-group-saved-groups 41 | '(("default" 42 | ("Git" 43 | (directory . ".git") 44 | (file . ".gitignore")) 45 | ("Directory" 46 | (directory)) 47 | ("PDF" 48 | (extension . "pdf")) 49 | ("LaTeX" 50 | (extension "tex" "bib")) 51 | ("Code" 52 | (extension "rs" "c" "cpp" "h" "hpp" "cc" "rb" "py" "el" "html" "js" "css" "jl" "rs" "m" "v" "hs" "lhs" "pl")) 53 | ("Text" 54 | (extension "md" "rst" "txt")) 55 | ("Org" 56 | (extension "org")) 57 | ("Archives" 58 | (extension "zip" "rar" "tar" "gz" "bz2" "xz")) 59 | ("Images" 60 | (extension "jpg" "jpeg" "webp" "png" "bmp" "gif" "tiff" "xcf")))))) 61 | 62 | (use-package nerd-icons-dired 63 | :hook (dired-mode . nerd-icons-dired-mode)) 64 | 65 | (use-package diredfl 66 | :after (dired) 67 | :config 68 | (diredfl-global-mode)) 69 | 70 | (provide 'prelude-dired) 71 | -------------------------------------------------------------------------------- /modules/prelude-ibuffer.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;; ibuffer is chosen in favor of list-buffers in core.el 3 | 4 | (use-package ibuffer 5 | :ensure nil 6 | :commands (ibuffer ibuffer-switch-to-saved-filter-groups) 7 | :hook (ibuffer-mode . (lambda () (ibuffer-switch-to-saved-filter-groups "Normal"))) 8 | :hook (ibuffer-mode . nerd-icons-ibuffer-mode) 9 | :config 10 | (setq ibuffer-saved-filter-groups 11 | '(("Normal" 12 | ("Dired" (mode . dired-mode)) 13 | ("Emacs" (or 14 | (name . "^\\*scratch\\*$") 15 | (name . "^\\*Messages\\*$") 16 | (name . "^\\*compilation\\*") 17 | (name . "^\\*dashboard\\*$") 18 | (name . "^\\*Backtrace\\*$") 19 | (name . "^\\*Packages\\*$"))) 20 | ("Text" (or 21 | (mode . org-mode) 22 | (mode . markdown-mode) 23 | (mode . text-mode))) 24 | ("Tex" (mode . tex-mode)) 25 | ("Code" (or 26 | (mode . emacs-lisp-mode) 27 | (mode . haskell-mode) 28 | (mode . rust-mode) 29 | (mode . rustic-mode) 30 | (mode . html-mode) 31 | (mode . css-mode) 32 | (mode . python-mode) 33 | (mode . prog-mode))) 34 | ("Magit" (name . "^magit")) 35 | ("Help" (or 36 | (name . "^\\*Help\\*$") 37 | (name . "^\\*info\\*$") 38 | (name . "^\\*Apropos\\*$"))) 39 | ("Custom" (or 40 | (mode . customi-mode) 41 | (name . "^\\*Customize"))) 42 | ("IRC" (or 43 | (mode . erc-mode))) 44 | ("Telega" (or 45 | (mode . telega-root-mode) 46 | (mode . telega-chat-mode)))))) 47 | (setq ibuffer-show-empty-filter-groups nil 48 | ibuffer-default-sorting-mode 'filename/process) 49 | 50 | (use-package nerd-icons-ibuffer 51 | :commands (nerd-icons-ibuffer-mode))) 52 | 53 | (use-package ibuffer-project 54 | :disabled ;; Not very useful 55 | :after (ibuffer) 56 | :custom 57 | (ibuffer-project-use-cache t) 58 | :hook (ibuffer . (lambda () 59 | (setq ibuffer-filter-groups (ibuffer-project-generate-filter-groups))))) 60 | 61 | (provide 'prelude-ibuffer) 62 | -------------------------------------------------------------------------------- /lisp/apple-docs.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | ;; Stolen from somewhere I don't remember. 4 | 5 | ;;;###autoload 6 | (defun counsel-apple-search () 7 | "Ivy interface for dynamically querying apple.com docs." 8 | (interactive) 9 | (require 'request) 10 | (require 'json) 11 | (require 'url-http) 12 | (ivy-read "apple docs: " 13 | (lambda (input) 14 | (let* ((url (url-encode-url (format "https://developer.apple.com/search/search_data.php?q=%s" input))) 15 | (c1-width (round (* (- (window-width) 9) 0.3))) 16 | (c2-width (round (* (- (window-width) 9) 0.5))) 17 | (c3-width (- (window-width) 9 c1-width c2-width))) 18 | (or 19 | (ivy-more-chars) 20 | (let ((request-curl-options (list "-H" (string-trim (url-http-user-agent-string))))) 21 | (request url 22 | :type "GET" 23 | :parser 'json-read 24 | :success (cl-function 25 | (lambda (&key data &allow-other-keys) 26 | (ivy-update-candidates 27 | (mapcar (lambda (item) 28 | (let-alist item 29 | (propertize 30 | (format "%s %s %s" 31 | (truncate-string-to-width (propertize (or .title "") 32 | 'face '(:foreground "yellow")) c1-width nil ?\s "…") 33 | (truncate-string-to-width (or .description "") c2-width nil ?\s "…") 34 | (truncate-string-to-width (propertize (string-join (or .api_ref_data.languages "") "/") 35 | 'face '(:foreground "cyan1")) c3-width nil ?\s "…")) 36 | 'url .url))) 37 | (cdr (car data))))))) 38 | 0)))) 39 | :action (lambda (selection) 40 | (browse-url (concat "https://developer.apple.com" 41 | (get-text-property 0 'url selection)))) 42 | :dynamic-collection t 43 | :caller 'ar/counsel-apple-search)) 44 | -------------------------------------------------------------------------------- /lisp/bionic-reading.el: -------------------------------------------------------------------------------- 1 | ;;; bionic-reading.el --- Bionic reading -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 ksqsf 4 | 5 | ;; Author: ksqsf 6 | ;; Keywords: convenience 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; This package implements something similar to bionic reading in Emacs. 24 | ;; 25 | ;; Note that bionic reading is patented. 26 | 27 | ;;; Code: 28 | 29 | (defvar-local bionic-overlays nil 30 | "The overlays for bionicification in the current buffer.") 31 | 32 | ;;;###autoload 33 | (defun bionic-word () 34 | "Bionicify the word at point" 35 | (interactive) 36 | (let* ((bounds (bounds-of-thing-at-point 'word)) 37 | (beg (car bounds)) 38 | (end (cdr bounds)) 39 | (whole-len (- end beg))) 40 | (cond 41 | ((>= whole-len 2) 42 | (let* ((half-len (/ whole-len 2)) 43 | (real-len (if (or (> whole-len 6) (= whole-len 3)) 44 | (+ half-len 1) 45 | half-len)) 46 | (ov (make-overlay beg (+ beg real-len)))) 47 | (overlay-put ov 'face 'bold) 48 | (push ov bionic-overlays))) 49 | ((> (- end beg) 1) 50 | (let ((ov (make-overlay beg (+ beg 1)))) 51 | (overlay-put ov 'face 'bold) 52 | (push ov bionic-overlays))) 53 | (t nil)))) 54 | 55 | ;;;###autoload 56 | (defun bionic-buffer () 57 | "Bionicify all the visible parts of the current buffer." 58 | (interactive) 59 | (if (not (null bionic-overlays)) 60 | (bionic-debuffer)) 61 | (save-excursion 62 | (goto-char (point-min)) 63 | (while (not (= (point) (point-max))) 64 | (if (looking-at "\\w") 65 | (bionic-word)) 66 | (forward-to-word 1)))) 67 | 68 | ;;;###autoload 69 | (defun bionic-debuffer () 70 | "Undo the bionicification." 71 | (interactive) 72 | (dolist (ov bionic-overlays) 73 | (delete-overlay ov))) 74 | 75 | (provide 'bionic-reading) 76 | ;;; bionic-reading.el ends here 77 | -------------------------------------------------------------------------------- /modules/prelude-iload.el: -------------------------------------------------------------------------------- 1 | ;;; incremental loading -*- lexical-binding:t; -*- 2 | ;; inspired by https://github.com/Elilif/.elemacs/core-incremental-loading.el 3 | ;; usage: (+iload '(feature1 feature2 ...)) 4 | ;; then each of them will be loaded incrementally after emacs startup. 5 | 6 | (unless (package-installed-p 'queue) (package-install 'queue)) 7 | (require 'queue) 8 | 9 | (defvar +iload-queue (make-queue) 10 | "The list of features to be loaded incrementally.") 11 | (defvar +iload-timer nil 12 | "The timer that runs the incremental loading process.") 13 | (defvar +iload-delay 1.0 14 | "The delay between each incremental load.") 15 | 16 | (defun +iload (features) 17 | "Register FEATURE to be loaded incrementally. 18 | 19 | Each feature in FEATURES will be loaded one by one in the order." 20 | (dolist (feature features) 21 | (queue-enqueue +iload-queue feature))) 22 | 23 | (defun +iload-do-load-one () 24 | "Pop one feature from `+iload-queue' and load it. 25 | 26 | Returns t if there are more features to load, nil otherwise." 27 | (when-let ((feature (queue-dequeue +iload-queue))) 28 | ;; (message "Incrementally loading %s..." feature) 29 | (let ((inhibit-message t)) 30 | (require feature nil t)) 31 | (not (null (queue-head +iload-queue))))) 32 | 33 | (defun +iload-start () 34 | "Start the incremental loading process." 35 | (when +iload-timer 36 | (cancel-timer +iload-timer)) 37 | (setq +iload-timer 38 | (run-with-idle-timer +iload-delay t 39 | #'(lambda () 40 | (or (+iload-do-load-one) 41 | (prog1 (cancel-timer +iload-timer) 42 | (setq +iload-timer nil))))))) 43 | 44 | (defmacro iload (&rest features) 45 | "A slightly easier wrapper over `+iload'. 46 | 47 | Example: (iload org-macs org-compat org)" 48 | `(+iload (quote ,features))) 49 | 50 | (add-hook 'emacs-startup-hook #'+iload-start) 51 | 52 | ;;; use-package keyword :iload 53 | (add-to-list 'use-package-keywords :iload) 54 | 55 | (defun use-package-normalize/:iload (name-symbol keyword args) 56 | (use-package-only-one (symbol-name keyword) args 57 | (lambda (label arg) 58 | (cond ((symbolp arg) (list arg)) 59 | ((listp arg) arg) 60 | (t (use-package-error ":iload wants a list of features")))))) 61 | 62 | (defun use-package-handler/:iload (name-symbol keyword features rest state) 63 | (let ((body (use-package-process-keywords name-symbol rest state))) 64 | (if (not (listp features)) 65 | body 66 | (use-package-concat 67 | body 68 | `((iload ,@features)))))) 69 | 70 | (provide 'prelude-iload) 71 | -------------------------------------------------------------------------------- /lisp/clipboard.el: -------------------------------------------------------------------------------- 1 | ;;; clipboard.el --- Clipboard utilities -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 ksqsf 4 | 5 | ;; Author: ksqsf 6 | ;; Keywords: tools 7 | ;; Package-Requires: ((emacs "28.1")) 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation, either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see . 21 | 22 | ;;; Commentary: 23 | 24 | ;; 25 | 26 | ;;; Code: 27 | 28 | (defun clipboard--type-is-image (type) 29 | "Check whether TYPE indicates an image." 30 | (let* ((maybe-format (string-trim-left (symbol-name type) "image/")) 31 | (maybe-format (intern maybe-format))) 32 | (and (member maybe-format image-types) 33 | (image-type-available-p maybe-format)))) 34 | 35 | (defun clipboard--type-image-format (type) 36 | "Get the image format from TYPE. 37 | 38 | Only meaningful when TYPE satisfies `clipboard--type-is-image'." 39 | (intern (string-trim-left (symbol-name type) "image/"))) 40 | 41 | (defun clipboard--insert (data type) 42 | (cond ((clipboard--type-is-image type) 43 | (insert-image (list 'image 44 | :data data 45 | :type (clipboard--type-image-format type)))) 46 | (t 47 | (insert (format "%s" data))))) 48 | 49 | ;;;###autoload 50 | (defun clipboard-inspect () 51 | (interactive) 52 | (catch 'foo 53 | (let ((buf (get-buffer-create "*clipboard*")) 54 | (targets (gui-get-selection 'CLIPBOARD 'TARGETS))) 55 | (unless (vectorp targets) 56 | (message "Clipboard does not have TARGETS info.") 57 | (throw 'foo nil)) 58 | (with-current-buffer buf 59 | (read-only-mode -1) 60 | (widen) 61 | (delete-region (point-min) (point-max)) 62 | (mapc (lambda (target) 63 | (insert " \n") 64 | (insert (propertize (symbol-name target) 'face 'bold)) 65 | (insert "\n") 66 | (clipboard--insert (gui-get-selection 'CLIPBOARD target) target) 67 | (insert "\n\n")) 68 | targets)) 69 | (pop-to-buffer buf) 70 | (read-only-mode 1) 71 | (beginning-of-buffer)))) 72 | 73 | (provide 'clipboard) 74 | ;;; clipboard.el ends here 75 | -------------------------------------------------------------------------------- /modules/prelude-tex.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (use-package tex 4 | :ensure auctex 5 | :mode (("\\.tex\\'" . LaTeX-mode)) 6 | :commands (TeX-revert-document-buffer TeX-PDF-mode TeX-source-correlate-mode) 7 | :config 8 | (setq-default TeX-engine 'default) 9 | (add-hook 'LaTeX-mode-hook 10 | #'(lambda () 11 | (setq fill-column 80))) 12 | 13 | 14 | (when k|mac 15 | (setq TeX-view-program-selection '((output-pdf "PDF Tools")) 16 | TeX-source-correlate-start-server t)) 17 | (when k|wsl 18 | (setq TeX-view-program-list '(("xdg-open" ("xdg-open '%o'")))) 19 | (setq TeX-view-program-selection '((output-pdf "xdg-open")))) 20 | 21 | (setq TeX-command-default "LaTeXMk") 22 | 23 | (setq TeX-auto-save t 24 | TeX-parse-self t) 25 | (setq-default TeX-master nil) 26 | 27 | (setq-default LaTeX-electric-left-right-brace t) 28 | 29 | (add-hook 'LaTeX-mode-hook #'TeX-source-correlate-mode) 30 | (add-hook 'LaTeX-mode-hook #'TeX-PDF-mode) 31 | (add-hook 'LaTeX-mode-hook #'visual-line-mode) 32 | (add-hook 'LaTeX-mode-hook #'flyspell-mode) 33 | (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer) 34 | (add-hook 'LaTeX-mode-hook #'+latex-setup) 35 | 36 | (defun +latex-setup () 37 | ;; remove { and } so that cape-file can work inside {} 38 | (setq-local thing-at-point-file-name-chars "-@~/[:alnum:]_.$#%,:")) 39 | 40 | (with-eval-after-load 'eglot 41 | (add-to-list 'eglot-server-programs '(latex-mode "texlab")))) 42 | 43 | (use-package reftex 44 | :ensure nil 45 | :hook (LaTeX-mode . reftex-mode) 46 | :config 47 | (setq reftex-plug-into-AUCTeX t)) 48 | 49 | (use-package cdlatex 50 | :defer t 51 | ;; :hook ((latex-mode LaTeX-mode) . turn-on-cdlatex) 52 | :config 53 | (setq cdlatex-command-alist 54 | '(("sum" "Insert \\sum_{}^{}" "\\sum_{?}^{}" cdlatex-position-cursor nil nil t) 55 | ("prd" "Insert \\prod_{}^{}" "\\prod_{?}^{}" cdlatex-position-cursor nil nil t) 56 | ("tt" "Insert \\texttt{}" "\\texttt{?}" cdlatex-position-cursor nil t t) 57 | ("fr" "Insert \\frac{}{}" "\\frac{?}{?}" cdlatex-position-cursor nil nil t) 58 | ("rm" "Insert \\mathrm{}" "\\mathrm{?}" cdlatex-position-cursor nil nil t))) 59 | (setq cdlatex-env-alist 60 | '(("frame" "\\begin{frame}\n\\frametitle{?}\n\n\\end{frame}" nil))) 61 | (setq cdlatex-math-symbol-alist 62 | '((?0 ("\\varnothing" "\\emptyset")) 63 | (?1 ("\\ONE" "\\one")) 64 | (?. ("\\cdot" "\\circ")) 65 | (?v ("\\vee" "\\bigvee")) 66 | (?& ("\\wedge" "\\bigwedge")) 67 | (?~ ("\\sim" "\\approx" "\\propto")))) 68 | (setq cdlatex-parens-pairs "$[{(")) 69 | 70 | (use-package typst-ts-mode 71 | :vc (:fetcher sourcehut :repo "meow_king/typst-ts-mode") 72 | :mode ("\\.typ\\'" . typst-ts-mode)) 73 | 74 | (provide 'prelude-tex) 75 | -------------------------------------------------------------------------------- /volatile/20-highlight-tail.el.disabled: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | ;; Credit: K. Scarlet 4 | ;; Copied from Emacs China Telegram Group Chat 5 | 6 | (require 'highlight-tail) 7 | 8 | (setq highlight-tail-steps 40) 9 | (defvar blink-cursor-colors (list "#92c48f" "#6785c5" "#be369c" "#d9ca65")) 10 | (defvar blink-highlight-colors (list "#5D7E79" "#475E94" "#733780" "#808164")) 11 | (setq highlight-tail-colors '(("#5D7E79" . 0))) 12 | (highlight-tail-mode 1) 13 | (setq blink-highlight-fade-lists 14 | (mapcar (lambda (color) 15 | (setq highlight-tail-colors `((,color . 0))) 16 | (setq highlight-tail-colors-fade-list nil) 17 | (setq highlight-tail-colors-with-100 18 | (if (= (cdr (nth (1- (length highlight-tail-colors)) 19 | highlight-tail-colors)) 20 | 100) 21 | highlight-tail-colors 22 | (append highlight-tail-colors (list '(null . 100))))) 23 | (highlight-tail-add-colors-fade-table 'default) 24 | highlight-tail-colors-fade-list) 25 | blink-highlight-colors)) 26 | (highlight-tail-get-colors-fade-table-with-key 'default) 27 | (setq blink-cursor-count 0) 28 | (defun blink-cursor-timer-function () 29 | (when (not (internal-show-cursor-p)) 30 | (when (>= blink-cursor-count (length blink-cursor-colors)) 31 | (setq blink-cursor-count 0)) 32 | (let ((color (nth blink-cursor-count blink-cursor-colors)) 33 | (hl-color (nth blink-cursor-count blink-highlight-colors)) 34 | (hl-color-list (nth blink-cursor-count blink-highlight-fade-lists))) 35 | (set-cursor-color color) 36 | (setq highlight-tail-colors `((,hl-color . 0))) 37 | (setq highlight-tail-colors-fade-list nil 38 | highlight-tail-nonhtfaces-bgcolors nil 39 | highlight-tail-const-overlays-list nil 40 | highlight-tail-update-const-overlays-to-this-list nil 41 | highlight-tail-face-max nil) 42 | (setq highlight-tail-colors-with-100 43 | (if (= (cdr (nth (1- (length highlight-tail-colors)) 44 | highlight-tail-colors)) 45 | 100) 46 | highlight-tail-colors 47 | (append highlight-tail-colors (list '(null . 100))))) 48 | (highlight-tail-add-colors-fade-table 'start) 49 | (highlight-tail-add-colors-fade-table 'default) 50 | (setq highlight-tail-colors-fade-list hl-color-list) 51 | (setq highlight-tail-face-max highlight-tail-steps) 52 | (highlight-tail-make-faces 53 | (highlight-tail-get-colors-fade-table-with-key 'default)) 54 | (setq blink-cursor-count (+ 1 blink-cursor-count)))) 55 | (internal-show-cursor nil (not (internal-show-cursor-p)))) 56 | (define-globalized-minor-mode highlight-tail-global-mode highlight-tail-mode 57 | (lambda () (highlight-tail-mode 1))) 58 | (highlight-tail-global-mode 1) 59 | -------------------------------------------------------------------------------- /modules/prelude-workspace.el: -------------------------------------------------------------------------------- 1 | ;;; prelude-workspace.el --- -*- lexical-binding: t; -*- 2 | 3 | (use-package bufferlo 4 | :hook (after-init . bufferlo-mode) 5 | :config 6 | (diminish 'bufferlo-mode) 7 | (with-eval-after-load 'consult 8 | (defvar my:bufferlo-consult--source-local-buffers 9 | (list :name "Local Buffers" 10 | :narrow ?l 11 | :category 'buffer 12 | :face 'consult-buffer 13 | :history 'buffer-name-history 14 | :state #'consult--buffer-state 15 | :default t 16 | :items (lambda () (consult--buffer-query 17 | :predicate #'bufferlo-local-buffer-p 18 | :sort 'visibility 19 | :as #'buffer-name))) 20 | "Local Bufferlo buffer candidate source for `consult-buffer'.") 21 | 22 | (defvar my:bufferlo-consult--source-other-buffers 23 | (list :name "Other Buffers" 24 | :narrow ?b 25 | :category 'buffer 26 | :face 'consult-buffer 27 | :history 'buffer-name-history 28 | :state #'consult--buffer-state 29 | :items (lambda () (consult--buffer-query 30 | :predicate #'bufferlo-non-local-buffer-p 31 | :sort 'visibility 32 | :as #'buffer-name))) 33 | "Non-local Bufferlo buffer candidate source for `consult-buffer'.") 34 | 35 | (add-to-list 'consult-buffer-sources 'my:bufferlo-consult--source-other-buffers) 36 | (add-to-list 'consult-buffer-sources 'my:bufferlo-consult--source-local-buffers))) 37 | 38 | (use-package tabspaces 39 | :disabled 40 | :hook (after-init . tabspaces-mode) 41 | :commands (tabspaces-switch-or-create-workspace 42 | tabspaces-open-or-create-project-and-workspace) 43 | :custom 44 | ;; (tabspaces-session t) 45 | ;; (tabspaces-session-auto-restore t) 46 | (tab-bar-new-tab-choice "*scratch*") 47 | :bind 48 | (("C-c TAB TAB" . tabspaces-switch-or-create-workspace)) 49 | :config 50 | (setq tabspaces-use-filtered-buffers-as-default t 51 | tabspaces-default-tab "Default" 52 | tabspaces-remove-to-default t 53 | tabspaces-include-buffers '("*scratch*")) 54 | 55 | ;; consult integration 56 | (with-eval-after-load 'consult 57 | (consult-customize 58 | consult--source-buffer 59 | :hidden t 60 | :default nil 61 | :state nil 62 | :narrow ?b) 63 | (defvar consult--source-workspace 64 | (list :name "Workspace Buffers" 65 | :narrow ?w 66 | :history 'buffer-name-history 67 | :category 'buffer 68 | :state #'consult--buffer-state 69 | :default t 70 | :items (lambda () (consult--buffer-query 71 | :predicate #'tabspaces--local-buffer-p 72 | :sort 'visibility 73 | :as #'buffer-name)))) 74 | (add-to-list 'consult-buffer-sources 'consult--source-workspace))) 75 | 76 | (provide 'prelude-workspace) 77 | ;;; prelude-workspace.el ends here 78 | -------------------------------------------------------------------------------- /modules/prelude-git.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (with-eval-after-load 'diff-mode 4 | (keymap-unset diff-mode-map "M-o")) 5 | 6 | (use-package magit 7 | :defer t 8 | :iload (magit-base magit-git magit-mode magit-process magit-status magit-submodule magit) 9 | :bind (("C-x g" . magit-status)) 10 | :custom 11 | (magit-clone-set-remote.pushDefault t) 12 | (magit-clone-default-directory (expand-file-name (expand-file-name "src/Clone/" (getenv "HOME")))) 13 | (magit-refresh-status-buffer nil) 14 | (magit-display-buffer 'display-buffer) ;; I've done my own customization 15 | :bind (("C-c g" . magit-file-dispatch)) 16 | :hook ((magit-status-mode magit-diff-mode) . buffer-disable-undo) 17 | :config 18 | (setq magit-wip-mode +1) 19 | (when k|mac 20 | (setq magit-git-executable "/usr/local/bin/git")) 21 | (setq magit-tramp-pipe-stty-settings 'pty)) 22 | 23 | (use-package forge 24 | :after magit 25 | :config 26 | (setq forge-owned-accounts '(("ksqsf" . (remote-name "personal"))))) 27 | 28 | (defun github-parse-remote-url (remote) 29 | "Parse a git remote hosted on github to a (user,repo) pair. 30 | 31 | This function returns nil if it cannot parse REMOTE." 32 | (let ((ssh-regexp "git@github\\.com:\\(.*\\)/\\(.*\\)\\.git") 33 | (https-regexp "https://github.com/\\(.*\\)/\\(.*\\)\\.git")) 34 | (let ((maybe-ssh (string-match ssh-regexp remote))) 35 | (if maybe-ssh 36 | (cons (match-string-no-properties 1 remote) (match-string-no-properties 2 remote)) 37 | (let ((maybe-https (string-match https-regexp remote))) 38 | (if maybe-https 39 | (cons (match-string-no-properties 1 remote) (match-string-no-properties 2 remote)) 40 | nil)))))) 41 | 42 | (defun github-copy-reference-url-at-point () 43 | "Copy a link to the current line on the GitHub Web interface." 44 | (interactive) 45 | (require 'magit) 46 | (save-buffer) 47 | (let* ((remote (or (magit-get-remote) 48 | (magit-get-upstream-remote) 49 | (magit-get-remote "main") 50 | (magit-get-remote "master") 51 | (error "Unknown remote!"))) 52 | (remote-url (magit-git-str "remote" "get-url" remote)) 53 | (commit (magit-rev-parse "--short" "HEAD")) 54 | (user/repo (github-parse-remote-url remote-url)) 55 | (relative-path (file-relative-name buffer-file-name (vc-git-root buffer-file-name))) 56 | (locator (if (use-region-p) 57 | (format "L%d,L%d" (line-number-at-pos (use-region-beginning)) (line-number-at-pos (use-region-end))) 58 | (format "L%d" (line-number-at-pos)))) 59 | (url (format "https://github.com/%s/%s/blob/%s/%s#%s" (car user/repo) (cdr user/repo) commit relative-path locator))) 60 | (kill-new url) 61 | (message "%s" url))) 62 | 63 | ;; Inspired by https://twitter.com/samcraigjohnson/status/1747412804112703734 64 | (defun open-github-pullreq () 65 | "Visit the github pr creation link." 66 | (interactive) 67 | (magit-process-buffer) 68 | (goto-char (point-max)) 69 | (re-search-backward "https://github.com/[^ ]+pull[^ ]") 70 | (let ((url (buffer-substring (point) (pos-eol)))) 71 | (browse-url (s-trim url)))) 72 | 73 | (defun open-gitlab-mergereq () 74 | (interactive) 75 | (magit-process-buffer) 76 | (goto-char (point-max)) 77 | (re-search-backward "https://[^ ]+/merge_requests/[^ ]+") 78 | (let ((url (buffer-substring (point) (pos-eol)))) 79 | (browse-url (s-trim url)))) 80 | 81 | (provide 'prelude-git) 82 | -------------------------------------------------------------------------------- /modules/prelude-lang-rust.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;;; Rust 3 | 4 | (use-package rust-mode 5 | :hook (rust-mode . k|lsp-ensure) 6 | :hook (rust-mode . subword-mode) 7 | :hook (rust-mode . electric-pair-mode) 8 | :hook (rust-mode . cargo-minor-mode) 9 | :init 10 | (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode)) 11 | 12 | :config 13 | ;; Remove syntax-ppss-flush-cache to fix the performance issue with 14 | ;; adaptive-wrap-prefix-mode. See 15 | ;; https://github.com/rust-lang/rust-mode/issues/558 16 | (defun rust-in-comment-paragraph (body) 17 | (save-excursion 18 | (when (not (nth 4 (syntax-ppss))) 19 | (beginning-of-line) 20 | (when (looking-at (concat "[[:space:]\n]*" comment-start-skip)) 21 | (goto-char (match-end 0)))) 22 | 23 | (let ((next-bol (line-beginning-position 2))) 24 | (while (save-excursion 25 | (end-of-line) 26 | (and (nth 4 (syntax-ppss)) 27 | (save-excursion 28 | (beginning-of-line) 29 | (looking-at paragraph-start)) 30 | (looking-at "[[:space:]]*$") 31 | (nth 4 (syntax-ppss next-bol)))) 32 | (goto-char next-bol))) 33 | (when (save-excursion 34 | (and (nth 4 (syntax-ppss (line-beginning-position 1))) 35 | (looking-at "[[:space:]]*\\*/"))) 36 | (goto-char (line-end-position 0))) 37 | (funcall body)))) 38 | 39 | ;; support rustc output 40 | (with-eval-after-load 'compile 41 | (add-to-list 'compilation-error-regexp-alist 'rustc) 42 | (add-to-list 'compilation-error-regexp-alist-alist 43 | '(rustc . ("\\(?:\\(?:error\\)\\|\\(warning\\)\\).*?: .* 44 | +--> \\(.*?\\):\\([0-9]+\\):\\([0-9]+\\)" 2 3 4 (1))))) 45 | 46 | (use-package cargo 47 | :defer t) 48 | 49 | (use-package ob-rust 50 | :after org) 51 | 52 | (defun cargo-play (arg) 53 | (interactive "P") 54 | (let* ((release-flag (if arg "--release" "")) 55 | (command (format "cargo play %s %s &" release-flag (buffer-file-name)))) 56 | (shell-command command "*Cargo Play*"))) 57 | 58 | (defun cargo (package-name) 59 | "Create a new cargo package under /tmp." 60 | (interactive "sPackage name: ") 61 | (let ((old-pwd default-directory)) 62 | (cd "/tmp") 63 | (shell-command (concat "cargo new " package-name)) 64 | (cd old-pwd)) 65 | (find-file (concat "/tmp/" package-name "/src/main.rs"))) 66 | 67 | (defun rust-show (flags mode) 68 | (let* ((command (format "rustc %s -o /tmp/emacs-output %s" (mapconcat #'identity flags " ") (buffer-file-name))) 69 | (buffer (get-buffer-create "*Rust Show*"))) 70 | (shell-command command " *Rust Show Output*") 71 | (switch-to-buffer-other-window buffer) 72 | (insert-file-contents "/tmp/emacs-output") 73 | (funcall mode))) 74 | 75 | (defun rust-show-mir (arg) 76 | (interactive "P") 77 | (let* ((opt-flag (if arg "-O" ""))) 78 | (rust-show (list opt-flag "--emit=mir") #'fundamental-mode))) 79 | 80 | (defun rust-show-asm (arg) 81 | (interactive "P") 82 | (let* ((opt-flag (if arg "-O" ""))) 83 | (rust-show (list opt-flag "--emit=asm") #'asm-mode))) 84 | 85 | (defun rust-show-llvm-ir (arg) 86 | (interactive "P") 87 | (let* ((opt-flag (if arg "-O" ""))) 88 | (rust-show (list opt-flag "--emit=llvm-ir") #'fundamental-mode))) 89 | 90 | (defun rust (_arg) 91 | (interactive "P") 92 | (find-file "/tmp/play.rs")) 93 | 94 | (use-package pest-mode 95 | :load-path "lisp/pest-mode" 96 | :mode ("\\.pest\\'" . pest-mode)) 97 | 98 | (provide 'prelude-lang-rust) 99 | -------------------------------------------------------------------------------- /modules/prelude-evil.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | ;; Currently, I consider this an ``addition'' -- my config still works if you disable this. 4 | ;; Therefore, evil is loaded AFTER other modules, and other modules should not assume the presence of evil. 5 | 6 | (use-package evil 7 | :demand t 8 | :init 9 | (setq evil-respect-visual-line-mode t) 10 | (setq evil-want-abbrev-expand-on-insert-exit t) 11 | (setq evil-want-keybinding nil) 12 | (setq evil-undo-system 'undo-redo) 13 | (setq evil-disable-insert-state-bindings t) 14 | :bind (:map evil-normal-state-map 15 | (("C-e" . end-of-line) 16 | ("C-r" . isearch-backward) 17 | ("U" . evil-redo) 18 | ("M-." . xref-find-definitions)) 19 | :map evil-visual-state-map 20 | (("DEL" . delete-region))) 21 | :config 22 | (evil-mode t)) 23 | 24 | (use-package evil-collection 25 | :after evil 26 | :demand t 27 | :config 28 | (evil-collection-init) 29 | (diminish 'evil-collection-unimpaired-mode)) 30 | 31 | (use-package evil-surround 32 | :after evil 33 | :demand t 34 | :config 35 | (global-evil-surround-mode 1)) 36 | 37 | (use-package evil-org 38 | :after (evil org) 39 | :demand t 40 | :config 41 | (require 'evil-org-agenda) 42 | (evil-org-agenda-set-keys)) 43 | 44 | ;;; Quit corfu on Escape 45 | (with-eval-after-load 'corfu 46 | (keymap-set corfu-map "C-j" 'corfu-next) 47 | (kemapp-set corfu-map "C-k" 'corfu-previous) 48 | (evil-define-key 'insert corfu-map (kbd "") 'corfu-quit)) 49 | 50 | ;;; Leader key 51 | (use-package evil-leader 52 | :demand t 53 | :init 54 | (global-evil-leader-mode) 55 | :config 56 | (evil-leader/set-leader "")) 57 | 58 | ;;; Keybindings 59 | (evil-leader/set-key 60 | "SPC" 'projectile-switch-to-buffer 61 | ;; file 62 | "fb" 'bookmark-bmenu-list 63 | ;; buffer 64 | "bi" 'ibuffer 65 | "bb" 'switch-to-buffer 66 | "bc" 'clone-indirect-buffer 67 | "bd" 'delete-trailing-lines 68 | "bu" 'revert-buffer 69 | ;; projectile 70 | "pp" 'projectile-switch-project 71 | "ps" 'projectile-vterm 72 | "pb" 'projectile-ibuffer 73 | "pk" 'projectile-kill-buffers 74 | "pg" 'projectile-grep 75 | "pa" 'projectile-find-other-file-other-window 76 | ;; profiler 77 | "PP" 'profiler-start 78 | "PS" 'profiler-stop 79 | ;; gptel 80 | "gg" 'gptel 81 | "gm" 'gptel-menu 82 | "gp" 'gptel-system-prompt 83 | "gs" 'gptel-send) 84 | 85 | (with-eval-after-load 'haskell-mode 86 | (evil-define-key 'normal haskell-mode-map (kbd "gz") 'haskell-interactive-switch)) 87 | 88 | (with-eval-after-load 'gptel 89 | (evil-define-key 'motion 'global (kbd "gs") 'gptel-send) 90 | (evil-define-key 'visual 'global (kbd "gs") 'gptel-send)) 91 | 92 | (with-eval-after-load 'org 93 | (evil-define-key 'normal org-mode-map [tab] 'org-cycle) 94 | (evil-define-key 'insert org-mode-map [tab] 'org-metaright) 95 | (evil-define-key 'insert org-mode-map [S-tab] 'org-metaleft)) 96 | 97 | (with-eval-after-load 'telega 98 | ;; Emacs-mode by default in telega-chat-mode 99 | (push 'telega-chat-mode evil-emacs-state-modes)) 100 | 101 | (with-eval-after-load 'sqlite-mode 102 | (evil-define-key 'normal sqlite-mode-map (kbd "RET") 'sqlite-mode-list-data) 103 | (evil-define-key 'normal sqlite-mode-map (kbd "g c") 'sqlite-mode-list-columns) 104 | (evil-define-key 'normal sqlite-mode-map (kbd "g r") 'sqlite-mode-list-tables) 105 | (evil-define-key 'normal sqlite-mode-map (kbd "DEL") 'sqlite-mode-delete)) 106 | 107 | (provide 'prelude-evil) 108 | -------------------------------------------------------------------------------- /init.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;; I don't support any version other than what I use. 3 | (when (version< emacs-version "30") 4 | (warn "This configuration is only tested on Emacs 30")) 5 | 6 | ;; Hacks for speeding up initialization. 7 | (defconst +file-name-handler-alist file-name-handler-alist) 8 | (setq file-name-handler-alist nil) 9 | (setq gc-cons-threshold most-positive-fixnum) 10 | ;; Packages should have been made available. Disable it to speed up 11 | ;; installing packages during initialization. 12 | (setq package-quickstart nil) 13 | 14 | ;; Directories. 15 | (defvar prelude-lisp-dir (expand-file-name "lisp" user-emacs-directory) 16 | "This directory contains third-party Lisp files.") 17 | (defvar prelude-modules-dir (expand-file-name "modules" user-emacs-directory) 18 | "The directory contains all modules.") 19 | (defvar prelude-volatile-dir (expand-file-name "volatile" user-emacs-directory) 20 | "This directory contains volatile configuration. All Lisp 21 | files are loaded automatically. You shouldn't byte-compile 22 | these Lisp files. To disable a file, just change the extension 23 | from .el to whatever else.") 24 | (add-to-list 'load-path prelude-lisp-dir) 25 | (add-to-list 'load-path prelude-modules-dir) 26 | 27 | ;; Replace Emacs paths early -- before doing anything. 28 | (use-package no-littering 29 | :ensure t 30 | :demand t 31 | :config 32 | (setq auto-save-file-name-transforms 33 | `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))) 34 | 35 | ;;; Set up autoloads from prelude-lisp-dir. 36 | (require 'prelude-loaddefs) 37 | 38 | ;; Modules. 39 | (require 'prelude-package) 40 | (require 'prelude-iload) 41 | (require 'prelude-common) 42 | 43 | (require 'prelude-benchmark) 44 | (require 'prelude-core) 45 | (require 'prelude-ui) 46 | (require 'prelude-term) 47 | (require 'prelude-project) 48 | (require 'prelude-completion) 49 | (require 'prelude-workspace) 50 | 51 | (require 'prelude-search) 52 | (require 'prelude-ibuffer) 53 | (require 'prelude-chinese) 54 | (require 'prelude-dired) 55 | (require 'prelude-git) 56 | (require 'prelude-tex) 57 | (require 'prelude-org) 58 | (require 'prelude-blog) 59 | 60 | (require 'prelude-prog) 61 | (require 'prelude-lang-lisp) 62 | (require 'prelude-lang-cc) 63 | (require 'prelude-lang-python) 64 | (require 'prelude-lang-rust) 65 | (require 'prelude-lang-coq) 66 | (require 'prelude-lang-lean) 67 | (require 'prelude-lang-ml) 68 | (require 'prelude-lang-js) 69 | (require 'prelude-lang-haskell) 70 | (require 'prelude-lang-agda) 71 | (require 'prelude-lang-web) 72 | (require 'prelude-lang-zig) 73 | (require 'prelude-lang-swift) 74 | (require 'prelude-lang-lua) 75 | 76 | (require 'prelude-help) 77 | (require 'prelude-os) 78 | (require 'prelude-mail) 79 | (require 'prelude-apps) 80 | (require 'prelude-irc) 81 | (require 'prelude-ai) 82 | 83 | ;; (require 'prelude-evil) 84 | ;;(require 'prelude-nix) 85 | 86 | ;; Volatile. 87 | (when (file-exists-p prelude-volatile-dir) 88 | (message "Loading volatile configuration files in %s..." prelude-volatile-dir) 89 | (mapc 'load (directory-files prelude-volatile-dir 't "^[^#\.].*el$"))) 90 | 91 | ;; Customization. 92 | (load custom-file t) 93 | 94 | ;; End of hacks. 95 | (setq file-name-handler-alist +file-name-handler-alist) 96 | (setq gc-cons-threshold 16777216) ;; 16mb 97 | ;; Re-enable package-quickstart. 98 | (setq package-quickstart t) 99 | 100 | (put 'dired-find-alternate-file 'disabled nil) 101 | (put 'list-timers 'disabled nil) 102 | (put 'window-swap-states 'disabled t) 103 | (put 'narrow-to-page 'disabled nil) 104 | (put 'narrow-to-region 'disabled nil) 105 | (put 'erase-buffer 'disabled nil) 106 | -------------------------------------------------------------------------------- /lisp/fish-protector.el: -------------------------------------------------------------------------------- 1 | ;;; fish-protector.el --- Protect your fish -*- lexical-binding: t; -*- 2 | 3 | ;; THIS FILE IS A WORK IN PROGRESS. 4 | 5 | ;; 魚之護衛者 Fish Protector tries to protect your fish by monitoring 6 | ;; your use of non-focus buffers. 7 | 8 | (defvar fp-alist nil) 9 | 10 | (defvar fp--timer nil) 11 | (defvar fp--timetable (make-hash-table)) 12 | (defvar fp--callbacks (make-hash-table)) 13 | 14 | ;;;###autoload 15 | (defun fp-start () 16 | (interactive) 17 | (unless fp--timer 18 | (setq fp--timer (run-with-timer nil 1 'fp--tick)))) 19 | 20 | ;;;###autoload 21 | (defun fp-stop () 22 | (interactive) 23 | (cancel-timer fp--timer) 24 | (setq fp--timer nil)) 25 | 26 | ;;;###autoload 27 | (defun fp-reset () 28 | (interactive) 29 | (clrhash fp--timetable) 30 | (when fp--timer 31 | (fp-stop) 32 | (fp-start))) 33 | 34 | ;;;###autoload 35 | (defun fp-show-stats () 36 | (interactive) 37 | (with-current-buffer (get-buffer-create "*fish-protector*") 38 | (erase-buffer) 39 | (maphash (lambda (group time) 40 | (insert (format "%s\t%d\n" group time))) 41 | fp--timetable) 42 | (pop-to-buffer (current-buffer)))) 43 | 44 | (defun fp-alert (message) 45 | (let ((buf (get-buffer-create "*fish-protector-alert*"))) 46 | (with-current-buffer buf 47 | (insert "-----\n") 48 | (insert message) 49 | (insert "\n-----\n\n") 50 | (display-buffer (current-buffer)) 51 | (goto-char (point-max))))) 52 | 53 | (defun fp-add-reach-limit (group limit-secs callback) 54 | (cl-assert (and (symbolp group) 55 | (integerp limit-secs))) 56 | (puthash group (cons limit-secs callback) fp--callbacks)) 57 | 58 | (defun fp-remove-limit-callback (group) 59 | (remhash group fp--callbacks)) 60 | 61 | (defun fp--tick () 62 | (when (selected-frame) 63 | (let ((buf-group (catch 'found 64 | (dolist (rule fp-alist) 65 | (when (fp--group-rule-match-p (cdr rule) (current-buffer)) 66 | (throw 'found (car rule))))))) 67 | (when buf-group 68 | (let* ((old (gethash buf-group fp--timetable 0)) 69 | (new (1+ old))) 70 | (puthash buf-group new fp--timetable) 71 | (let* ((limit/cb (gethash buf-group fp--callbacks (cons most-positive-fixnum 'ignore))) 72 | (limit (car limit/cb)) 73 | (cb (cdr limit/cb))) 74 | (when (> new limit) 75 | (funcall cb new)))))))) 76 | 77 | ;; Test this file: (fp--group-rule-match-p '(project ".emacs.d")) 78 | 79 | (defun fp--group-rule-match-p (rule &optional buffer) 80 | (setq buffer (or buffer (current-buffer))) 81 | (pcase-exhaustive rule 82 | ;; Always match 83 | ('t t) 84 | ('nil nil) 85 | 86 | ;; (and rules...) 87 | (`(and . ,rules) 88 | (catch 'foo 89 | (dolist (rule rules) 90 | (unless (fp--group-rule-match-p rule buffer) 91 | (throw 'foo nil))) 92 | t)) 93 | 94 | ;; (or rules...) 95 | (`(or . ,rules) 96 | (catch 'foo 97 | (dolist (rule rules) 98 | (when (fp--group-rule-match-p rule buffer) 99 | (throw 'foo t))) 100 | nil)) 101 | 102 | ;; Major modes 103 | ((or `(mode ,mode) 104 | `(major ,mode) 105 | `(major-mode ,mode)) 106 | (derived-mode-p mode)) 107 | 108 | ;; Minor modes 109 | ((or `(minor ,mode) 110 | `(minor-mode ,mode)) 111 | (and (boundp mode) 112 | (eval mode))) 113 | 114 | ;; File names 115 | ((pred stringp) 116 | (and buffer-file-name 117 | (string-match rule buffer-file-name))) 118 | ((or `(file ,regexp) 119 | `(path ,regexp)) 120 | (and buffer-file-name 121 | (string-match regexp buffer-file-name))) 122 | 123 | ;; Projects 124 | (`(project ,regexp) 125 | (and buffer-file-name 126 | (let* ((proj (project-current)) 127 | (root (and proj (project-root proj)))) 128 | (if root 129 | (string-match regexp root) 130 | nil)))) 131 | 132 | ;; Arbitrary predicates 133 | (`(pred ,p) 134 | (funcall p buffer)))) 135 | 136 | (provide 'fish-protector) 137 | ;;; fish-protector.el ends here 138 | ;; Local Variables: 139 | ;; read-symbol-shorthands: (("fp-" . "fish-protector-")) 140 | ;; End: 141 | -------------------------------------------------------------------------------- /modules/prelude-os.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | ;; talk with os-level utilities 4 | 5 | (defun json-format () 6 | (interactive) 7 | (save-excursion 8 | (shell-command-on-region (mark) (point) "python3 -m json.tool" (buffer-name) t))) 9 | 10 | (defun open-directory-here () 11 | "Open the current directory in the GUI file manager. 12 | 13 | In most cases, this means the current directory of the current buffer." 14 | (interactive) 15 | (k|with-suppressed-message 16 | (shell-command (format "%s %s" k|default-opener (shell-quote-argument (expand-file-name default-directory)))))) 17 | (global-set-key (kbd "C-c d") #'open-directory-here) 18 | 19 | (defun open-term-here () 20 | "Open `default-directory' in iTerm2." 21 | (interactive) 22 | (k|with-suppressed-message 23 | (shell-command (format "open -a iTerm %s" (shell-quote-argument (expand-file-name default-directory)))))) 24 | (defalias 'iterm2 'open-term-here) 25 | 26 | (when k|mac 27 | (defun dic () 28 | (interactive) 29 | (shell-command-to-string 30 | (format "open dict://%s" (word-at-point))))) 31 | 32 | ;; 33 | ;; Recenter the frame on startup. 34 | ;; The following code is from https://christiantietze.de/posts/2021/06/emacs-center-window-on-current-monitor/ 35 | ;; 36 | (defun ct/frame-monitor-usable-height (&optional frame) 37 | "Return the usable height in pixels of the monitor of FRAME. 38 | FRAME can be a frame name, a terminal name, or a frame. 39 | If FRAME is omitted or nil, use currently selected frame. 40 | 41 | Uses the monitor's workarea. See `display-monitor-attributes-list'." 42 | (cadddr (frame-monitor-workarea frame))) 43 | 44 | (defun ct/frame-monitor-usable-width (&optional frame) 45 | "Return the usable width in pixels of the monitor of FRAME. 46 | FRAME can be a frame name, a terminal name, or a frame. 47 | If FRAME is omitted or nil, use currently selected frame. 48 | 49 | Uses the monitor's workarea. See `display-monitor-attributes-list'." 50 | (caddr (frame-monitor-workarea frame))) 51 | 52 | (defun ct/center-box (w h cw ch) 53 | "Center a box inside another box. 54 | 55 | Returns a list of `(TOP LEFT)' representing the centered position 56 | of the box `(w h)' inside the box `(cw ch)'." 57 | (list (/ (- cw w) 2) (/ (- ch h) 2))) 58 | 59 | (defun ct/frame-get-center (frame) 60 | "Return the center position of FRAME on it's display." 61 | (ct/center-box (frame-pixel-width frame) (frame-pixel-height frame) 62 | (ct/frame-monitor-usable-width frame) (ct/frame-monitor-usable-height frame))) 63 | 64 | (defun ct/frame-center (&optional frame) 65 | "Center a frame on the screen." 66 | (interactive) 67 | (let* ((frame (or frame (selected-frame))) 68 | (center (ct/frame-get-center frame))) 69 | (apply 'set-frame-position (flatten-list (list frame center))))) 70 | 71 | ;;; WSL2 72 | ;;; Requires 'wslu' (https://wslutiliti.es/wslu/install.html) 73 | (defun wsl-copy (beg end) 74 | "In a WSL2 environment, copy region to the system clipboard." 75 | (interactive "r") 76 | (k|with-suppressed-message 77 | (let ((default-directory "/")) 78 | (shell-command-on-region beg end "iconv -f utf-8 -t utf16le | clip.exe" " *wsl-copy*"))) 79 | (deactivate-mark)) 80 | 81 | (defun wsl-get-clipboard () 82 | "In a WSL2 environment, get the clipboard text." 83 | (let ((clipboard 84 | (let ((default-directory "/")) 85 | (shell-command-to-string "powershell.exe -command 'Get-Clipboard' 2>/dev/null") 86 | ;; WSLu utility 87 | ;; (shell-command-to-string "wslclip --get") 88 | ))) 89 | (setq clipboard (replace-regexp-in-string "\r" "" clipboard)) 90 | (setq clipboard (substring clipboard 0 -1)) 91 | clipboard)) 92 | 93 | (defun wsl-paste () 94 | "In a WSL2 environment, paste the text from the system clipboard." 95 | (interactive) 96 | (cond 97 | ((derived-mode-p 'vterm-mode) 98 | (vterm-insert (wsl-get-clipboard))) 99 | ((derived-mode-p 'eat-mode) 100 | (eat-term-send-string-as-yank eat-terminal (wsl-get-clipboard))) 101 | (t 102 | (insert (wsl-get-clipboard))))) 103 | 104 | (when k|wsl 105 | (advice-add 'gui-select-text :before 106 | (lambda (text) 107 | (when select-enable-clipboard 108 | (with-temp-buffer 109 | (insert text) 110 | (wsl-copy (point-min) (point-max))))))) 111 | 112 | (provide 'prelude-os) 113 | -------------------------------------------------------------------------------- /lisp/flygpt.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (require 'gptel) 4 | (require 'flymake) 5 | 6 | (defconst flygpt-prompt 7 | "You are a professional programming expert, and are asked to find any potential problems in the code, including bugs, security vulnerabilities, style issues, and documentation/comment conformance. 8 | 9 | You report your findings in JSON format: 10 | [{ \"line\": , \"diag\": }, 11 | ... ] 12 | 13 | If nothing is wrong, reply [].") 14 | 15 | (defun line-numbered-string (str) 16 | (with-temp-buffer 17 | (setq cnt 0) 18 | (dolist (line (split-string str "\n")) 19 | (cl-incf cnt) 20 | (insert (format "%d: %s\n" cnt line))) 21 | (buffer-string))) 22 | 23 | ;;;###autoload 24 | (defun flygpt-buffer () 25 | (interactive) 26 | (let* ((source-buffer (current-buffer)) 27 | (url-request-method "POST") 28 | (url-request-extra-headers 29 | `(("Content-Type" . "application/json") 30 | ("Authorization" . ,(concat "Bearer " (gptel-api-key-from-auth-source))))) 31 | (url-proxy-services '(("https" . "127.0.0.1:7890"))) 32 | (url-request-data (json-encode `(("model" . "gpt-3.5-turbo-16k") 33 | ("temperature" . 0) 34 | ("messages" . [(:role "system" :content ,flygpt-prompt) 35 | (:role "user" :content ,(line-numbered-string (buffer-string)))]))))) 36 | (url-retrieve 37 | "https://api.openai.com/v1/chat/completions" 38 | (lambda (status) 39 | (goto-char (point-min)) 40 | (search-forward-regexp "^$") 41 | (let* ((json-object-type 'plist) 42 | (api-resp (json-read)) 43 | (diags-json (plist-get (plist-get (seq-first (plist-get api-resp :choices)) :message) :content)) 44 | (diags (with-temp-buffer 45 | (insert diags-json) 46 | (goto-char (point-min)) 47 | (json-read)))) 48 | (with-current-buffer (get-buffer-create "*flygpt*") 49 | (erase-buffer) 50 | (cl-loop 51 | for diag in (seq-into diags 'list) 52 | for msg = (plist-get diag :diag) 53 | for line = (plist-get diag :line) 54 | do (insert (format "Line %d: %s\n" line msg)) 55 | finally (display-buffer "*flygpt*")))))))) 56 | 57 | (setq flymake-gpt-buffer nil) 58 | 59 | (defun flymake-gpt (report-fn &rest _) 60 | (when flymake-gpt-buffer 61 | (kill-process (get-buffer-process flymake-gpt-buffer)) 62 | (kill-buffer flymake-gpt-buffer)) 63 | (let* ((source-buffer (current-buffer)) 64 | (url-request-method "POST") 65 | (url-request-extra-headers 66 | `(("Content-Type" . "application/json") 67 | ("Authorization" . ,(concat "Bearer " (gptel-api-key-from-auth-source))))) 68 | (url-proxy-services '(("https" . "127.0.0.1:7890"))) 69 | (url-request-data (json-encode `(("model" . "gpt-3.5-turbo-16k") 70 | ("temperature" . 0) 71 | ("messages" . [(:role "system" :content ,flygpt-prompt) 72 | (:role "user" :content ,(line-numbered-string (buffer-string)))]))))) 73 | (url-retrieve 74 | "https://api.openai.com/v1/chat/completions" 75 | (lambda (status) 76 | (goto-char (point-min)) 77 | (search-forward-regexp "^$") 78 | (let* ((json-object-type 'plist) 79 | (api-resp (json-read)) 80 | (diags-json (plist-get (plist-get (seq-first (plist-get api-resp :choices)) :message) :content)) 81 | (diags (with-temp-buffer 82 | (insert diags-json) 83 | (goto-char (point-min)) 84 | (json-read)))) 85 | (cl-loop 86 | for diag in (seq-into diags 'list) 87 | for msg = (plist-get diag :diag) 88 | for line = (plist-get diag :line) 89 | for (beg . end) = (flymake-diag-region source-buffer line) 90 | collect (flymake-make-diagnostic source-buffer beg end :warning msg) 91 | into flymake-diags 92 | finally (funcall report-fn flymake-diags)) 93 | (kill-buffer flymake-gpt-buffer) 94 | (setq flymake-gpt-buffer nil)))))) 95 | 96 | ;;;###autoload 97 | (defun turn-on-flymake-gpt () 98 | (interactive) 99 | (add-hook 'flymake-diagnostic-functions 'flymake-gpt nil t) 100 | (setq flymake-no-changes-timeout nil) 101 | (flymake-mode)) 102 | 103 | (provide 'flygpt) 104 | -------------------------------------------------------------------------------- /modules/prelude-term.el: -------------------------------------------------------------------------------- 1 | ;;; prelude-term.el --- In-Emacs terminal settings -*- lexical-binding: t; -*- 2 | 3 | ;;; Code: 4 | 5 | ;; Do not query on exit if the term process has no running child process. 6 | (advice-add 'process-kill-buffer-query-function :before-until 7 | (lambda (&rest args) 8 | (and (derived-mode-p 'vterm-mode 'shell-mode 'term-mode 'comint-mode 'eat-mode) 9 | (and (get-buffer-process (current-buffer)) 10 | (not (process-running-child-p (get-buffer-process (current-buffer)))))))) 11 | 12 | ;; Eat 13 | (use-package eat 14 | :bind 15 | (("M-g v" . eat) ;; Take place of vterm-toggle 16 | :map eat-semi-char-mode-map 17 | ("M-o" . other-window)) 18 | :config 19 | (setq eat-kill-buffer-on-exit t) 20 | (setq eat-enable-blinking-text t) 21 | ;; Note: this integration only works if $TERM starts with "eat". 22 | ;; So you cannot set $TERM to xterm. 23 | (setq eat-enable-directory-tracking t)) 24 | 25 | ;; VTerm 26 | (use-package vterm 27 | :disabled 28 | :commands (vterm) 29 | :bind (:map vterm-mode-map 30 | ("C-c C-t" . vterm-copy-mode) 31 | ("M-!" . shell-command)) 32 | :custom 33 | (vterm-always-compile-module t) 34 | :hook (vterm-mode . goto-address-mode) 35 | :config 36 | 37 | ;; Integration with desktop-save-mode 38 | (defvar vterm-persist-buffer-contents t 39 | "When t, desktop-save-mode also saves the buffer contents.") 40 | (defun vterm-save-desktop-buffer (dirname) 41 | (cons 42 | (desktop-file-name default-directory dirname) 43 | (if vterm-persist-buffer-contents (buffer-string) ""))) 44 | (defun vterm-restore-desktop-buffer (_filename buffer-name misc) 45 | "MISC is the saved return value of `desktop-save-vterm'." 46 | (let ((default-directory (car misc))) 47 | (require 'vterm) 48 | (with-current-buffer (get-buffer-create buffer-name) 49 | (when vterm-persist-buffer-contents 50 | (insert (cdr misc)) 51 | (insert "\n\n")) 52 | (vterm-mode)))) 53 | (add-to-list 'desktop-buffer-mode-handlers '(vterm-mode . vterm-restore-desktop-buffer)) 54 | (add-hook 'vterm-mode-hook #'(lambda () (setq-local desktop-save-buffer 'vterm-save-desktop-buffer)))) 55 | 56 | ;; (use-package vterm-toggle 57 | ;; :bind ("M-g v" . vterm-toggle)) 58 | 59 | ;; Dropdown terminal 60 | (defun drop-down-term () 61 | "Open a drop-down terminal in the same directory as the current file." 62 | (interactive) 63 | (let ((buffer (get-buffer-create "*dd-term*")) 64 | win) 65 | (with-current-buffer buffer 66 | (unless (eq major-mode 'eat-mode) 67 | (eat-mode)) 68 | (unless (and eat-terminal 69 | (eat-term-parameter eat-terminal 'eat--process)) 70 | (eat-exec buffer (buffer-name) "/usr/bin/env" nil 71 | (list "sh" "-c" shell-file-name)))) 72 | (setq win 73 | (display-buffer-in-side-window 74 | buffer 75 | '((side . top) 76 | (dedicated . t)))) 77 | (select-window win))) 78 | 79 | (defalias 'dd-term 'drop-down-term) 80 | (defalias 'ddt 'drop-down-term) 81 | 82 | (provide 'prelude-term) 83 | ;;; prelude-term.el ends here 84 | 85 | 86 | ;;; In the past I tried to parametrize my choice of terminal. But 87 | ;;; different terminal packages behave too differently. I think I will 88 | ;;; just stick to vterm or eat. 89 | 90 | ;; ;; Allow setting preferred terminal emulator. 91 | ;; ;; Think 'pterm' as (1) preferred term, (2) prelude-term, or (3) parametric term. 92 | ;; (defcustom prelude-term 'vterm 93 | ;; "Preferred terminal emulator. Used by project." 94 | ;; :group 'prelude 95 | ;; :type 'symbol) 96 | 97 | ;; (defun pterm (&rest args) 98 | ;; (interactive) 99 | ;; (cond ((eq prelude-term 'eat) 100 | ;; (funcall 'eat nil args)) 101 | ;; (t 102 | ;; (funcall 'vterm args)))) 103 | 104 | ;; (defun pterm-other-window (&rest args) 105 | ;; (interactive) 106 | ;; (cond ((eq prelude-term 'eat) 107 | ;; (funcall 'eat-other-window nil args)) 108 | ;; (t 109 | ;; (funcall 'vterm-other-window args)))) 110 | 111 | ;; (defun pterm-run-in-current-buffer () 112 | ;; (interactive) 113 | ;; (cond ((eq prelude-term 'eat) 114 | ;; (progn 115 | ;; (unless (derived-mode-p 'eat-mode) 116 | ;; (let ((program (or explicit-shell-file-name 117 | ;; (getenv "ESHELL") 118 | ;; shell-file-name))) 119 | ;; (eat-mode) 120 | ;; (eat-exec (current-buffer) (buffer-name) "/usr/bin/env" nil 121 | ;; (list "sh" "-c" program)))))) 122 | ;; (t 123 | ;; (unless (derived-mode-p 'vterm-mode) 124 | ;; (vterm-mode))))) 125 | -------------------------------------------------------------------------------- /lisp/octo.el: -------------------------------------------------------------------------------- 1 | ;;; octo.el --- Multi-root window management in Emacs -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2025 ksqsf 4 | 5 | ;; Author: ksqsf 6 | ;; Keywords: convenience 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; octo.el is an opinionated, experimental library for multi-root 24 | ;; window management. With octo.el, you can: 25 | ;; 26 | ;; - Create "gen"-windows. They are virtual root windows where 27 | ;; window-management commands are effective. 28 | ;; 29 | ;; - Focus on one gen window, so that window commands ONLY affect 30 | ;; windows in the currently focused gen window. 31 | ;; 32 | ;; * Usage 33 | ;; 34 | ;; 1) Activate `octo-mode'. 35 | ;; 36 | ;; 2) Create your top-level layout, then call `octo-activate'. 37 | ;; 38 | ;; At this point, each child (internal or not) of the root window will 39 | ;; be a Gen window. And, for example, `C-x 1' will be confined to the 40 | ;; currently selected Gen window. 41 | 42 | ;;; Code: 43 | 44 | (defun octo--set-gen (win flag) 45 | "Mark whether window WIN is a gen according to FLAG." 46 | (set-window-parameter win 'octo-is-gen flag)) 47 | 48 | (defun octo--gen-p (win) 49 | "Check if window WIN is a gen window." 50 | (window-parameter win 'octo-is-gen)) 51 | 52 | (defun octo--gen (&optional win) 53 | "Find the gen window of window WIN, or the selected window if nil." 54 | (cond 55 | ((eq win nil) (octo--gen (selected-window))) 56 | ((frame-root-window-p win) win) 57 | ((octo--gen-p win) win) 58 | (t (octo--gen (window-parent win))))) 59 | 60 | (defun octo-delete-windows () 61 | "Delete all windows belong to the current gen." 62 | (interactive) 63 | (delete-window (octo--gen))) 64 | 65 | (defun octo-split-window-1 (fun) 66 | "Split window while maintaining GEN flags." 67 | ;; Always create a Gen internal window when splitting a Gen leaf window. 68 | (let* ((win (selected-window)) 69 | (win-is-gen (octo--gen-p win)) 70 | (window-combination-limit (or win-is-gen window-combination-limit)) 71 | (oldp (window-parent win)) 72 | (new (funcall fun)) 73 | (newp (window-parent win))) 74 | ;; If a new parent window is installed, transfer the GEN flag to it. 75 | (unless (eq oldp newp) 76 | (octo--set-gen newp (octo--gen-p win)) 77 | (octo--set-gen win nil)) 78 | ;; Return the new window. 79 | new)) 80 | 81 | (defun octo-split-window-below () 82 | "Split window below while maintaining GEN flags." 83 | (interactive) 84 | (octo-split-window-1 #'split-window-below)) 85 | 86 | (defun octo-split-window-right () 87 | "Split window right while maintaining GEN flags." 88 | (interactive) 89 | (octo-split-window-1 #'split-window-right)) 90 | 91 | (defun octo-delete-window () 92 | "Delete window while maintaing GEN flags." 93 | (interactive) 94 | ;; Deletion of a leaf window may result in deletion of its parent 95 | ;; internal window. 96 | (let* ((win (selected-window)) 97 | (parent (window-parent win))) 98 | (when (eq win (octo--gen win)) 99 | (error "Cannot delete the sole window.")) 100 | (when (<= (window-child-count parent) 2) 101 | (let ((sibling (or (window-next-sibling win) 102 | (window-prev-sibling win)))) 103 | (octo--set-gen sibling (octo--gen-p parent)))) 104 | (delete-window win))) 105 | 106 | (defun octo-delete-other-windows () 107 | "Mark the current window fill its gen." 108 | (interactive) 109 | (let* ((win (selected-window)) 110 | (gen (octo--gen win))) 111 | (cond 112 | ((eq win gen) 113 | (message "No other windows to delete.")) 114 | (t 115 | (delete-other-windows-internal win gen) 116 | (octo--set-gen win t))))) 117 | 118 | (defun octo-deactivate () 119 | "Clear all GEN flags." 120 | (interactive) 121 | (dolist (win (window-list)) 122 | (octo--set-gen win nil))) 123 | 124 | (defun octo-activate () 125 | "Set each child window of the root window to be a new gen window." 126 | (interactive) 127 | (octo-deactivate) 128 | (let* ((root (frame-root-window)) 129 | (cur (window-child root))) 130 | (while cur 131 | (octo--set-gen cur t) 132 | (setq cur (window-next-sibling cur))))) 133 | 134 | (defvar octo-mode-map 135 | (let ((map (make-sparse-keymap))) 136 | (define-key map (kbd "C-x 0") 'octo-delete-window) 137 | (define-key map (kbd "C-x 1") 'octo-delete-other-windows) 138 | (define-key map (kbd "C-x 2") 'octo-split-window-below) 139 | (define-key map (kbd "C-x 3") 'octo-split-window-right) 140 | map) 141 | "Keymap for `octo-mode`.") 142 | 143 | ;;;###autoload 144 | (define-minor-mode octo-mode 145 | "A global minor mode with custom window management key bindings." 146 | :lighter " Octo" 147 | :keymap octo-mode-map 148 | :global t) 149 | 150 | (provide 'octo) 151 | ;;; octo.el ends here 152 | -------------------------------------------------------------------------------- /lisp/lojban.el: -------------------------------------------------------------------------------- 1 | ;;; lojban.el --- The language environment for Lojban, a logical language -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 ksqsf 4 | 5 | ;; Author: ksqsf 6 | ;; Keywords: i18n, languages 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; This package currently contains required setup and a Quail input 24 | ;; method for Zbalermorna, a writing system for Lojban. 25 | 26 | ;; To display zbalermorna text correctly, you will need 27 | ;; 28 | ;; 1. Download and install a font for zbalermorna from 29 | ;; https://github.com/jackhumbert/zbalermorna/tree/master/fonts/. 30 | ;; 31 | ;; (OPTIONAL) If you have other fonts that conflict with 32 | ;; zbalermorna codepoints, consider using setting 33 | ;; `zbalermorna-font'. 34 | ;; 35 | ;; 2. Call `zbalermorna-setup', or use and choose Lojban, 36 | ;; at least once. 37 | 38 | ;; To globally enable zbalermorna, consider adding config to your init 39 | ;; file: 40 | ;; 41 | ;; (require 'lojban) 42 | ;; (setq zbalermorna-font "Crisa") 43 | ;; (zbalermorna-setup) 44 | 45 | ;;; Code: 46 | 47 | (require 'quail) 48 | 49 | (defun zbalermorna-font-setup (font-family) 50 | "Specify FONT-FAMILY to be used for the Unicode range of zbalermorna. 51 | 52 | The fonts can be downloaded from https://github.com/jackhumbert/zbalermorna/tree/master/fonts/. 53 | 54 | Example: (zbalermorna-font-setup \"Crisa\")" 55 | (set-fontset-font t '(#xed80 . #xedbf) font-family)) 56 | 57 | (defgroup lojban nil 58 | "The customization group for Lojban, a logical language.") 59 | 60 | (defun zbalermorna--set-font (symbol value) 61 | (set-default symbol value) 62 | (when (not (string-empty-p value)) 63 | (zbalermorna-font-setup value))) 64 | 65 | (defcustom zbalermorna-font "" 66 | "The default font used for zbalermona. If empty, the font will be 67 | chosen by Emacs automatically. 68 | 69 | The change immediately takes effect after you modify it using the 70 | Custom interface." 71 | :type 'string 72 | :group 'lojban 73 | :set 'zbalermorna--set-font) 74 | 75 | (quail-define-package 76 | "zbalermorna" "Lojban" "" t 77 | "Compose-like input method for zbalermorna. 78 | 79 | Examples: 80 | zbalermorna -> " 81 | nil t t nil nil nil nil nil nil nil t) 82 | 83 | (defconst zbalermorna-cnimaho ? 84 | "The attitudinal shorthand for ._'_ 85 | 86 | Usually such a shorthand shape is created automatically from 87 | ligatures. The input method will not generate this character.") 88 | 89 | (quail-define-rules 90 | ;; consonants 91 | ("p" 60800) ("t" 60801) ("k" 60802) ("f" 60803) 92 | ("l" 60804) ("s" 60805) ("c" 60806) ("m" 60807) 93 | ("x" 60808) ("." 60809) ("h" 60810) ("'" 60810) 94 | ("b" 60816) ("d" 60817) ("g" 60818) ("v" 60819) 95 | ("r" 60820) ("z" 60821) ("j" 60822) ("n" 60823) 96 | 97 | ;; vowels 98 | ("a" 60832) ("e" 60833) ("i" 60834) ("o" 60835) 99 | ("u" 60836) ("y" 60837) ("ai" 60838) ("ei" 60839) 100 | ("oi" 60840) ("au" 60841) 101 | 102 | ;; full vowels 103 | ("A" 60848) ("E" 60849) ("I" 60850) ("O" 60851) 104 | ("U" 60852) ("Y" 60853) ("AI" 60854) ("EI" 60855) 105 | ("OI" 60856) ("AU" 60857) 106 | 107 | ("`" 60824) ;; strech 108 | ("-" 60825) ;; pause 109 | ("~" 60827) ;; strech 110 | 111 | ;; intonation 112 | ("1" 60812) ("2" 60813) ("3" 60814) ("4" 60815) 113 | 114 | ;; semivowels 115 | ("q" 60842) 116 | ("w" 60843)) 117 | 118 | ;;;###autoload 119 | (defun zbalermorna-setup () 120 | "Set up the composition rules for zbalermonrna." 121 | (interactive) 122 | 123 | (when (not (string= zbalermorna-font "")) 124 | (zbalermorna-font-setup zbalermorna-font)) 125 | 126 | (dolist (v (number-sequence #xeda0 #xeda9)) 127 | (put-char-code-property v 'canonical-combining-class (encode-composition-rule '(tc . bc)))) 128 | 129 | (let* ((c "\\([\uED80-\uED97]\\|\uEDAA\\|\uEDAB\\)") 130 | (v "[\uEDA0-\uEDA9]") 131 | (dot "\uED89") 132 | (h "\uED8A") 133 | (pattern1 (concat c v)) 134 | (pattern2 (concat v h v))) 135 | (set-char-table-range 136 | composition-function-table '(#xeda0 . #xeda9) 137 | (list (vector pattern2 2 #'compose-gstring-for-graphic) 138 | (vector pattern1 1 #'compose-gstring-for-graphic) 139 | [nil 0 font-shape-gstring])))) 140 | 141 | ;;;###autoload 142 | (defun lojban-setup () 143 | "Set up the Lojban language environment." 144 | (interactive) 145 | (zbalermorna-setup)) 146 | 147 | ;;;###autoload 148 | (set-language-info-alist 149 | "Lojban" 150 | '((charset unicode) 151 | (coding-system utf-8) 152 | (coding-priority utf-8) 153 | (input-method . "zbalermorna") 154 | (documentation . "\ 155 | Using the zbalermorna script and its associated input method.") 156 | (sample-text . "         ") 157 | (setup-function . lojban-setup))) 158 | 159 | (provide 'lojban) 160 | ;;; lojban.el ends here 161 | -------------------------------------------------------------------------------- /modules/prelude-mail.el: -------------------------------------------------------------------------------- 1 | ;; Be sure to customize: 2 | ;; * user-full-name will be displayed on all emails you send 3 | ;; * user-mail-address a GMail address 4 | 5 | (use-package gnus 6 | :commands (gnus) 7 | :ensure nil 8 | :config 9 | (setq gnus-use-cache t) 10 | (setq gnus-use-scoring nil) 11 | (setq gnus-suppress-duplicates t) 12 | (setq gnus-novice-user nil) 13 | (setq gnus-expert-user t) 14 | (setq gnus-interactive-exit 'quiet) 15 | (setq gnus-inhibit-startup-message t) 16 | (setq gnus-select-method '(nnnil "")) 17 | (setq gnus-secondary-select-methods '((nntp "news.gmane.io") 18 | (nntp "nntp.lore.kernel.org") 19 | (nnimap "GMail" 20 | (nnimap-inbox "INBOX") 21 | (nnimap-address "imap.gmail.com") 22 | (nnimap-server-port "imaps") 23 | (nnimap-stream ssl) 24 | (nnimap-expunge 'nerver)))) 25 | ;;; Startup functions 26 | (setq gnus-save-killed-list nil) 27 | (setq gnus-check-new-newsgroups 'ask-server) 28 | ;; No other newsreader is used. 29 | (setq gnus-save-newsrc-file nil) 30 | (setq gnus-read-newsrc-file nil) 31 | (setq gnus-subscribe-newsgroup-method 'gnus-subscribe-zombies) 32 | ;; Emacs 28 introduces a unified query lang 33 | (setq gnus-search-use-parsed-queries t) 34 | ;;; Article mode for Gnus 35 | (setq gnus-visible-headers (rx line-start (or "From" 36 | "Subject" 37 | "Mail-Followup-To" 38 | "Date" 39 | "To" 40 | "Cc" 41 | "Newsgroups" 42 | "User-Agent" 43 | "X-Mailer" 44 | "X-Newsreader") 45 | ":")) 46 | (setq gnus-article-sort-functions '((not gnus-article-sort-by-number) 47 | (not gnus-article-sort-by-date))) 48 | (setq gnus-article-browse-delete-temp t) 49 | ;; Display more MINE stuff 50 | (setq gnus-mime-display-multipart-related-as-mixed t) 51 | ;;; Asynchronous support for Gnus 52 | (setq gnus-asynchronous t) 53 | (setq gnus-use-header-prefetch t) 54 | ;;; Cache interface for Gnus 55 | (setq gnus-cache-enter-articles '(ticked dormant unread)) 56 | (setq gnus-cache-remove-articles '(read)) 57 | (setq gnus-cacheable-groups "^\\(nntp\\|nnimap\\)")) 58 | 59 | ;; Group mode commands for Gnus 60 | (use-package gnus-group 61 | :ensure nil 62 | :defer t 63 | :hook (gnus-group-mode . gnus-topic-mode) 64 | :config 65 | (require 'gnus-topic) 66 | ;; indentation ------------. 67 | ;; # process mark ----------. | 68 | ;; level --------. | | 69 | ;; subscribed ------. | | | 70 | ;; % new mail ----. | | | | 71 | ;; * marked articles --. | | | | | 72 | ;; | | | | | | Ticked New Unread open-status Group 73 | (setq gnus-group-line-format "%M%m%S%L%p%P %1(%7i%) %3(%7U%) %3(%7y%) %4(%B%-45G%) %d\n") 74 | (setq gnus-group-sort-function '(gnus-group-sort-by-level gnus-group-sort-by-alphabet))) 75 | 76 | ;; Summary mode commands for Gnus 77 | (use-package gnus-sum 78 | :ensure nil 79 | :defer t 80 | :hook (gnus-select-group . gnus-group-set-timestamp) 81 | :config 82 | ;; Pretty marks 83 | (setq gnus-sum-thread-tree-root "┌ ") 84 | (setq gnus-sum-thread-tree-false-root "◌ ") 85 | (setq gnus-sum-thread-tree-single-indent "◎ ") 86 | (setq gnus-sum-thread-tree-vertical "│") 87 | (setq gnus-sum-thread-tree-indent " ") 88 | (setq gnus-sum-thread-tree-leaf-with-other "├─►") 89 | (setq gnus-sum-thread-tree-single-leaf "╰─►") 90 | (setq gnus-summary-line-format "%U%R %3d %[%-23,23f%] %B %s\n") 91 | ;; Loose threads 92 | (setq gnus-summary-make-false-root 'adopt) 93 | (setq gnus-simplify-subject-functions '(gnus-simplify-subject-re gnus-simplify-whitespace)) 94 | (setq gnus-summary-thread-gathering-function 'gnus-gather-threads-by-subject) 95 | ;; Filling in threads 96 | ;; 2 old articles are enough for memory 97 | (setq gnus-fetch-old-headers 2) 98 | (setq gnus-fetch-old-ephemeral-headers 2) 99 | (setq gnus-build-sparse-threads 'some) 100 | ;; More threading 101 | (setq gnus-show-threads t) 102 | (setq gnus-thread-indent-level 2) 103 | (setq gnus-thread-hide-subtree nil) 104 | ;; Sorting 105 | (setq gnus-thread-sort-functions '(gnus-thread-sort-by-most-recent-date)) 106 | (setq gnus-subthread-sort-functions '(gnus-thread-sort-by-date)) 107 | ;; Viewing 108 | (setq gnus-view-pseudos 'automatic) 109 | (setq gnus-view-pseudos-separately t) 110 | (setq gnus-view-pseudo-asynchronously t) 111 | ;; No auto select 112 | (setq gnus-auto-select-first nil) 113 | (setq gnus-auto-select-next nil) 114 | (setq gnus-paging-select-next nil)) 115 | 116 | (use-package message 117 | :ensure nil 118 | :defer t 119 | :hook (message-mode . auto-fill-mode) 120 | :config 121 | (setq message-kill-buffer-on-exit t) 122 | (setq message-signature user-full-name) 123 | (setq message-mail-alias-type 'ecomplete) 124 | (setq message-send-mail-function #'message-use-send-mail-function) 125 | (setq send-mail-function #'smtpmail-send-it) 126 | (setq smtpmail-smtp-server "smtp.gmail.com") 127 | (setq smtpmail-smtp-user user-mail-address) 128 | (setq smtpmail-smtp-service 587)) 129 | 130 | (provide 'prelude-mail) 131 | -------------------------------------------------------------------------------- /lisp/smtlib2-mode.el: -------------------------------------------------------------------------------- 1 | ;;; smtlib2-mode.el --- A SMTLIBv2 interactive development environment -*-lexical-binding: t-*- 2 | 3 | ;; Version: 0.0.1 4 | ;; Author: ksqsf 5 | ;; Homepage: https://github.com/ksqsf/emacs-config 6 | ;; Keywords: z3 yices mathsat cvc4 cvc5 smt beaver 7 | ;; Package-Requires: ((emacs "27")) 8 | 9 | ;; This file is not part of GNU Emacs. 10 | 11 | ;; This file is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation; either version 3, or (at your option) 14 | ;; any later version. 15 | 16 | ;; This file is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this file. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; An interactive development environment for SMT-LIB files. All SMT 27 | ;; solvers that support the SMT-LIBv2 format are supported 28 | ;; automatically. 29 | 30 | ;; This package is a fork of Zephyr Pellerin's z3-mode. I enhanced the 31 | ;; package to support: 32 | 33 | ;; - Proper indentation for several constructs, e.g. forall, declare-*. 34 | ;; - Highlighting for more commands 35 | ;; - Limited support for SyGuS 36 | 37 | (eval-when-compile 38 | (require 'cl-lib)) 39 | 40 | ;;; Code: 41 | (defgroup smtlib2 nil 42 | "SMTLib2 Mode" 43 | :group 'languages 44 | :prefix "smtlib2-") 45 | 46 | (defcustom smtlib2-solver-cmd "z3" 47 | "The command used when you run the solver." 48 | :type 'file 49 | :group 'smtlib2) 50 | 51 | 52 | 53 | (defvar smtlib2-mode-map 54 | (let ((map (make-sparse-keymap))) 55 | (define-key map (kbd "C-c C-c") 'smtlib2-execute-region) 56 | map) 57 | "Keymap for smtlib2-mode.") 58 | 59 | 60 | ;; font-lock support 61 | 62 | ;; Matches alternative base numeric primitives such as `#xF0FA' & `#b010' 63 | (defconst smtlib2-altbase-literal-regexp "\\(#x[0-9a-fA-F]+\\|#b[01]+\\)") 64 | 65 | ; Matches the simplest symbol regexp format 66 | (defconst smtlib2-symbol-regexp "[a-zA-Z~!@$%^&*_+=<>.?/-][0-9a-zA-Z~!@$%^&*_+=<>.?/-]*") 67 | 68 | ;; Matches an alternative quote symbol regexp format 69 | (defconst smtlib2-quoted-symbol-regexp "|[]!-[^-{}~ \t\r\n]*|") 70 | 71 | ;; Matches lisp-symbol style keywords, i.e `:keyword' 72 | (defconst smtlib2-keyword-symbol-regexp ":[0-9a-zA-Z~!@$%^&*_+=<>.?/-]+") 73 | 74 | ;; Reserved words as defined in the SMT-LIB standard 75 | (defconst smtlib2-keywords 76 | '("!" "_" "as" "exists" "forall" "let" "match" "par" 77 | "assert" "check-sat" "check-sat-assuming" "declare-const" 78 | "declare-datatype" "declare-datatypes" "declare-fun" "declare-sort" 79 | "define-fun" "define-fun-rec" "define-sort" "echo" "exit" "get-assertions" 80 | "get-assignment" "get-info" "get-model" "get-option" "get-proof" 81 | "get-unsat-assumptions" "get-unsat-core" "get-value" "pop" "push" 82 | "reset" "reset-assertions" "set-info" "set-logic" "set-option" 83 | 84 | ;; SyGuS 85 | "synth-fun" "constraint" "check-synth" 86 | ) 87 | "Commands and other keywords defined by the SMT-LIB standard.") 88 | 89 | ;; Define our font-lock 90 | (defvar smtlib2-keywords-regexp (regexp-opt smtlib2-keywords 'words)) 91 | 92 | (defvar smtlib2-font-lock-defaults 93 | `(((,smtlib2-keywords-regexp . font-lock-keyword-face) 94 | (,smtlib2-keyword-symbol-regexp . font-lock-builtin-face) 95 | (,smtlib2-altbase-literal-regexp . font-lock-constant-face) 96 | ;; We *can* highlight symbols, but it compromises the clarity 97 | ;; (,smtlib2-symbol-regexp . font-lock-function-name-face) 98 | ))) 99 | 100 | 101 | (let ((l '((forall 1) 102 | (exists 1) 103 | (let 1) 104 | (match 1) 105 | (par 1) 106 | (declare-const 2) 107 | (declare-datatype 1) 108 | (declare-datatypes 1) 109 | (declare-fun 3) 110 | (declare-sort 1) 111 | (define-fun 3) 112 | (define-fun-rec 3) 113 | (define-sort 1) 114 | (ite 2) 115 | (synth-fun 3)))) 116 | (cl-loop for (keyword indent) in l 117 | do (put keyword 'common-lisp-indent-function indent))) 118 | 119 | 120 | ;; mode-command and utility functions 121 | 122 | ;; Define the mode 123 | ;;;###autoload 124 | (define-derived-mode smtlib2-mode lisp-mode "SMT2" 125 | "Major mode for editing SMT-LIB2 files" 126 | (setq font-lock-defaults smtlib2-font-lock-defaults) 127 | ;; use cl-indent 128 | (require 'cl-indent) 129 | (setq-local lisp-indent-function 'common-lisp-indent-function) 130 | ;; disable sly-mode 131 | (when (fboundp 'sly-editing-mode) 132 | (sly-editing-mode -1)) 133 | (when (fboundp 'sly-mode) 134 | (sly-mode -1))) 135 | 136 | ;; Command to run SMT solver on the whole buffer 137 | (defun smtlib2-execute-region () 138 | "Pass optional header and region to a prover for noninteractive execution. 139 | The working directory is that of the buffer, and only environment variables 140 | are already set which is why you can mark a header within the script." 141 | (interactive) 142 | (shell-command-on-region (if (region-active-p) (region-beginning) (point-min)) 143 | (if (region-active-p) (region-end) (point-max)) 144 | smtlib2-solver-cmd)) 145 | 146 | 147 | 148 | (setq auto-mode-alist 149 | (append 150 | '(("\\.smt[2]?\\'" . smtlib2-mode) 151 | ("\\.sygus[2]?\\'" . smtlib2-mode)) auto-mode-alist)) 152 | 153 | (provide 'smtlib2-mode) 154 | 155 | ;;; smtlib2-mode.el ends here 156 | -------------------------------------------------------------------------------- /lisp/hou.el: -------------------------------------------------------------------------------- 1 | ;;; NOTE: PLEASE DO NOT USE THIS FILE AND DON'T BELIEVE ANY WORDS IN 2 | ;;; IT. This file does not contain any code, and the designs are not 3 | ;;; verified to work. It's been in my .emacs.d for a good while since 4 | ;;; 2021, and I never take time to finish it. I committed this file 5 | ;;; just because I don't want to see this file listed in "untracked 6 | ;;; files", not because I believe it is useful to anyone. 7 | 8 | ;;; hou.el --- Opinionated objed replacement -*- lexical-binding: t; -*- 9 | 10 | ;; Copyright (C) 2021 ksqsf 11 | 12 | ;; Author: ksqsf 13 | ;; Keywords: convenience 14 | 15 | ;;; Commentary: 16 | 17 | ;; This package defines is a modular approach to structured text 18 | ;; editing, drawing inspiration from vim, kakoune, objed, and 19 | ;; more. The main difference is, hou is mainly designed to be used by 20 | ;; myself, thus very opinionated. Hou tries to be as smart as possible 21 | ;; -- do the right thing without any user input. 22 | 23 | ;; Hou views text as a pool of "objects", between which there might be 24 | ;; relationships. For example, in the following simple C program, 25 | ;; 26 | ;; 1 int 27 | ;; 2 main() 28 | ;; 3 { 29 | ;; 4 printf ("Hi\ 30 | ;; 5 "); 31 | ;; 6 return 0; 32 | ;; 7 } 33 | ;; 8 34 | ;; 35 | ;; 0 is an expression which is not only embedded in a statement, but 36 | ;; also a line. Contrary to it, "Hi..." is also an expression, 37 | ;; embedded in a statement, but doesn't belong to a single line. 38 | ;; 39 | ;; Hou defines two basic relationships between objects, 40 | ;; 1. A Embeds B. B is fully contained in A. 41 | ;; 2. A Touches B. Part of B is contained in A. 42 | ;; 43 | ;; Therefore, Hou views a buffer as a directed acyclic graph, where 44 | ;; the Embed-induced subgraph is a tree. The Embed relation is always 45 | ;; static, defined by the object provider, while the Touch relation is 46 | ;; always inferred dynamically. (To be precise, the DAG is an 47 | ;; interleaving forest.) We can draw a hierarchy of the text objects, 48 | ;; 49 | ;; buffer 50 | ;; / \ 51 | ;; page {item} 52 | ;; | \ 53 | ;; paragraph function 54 | ;; / \ \ 55 | ;; line sentence ... 56 | ;; \ / 57 | ;; word 58 | ;; | 59 | ;; subword 60 | ;; | 61 | ;; char 62 | ;; 63 | ;; Given a point, each node in the hierarchy graph will have two outcomes, 64 | ;; 1. Invalid. E.g., you put the cursor on line 8, then it's impossible to find a statement there. 65 | ;; 2. Valid with (actual region, select region). The select region is no smaller than the actual region. 66 | ;; 67 | ;; The valid pairs can overlap. E.g., put the cursor on "0", 68 | ;; "expression", "statement", "function", "line", and more, are valid, 69 | ;; and they overlap. Hou will find a region the user maybe wants. If 70 | ;; not, the user can extend the region, until the region is found. 71 | ;; 72 | ;; It's important for Hou to be fast. Hou will take advantage of the 73 | ;; fact that, if a node T keeps intact after the point changed, then 74 | ;; all of its parents will keep intact as well. Therefore, we start 75 | ;; from all the leaves and update the tree until a node doesn't 76 | ;; change. Hou tries to be lazy: the tree won't update before a 77 | ;; certain amount of idle time. 78 | ;; 79 | ;; FIXME: text change? 80 | ;; 81 | ;; Hou has the following basic operations, 82 | ;; 1. Up, go up to the embedder. 83 | ;; 2. Down, jump to the first "child" (an object embedded by the current object). 84 | ;; 3. Next, go to the next object on the same level. 85 | ;; 4. Previous, go to the previous object on the same level. 86 | ;; 5. Mark, mark the current object 87 | ;; 6. Inner, go inner. 88 | ;; 7. Exchange, jump to the other side of the current object. 89 | ;; 8. Expand, expand the region (by repeating the same mark command). 90 | ;; 91 | ;; Hou minimizes interference in user interacting with normal Emacs 92 | ;; commands. Therefore, you would still use C-w for kill, C-y for 93 | ;; yank, etc. Moreover, Hou determines automatically a most probable 94 | ;; candidate for the mark command. Eg, in the program above, try to 95 | ;; mark the current line inside the string on line 4, it will first 96 | ;; mark the string from H through the EOL. Using the Expand feature, 97 | ;; you can expand it to the whole line. 98 | ;; 99 | ;; Objects are defined by "object providers". A buffer can contain 100 | ;; many different object providers. For example, lines, paragraphs, 101 | ;; pages, and the buffer, are provided by a provider aware of text. 102 | ;; On the other hand, a major mode can define its own provider aware 103 | ;; of syntactical elements. 104 | ;; 105 | ;; Hou doesn't keep many internal states. Most of the operations are 106 | ;; local searches on demand. 107 | ;; 108 | ;; Hou is designed to be easily extensible. It's very easy to define 109 | ;; your own provider, and Hou will figure out the rest of the 110 | ;; job. E.g., you can define a provider for "program sections". Since 111 | ;; a text object is really an object in the OOP sense, you can easily 112 | ;; add your own operations to them. Hou is designed with formal 113 | ;; grammars (e.g. tree-sitter) in mind to provide automatic and 114 | ;; precise structural editing experience. 115 | ;; 116 | ;; Even though, Hou is opinionated, I still hope it can be a general 117 | ;; framework on which other applications can be built, like code 118 | ;; folding. 119 | ;; 120 | ;; As a side note, "hóu" means monkey in Chinese. Jumping between text 121 | ;; objects looks like monkeys jumping between trees. 122 | 123 | ;;; Code: 124 | 125 | ;; PROVIDER 126 | ;; ======== 127 | ;; Infer: given the point, get a list of most probable candidate objects. 128 | 129 | ;; THE HOU ALGORITHM: PROVIDER INFER 130 | ;; ================================= 131 | ;; 132 | ;; INPUT: point 133 | ;; OUTPUT: a list of candidate objects 134 | ;; 135 | ;; Starting from the most 136 | 137 | (provide 'hou) 138 | ;;; hou.el ends here 139 | -------------------------------------------------------------------------------- /lisp/treefold.el: -------------------------------------------------------------------------------- 1 | ;;; treefold.el --- Fold tree-format texts as if it is a tree -*- lexical-binding: t; -*- 2 | 3 | ;; Author: ksqsf 4 | ;; URL: https://github.com/ksqsf/emacs-config 5 | ;; Version: 0.1 6 | ;; Package-Requires: ((emacs "26.3")) 7 | ;; Keywords: convenience 8 | 9 | ;; Copyright (C) 2020 ksqsf 10 | 11 | ;; This program is free software: you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; treefold.el provides a minor mode, which contains commands to help 27 | ;; you fold any text that looks like a tree, e.g. the output of 28 | ;; tree(1) or that of cargo-tree. 29 | 30 | ;; For instance, the output of tree(1): 31 | 32 | ;; . 33 | ;; ├── Cargo.lock 34 | ;; ├── Cargo.toml 35 | ;; ├── deps 36 | ;; └── src 37 | ;; ├── errors.rs 38 | ;; └── main.rs 39 | 40 | ;; If you press Return on the line "src", you will fold the 41 | ;; contents of "src", and get the result: 42 | 43 | ;; . 44 | ;; ├── Cargo.lock 45 | ;; ├── Cargo.toml 46 | ;; ├── deps 47 | ;; └── src [...] 48 | 49 | ;;; Code: 50 | 51 | (setq treefold-continuation-regexp (rx (or " " " " "|" "│"))) 52 | (setq treefold-subtree-indicator-regexp (rx (or "+" "└" "├" "|-"))) 53 | 54 | (defun treefold--subtree-end () 55 | (save-excursion 56 | (let (end (col (current-column))) 57 | (while (and (= (forward-line) 0) 58 | (= (move-to-column col) col) 59 | (looking-at treefold-continuation-regexp)) 60 | nil) 61 | (1+ (point-at-eol 0))))) 62 | 63 | (defun treefold--folded-line (start end) 64 | (save-excursion 65 | (goto-char start) 66 | (format "%s [...]\n" (buffer-substring start (point-at-eol))))) 67 | 68 | (defun treefold--fold-subtree (start end) 69 | "Fold a subtree, which spans region from START to END." 70 | (save-excursion 71 | (goto-char start) 72 | (let* ((eol (point-at-eol)) 73 | (line-overlay (make-overlay start eol)) 74 | (hide-overlay (make-overlay (1+ eol) end))) 75 | (if (>= (1+ eol) end) 76 | ;; There's nothing to hide. 77 | (progn 78 | (delete-overlay line-overlay) 79 | (delete-overlay hide-overlay)) 80 | (overlay-put line-overlay 'after-string " [...]") 81 | (overlay-put line-overlay 'evaporate t) 82 | (overlay-put line-overlay 'treefold 'line) 83 | (overlay-put line-overlay 'treefold-link hide-overlay) 84 | (overlay-put hide-overlay 'invisible t) 85 | (overlay-put line-overlay 'evaporate t) 86 | (overlay-put hide-overlay 'treefold 'hide) 87 | (overlay-put hide-overlay 'treefold-link line-overlay))))) 88 | 89 | (defun treefold--unfold-in-region (start end) 90 | "Unfold all subtrees in region (START, END)." 91 | (dolist (overlay (overlays-in start end)) 92 | (when (overlay-get overlay 'treefold) 93 | (delete-overlay overlay)))) 94 | 95 | (defun treefold--fold () 96 | "Fold the subtree found on this line." 97 | (beginning-of-line) 98 | (unless (re-search-forward treefold-subtree-indicator-regexp 99 | (point-at-eol) t 1) 100 | (error "This line doesn't have a subtree.")) 101 | (let* ((start (point-at-bol)) 102 | (end (treefold--subtree-end))) 103 | (treefold--unfold-in-region start end) 104 | (treefold--fold-subtree start end))) 105 | 106 | (defun treefold--line-overlay (pos) 107 | "Return the line overlay at POS, or nil if none is found." 108 | (catch 'return 109 | (dolist (overlay (overlays-at (point))) 110 | (when (eq 'line (overlay-get overlay 'treefold)) 111 | (throw 'return overlay))) 112 | (throw 'return nil))) 113 | 114 | ;;;###autoload 115 | (defun treefold-toggle () 116 | "Toggle whether the current subtree is folded." 117 | (interactive) 118 | (save-excursion 119 | (let ((line-overlay (treefold--line-overlay (point)))) 120 | (if line-overlay 121 | (treefold--unfold line-overlay) 122 | (treefold--fold))))) 123 | 124 | (defun treefold--unfold (line-overlay) 125 | "Unfold the subtree found on this line." 126 | ;; Delete hide-overlay. 127 | (delete-overlay (overlay-get line-overlay 'treefold-link)) 128 | ;; Delete line-overlay. 129 | (delete-overlay line-overlay)) 130 | 131 | ;;;###autoload 132 | (defun treefold-unfold-all () 133 | "Unfold all subtrees in this buffer." 134 | (interactive) 135 | (treefold--unfold-in-region (point-min) (point-max))) 136 | 137 | ;;;###autoload 138 | (defun treefold-forward-subtree (&optional n) 139 | "Go to the position of the indicator for the next N-th subtree. 140 | 141 | If N is negative, search backwards." 142 | (interactive) 143 | (re-search-forward treefold-subtree-indicator-regexp nil nil (or n 1))) 144 | 145 | ;;;###autoload 146 | (defun treefold-backward-subtree (&optional n) 147 | "Go to the position of the indicator for the previous N-th subtree. 148 | 149 | If N is negative, search forward." 150 | (interactive) 151 | (treefold-forward-subtree (- (or n 1)))) 152 | 153 | ;;;###autoload 154 | (define-minor-mode treefold-mode 155 | "Enable treefold functions in this buffer." 156 | :keymap (let ((keymap (make-sparse-keymap))) 157 | (define-key keymap (kbd "") #'treefold-toggle) 158 | (define-key keymap (kbd "C-c C-n") #'treefold-forward-subtree) 159 | (define-key keymap (kbd "C-c C-p") #'treefold-backward-subtree) 160 | keymap)) 161 | 162 | (provide 'treefold) 163 | 164 | ;;; treefold.el ends here. 165 | -------------------------------------------------------------------------------- /modules/prelude-help.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | (use-package helpful 4 | :bind (("C-h f" . helpful-callable) 5 | ("C-h v" . helpful-variable) 6 | ("C-h x" . helpful-command)) 7 | :config 8 | (defun advice-helpful--navigate (button) 9 | "Do not use `find-file' to open a buffer." 10 | (pop-to-buffer (find-file-noselect (substring-no-properties (button-get button 'path)))) 11 | (-when-let (pos (get-text-property button 'position 12 | (marker-buffer button))) 13 | (helpful--goto-char-widen pos))) 14 | (advice-add 'helpful--navigate :override 'advice-helpful--navigate)) 15 | 16 | ;; Add a "Remove" button to the advice list 17 | ;; Credit: https://emacs-china.org/t/advice/7566 18 | (add-hook 'help-mode-hook 'cursor-sensor-mode) 19 | 20 | (defun function-advices (function) 21 | "Return FUNCTION's advices." 22 | (when (symbolp function) 23 | (let ((function-def (advice--symbol-function function)) 24 | (ad-functions '())) 25 | (while (advice--p function-def) 26 | (setq ad-functions (append `(,(advice--car function-def)) ad-functions)) 27 | (setq function-def (advice--cdr function-def))) 28 | ad-functions))) 29 | 30 | (defconst +prelude--advice-regex+ 31 | (if (version<= "27.1" emacs-version) 32 | "^\\(?:This function has \\)?:[-a-z]+ advice: [‘'`]\\(.+\\)[’'']\\.?$" 33 | "^:[-a-z]+ advice: [‘'`]\\(.+\\)[’'']$")) 34 | 35 | (define-advice describe-function-1 (:after (function) advice-remove-button) 36 | "Add a button to remove advice." 37 | (when (get-buffer "*Help*") 38 | (with-current-buffer "*Help*" 39 | (save-excursion 40 | (goto-char (point-min)) 41 | (let ((ad-index 0) 42 | (ad-list (reverse (function-advices function)))) 43 | (while (re-search-forward +prelude--advice-regex+ nil t) 44 | (let* ((name (string-trim (match-string 1) "'" "'")) 45 | (advice (or (intern-soft name) (nth ad-index ad-list)))) 46 | (when (and advice (functionp advice)) 47 | (let ((inhibit-read-only t)) 48 | (insert " » ") 49 | (insert-text-button 50 | "Remove" 51 | 'cursor-sensor-functions `((lambda (&rest _) (message "%s" ',advice))) 52 | 'help-echo (format "%s" advice) 53 | 'action 54 | ;; In case lexical-binding is off 55 | `(lambda (_) 56 | (when (yes-or-no-p (format "Remove %s ? " ',advice)) 57 | (message "Removing %s of advice from %s" ',function ',advice) 58 | (advice-remove ',function ',advice) 59 | (revert-buffer nil t))) 60 | 'follow-link t)))) 61 | (setq ad-index (1+ ad-index)))))))) 62 | 63 | (defvar Info-special-url-format 64 | '(("hyperbole" . "https://www.gnu.org/software/hyperbole/man/hyperbole.html#%s") 65 | ("magit" . "https://magit.vc/manual/magit.html#%s") 66 | ("forge" . "https://magit.vc/manual/forge.html#%s") 67 | ("transient" . "https://magit.vc/manual/transient.html#%s") 68 | ("org" . "https://orgmode.org/manual/%s.html") 69 | ("org-roam" . "https://www.orgroam.com/manual.html#%s") 70 | ("orderless" . "https://elpa.gnu.org/packages/doc/orderless.html#%s")) 71 | "`Info-get-online-url' defaults to GNU's official manual URL base. 72 | %s will be replaced by the node name.") 73 | 74 | (defun Info-get-online-url () 75 | "Get the online URL of this manual node. 76 | 77 | If a region is active, it is considered text anchor. This 78 | feature is incomplete." 79 | (let* ((file (file-name-sans-extension 80 | (file-name-nondirectory Info-current-file))) 81 | (node (mapconcat (lambda (ch) 82 | (if (or (< ch 32) ; ^@^A-^Z^[^\^]^^^- 83 | (<= 33 ch 47) ; !"#$%&'()*+,-./ 84 | (<= 58 ch 64) ; :;<=>?@ 85 | (<= 91 ch 96) ; [\]_` 86 | (<= 123 ch 127)) ; {|}~ DEL 87 | (format "_00%x" ch) 88 | (char-to-string ch))) 89 | Info-current-node "")) 90 | (node (string-replace " " "-" node)) 91 | (node (if (string= node "Top") "index" 92 | node)) 93 | (url-base (or (cdr (assoc file Info-special-url-format #'equal)) 94 | (format "https://www.gnu.org/software/emacs/manual/html_node/%s/%%s.html" file))) 95 | (url (format url-base node))) 96 | (if (not (use-region-p)) 97 | url 98 | (let* ((selected (buffer-substring-no-properties (region-beginning) (region-end))) 99 | (text-anchor (mapconcat (lambda (ch) 100 | (if (or (= ch 10) 101 | (= ch 8220) (= ch 8221)) 102 | "" 103 | (if (= ch ?-) 104 | "%2D" 105 | (char-to-string ch)))) 106 | selected))) 107 | (format "%s#:~:text=%s" url (url-encode-url text-anchor)))))) 108 | 109 | (defun Info-copy-online-url () 110 | "Put the online url of the current Info node into the kill ring. 111 | 112 | If a region is active, it is considered text anchor. 113 | 114 | This command is only meaningful for the official manuals, and it 115 | does not work in TOC nodes." 116 | (interactive) 117 | (let ((url (Info-get-online-url))) 118 | (kill-new url) 119 | (message "%s" url))) 120 | 121 | (defun Info-copy-markdown-link () 122 | "Put a markdown link to the online manual into the kill ring. 123 | 124 | The link is obtained as if it was returned from 125 | `Info-copy-online-url', and the label 126 | `Info-copy-current-node-name'." 127 | (interactive) 128 | (let* ((file (file-name-sans-extension 129 | (file-name-nondirectory Info-current-file))) 130 | (node Info-current-node) 131 | (url (Info-get-online-url)) 132 | (md-link (format "[(info \"(%s) %s\")](%s)" file node url))) 133 | (kill-new md-link) 134 | (message "%s" md-link))) 135 | 136 | (use-package info 137 | :ensure nil 138 | :bind (:map 139 | Info-mode-map 140 | ("C" . Info-copy-online-url) 141 | ("C-c C-c" . Info-copy-markdown-link))) 142 | 143 | (use-package info-colors 144 | :after (info) 145 | :defer t 146 | :hook (Info-selection . info-colors-fontify-node)) 147 | 148 | (provide 'prelude-help) 149 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * My Emacs Config 2 | 3 | This is my personal, "slightly configured" Emacs. 4 | 5 | There are some packages written by me for my own convenience. See the 6 | last section. 7 | 8 | ** Supported Platforms 9 | 10 | This configuration supports macOS, and Linux. It should work on 11 | Windows for the most part. 12 | 13 | The following Emacs versions are supported: 14 | 15 | - Emacs 30, the current stable version 16 | - Emacs 31, the current development version 17 | 18 | 19 | *** Which Emacs do I use, exactly? 20 | 21 | I use a custom build of Emacs, both on Linux and macOS, patched with 22 | my own changes to the Emacs core. 23 | 24 | BTW, Emacs is super easy to build from source. 25 | 26 | 27 | ** Structure 28 | 29 | There are two kinds of configuration, Module and Volatile. The 30 | distinction is (in my mind) very successful. 31 | 32 | *** Module 33 | 34 | Modules make up the functionality and keybindings. They are stable 35 | and can be byte compiled safely and cleanly. Ideally, they use 36 | lexical scoping. 37 | 38 | *** Volatile 39 | 40 | Volatile configuration files are more fragile and personal. They are 41 | mainly temporary tests or (let's hope not) ugly hacks. A volatile 42 | Lisp file should never be byte compiled. 43 | 44 | ** Fonts 45 | 46 | The following fonts are "blessed" in this configuration. 47 | 48 | | Font | Point | Usage | 49 | |----------------------+-------+-------------------------------| 50 | | (Any monospace font) | 14 | General use (non-Chinese) | 51 | | Sarasa Mono | 14 | For perfect Chinese alignment | 52 | 53 | ** Better Defaults 54 | 55 | These modifications are generally in =prelude-core.el= and 56 | =prelude-ui.el=, and a few other files. They are loaded before anything 57 | else. 58 | 59 | Prominent user-visible modifications are listed below. 60 | 61 | *** UI 62 | **** Mode line 63 | 64 | Any of the following looks pretty good: 65 | 66 | - Default 67 | - Doom mode line 68 | - Moody 69 | 70 | **** Modal editing (Evil) 71 | 72 | I'm not fully invested in Evil, so currently it can be disabled 73 | *completely* by commenting out =(require 'prelude-evil)= in =init.el=. 74 | 75 | I use Evil, because the Vi-style editing commands are the de facto 76 | standard right now, and evil-collection is great, and I still retain 77 | some muscle memory (I used to be a Vim user). Though, conceptually, I 78 | like Meow better. 79 | 80 | Evil-collection offers a large collection of consistent, 81 | pre-configured keybindings for a variety of major modes. See its 82 | [[https://github.com/emacs-evil/evil-collection][readme]]. 83 | 84 | I use a hybrid editing style, i.e. in the insert mode, most Emacs 85 | commands are still available, so I can keep my muscle memory as much 86 | as possible. This is done by customizing 87 | =evil-disable-insert-state-bindings=. The conflicts are resolved 88 | depending on which is most familiar to me. 89 | 90 | **** Completion 91 | 92 | I use Vertico, Prescient, and Orderless. 93 | 94 | Vertico offers a lightweight yet versatile frontend to 95 | =completion-at-point=. 96 | 97 | Orderless significantly enhances the default completion algorithms, 98 | though its sorting is less than ideal. 99 | 100 | **** Side windows 101 | 102 | Many temporary buffers pop up in the side windows. 103 | 104 | =C-TAB= toggles whether or not side windows are displayed. 105 | 106 | *** Editing 107 | **** Expand region 108 | 109 | =C-SPC C-SPC= to expand the current region meaningfully. It supports 110 | tree-sitter-based expansion. 111 | 112 | **** Zap up to char 113 | 114 | =M-z= was assigned to =zap-up-to-char=. You can easily achieve what 115 | =zap-to-char= does with =M-z C-d=. 116 | 117 | At first, I thought =M-z t= and =M-z f= would be cool, but it turned 118 | out to be a horrible idea, because one must stop to think which key to 119 | press. 120 | 121 | **** Structured Editing 122 | 123 | Structured editing operates on the level of syntactical structures, be 124 | it identifiers, expressions, or statements. 125 | 126 | I use paredit for the Lisp family. For tree-sitter-powered major 127 | modes, use combobulate-mode. 128 | 129 | *** Programming 130 | **** LSP 131 | 132 | I've fully embraced LSP! (Used to be a skeptic.) 133 | 134 | - =eglot= as the LSP client. (Easier to hack than =lsp-mode=.) 135 | - =corfu= as the universal completion frontend. 136 | 137 | 138 | Language servers can be installed via Homebrew or Nix. Eglot is 139 | intelligent enough to work with any server without special 140 | configuration. 141 | 142 | **** REPL 143 | 144 | Many major modes support REPL, but their keybindings are often 145 | very different. The following keychords are adopted by Emacs 146 | itself, so they are preferred when possible. 147 | 148 | | Key | Function | 149 | |---------+----------------------| 150 | | C-c C-c | Send buffer | 151 | | C-c C-l | Send file | 152 | | C-x C-e | Send this expression | 153 | | C-M-x | Send this function | 154 | 155 | ***** Haskell, Idris 156 | 157 | Haskell-mode has Interactive Haskell mode supporting ghci. Due to 158 | Haskell's specialness, all you can do is =C-c C-l=. 159 | 160 | ***** Python 161 | 162 | Elpy defined a bunch of send functions, but they turn out to be not so 163 | helpful. 164 | 165 | ***** Coq 166 | 167 | Coq is an interactive proof assistant, whose Emacs interface is Proof 168 | General, which defines its own set of commands. 169 | 170 | ** Unlimited Lisp Works 171 | This repo hosts some other Emacs Lisp works made by me for my own 172 | convenience. It might be useful for you, too, so I made a list here 173 | for better discoverability. 174 | 175 | | Who | Where | What | 176 | |----------------+--------------------------+------------------------------------| 177 | | pest-mode | github ksqsf/pest-mode | Major mode for Pest files | 178 | | treefold | ./lisp/treefold.el | Fold any tree-like text | 179 | | smtlib2-mode | ./lisp/smtlib2-mode.el | Major mode for SMT scripts | 180 | | lojban | ./lisp/lojban.el | Zbalermorna input and rendering | 181 | | bionic-reading | ./lisp/bionic-reading.el | A (sort of) bionic reader in Emacs | 182 | | clipboard | ./lisp/clipboard.el | clipboard inspector | 183 | | clipmgr | ./lisp/clipmgr.el | (WIP) a clipboard manager | 184 | | region-mark | ./lisp/region-mark.el | | 185 | | fish-protector | ./lisp/fish-protector.el | 魚の護衛者 | 186 | | logseq | ./lisp/logseq.el | Logseq HTTP API | 187 | | leitner | ./lisp/leitner.el | The Leitner system (a kind of SRS) | 188 | | org-xlatex | github ksqsf/org-xlatex | instant latex preview | 189 | | flygpt | ./lisp/flygpt.el | semantic flymake based on GPT | 190 | -------------------------------------------------------------------------------- /modules/prelude-lang-haskell.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | ;; Currently, only haskell-mode is supported. 4 | 5 | (use-package haskell-mode 6 | :mode ("\\.hs\\'" . haskell-mode) 7 | :bind (("C-c C-l" . haskell-process-load-or-reload) 8 | ("C-c C-f" . haskell-mode-format-imports) 9 | ("" . haskell-navigate-imports)) 10 | :hook (haskell-mode . interactive-haskell-mode) 11 | ;; :hook (haskell-mode . k|setup-haskell-prettify-symbols) 12 | :hook (inferior-haskell-mode . (lambda () (comint-use-persistent-input-history "~/.ghc/ghci_history"))) 13 | :hook (haskell-mode . k|lsp-ensure) 14 | :hook (after-save . k|hpack-after-save) 15 | 16 | :config 17 | 18 | (defun k|hpack-after-save () 19 | (when (string= (buffer-name) "package.yaml") 20 | (start-process "hpack" " *hpack*" "hpack"))) 21 | 22 | (defun k|setup-haskell-prettify-symbols () 23 | (setq prettify-symbols-alist 24 | '(("\\" . ?λ) 25 | ("forall" . ?∀) 26 | ("\\/" . ?∨) 27 | ("/\\" . ?∧) 28 | (">-" . ?⤚) 29 | ("-<" . ?⤙) 30 | (">>-" . ?⤜) 31 | ("-<<" . ?⤛) 32 | ("[|" . 10214) ;; ⟦ 33 | ("|]" . 10215) ;; ⟧ 34 | )) 35 | (prettify-symbols-mode)) 36 | 37 | ;; (setq haskell-process-wrapper-function 38 | ;; (lambda (args) (apply 'nix-shell-command (nix-current-sandbox) args))) 39 | ) 40 | 41 | (use-package speedbar 42 | :ensure nil 43 | :after haskell-mode 44 | :config 45 | (speedbar-add-supported-extension ".hs")) 46 | 47 | (use-package align 48 | :ensure nil 49 | :after haskell-mode 50 | :config 51 | (add-to-list 'align-rules-list 52 | '(haskell-types 53 | (regexp . "\\(\\s-+\\)\\(::\\|∷\\)\\s-+") 54 | (modes quote (haskell-mode literate-haskell-mode)))) 55 | (add-to-list 'align-rules-list 56 | '(haskell-assignment 57 | (regexp . "\\(\\s-+\\)=\\s-+") 58 | (modes quote (haskell-mode literate-haskell-mode)))) 59 | (add-to-list 'align-rules-list 60 | '(haskell-arrows 61 | (regexp . "\\(\\s-+\\)\\(->\\|→\\)\\s-+") 62 | (modes quote (haskell-mode literate-haskell-mode)))) 63 | (add-to-list 'align-rules-list 64 | '(haskell-left-arrows 65 | (regexp . "\\(\\s-+\\)\\(<-\\|←\\)\\s-+") 66 | (modes quote (haskell-mode literate-haskell-mode))))) 67 | 68 | (use-package flycheck-haskell 69 | :after haskell-mode 70 | :hook (flycheck-mode-hook . flycheck-haskell-setup)) 71 | 72 | ;; (use-package w3m-haddock 73 | ;; :disabled 74 | ;; :ensure w3m 75 | ;; :after haskell-mode 76 | ;; :config 77 | ;; (setq w3m-mode-map (make-sparse-keymap)) 78 | 79 | ;; (define-key w3m-mode-map (kbd "RET") 'w3m-view-this-url) 80 | ;; (define-key w3m-mode-map (kbd "q") 'bury-buffer) 81 | ;; (define-key w3m-mode-map (kbd "") 'w3m-maybe-url) 82 | ;; (define-key w3m-mode-map [f5] 'w3m-reload-this-page) 83 | ;; (define-key w3m-mode-map (kbd "C-c C-d") 'haskell-w3m-open-haddock) 84 | ;; (define-key w3m-mode-map (kbd "M-") 'w3m-view-previous-page) 85 | ;; (define-key w3m-mode-map (kbd "M-") 'w3m-view-next-page) 86 | ;; (define-key w3m-mode-map (kbd "M-.") 'w3m-haddock-find-tag) 87 | 88 | ;; (defun w3m-maybe-url () 89 | ;; (interactive) 90 | ;; (if (or (equal '(w3m-anchor) (get-text-property (point) 'face)) 91 | ;; (equal '(w3m-arrived-anchor) (get-text-property (point) 'face))) 92 | ;; (w3m-view-this-url))) 93 | 94 | ;; (add-hook 'w3m-display-hook 'w3m-haddock-display)) 95 | 96 | (use-package ormolu 97 | :after (haskell-mode) 98 | ;; :hook (haskell-mode . ormolu-format-on-save-mode) 99 | :bind 100 | (:map haskell-mode-map 101 | ("C-c C-f" . ormolu-format-buffer))) 102 | 103 | (defvar k|latest-stackage-lts nil 104 | "Cached value of stackage LTS snapshot version.") 105 | (defvar k|stack-proc nil) 106 | (defun k|get-latest-stackage-lts () 107 | "Returns the latest version of the Stackage LTS resolver. 108 | 109 | This data is fetched from `stack ls snapshots --resolver remote'. 110 | If no data can be fetched, a default value (lts-14.20) is returned." 111 | (if k|latest-stackage-lts 112 | k|latest-stackage-lts 113 | (let ((default "lts-18.25") 114 | answer) 115 | (with-temp-buffer 116 | (message "Fetching version from stack...") 117 | (call-process "stack" nil (current-buffer) nil 118 | "--no-terminal" "ls" "snapshots" "--lts" "remote") 119 | (goto-char (point-max)) 120 | (if (re-search-backward "Resolver name: \\(lts-[0-9]+\\.[0-9]+\\)" nil t) 121 | (setq answer (match-string-no-properties 1)) 122 | (warn "Unable to get the version number! Returning a default value: %s" default) 123 | (setq answer default)) 124 | (setq k|latest-stackage-lts answer))))) 125 | 126 | (define-derived-mode haskell-iface-mode fundamental-mode "Haskell Iface" 127 | "View the contents of Haskell interface files, by invoking `ghc --show-iface'. 128 | 129 | This mode is not reliable: the ghc version will probably not 130 | match that of the file." 131 | (delete-region (point-min) (point-max)) 132 | (call-process "ghc" nil t nil "--show-iface" (buffer-file-name)) 133 | (read-only-mode) 134 | (set-buffer-modified-p nil) 135 | (goto-char (point-min))) 136 | 137 | (add-to-list 'auto-mode-alist '("\\.hi\\'" . haskell-iface-mode)) 138 | 139 | (defun haskell () 140 | (interactive) 141 | (find-file "/tmp/Main.hs") 142 | (when (= 0 (- (point-max) (point-min))) 143 | (insert "module Main where\n\nmain = undefined\n\n"))) 144 | 145 | (defun cabal (package-name) 146 | "Create a new cabal package under /tmp." 147 | (interactive "sPackage name: ") 148 | (let ((old-pwd default-directory)) 149 | (cd "/tmp") 150 | (mkdir package-name t) 151 | (cd (concat "/tmp/" package-name)) 152 | (with-temp-buffer 153 | (insert "cabal-version: 2.4\n") 154 | (insert (concat "name: " package-name "\n")) 155 | (insert "version: 0.1.0.0 156 | library 157 | default-language: Haskell2010 158 | exposed-modules: 159 | Lib 160 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints\n") 161 | (insert " default-extensions: NoImplicitPrelude OverloadedStrings\n") 162 | (insert " build-depends: base, text, bytestring, array, vector, stm, async, directory, filepath, rio, unliftio, microlens-platform\n") 163 | (write-file (concat package-name ".cabal"))) 164 | (async-shell-command "git init") 165 | (cd old-pwd)) 166 | (find-file (concat "/tmp/" package-name "/Lib.hs")) 167 | (delete-region (point-min) (point-max)) 168 | (insert "module Lib where 169 | 170 | import RIO 171 | 172 | run :: IO () 173 | run = runSimpleApp $ do 174 | pure ()")) 175 | 176 | (provide 'prelude-lang-haskell) 177 | -------------------------------------------------------------------------------- /modules/prelude-chinese.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | ;; Must-have. 4 | (setopt word-wrap-by-category t) 5 | 6 | ;; Table font for perfect alignment. 7 | ;; Warning: this may not work on different font settings. 8 | (with-eval-after-load 'org 9 | (set-face-font 'org-table "Iosevka Term-14") 10 | (set-face-font 'org-todo "Iosevka Term-14") 11 | (set-face-font 'org-done "Iosevka Term-14")) 12 | 13 | (with-eval-after-load 'markdown 14 | (set-face-font 'markdown-table-face "Iosevka Term-14")) 15 | 16 | (defconst +font-family-list (font-family-list) 17 | "A cache of the list of known font families on startup.") 18 | 19 | ;; font for Chinese texts 20 | (setq +preferred-chinese-fonts 21 | '("LXGW WenKai" 22 | "PingFang TC")) 23 | (setq +preferred-chinese-font (car-safe +preferred-chinese-fonts)) 24 | (when (and +preferred-chinese-font 25 | (member +preferred-chinese-font +font-family-list)) 26 | (set-fontset-font t 'han +preferred-chinese-font) 27 | (set-fontset-font t 'kana +preferred-chinese-font) 28 | (set-fontset-font t 'kanbun +preferred-chinese-font) 29 | (set-fontset-font t 'hangul +preferred-chinese-font) 30 | (set-fontset-font t 'cjk-misc +preferred-chinese-font) 31 | (set-fontset-font t 'unicode +preferred-chinese-font nil 'append)) 32 | 33 | ;; font for emoji 34 | (when k|mac 35 | (set-fontset-font t 'unicode (font-spec :family "Apple Color Emoji") nil 'prepend)) 36 | 37 | ;; fallback font 38 | (when (member "TH-Tshyn-P0" +font-family-list) 39 | (dolist (thfont '("TH-Feon" "TH-Sy-P0" "TH-Sy-P2" "TH-Sy-P16" "TH-Tshyn-P0")) 40 | (set-fontset-font t 'unicode thfont nil 'append))) 41 | 42 | ;; Hey, Org mode? 43 | (with-eval-after-load 'org 44 | ;; (setq org-emphasis-regexp-components 45 | ;; (list (concat " \t('\"{" "[:alpha:]") 46 | ;; (concat "- \t.,:!?;'\")}\\[" "[:alpha:]") 47 | ;; " \t\r\n,\"'" 48 | ;; "." 49 | ;; 1)) 50 | ;; (org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components) 51 | ;; (setcar (nthcdr 1 org-emphasis-regexp-components) "-[:space:].。,,:;!!??;;'\")}\\") 52 | ;; (org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components) 53 | ) 54 | 55 | ;; Even though I speak Chinese, please use English dictionary for 56 | ;; spell checking. 57 | (setq ispell-dictionary "en_US") 58 | 59 | ;; Overwrite the builtin version, to stop `join-line' (alias of 60 | ;; `delete-indentation') insert whitespace between CJK characters. 61 | (define-advice k|fixup-whitespace (:override ()) 62 | (interactive "*") 63 | (save-excursion 64 | (delete-horizontal-space) 65 | (if (or (looking-at "^\\|$\\|\\s)\\|\\cc\\|\\cj\\|\\ch") 66 | (save-excursion (forward-char -1) 67 | (looking-at "$\\|\\s(\\|\\s'\\|\\cc\\|\\cj\\|\\ch"))) 68 | nil 69 | (insert ?\s)))) 70 | 71 | ;; Pinyin support for Ivy 72 | ;; Shamelessly stolen from Centaur Emacs 73 | (use-package pinyinlib 74 | :commands pinyinlib-build-regexp-string 75 | :after ivy 76 | :init 77 | (with-no-warnings 78 | (defun ivy--regex-pinyin (str) 79 | "The regex builder wrapper to support pinyin." 80 | (or (pinyin-to-utf8 str) 81 | (and (fboundp 'ivy-prescient-non-fuzzy) 82 | (ivy-prescient-non-fuzzy str)) 83 | (ivy--regex-plus str))) 84 | 85 | (defun my-pinyinlib-build-regexp-string (str) 86 | "Build a pinyin regexp sequence from STR." 87 | (cond ((equal str ".*") ".*") 88 | (t (pinyinlib-build-regexp-string str t)))) 89 | 90 | (defun my-pinyin-regexp-helper (str) 91 | "Construct pinyin regexp for STR." 92 | (cond ((equal str " ") ".*") 93 | ((equal str "") nil) 94 | (t str))) 95 | 96 | (defun pinyin-to-utf8 (str) 97 | "Convert STR to UTF-8." 98 | (cond ((equal 0 (length str)) nil) 99 | ((equal (substring str 0 1) "!") 100 | (mapconcat 101 | #'my-pinyinlib-build-regexp-string 102 | (remove nil (mapcar 103 | #'my-pinyin-regexp-helper 104 | (split-string 105 | (replace-regexp-in-string "!" "" str ) 106 | ""))) 107 | "")) 108 | (t nil))) 109 | 110 | (mapcar 111 | (lambda (item) 112 | (let ((key (car item)) 113 | (value (cdr item))) 114 | (when (member value '(ivy-prescient-non-fuzzy 115 | ivy--regex-plus)) 116 | (setf (alist-get key ivy-re-builders-alist) 117 | #'ivy--regex-pinyin)))) 118 | ivy-re-builders-alist))) 119 | 120 | ;; rime 121 | (use-package rime 122 | :defer t 123 | :custom 124 | (default-input-method "rime") 125 | :init 126 | (when k|mac 127 | (setq rime-emacs-module-header-root "/Applications/Emacs.app/Contents/Resources/include")) 128 | (defun rime-menu () 129 | (interactive) 130 | (let ((last-input-event (aref (kbd "C-`") 0))) 131 | (rime-send-keybinding))) 132 | (defun rime-moran-trad () 133 | (interactive) 134 | (rime-lib-set-option "simplification" nil)) 135 | (defun rime-moran-simp () 136 | (interactive) 137 | (rime-lib-set-option "simplification" t)) 138 | 139 | ;; Keymaps 140 | (setq rime-translate-keybindings 141 | '("C-f" "C-b" "C-n" "C-p" "" "" "" "" "" "" "" 142 | "C-`" "C-s" "C-k" "C-q" "C-k" "C-i"))) 143 | 144 | ;; use rime for search 145 | (use-package rime-regexp 146 | :load-path "lisp/rime-regexp" 147 | :after (rime) 148 | :commands (rime-regexp-mode) 149 | :config 150 | (rime-regexp-mode 1)) 151 | 152 | ;; Define category O for Unicode's private use areas 153 | (defvar unicode-pua-ranges 154 | '((#xE000 . #xF8FF) 155 | (#xF0000 . #xFFFFD) 156 | (#x100000 . #x10FFFD)) 157 | "List of ranges of Unicode's private use areas.") 158 | (define-category ?O "Other (PUA)") 159 | (mapc (lambda (range) 160 | (modify-category-entry range ?O)) 161 | unicode-pua-ranges) 162 | 163 | (use-package emt 164 | :load-path "lisp/emt" 165 | :if k|mac 166 | :bind (("M-f" . emt-forward-word) 167 | ("M-b" . emt-backward-word)) 168 | :config 169 | (setq emt-lib-path (expand-file-name "lisp/emt/module/.build/release/libEMT.dylib" user-emacs-directory)) 170 | (emt-ensure)) 171 | 172 | (defun opencc-on-region (beg end conf) 173 | (let ((prefix (if (file-exists-p "/usr/local/share/opencc/s2t.json") 174 | "/usr/local/share/opencc" 175 | "/usr/share/opencc"))) 176 | (shell-command-on-region beg end (format "opencc -c %s/%s.json" prefix conf)))) 177 | 178 | (defun opencc-t2s (beg end) 179 | "Use opencc to convert trad to simp on region." 180 | (interactive "r") 181 | (opencc-on-region beg end "t2s")) 182 | 183 | (defun opencc-s2t (beg end) 184 | "Use opencc to convert simp to trad on region." 185 | (interactive "r") 186 | (opencc-on-region beg end "s2t")) 187 | 188 | (provide 'prelude-chinese) 189 | -------------------------------------------------------------------------------- /lisp/vc-use-package.el: -------------------------------------------------------------------------------- 1 | ;;; vc-use-package.el --- Primitive `package-vc' integration into `use-package' -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2015 Steckerhalter 4 | ;; 2022 Tony Zorman 5 | ;; 6 | ;; Author: steckerhalter (quelpa-use-package: https://github.com/quelpa/quelpa-use-package/) 7 | ;; Tony Zorman (vc-use-package: https://github.com/slotThe/vc-use-package) 8 | ;; Keywords: convenience, use-package, package-vc 9 | ;; Version: 0.1 10 | ;; Package-Requires: ((emacs "29.0")) 11 | ;; Homepage: https://github.com/slotThe/vc-use-package 12 | 13 | ;; This file is NOT part of GNU Emacs. 14 | 15 | ;;; License: 16 | 17 | ;; This program is free software; you can redistribute it and/or modify 18 | ;; it under the terms of the GNU General Public License as published by 19 | ;; the Free Software Foundation, either version 3 of the License, or (at 20 | ;; your option) any later version. 21 | ;; 22 | ;; This program is distributed in the hope that it will be useful, but 23 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 24 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 | ;; General Public License for more details. 26 | ;; 27 | ;; You should have received a copy of the GNU General Public License 28 | ;; along with this program. If not, see . 29 | 30 | ;;; Commentary: 31 | 32 | ;; A `:vc' handler for `use-package'. Install with `package-vc-install': 33 | ;; 34 | ;; (package-vc-install "https://github.com/slotThe/vc-use-package") 35 | ;; 36 | ;; Example usage: 37 | ;; 38 | ;; (use-package math-delimiters 39 | ;; :vc (:fetcher github :repo oantolin/math-delimiters)) 40 | ;; 41 | ;; See the README for more details. 42 | 43 | ;;; Code: 44 | 45 | (eval-when-compile (require 'cl-lib)) 46 | (require 'use-package-ensure) 47 | 48 | (defvar vc-use-package-keywords 49 | '(:fetcher :repo :rev :backend) 50 | "All arguments that `package-vc-install' supports.") 51 | 52 | (defconst vc-use-package-fetchers 53 | '(:github "https://github.com/" 54 | :gitlab "https://gitlab.com/" 55 | :codeberg "https://codeberg.org/" 56 | :sourcehut "https://git.sr.ht/~") 57 | "Places from where to fetch packages.") 58 | 59 | (defun vc-use-package--installed? (verbatim name) 60 | "Check if the given package is installed. 61 | VERBATIM are possible verbatim arguments to `package-vc-install'; 62 | this is either just the name of the package, or a list. In the 63 | latter case, the car of the list is the name of the package. If 64 | VERBATIM is nil, then NAME is the name of the package." 65 | (package-installed-p 66 | (or (if (listp verbatim) (car verbatim) verbatim) 67 | name))) 68 | 69 | (cl-defun vc-use-package--install (&key verbatim fetcher repo name rev backend) 70 | "Thin wrapper around `package-vc-install'. 71 | This exists so we can have sane keywords arguments, yet don't 72 | have to go overboard when normalising." 73 | (unless (vc-use-package--installed? verbatim name) 74 | (if verbatim 75 | (package-vc-install verbatim) 76 | (package-vc-install (concat fetcher repo) rev backend name)))) 77 | 78 | ;;;; Normalisation 79 | 80 | (defun vc-use-package--check-fetcher (val) 81 | "Check whether VAL is a correct `:fetcher' argument. 82 | More specifically, check if it's (i) URL or (ii) either a string 83 | or a symbol representing one possible destination in 84 | `vc-use-package-keywords'." 85 | (cond 86 | ((string-prefix-p "https://" val) val) 87 | ((plist-get vc-use-package-fetchers (intern (concat ":" val)))) 88 | (t (use-package-error 89 | (format ":fetcher is not a url or one of %s." 90 | (mapcar #'car vc-use-package-fetchers)))))) 91 | 92 | (defun vc-use-package--normalise-args (args) 93 | "Normalise the plist given to `:vc'." 94 | (cl-flet* ((mk-string (s) 95 | (if (stringp s) s (symbol-name s))) 96 | (normalise (arg val) 97 | (pcase arg 98 | (:fetcher (vc-use-package--check-fetcher (mk-string val))) 99 | (:rev (if (eq val :last-release) val (mk-string val))) 100 | (:repo (mk-string val)) 101 | (_ val)))) 102 | (cl-loop for (k v) on args by #'cddr 103 | nconc (list k (normalise k v))))) 104 | 105 | (defun vc-use-package--handle-errors (arg) 106 | "Primitive error handling for the most common cases." 107 | (cl-flet ((err (s &rest os) 108 | (use-package-error (apply #'format s os)))) 109 | (let* ((keywords (cl-loop for (k _) on arg by #'cddr 110 | if (not (memq k vc-use-package-keywords)) 111 | do (err ":vc declaration contains unknown keywords: %s. Known keywords are: %s" 112 | k vc-use-package-keywords) 113 | collect k))) 114 | (cond 115 | ((not (memq :fetcher keywords)) 116 | (err ":vc plist declaration must at least contain the `:fetcher' keyword")) 117 | ((not (plistp arg)) 118 | (use-package-error "Argument given to :vc must be a plist.")))))) 119 | 120 | ;;;###autoload 121 | (defun use-package-normalize/:vc (name _keyword args) 122 | (let ((arg (car args))) 123 | (cl-flet ((spec? (xs) 124 | (and (consp xs) (not (keywordp (car xs)))))) 125 | (pcase arg 126 | ;; A symbol or a cons-cell as an input means to verbatim forward 127 | ;; the argument to package-vc-install. 128 | ((or 'nil 't) `(:verbatim ,name)) 129 | ((or (pred symbolp) (pred spec?)) `(:verbatim ,arg)) 130 | ;; A plist represents a more complex argument structure. 131 | (_ (vc-use-package--handle-errors arg) 132 | (vc-use-package--normalise-args (plist-put arg :name name))))))) 133 | 134 | ;;;; Handler 135 | 136 | ;;;###autoload 137 | (defun use-package-handler/:vc (name-symbol _keyword args rest state) 138 | (let ((body (use-package-process-keywords name-symbol rest state))) 139 | ;; This happens at macro expansion time, not when the expanded code 140 | ;; is compiled or evaluated. 141 | (when args 142 | (if (bound-and-true-p byte-compile-current-file) 143 | (apply #'vc-use-package--install args) 144 | (push `(apply #'vc-use-package--install ',args) body))) 145 | body)) 146 | 147 | ;;;; Play nice with `use-package-always-ensure' 148 | 149 | (defun vc-use-package--override-:ensure (func name-symbol keyword ensure rest state) 150 | (let ((ensure (unless (plist-member rest :vc) 151 | ensure))) 152 | (funcall func name-symbol keyword ensure rest state))) 153 | 154 | (defun vc-use-package-activate-advice () 155 | (advice-add 'use-package-handler/:ensure :around 156 | #'vc-use-package--override-:ensure)) 157 | 158 | (defun vc-use-package-deactivate-advice () 159 | (advice-remove 'use-package-handler/:ensure 160 | #'vc-use-package--override-:ensure)) 161 | 162 | ;;;; Activate 163 | 164 | (defun vc-use-package-set-keyword () 165 | "Insert `vc-use-package-keyword' into `use-package-keywords'. 166 | More specifically, insert it after `:unless' so that we only run 167 | if either `:if', `:when', `:unless' or `:requires' are satisfied." 168 | (unless (member :vc use-package-keywords) 169 | (let ((unless (member :unless use-package-keywords))) 170 | (when unless 171 | (setcdr unless (cons :vc (cdr unless))))))) 172 | 173 | (vc-use-package-set-keyword) ; register keyword on require 174 | 175 | (when use-package-always-ensure 176 | (vc-use-package-activate-advice)) 177 | 178 | (provide 'vc-use-package) 179 | ;;; vc-use-package.el ends here 180 | -------------------------------------------------------------------------------- /modules/prelude-apps.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;;; things that don't fit elsewhere 3 | (use-package wakatime-mode 4 | ;; :when (not (eq system-type 'darwin)) ; wakatime can cause emacs to hang on macos 5 | :diminish "" 6 | :commands (global-wakatime-mode) 7 | :defer 3 8 | :config 9 | (global-wakatime-mode t)) 10 | 11 | ;; eshell sends notifications when commands finished 12 | ;; stolen from https://blog.hoetzel.info/post/eshell-notifications/ 13 | (use-package alert 14 | :after (eshell) 15 | :commands (eshell-command-alert) 16 | :config 17 | (when (fboundp 'mac-do-applescript) 18 | (define-advice alert-osx-notifier-notify (:override (info)) 19 | (mac-do-applescript (format "display notification %S with title %S" 20 | (alert-encode-string (plist-get info :message)) 21 | (alert-encode-string (plist-get info :title)))) 22 | (alert-message-notify info))) 23 | 24 | (alert-add-rule :status '(buried) 25 | :mode 'eshell-mode 26 | :style (if k|mac 27 | 'osx-notifier 28 | 'notifications)) 29 | 30 | (defun eshell-command-alert (process status) 31 | (let* ((cmd (process-command process)) 32 | (buffer (process-buffer process)) 33 | (msg (format "%s: %s" (mapconcat 'identity cmd " ") status))) 34 | (if (string-prefix-p "finished" status) 35 | (alert msg :buffer buffer :severity 'normal) 36 | (alert msg :buffer buffer :severity 'urgent)))) 37 | (add-hook 'eshell-kill-hook #'eshell-command-alert)) 38 | 39 | ;; cabon now sh 40 | (use-package carbon-now-sh 41 | :commands (carbon-now-sh)) 42 | 43 | ;; elfeed 44 | (use-package elfeed 45 | :commands (elfeed)) 46 | 47 | ;; speed-type 48 | (use-package speed-type 49 | :commands (speed-type-text speed-type-buffer)) 50 | 51 | ;; telega 52 | (use-package telega 53 | :commands (telega) 54 | :config 55 | (setq telega-avatar-workaround-gaps-for '(return t))) 56 | 57 | ;; google translate 58 | (use-package google-translate 59 | :commands (google-translate-buffer google-translate-at-point) 60 | :config 61 | (define-advice google-translate-json-suggestion (:override (json)) 62 | (let ((info (aref json 7))) 63 | (if (and info (> (length info) 0)) 64 | (aref info 1) 65 | nil)))) 66 | 67 | (use-package treemacs 68 | :bind (("C-c t" . treemacs) 69 | ("M-0" . treemacs-select-window)) 70 | :commands (treemacs) 71 | :config 72 | 73 | (setq treemacs-is-never-other-window t) 74 | 75 | ;; Add the name of the current workspace to the mode line 76 | (defun prelude-treemacs-mode-line () 77 | '(" Treemacs " (:eval))) 78 | (setq treemacs-user-mode-line-format '(" Treemacs [" (:eval (treemacs-workspace->name (treemacs-current-workspace))) "] ")) 79 | 80 | ;; 81 | (setq prelude-treemacs-default-workspace "Default") 82 | 83 | (defun -treemacs-get-workspace (name) 84 | (let ((workspaces (->> treemacs--workspaces 85 | (--reject (eq it (treemacs-current-workspace))) 86 | (--map (cons (treemacs-workspace->name it) it))))) 87 | (cdr (--first (string= (car it) name) workspaces)))) 88 | 89 | (defun -treemacs-create-workspace (name) 90 | (treemacs-block 91 | (treemacs-return-if (treemacs--is-name-invalid? name) 92 | `(invalid-name ,name)) 93 | (-when-let (ws (--first (string= name (treemacs-workspace->name it)) 94 | treemacs--workspaces)) 95 | (treemacs-return `(duplicate-name ,ws))) 96 | (-let [workspace (treemacs-workspace->create! :name name)] 97 | (add-to-list 'treemacs--workspaces workspace :append) 98 | (treemacs--persist) 99 | (run-hook-with-args 'treemacs-create-workspace-functions workspace) 100 | `(success ,workspace)))) 101 | 102 | (defun -treemacs-get-or-create-workspace (name) 103 | (or (--first (string= name (treemacs-workspace->name it)) 104 | treemacs--workspaces) 105 | (let ((res (-treemacs-create-workspace name))) 106 | (if (equal (car res) 'success) 107 | (cdr res) 108 | (error "Couldn't create workspace"))))) 109 | 110 | (defun -treemacs-switch-to-workspace (ws) 111 | (setf (treemacs-current-workspace) ws) 112 | (treemacs--invalidate-buffer-project-cache) 113 | (treemacs--rerender-after-workspace-change) 114 | (run-hooks 'treemacs-switch-workspace-hook)) 115 | 116 | (defun -treemacs-which-workspace () 117 | "Which workspace does the current file belong to?" 118 | (--first (treemacs-is-path (buffer-file-name) :in-workspace it) (treemacs-workspaces))) 119 | 120 | ;; (setq current-file-ws (-treemacs-which-workspace)) 121 | ;; (setq default-ws (-treemacs-get-or-create-workspace prelude-treemacs-default-workspace)) 122 | 123 | ;; (defun -treemacs-add-project-to-workspace-or-switch () 124 | ;; "Add the current project to the default workspace, or locate it if it's already known in a workspace. 125 | 126 | ;; The default workspace is specificied by `prelude-treemacs-default-workspace'" 127 | ;; (let (current-file-ws (-treemacs-which-workspace)) 128 | ;; (if current-file-ws 129 | ;; (progn 130 | ;; (-treemacs-switch-to-workspace current-file-ws) 131 | ;; (treemacs--follow)) 132 | ;; (let ((default-ws (-treemacs-get-or-create-workspace prelude-treemacs-default-workspace))) 133 | ;; (message "I'm here!") 134 | ;; (-treemacs-switch-to-workspace default-ws) 135 | ;; (treemacs-display-current-project-exclusively))))) 136 | 137 | ;; (defun -treemacs-locate-project-if-in-workspace () 138 | ;; (let ((ws (-treemacs-which-workspace))) 139 | ;; (when (and (not (null ws)) 140 | ;; (not (eq ws (treemacs-current-workspace)))) 141 | ;; (-treemacs-switch-to-workspace ws)))) 142 | 143 | ;; (add-hook 'treemacs-mode-hook #'variable-pitch-mode) 144 | ;; (add-hook 'projectile-after-switch-project-hook #'-treemacs-add-project-to-workspace-or-switch) 145 | ;; (add-hook 'buffer-list-update-hook #'-treemacs-locate-project-if-in-workspace) 146 | 147 | (use-package treemacs-projectile 148 | :after (projectile) 149 | :demand t) 150 | 151 | (use-package treemacs-tab-bar 152 | :after (tab-bar) 153 | :demand t) 154 | 155 | (use-package treemacs-nerd-icons 156 | :demand t 157 | :config 158 | (treemacs-load-theme "nerd-icons")) 159 | 160 | (use-package treemacs-persp 161 | :after (persp-mode) 162 | :demand t)) 163 | 164 | (use-package pdf-tools 165 | :mode ("\\.pdf\\'" . pdf-view-mode) 166 | :config 167 | (pdf-tools-install) 168 | 169 | (use-package pdf-continuous-scroll-mode 170 | :disabled ; This mode barely does anything useful 171 | :vc (:fetcher github :repo "dalanicolai/pdf-continuous-scroll-mode.el") 172 | :hook (pdf-view-mode . pdf-continuous-scroll-mode))) 173 | 174 | 175 | 176 | (use-package dash-at-point 177 | :commands (dash-at-point) 178 | :preface 179 | :bind (("M-g M-d" . dash-at-point) 180 | ("M-g d" . dash-at-point)) 181 | :config 182 | (with-eval-after-load 'embark 183 | (define-key embark-symbol-map (kbd "d") 'dash-at-point))) 184 | 185 | ;; epub reader 186 | (use-package nov 187 | :commands (nov-mode)) 188 | 189 | ;; debbugs 190 | (use-package debbugs 191 | :defer t) 192 | 193 | (use-package ledger-mode 194 | :defer t) 195 | 196 | ;; world-clock 197 | (setq world-clock-list 198 | '(("Asia/Chongqing" "China") 199 | ("Asia/Tokyo" "Japan") 200 | ("Europe/Berlin" "Germany") 201 | ("Europe/Paris" "France") 202 | ("America/New_York" "New York") 203 | ("America/Los_Angeles" "Los Angeles"))) 204 | 205 | (provide 'prelude-apps) 206 | -------------------------------------------------------------------------------- /modules/prelude-completion.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | 3 | (use-package completion 4 | :ensure nil 5 | :custom 6 | (completion-styles '(prescient orderless))) 7 | 8 | (use-package prescient 9 | :demand t 10 | :config 11 | (prescient-persist-mode)) 12 | 13 | (use-package orderless 14 | :demand t 15 | :custom 16 | (orderless-matching-styles 17 | '(orderless-prefixes ; Match a component as multiple word prefixes. 18 | orderless-initialism ; Match a component as an initialism. 19 | orderless-flex ; Match a component in flex style. 20 | orderless-regexp)) ; Match a component as a regexp. 21 | (orderless-smart-case t)) 22 | 23 | (use-package vertico 24 | :hook (after-init . vertico-mode) 25 | :config 26 | (define-key vertico-map "\r" #'vertico-directory-enter) 27 | (define-key vertico-map "\d" #'vertico-directory-delete-char) 28 | (define-key vertico-map "\M-\d" #'vertico-directory-delete-word) 29 | (add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy) 30 | (vertico-mouse-mode +1) 31 | 32 | ;; Configure how vertico is displayed on a per-category or 33 | ;; per-command basis. 34 | (setq vertico-multiform-commands 35 | '((consult-imenu buffer indexed) 36 | (consult-line buffer) 37 | (consult-line-multi buffer))) 38 | (setq vertico-multiform-categories 39 | '((consult-grep buffer))) 40 | (vertico-multiform-mode t) 41 | 42 | ;; Emacs requires the point to be always in the view. When the 43 | ;; point is near the modeline, pressing e.g. M-x will cause a 44 | ;; recenter, which is annoying. 45 | (defun +move-point-to-the-beginning-of-the-window () 46 | (dolist (win (window-list)) 47 | (let ((buf (window-buffer))) 48 | (with-current-buffer buf 49 | (unless (minibufferp) 50 | (goto-char (window-start win)) 51 | (push-mark)))))) 52 | (defun +restore-saved-point () 53 | (dolist (win (window-list)) 54 | (let ((buf (window-buffer win))) 55 | (unless (minibufferp) 56 | (with-current-buffer buf 57 | (pop-mark)))))) 58 | ;; Unfortunately, this does not work yet. 59 | ;; (add-hook 'minibuffer-setup-hook #'+move-point-to-the-beginning-of-the-window) 60 | ;; (add-hook 'minibuffer-exit-hook #'+restore-saved-point) 61 | 62 | ;; vertico-repeat 63 | (add-hook 'minibuffer-setup-hook #'vertico-repeat-save) 64 | (keymap-set vertico-map "C-r" #'vertico-repeat) 65 | (keymap-set vertico-map "C-s" #'vertico-repeat-next) 66 | (with-eval-after-load 'vertico-repeat 67 | (setq vertico-repeat-filter 68 | (remove 'execute-extended-command 69 | (remove 'execute-extended-command-for-buffer 70 | vertico-repeat-filter)))) 71 | ) 72 | 73 | (use-package vertico-posframe 74 | :disabled 75 | :after (vertico) 76 | :hook (vertico-mode . vertico-posframe-mode) 77 | :config 78 | (setq vertico-posframe-border-width 3)) 79 | 80 | (use-package marginalia 81 | :demand t 82 | :config 83 | (marginalia-mode)) 84 | 85 | (use-package nerd-icons-completion 86 | :config 87 | (nerd-icons-completion-mode)) 88 | 89 | (use-package embark 90 | :bind 91 | (("C-," . embark-act) 92 | ("C-." . embark-dwim) 93 | ("C-h B" . embark-bindings) 94 | :map minibuffer-mode-map 95 | ("C-c C-o" . embark-export) ;; This is the default binding of Ivy-Occur 96 | ) 97 | :config 98 | (add-to-list 'display-buffer-alist 99 | '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" 100 | nil 101 | (window-parameters (mode-line-format . none))))) 102 | 103 | (use-package consult 104 | :bind 105 | ;; ctl-x-map 106 | (("C-x M-:" . consult-complex-command) 107 | ("C-x b" . consult-buffer) 108 | ("C-x 4 b" . consult-buffer-other-window) 109 | ("C-x 5 b" . consult-buffer-other-frame) 110 | ("C-x r b" . consult-bookmark) 111 | 112 | ;; goto-map 113 | ("M-g e" . consult-compile-error) 114 | ("M-g f" . consult-flymake) 115 | ("M-g g" . consult-goto-line) 116 | ("M-g M-g" . consult-goto-line) 117 | ("M-g o" . consult-outline) 118 | ("M-g m" . consult-mark) 119 | ("M-g k" . consult-global-mark) 120 | ("M-g i" . consult-imenu) 121 | ("M-g I" . consult-imenu-multi) 122 | 123 | ;; search-map 124 | ("M-s d" . consult-find) 125 | ("M-s D" . consult-locate) 126 | ("M-s g" . consult-grep) 127 | ("M-s G" . consult-git-grep) 128 | ("M-s r" . consult-ripgrep) 129 | ("M-s l" . consult-line) 130 | ("M-s L" . consult-line-multi) 131 | ("M-s k" . consult-keep-lines) 132 | ("M-s u" . consult-focus-lines) 133 | 134 | ;; isearch related 135 | ("M-s e" . consult-isearch-history) 136 | :map isearch-mode-map 137 | ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string 138 | ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string 139 | ("M-s l" . consult-line) ;; needed by consult-line to detect isearch 140 | ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch 141 | ) 142 | 143 | :hook 144 | (completion-list-mode . consult-preview-at-point-mode) 145 | 146 | :init 147 | 148 | (setq xref-show-xrefs-function #'consult-xref 149 | xref-show-definitions-function #'consult-xref) 150 | 151 | :config 152 | (setq consult-narrow-key "<") 153 | (setq consult-async-min-input 1) 154 | 155 | ;; vertico-posframe overlaps with the matched line. 156 | (defun k|recenter-around-top () 157 | "Equivalent to `recenter-top-bottom' with argument 0.2." 158 | (recenter (round (* 0.2 (window-height))) t)) 159 | (setq consult-after-jump-hook '(k|recenter-around-top))) 160 | 161 | (use-package embark-consult 162 | :after (embark consult) 163 | :hook 164 | (embark-consult-mode . consult-preview-at-point-mode)) 165 | 166 | (use-package minibuffer 167 | :ensure nil 168 | :config 169 | (setq read-answer-short t) 170 | (setq resize-mini-windows 'grow-only) 171 | (setq enable-recursive-minibuffers t) 172 | (setq read-buffer-completion-ignore-case t) 173 | (setq read-file-name-completion-ignore-case t) 174 | (setq completion-category-defaults nil) 175 | (setq completions-format 'vertical) 176 | (setq-default completion-at-point-functions nil)) 177 | 178 | ;; Additional capf. 179 | (use-package cape 180 | :demand t 181 | :config 182 | (add-to-list 'completion-at-point-functions #'cape-file) 183 | ;;(add-to-list 'completion-at-point-functions #'cape-tex) 184 | (add-to-list 'completion-at-point-functions #'cape-dabbrev) 185 | ;;(add-to-list 'completion-at-point-functions #'cape-keyword) 186 | ;;(add-to-list 'completion-at-point-functions #'cape-sgml) 187 | ;;(add-to-list 'completion-at-point-functions #'cape-rfc1345) 188 | (add-to-list 'completion-at-point-functions #'cape-abbrev) 189 | ;;(add-to-list 'completion-at-point-functions #'cape-ispell) 190 | ;;(add-to-list 'completion-at-point-functions #'cape-dict) 191 | ;;(add-to-list 'completion-at-point-functions #'cape-symbol) 192 | ;;(add-to-list 'completion-at-point-functions #'cape-line) 193 | ) 194 | 195 | (use-package corfu 196 | :demand t 197 | :custom 198 | (corfu-auto t) 199 | (corfu-quit-no-match t) 200 | (corfu-preview-current t) 201 | (corfu-cycle t) 202 | (corfu-preselect 'valid) 203 | :bind (:map corfu-map 204 | ("" . nil) 205 | ("" . corfu-quit) 206 | ("M-d" . corfu-popupinfo-documentation) 207 | ("M-l" . corfu-info-location)) 208 | :init 209 | (global-corfu-mode) 210 | (corfu-popupinfo-mode) 211 | :config 212 | ;; Let RET be newlines. 213 | (define-key corfu-map (kbd "RET") nil)) 214 | 215 | (use-package nerd-icons-corfu 216 | :after (corfu) 217 | :config 218 | (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter)) 219 | 220 | (use-package kind-icon 221 | :disabled 222 | :after (corfu) 223 | :custom 224 | (kind-icon-default-face 'corfu-default) 225 | :config 226 | (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)) 227 | 228 | (use-package corfu-terminal 229 | :if (and (not (display-graphic-p)) ; not GUI 230 | (not (fboundp 'tty-tip-mode)) ; not compiled with tty child frames 231 | ) 232 | :demand t 233 | :init 234 | (corfu-terminal-mode)) 235 | 236 | (provide 'prelude-completion) 237 | -------------------------------------------------------------------------------- /modules/prelude-project.el: -------------------------------------------------------------------------------- 1 | ;;; -*- lexical-binding: t; -*- 2 | ;; Deal with the concept of `Project' 3 | 4 | (use-package project 5 | :ensure nil 6 | :bind (:map project-prefix-map 7 | ("s" . eat-project)) 8 | :custom 9 | (project-buffers-viewer 'project-list-buffers-buffer-ibuffer) 10 | (project-switch-use-entire-map nil) 11 | (project-switch-commands 'project-find-file) 12 | (project-vc-extra-root-markers '(".projectile" ".project")) 13 | 14 | :config 15 | (require 'projection) 16 | (require 'projection-multi) 17 | 18 | ;; vterm integraion 19 | (add-to-list 'project-kill-buffer-conditions '(major-mode . vterm-mode)) 20 | (defun project-vterm () 21 | (interactive) 22 | (require 'vterm) 23 | (defvar vterm-buffer-name) 24 | (let* ((default-directory (project-root (project-current t))) 25 | (vterm-buffer-name (project-prefixed-buffer-name "vterm")) 26 | (vterm-buffer (get-buffer vterm-buffer-name))) 27 | (if vterm-buffer 28 | (pop-to-buffer vterm-buffer current-prefix-arg) 29 | (vterm current-prefix-arg))))) 30 | 31 | (use-package projection 32 | :defer t 33 | :hook (after-init . global-projection-hook-mode) 34 | :bind-keymap 35 | ("C-x P" . projection-map) 36 | :bind (:map project-prefix-map 37 | ("a" . projection-find-other-file)) 38 | :config 39 | (setq projection-project-type-rime-config 40 | (projection-type 41 | :name 'rime-config 42 | :predicate (defun projection-rime-config-project-p () 43 | (and (file-exists-p "default.yaml"))) 44 | :configure "" 45 | :build "" 46 | :test "" 47 | :run "" 48 | :install "" 49 | :compile-multi-targets 50 | '(("rime:squirrel:deploy" . "\"/Library/Input Methods/Squirrel.app/Contents/MacOS/Squirrel\" --reload") 51 | ("rime:f5m:copy and deploy" . "./fcitx.sh && \"/Library/Input Methods/Fcitx5.app/Contents/bin/fcitx5-curl\" /config/addon/rime/deploy -X POST -d '{}'") 52 | ("rime:f5m:deploy" . "\"/Library/Input Methods/Fcitx5.app/Contents/bin/fcitx5-curl\" /config/addon/rime/deploy -X POST -d '{}'")))) 53 | (add-to-list 'projection-project-types projection-project-type-rime-config)) 54 | 55 | (use-package projection-multi 56 | :bind (:map project-prefix-map 57 | ("RET" . projection-multi-compile))) 58 | 59 | (use-package projectile 60 | :disabled 61 | ;; I'm really sad to let projectile go, but it does not handle remote files very well. 62 | ;; Currently, I will be experimenting with 63 | 64 | :hook (after-init . projectile-mode) 65 | :diminish "" 66 | 67 | ;; Steal `C-x p' from project.el. 68 | :bind-keymap ("C-x p" . projectile-command-map) 69 | :bind-keymap ("H-p" . projectile-command-map) 70 | 71 | :custom 72 | (projectile-auto-discover nil) 73 | (projectile-completion-system 'auto) 74 | (projectile-switch-project-action #'projectile-find-file) 75 | (projectile-current-project-on-switch 'move-to-end) 76 | (projectile-find-dir-includes-top-level t) 77 | (projectile-enable-caching t) 78 | (projectile-indexing-method 'hybrid) 79 | 80 | ;; Emulate project.el keybindings 81 | :bind (:map projectile-command-map 82 | ("f" . projectile-find-file) 83 | ("g" . projectile-ripgrep) 84 | ("s" . projectile-run-vterm-other-window) 85 | ("x x" . projectile-run-vterm) 86 | ("i" . projectile-install-project) 87 | ("I" . projectile-invalidate-cache) 88 | ;; ("s s" . projectile-ripgrep) 89 | ;; ("s a" . projectile-ag) 90 | ) 91 | 92 | :config 93 | (add-to-list 'projectile-globally-ignored-files ".tags") 94 | 95 | ;; disable project detecion on remote files 96 | (advice-add 'projectile-project-root :around 97 | (lambda (orig &optional dir) 98 | (when (not (file-remote-p default-directory)) 99 | (funcall orig dir)))) 100 | 101 | ;; Support for Citre (a u-ctags frontend). 102 | (advice-add 'projectile-regenerate-tags :around 103 | (lambda (orig &rest args) 104 | (if citre-mode 105 | (citre-update-this-tags-file) 106 | (apply orig args)))) 107 | 108 | ;; Register project types 109 | (projectile-register-project-type 110 | 'xmake 111 | '("xmake.lua") 112 | :configure "xmake config" 113 | :compile "xmake build" 114 | :run "xmake run" 115 | :install "xmake install" 116 | :package "xmake package") 117 | 118 | (add-to-list 'projectile-other-file-alist '("schema.yaml" "dict.yaml")) 119 | (add-to-list 'projectile-other-file-alist '("dict.yaml" "schema.yaml")) 120 | (projectile-register-project-type 121 | 'rime 122 | '("default.yaml") 123 | :project-file "default.yaml" 124 | :configure "" 125 | :compile "" 126 | :run "\"/Library/Input Methods/Squirrel.app/Contents/MacOS/Squirrel\" --reload" 127 | :install "" 128 | :package "") 129 | 130 | :preface 131 | 132 | ;; fix projectile bug 133 | (defun projectile-ripgrep (search-term &optional arg) 134 | "Run a ripgrep (rg) search with `SEARCH-TERM' at current project root. 135 | 136 | With an optional prefix argument ARG SEARCH-TERM is interpreted as a 137 | regular expression. 138 | 139 | This command depends on of the Emacs packages ripgrep or rg being 140 | installed to work." 141 | (interactive 142 | (list (projectile--read-search-string-with-default 143 | (format "Ripgrep %ssearch for" (if current-prefix-arg "regexp " ""))) 144 | current-prefix-arg)) 145 | (let ((args (mapcar (lambda (val) (concat "--glob !" (shell-quote-argument val))) 146 | (append projectile-globally-ignored-files 147 | projectile-globally-ignored-directories)))) 148 | ;; we rely on the external packages ripgrep and rg for the actual search 149 | ;; 150 | ;; first we check if we can load ripgrep 151 | (cond ((require 'ripgrep nil 'noerror) 152 | (ripgrep-regexp search-term 153 | (projectile-acquire-root) 154 | (if arg 155 | args 156 | (cons "--fixed-strings --hidden" args)))) 157 | ;; and then we try rg 158 | ((require 'rg nil 'noerror) 159 | (rg-run search-term 160 | "*" ;; all files 161 | (projectile-acquire-root) 162 | (not arg) ;; literal search? 163 | nil ;; no need to confirm 164 | args)) 165 | (t (error "Packages `ripgrep' and `rg' are not available"))))) 166 | 167 | ;; ;; PTerm related 168 | ;; (defun projectile-run-pterm-other-window (&optional arg) 169 | ;; "Invoke `pterm' in the project's root. 170 | 171 | ;; Switch to the project specific term buffer if it already exists. 172 | 173 | ;; Use a prefix argument ARG to indicate creation of a new process instead." 174 | ;; (interactive "P") 175 | ;; (projectile--pterm arg 'other-window)) 176 | 177 | ;; (defun projectile-run-pterm (&optional arg) 178 | ;; "Invoke `pterm' in the project's root. 179 | 180 | ;; Switch to the project specific term buffer if it already exists. 181 | 182 | ;; Use a prefix argument ARG to indicate creation of a new process instead." 183 | ;; (interactive "P") 184 | ;; (projectile--pterm arg)) 185 | 186 | ;; (defun projectile--pterm (&optional new-process other-window) 187 | ;; "Invoke `vterm' in the project's root. 188 | 189 | ;; Use argument NEW-PROCESS to indicate creation of a new process instead. 190 | ;; Use argument OTHER-WINDOW to indentation whether the buffer should 191 | ;; be displayed in a different window. 192 | 193 | ;; Switch to the project specific term buffer if it already exists." 194 | ;; (let* ((project (projectile-acquire-root)) 195 | ;; (buffer (projectile-generate-process-name "pterm" new-process project))) 196 | ;; (if (buffer-live-p (get-buffer buffer)) 197 | ;; (if other-window 198 | ;; (switch-to-buffer-other-window buffer) 199 | ;; (switch-to-buffer buffer)) 200 | ;; (projectile-with-default-dir project 201 | ;; (progn 202 | ;; (if other-window 203 | ;; (pterm-other-window buffer) 204 | ;; (pterm buffer)) 205 | ;; (rename-buffer (concat "*term: " (projectile-project-name) "*") t)))))) 206 | ) 207 | 208 | (provide 'prelude-project) 209 | -------------------------------------------------------------------------------- /modules/prelude-prog.el: -------------------------------------------------------------------------------- 1 | ;;; preloude-prog --- -*- lexical-binding: t; -*- 2 | 3 | ;;; Commentary: 4 | 5 | ;;; Configuration for programming needs. 6 | ;;; Some portions might be a standalone module. 7 | 8 | ;;; Code: 9 | 10 | (setq-default indent-tabs-mode nil) 11 | (show-paren-mode t) 12 | (setq show-paren-delay 0.0) 13 | (setq tab-always-indent 'complete) 14 | (add-hook 'prog-mode-hook 'turn-on-adaptive-wrap) 15 | (setq xref-history-storage 'xref-window-local-history) 16 | (setq xref-search-program 'ripgrep) 17 | (add-hook 'prog-mode-hook 'goto-address-prog-mode) 18 | 19 | 20 | ;;; Tree-sitter basic configuration 21 | (when (boundp 'treesit-extra-load-path) 22 | (add-to-list 'treesit-extra-load-path (no-littering-expand-var-file-name "tree-sitter"))) 23 | 24 | ;; (setq major-mode-remap-alist 25 | ;; '((python-mode . python-ts-mode) ;; python-ts-mode is better than python-mode at indentation 26 | ;; )) 27 | (use-package yaml-mode 28 | :iload (yaml-mode) 29 | :mode ("\\.ya?ml\\'" . yaml-mode)) 30 | (use-package dockerfile-mode 31 | :mode ("Dockerfile.*\\'" . dockerfile-mode) 32 | :mode (".dockerfile\\'" . dockerfile-mode)) 33 | 34 | (with-eval-after-load 'treesit 35 | (defun treesit--explorer-jump-advice-really-jump (button) 36 | "Really jump to the node, not just highlight it." 37 | (with-current-buffer treesit--explorer-source-buffer 38 | (goto-char (button-get button 'node-start)))) 39 | (advice-add 'treesit--explorer-jump :after #'treesit--explorer-jump-advice-really-jump)) 40 | 41 | 42 | ;;; Snippets 43 | (use-package yasnippet 44 | :commands (yas-minor-mode yas-global-mode)) 45 | 46 | (use-package yasnippet-snippets 47 | :after (yasnippet) 48 | :defer 10 49 | :config 50 | (yasnippet-snippets-initialize)) 51 | 52 | (use-package hl-todo 53 | :hook (prog-mode . hl-todo-mode)) 54 | 55 | (use-package rainbow-delimiters 56 | :hook (c-common-mode . rainbow-delimiters-mode)) 57 | 58 | (use-package puni 59 | :disabled 60 | :hook (prog-mode . puni-mode)) 61 | 62 | 63 | ;;; Language Server Protocol 64 | (setq read-process-output-max (* 1024 1024)) 65 | 66 | (use-package eglot 67 | :commands (eglot eglot-ensure) 68 | :config 69 | (setq eglot-confirm-server-initiated-edits nil 70 | eglot-autoreconnect 60 71 | eglot-autoshutdown t) 72 | 73 | (defun toggle-eglot-debug () 74 | (interactive) 75 | (if (= 0 eglot-events-buffer-size) 76 | (setq eglot-events-buffer-size 20000) 77 | (setq eglot-events-buffer-size 0))) 78 | (setq eglot-events-buffer-size 0) 79 | 80 | ;; eglot expansion should be okay these days... 81 | ;; (fset #'eglot--snippet-expansion-fn #'ignore) 82 | 83 | ;; The buster conflicts with corfu, causing repeated insertions. 84 | ;; (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster) 85 | ) 86 | 87 | (use-package eglot-booster 88 | :disabled 89 | :vc (:fetcher github :repo "jdtsmith/eglot-booster") 90 | :after eglot 91 | :config (eglot-booster-mode)) 92 | 93 | ;; 94 | ;; lsp-mode is powerful and cool! but it has severe performance 95 | ;; problems. not lsp-mode but emacs itself is to blame. 96 | ;; 97 | (use-package lsp-mode 98 | :commands (lsp lsp-mode) 99 | :config 100 | (setq lsp-headerline-breadcrumb-enable nil 101 | lsp-enable-snippet t) 102 | 103 | ;; LSP lens performs poorly on older Emacs versions. 104 | ;; Emacs 29 introduced noverlay -- which make overlays exponentially faster. 105 | (setq lsp-lens-enable (not (version< emacs-version "29"))) 106 | 107 | ;; *DANGER*: It warns that you shouldn't use this unless you know 108 | ;; the internals of lsp-mode. 109 | ;; その時はその時だ 110 | (setq lsp-auto-guess-root t)) 111 | 112 | (use-package lsp-ui 113 | :after (lsp-mode) 114 | :commands (lsp-ui-mode)) 115 | 116 | (defcustom prelude-auto-lsp nil 117 | "Whether to start lsp automatically on all supported languages." 118 | :group 'prelude 119 | :type 'boolean) 120 | 121 | (defcustom prelude-lsp-client 'eglot 122 | "The LSP client to use. 123 | 124 | One of `lsp-mode', `eglot', or `lsp-bridge'." 125 | :group 'prelude 126 | :type 'symbol) 127 | 128 | (defun k|lsp-ensure () 129 | "Ensure the preferred lsp client has started." 130 | (interactive) 131 | (catch 'foo 132 | (cond ((not prelude-auto-lsp) 133 | (throw 'foo nil)) 134 | ((eq prelude-lsp-client 'lsp-bridge) 135 | (lsp-bridge-mode) 136 | ;; Let lsp-bridge control when popups should be displayed. 137 | (setq corfu-auto nil)) 138 | ((eq prelude-lsp-client 'lsp-mode) 139 | (lsp-deferred)) 140 | ((eq prelude-lsp-client 'eglot) 141 | (eglot-ensure))))) 142 | 143 | 144 | (use-package dap-mode 145 | :commands (dap-mode) 146 | :config 147 | (require 'dap-lldb)) 148 | 149 | 150 | (use-package dumb-jump 151 | :commands (dumb-jump-xref-activate) 152 | :init 153 | (add-hook 'xref-backend-functions #'dumb-jump-xref-activate)) 154 | 155 | 156 | ;;; GDB 157 | ;; Refer to https://debbugs.gnu.org/cgi/bugreport.cgi?bug=33548 158 | (setq gdb-mi-decode-strings 'utf-8) 159 | 160 | 161 | (use-package copilot 162 | :disabled 163 | :hook (prog-mode . copilot-mode) 164 | :vc (:fetcher github :repo "zerolfx/copilot.el") 165 | :bind (:map copilot-completion-map 166 | ("" . copilot-accept-completion) 167 | ("" . copilot-accept-completion) 168 | ("M-f" . copilot-accept-completion-by-word) 169 | ("C-e" . copilot-accept-completion-by-line) 170 | ("M-n" . copilot-next-completion) 171 | ("M-p" . copilot-previous-completion)) 172 | :config 173 | (setq copilot-network-proxy '(:host "127.0.0.1" :port 7890)) 174 | (setq copilot-balancer-debug-buffer (get-buffer-create " *copilot-balancer*"))) 175 | 176 | 177 | (use-package citre 178 | :hook (c-mode-common . citre-mode) 179 | :hook (c-ts-mode . citre-mode) 180 | :hook (c++-ts-mode . citre-mode) 181 | :hook (swift-mode . citre-mode)) 182 | 183 | 184 | (use-package quickrun 185 | :defer t) 186 | 187 | 188 | (defun comint-use-persistent-input-history (filename) 189 | "Enable persistent input history in a Comint buffer. 190 | The history is stored in FILENAME." 191 | (setq comint-input-ring-file-name filename) 192 | (comint-read-input-ring t)) 193 | 194 | 195 | (use-package symbol-overlay 196 | :diminish "" 197 | :hook (prog-mode . symbol-overlay-mode) 198 | :bind ("C-c i" . symbol-overlay-put)) 199 | 200 | 201 | ;; show git diff info in fringe 202 | (use-package diff-hl 203 | :hook (prog-mode . diff-hl-mode) 204 | ;; diff-hl-dired-mode is buggy. 205 | ;; :hook (dired-mode . diff-hl-dired-mode) 206 | :config 207 | (diff-hl-margin-mode) 208 | (add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh) 209 | (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh) 210 | (setq diff-hl-disable-on-remote t)) 211 | 212 | 213 | ;; structural navigation and editing 214 | ;; depends on emacs 29 treesit.el 215 | (use-package combobulate 216 | :defer t 217 | :vc (:fetcher github :repo "mickeynp/combobulate") 218 | :hook ((python-ts-mode . combobulate-mode) 219 | (js-ts-mode . combobulate-mode) 220 | (css-ts-mode . combobulate-mode) 221 | (yaml-ts-mode . combobulate-mode) 222 | (typescript-ts-mode . combobulate-mode) 223 | (tsx-ts-mode . combobulate-mode)) 224 | :bind (:map combobulate-key-map 225 | (("C-M-U" . combobulate-mark-node-dwim) 226 | ("M-h" . nil)))) 227 | 228 | 229 | (use-package devdocs 230 | :commands (devdocs-install devdocs-update-all devdocs-search)) 231 | 232 | 233 | (use-package realgud 234 | :defer t) 235 | 236 | 237 | (with-eval-after-load 'ebrowse 238 | (keymap-unset ebrowse-tree-mode-map "C-l")) 239 | 240 | 241 | (use-package rmsbolt 242 | :defer t) 243 | 244 | (defun godbolt () 245 | "Send the current buffer to Compiler Explorer." 246 | (interactive) 247 | (let ((url (goldbolt--construct-url))) 248 | (browse-url (format "https://godbolt.org/%s" ())))) 249 | 250 | 251 | (use-package editorconfig 252 | :ensure nil 253 | :config 254 | (editorconfig-mode)) 255 | 256 | 257 | (use-package cmake-mode) 258 | 259 | (use-package eldoc-cmake 260 | :hook (cmake-mode-hook . eldoc-cmake-enable)) 261 | 262 | 263 | (use-package flymake 264 | :ensure nil 265 | :config 266 | (setopt flymake-show-diagnostics-at-end-of-line 'short)) 267 | 268 | 269 | (use-package indent-bars 270 | :config 271 | (setq indent-bars-no-descend-lists t) 272 | (setq indent-bars-display-on-blank-lines 'least) 273 | (setq indent-bars-treesit-support t) 274 | :hook ((prog-mode . indent-bars-mode) 275 | (lua-mode . (lambda () 276 | (setq indent-bars-spacing-override lua-indent-level))) 277 | (c-common-mode . (lambda () 278 | (setq-local indent-bars-spacing-override 4))))) 279 | 280 | 281 | (provide 'prelude-prog) 282 | ;;; prelude-prog.el ends here 283 | --------------------------------------------------------------------------------