;; -*- lexical-binding: t; -*- ;;; programming languages ;;;; Erlang (use-package erlang :ensure t :custom ((inferior-erlang-machine-options '("shell")) (inferior-erlang-machine "rebar3") (inferior-erlang-shell-type nil) (erlang-indent-level 4)) ;; :bind (:map erlang-mode-map (("C-c C-z" . jao-vterm-repl-pop-to-repl))) :init ;; (require 'jao-vterm-repl) ;; (add-to-list 'auto-mode-alist '("^rebar\\.config\\`" . erlang-mode)) ;; (jao-vterm-repl-register "rebar.config" "rebar3 shell" "^[0-9]+> ") :config ;; (defun jao-erlang-current-module () ;; (when (save-excursion (goto-char (point-min)) ;; (re-search-forward "^-module(\\([^)]+\\))" nil t)) ;; (match-string-no-properties 1))) ;; (defun jao-erlang-compile (arg) ;; (interactive "P") ;; (save-some-buffers) ;; (when-let ((mname (jao-erlang-current-module))) ;; (with-current-buffer (jao-vterm-repl) ;; (vterm-send-string (format "c(%s).\n" mname)) ;; (sit-for 0) ;; (setq compilation-last-buffer (current-buffer)) ;; (when arg (jao-vterm-repl-pop-to-repl))))) ;; (setq erlang-shell-function #'jao-vterm-repl ;; erlang-shell-display-function #'jao-vterm-repl-pop-to-repl ;; erlang-compile-function #'jao-erlang-compile) ) ;;;; Idris (use-package idris-mode :ensure t :custom ((idris-interpreter-path "idris2") (idris-pretty-printer-width 80) (idris-repl-history-file "~/.emacs.d/cache/idris-history.eld") (idris-stay-in-current-window-on-compiler-error t))) (jao-define-attached-buffer "^\\*idris.*") ;;;; Racket (use-package racket-mode :ensure t :init (setq racket-show-functions '(racket-show-echo-area) racket-documentation-search-location 'local) :config (jao-define-attached-buffer "\\`\\*Racket REPL") (jao-define-attached-buffer "\\`\\*Racket Describe" 0.5) (add-hook 'racket-mode-hook #'paredit-mode) (require 'racket-xp) (add-hook 'racket-mode-hook #'racket-xp-mode) :bind (:map racket-xp-mode-map (("C-c C-S-d" . racket-xp-documentation) ("C-c C-d" . racket-xp-describe)))) ;;; smart scan (use-package smartscan :ensure t :commands smartscan-mode :init (add-hook 'prog-mode-hook #'smartscan-mode) :diminish) ;;; easy escape (use-package easy-escape :ensure t :config (set-face-attribute 'easy-escape-face nil :underline t) (set-face-attribute 'easy-escape-delimiter-face nil :underline t) :hook (emacs-lisp-mode . easy-escape-minor-mode) :diminish (easy-escape-minor-mode . "^")) ;;; vterm (use-package vterm :ensure t :demand t :commands (vterm vterm-mode) :init (setq vterm-kill-buffer-on-exit t vterm-copy-exclude-prompt t jao-use-vterm t) :config (defun jao-vterm-send-C-c () (interactive) (vterm-send-key "c" nil nil t)) (jao-define-attached-buffer "\\*vterm\\*" 0.5) :bind (:map vterm-mode-map ("C-c C-c" . jao-vterm-send-C-c))) (defun jao-exec-in-vterm (cmd bname) (if (string-blank-p (or cmd "")) (vterm) (let ((vterm-shell cmd) (vterm-kill-buffer-on-exit t) (buff (generate-new-buffer bname))) (switch-to-buffer buff) (vterm-mode)))) ;;; ace window (use-package ace-window :ensure t :demand t :init (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l) aw-char-position 'top-left aw-ignore-current nil aw-dispatch-when-more-than 2 aw-leading-char-style 'path aw-display-mode-overlay t aw-scope 'frame) :config (defun jao-ace-consult-buffer-other-window (w) (interactive) (aw-switch-to-window w) (consult-buffer)) (setf (alist-get ?b aw-dispatch-alist) '(jao-ace-consult-buffer-other-window "Consult buffer")) (setf (alist-get ?B aw-dispatch-alist) (alist-get ?u aw-dispatch-alist)) :bind (("M-o" . ace-window) ("M-O" . ace-swap-window) ("C-x 4 t" . ace-swap-window))) ;;; switch window (use-package switch-window :ensure t :custom ((switch-window-minibuffer-shortcut ?z) (switch-window-background t) (switch-window-shortcut-style 'qwerty) (switch-window-shortcut-appearance 'text) (switch-window-timeout 7) (switch-window-threshold 2)) :config (defun jao-switch-window--then (prompt cmd) (let ((f `(lambda () (let ((default-directory ,default-directory)) (call-interactively ',cmd))))) (switch-window--then prompt f f))) (defun jao-switch-window-then-dired () (interactive) (jao-switch-window--then "Find directory" 'dired)) (defun jao-switch-window-then-find-file () (interactive) (jao-switch-window--then "Find file" 'find-file)) (defun jao-switch-window-then-consult-buffer () (interactive) (jao-switch-window--then "Switch to buffer" 'consult-buffer)) :bind (("s-o" . switch-window) ("C-x 4 d" . jao-switch-window-then-dired) ("C-x 4 f" . jao-switch-window-then-find-file) ("C-x 4 b" . jao-switch-window-then-consult-buffer))) ;;; git helpers (use-package dired-git-info :ensure t :bind (:map dired-mode-map (")" . dired-git-info-mode))) (use-package gist :ensure t) ;;; json (use-package json-mode :ensure t) (use-package json-navigator :ensure t) ;;; deft (use-package deft :ensure t :after jao-org-notes :commands deft :init (setq deft-extensions '("org" "md") deft-directory jao-org-notes-dir deft-use-filename-as-title nil deft-use-filter-string-for-name t deft-file-naming-rules '((noslash . "-") (nospace . "-") (case-fn . downcase)) deft-org-mode-title-prefix t deft-recursive t deft-recursive-ignore-dir-regexp (regexp-opt '("." ".." "attic")) deft-strip-summary-regexp (concat "\\([\n\t]" "\\|^#\\+\\(title\\|created\\|date\\|author\\):.*$" "\\|^#\\+\\(file\\)?tags: *\\)")) :config (setq deft-strip-title-regexp (concat "\\(^#\\+title: *\\)\\|" deft-strip-title-regexp)) :bind (("" . deft))) ;;; detached (use-package detached :ensure t :init (detached-init) :config (transient-define-prefix jao-transient-detached () ["Detached sessions" ("v" "view session output" detached-view-session) ("a" "attach to a session" detached-attach-session) ("=" "diff a session with another session" detached-diff-session) ("c" "open the session output in compilation mode" detached-compile-session) ("r" "rerun a session" detached-rerun-session) ("i" "insert the session's command at point" detached-insert-session-command) ("w" "copy the session's shell command" detached-copy-session-command) ("W" "copy the session's output" detached-copy-session) ("k" "kill an active session" detached-kill-session)]) :bind (;; Replace `async-shell-command' with `detached-shell-command' ([remap async-shell-command] . detached-shell-command) ;; Replace `compile' with `detached-compile' ([remap compile] . detached-compile) ([remap recompile] . detached-compile-recompile) ;; Replace built in completion of sessions with `consult' ([remap detached-open-session] . detached-consult-session) ("s-d" . jao-transient-detached)) :custom ((detached-show-output-on-attach t) (detached-terminal-data-command system-type))) (defun jao-detached-exec (command) (if (fboundp 'detached-create-session) (detached-create-session command) (jao-shell-exec command))) ;;; time display (setq display-time-world-list '(("Europe/Paris" "Barcelona") ("America/Los_Angeles" "Los Angeles") ("America/New_York" "New York") ("Europe/London" "London") ("Asia/Calcutta" "Bangalore") ("Asia/Tokyo" "Tokyo"))) (defun jao-time--pdt-hour () (jao-time-at-zone "%H" "America/Los_Angeles")) (defun jao-time--chicago-hour () (jao-time-at-zone "%H" "America/Chicago")) (defun jao-time-at-zone (format zone) (set-time-zone-rule zone) (prog1 (format-time-string format) (set-time-zone-rule nil))) (defun jao-time-echo-la-time () (interactive) (message (jao-time-at-zone "LA %H:%M" "America/Los_Angeles"))) (defun jao-time-echo-times () (interactive) (let ((msg (format "%s (%s)" (format-time-string "%a, %e %B - %H:%M") (jao-time-at-zone "%H:%M" "America/Los_Angeles")))) (jao-notify msg "" (jao-data-file "clock-world-icon.png")))) (defun jao-time-to-epoch (&optional s) "Transform a time string to an epoch integer in milliseconds." (interactive) (let ((s (or s (read-string "Time string: " (thing-at-point 'string))))) (message "%s = %s" s (round (* 1000 (time-to-seconds (parse-time-string s))))))) (defun jao-epoch-to-time (&optional v) "Transform an epoch, given in milliseconds, to a time string." (interactive) (let ((v (or v (read-number "Milliseconds: " (thing-at-point 'number))))) (message "%s = %s" v (format-time-string "%Y-%m-%d %H:%M:%S" (seconds-to-time (/ v 1000.0)))))) ;;; mu4e (jao-load-path "mu4e") (use-package mu4e :init (setq mu4e-attachment-dir (expand-file-name "~/var/download/attachments") mu4e-change-filenames-when-moving nil mu4e-completing-read-function 'completing-read mu4e-display-update-status-in-modeline nil mu4e-get-mail-command "true" ;; "run-mb.sh || [ $? -eq 1 ]" mu4e-headers-show-threads t mu4e-headers-sort-direction 'ascending mu4e-headers-visible-columns 100 mu4e-headers-visible-lines 12 mu4e-hide-index-messages t mu4e-index-cleanup t ;; don't do a full cleanup check mu4e-index-lazy-check t ;; don't consider up-to-date dirs mu4e-maildir "~/var/mail" mu4e-split-view 'horizontal ;; 'vertical mu4e-update-interval 300 mu4e-use-fancy-chars nil mu4e-user-mail-address-list jao-mails mu4e-view-show-addresses t mu4e-view-show-images t mu4e-maildir-shortcuts '((:maildir "/jao/inbox" :key ?j) (:maildir "/bigml/inbox" :key ?b)) jao-mu4e-uninteresting-mail-query (concat "flag:unread AND NOT flag:trashed" " AND NOT (maildir:/bigml/inbox OR maildir:/bigml/bugs OR" " maildir:/bigml/support OR maildir:/jao/inbox)") jao-mu4e-interesting-mail-query (concat "flag:unread AND NOT flag:trashed" " AND (maildir:/bigml/inbox OR maildir:/bigml/bugs OR" " maildir:/bigml/support OR maildir:/jao/inbox)") mu4e-bookmarks `((:name "Inbox" :query ,jao-mu4e-interesting-mail-query :key ?i) (:name "Other messages" :query ,jao-mu4e-uninteresting-mail-query :key 117) (:name "Today's messages" :query "date:today..now" :key 116) (:name "Last 7 days" :query "date:7d..now" :hide-unread t :key 119) (:name "Messages with PDFs" :query "mime:application/pdf OR mime:x-application/pdf" :key 112))) :config (defun jao-mu4e--maildir (msg) (when msg (let ((md (mu4e-message-field msg :maildir))) (when (string-match "/\\([^/]+\\)/.*" md) (match-string 1 md))))) (defun jao-mu4e--refile-folder (name) (lambda (msg) (let ((md (jao-mu4e--maildir msg))) (if (string= md name) (concat "/jao/" name) (format "/%s/%s" md name))))) (setq mu4e-sent-folder (jao-mu4e--refile-folder "sent")) (setq mu4e-drafts-folder (jao-mu4e--refile-folder "drafts")) (setq mu4e-trash-folder (jao-mu4e--refile-folder "trash")) (setq mu4e-refile-folder (jao-mu4e--refile-folder "trove")) (setq mu4e-contexts nil) (setq mu4e-view-show-images t) (when (fboundp 'imagemagick-register-types) (imagemagick-register-types)) (define-key mu4e-view-mode-map [remap mu4e-view-verify-msg-popup] 'epa-mail-verify) ;; View html message in browser (type aV) (add-to-list 'mu4e-view-actions '("ViewInBrowser" . mu4e-action-view-in-browser) t)) ;;; twtxt (use-package twtxt :ensure t :init (setq twtxt-file (expand-file-name "~/doc/jao.io/twtxt") twtxt-following '(("yarn" "https://twtxt.net/user/news/twtxt.txt")))) ;;; corfu bits (defun jao-corfu-enable-no-auto () (setq-local corfu-auto nil) (corfu-mode 1)) (defmacro jao-corfu-no-auto (mode) (let ((mode-name (intern (format "%s-mode" mode))) (hook-name (intern (format "%s-mode-hook" mode)))) `(with-eval-after-load ',mode (add-to-list 'corfu-excluded-modes ',mode-name) (add-hook ',hook-name #'jao-corfu-enable-no-auto)))) (jao-corfu-no-auto eshell) ;;; gnus bits (jao-transient-major-mode gnus-group ["Search" ("zc" "consult search" consult-notmuch) ("zf" "consult folder search" jao-consult-notmuch-folder) ("g" "gnus search" gnus-group-read-ephemeral-search-group)]) (defun jao-gnus-restart-servers () (interactive) (message "Restarting all servers...") (gnus-group-enter-server-mode) (gnus-server-close-all-servers) (gnus-server-open-all-servers) (gnus-server-exit) (message "Restarting all servers... done")) ;;;; startup and kill ;; close gnus when closing emacs, but ask when exiting (setq gnus-interactive-exit t) (defun jao-gnus-started-hook () (add-hook 'before-kill-emacs-hook 'gnus-group-exit)) (add-hook 'gnus-started-hook 'jao-gnus-started-hook) (defun jao-gnus-after-exiting-hook () (remove-hook 'before-kill-emacs-hook 'gnus-group-exit)) (add-hook 'gnus-after-exiting-gnus-hook 'jao-gnus-after-exiting-hook) ;; define a wrapper around the save-buffers-kill-emacs ;; to run the new hook before: (advice-add 'save-buffers-kill-emacs :before (lambda () (run-hooks 'before-kill-emacs-hook))) (defadvice save-buffers-kill-emacs (before my-save-buffers-kill-emacs activate) "Install hook when emacs exits before emacs asks to save this and that." (run-hooks 'before-kill-emacs-hook)) (advice-remove 'ad-Advice-save-buffers-kill-emacs 'save-buffers-kill-emacs) ;;;; delayed expiry (defvar jao-gnus--expire-every 50) (defvar jao-gnus--get-count (1+ jao-gnus--expire-every)) (defun jao-gnus-get-new-news (&optional arg) (interactive "p") (when (and jao-gnus--expire-every (> jao-gnus--get-count jao-gnus--expire-every)) (when jao-gnus-use-pm-imap (gnus-group-catchup "nnimap:pm/spam" t)) (gnus-group-expire-all-groups) (setq jao-gnus--get-count 0)) (setq jao-gnus--get-count (1+ jao-gnus--get-count)) (gnus-group-get-new-news (max (if (= 1 jao-gnus--get-count) 4 3) (or arg 0)))) (define-key gnus-group-mode-map "g" 'jao-gnus-get-new-news) (define-key gnus-group-mode-map "\C-x\C-s" #'gnus-group-save-newsrc) (defun jao-gnus--first-group () (when (derived-mode-p 'gnus-group-mode) (gnus-group-first-unread-group))) (with-eval-after-load "jao-afio" (add-hook 'jao-afio-switch-hook #'jao-gnus--first-group)) ;;;; remove HTML from From contents (arxiv with r2e) (require 'shr) (defvar jao-gnus--from-rx (concat "From: \\\"?\\( " jao-gnus--news-rx "\\)")) (defun jao-gnus-remove-anchors () (save-excursion (goto-char (point-min)) (cond ((re-search-forward jao-gnus--from-rx nil t) (replace-match "" nil nil nil 1)) ((re-search-forward "[gq].+ updates on arXiv.org: " nil t) (replace-match "") (let ((begin (point))) (when (re-search-forward "^\\(To\\|Subject\\):" nil t) (beginning-of-line) (let ((shr-width 10000)) (shr-render-region begin (1- (point)))))))))) (add-hook 'gnus-part-display-hook 'jao-gnus-remove-anchors) ;;;; find message id (defun jao-gnus-file-message-id (filename) (with-temp-buffer (insert-file filename) (goto-char (point-min)) (when (re-search-forward "^[Mm]essage-[Ii][Dd]: <]+\\)>?" nil t) (match-string 1)))) ;;; old volume controls (defun jao-player-volume-delta (raise) (jao-player-vol-delta (if raise 5 -5)) (sit-for 0.05) (jao-player-show-volume)) (defun jao-player-volume-raise () (interactive) (jao-player-volume-delta t)) (defun jao-player-volume-lower () (interactive) (jao-player-volume-delta nil)) (defun jao-player-show-volume () (interactive) (jao-notify "Volume" (format "%s%%" (jao-player-volume)))) ;;; corfu (use-package corfu :ensure t :init (setq corfu-echo-documentation 0.25 corfu-cycle t corfu-count 15 corfu-quit-no-match t corfu-auto t corfu-commit-predicate nil corfu-preview-current nil corfu-preselect-first t corfu-min-width 20 corfu-max-width 100) :config ;; show eldoc string immediately after accepted completion too (with-eval-after-load "eldoc" (eldoc-add-command-completions "corfu-")) (defun jao-corfu-no-auto () (setq-local corfu-auto nil) (corfu-mode)) (add-hook 'eshell-mode-hook #'jao-corfu-no-auto) (defun jao-corfu--active-p () (and (>= corfu--index 0) (/= corfu--index corfu--preselect))) (defun jao-corfu-quit-or-insert () (interactive) (if (jao-corfu--active-p) (corfu-insert) (corfu-quit))) (defun jao-corfu-quit-or-previous () (interactive) (if (jao-corfu--active-p) (corfu-previous) (corfu-quit) (previous-line))) :bind (:map corfu-map ("C-" . corfu-insert) ("\r" . jao-corfu-quit-or-insert) ("C-p" . jao-corfu-quit-or-previous))) (defun corfu-in-minibuffer () (when (not (bound-and-true-p vertico--input)) (setq-local corfu-echo-documentation nil) (corfu-mode 1))) (defun jao-corfu-maybe-enable () (when (and (not jao-wayland-enabled) (display-graphic-p)) (add-hook 'minibuffer-setup-hook #'corfu-in-minibuffer 1) (global-corfu-mode 1))) (add-hook 'after-init-hook #'jao-corfu-maybe-enable) ;;; company (use-package company :ensure t :custom ((company-backends '(company-capf company-bbdb company-files company-dabbrev company-keywords)) (company-global-modes '(not slack-message-buffer-mode circe-channel-mode telega-chat-mode)) (company-format-margin-function nil) ;; #'company-text-icons-margin (company-idle-delay 0.2) (company-lighter "") (company-lighter-base "") (company-show-numbers nil) (company-selection-wrap-around t) (company-tooltip-limit 15) (company-tooltip-align-annotations t) (company-tooltip-offset-display 'lines)) ;; 'scrollbar :config (defun jao-complete-at-point () "Complete using company unless we're in the minibuffer." (interactive) (if (or (not company-mode) (window-minibuffer-p)) (completion-at-point) (company-manual-begin))) (defun jao-company-use-in-tab () (global-set-key [remap completion-at-point] #'jao-complete-at-point) (global-set-key [remap completion-symbol] #'jao-complete-at-point) (global-set-key (kbd "M-TAB") #'jao-complete-at-point)) (jao-company-use-in-tab) :bind (:map company-active-map ("" . company-complete-common-or-cycle) ("TAB" . company-complete-common-or-cycle) ("C-h" . company-show-doc-buffer) ("M-." . company-show-location) ("C-" . company-complete-selection) ([remap return] . company-abort) ("RET" . company-abort) :filter (or (not (derived-mode-p 'eshell-mode)) (company-explicit-action-p)) ("" . company-complete-selection) ("RET" . company-complete-selection)) :diminish) (unless (display-graphic-p) (global-company-mode 1)) ;;; eldoc for magit status/log buffers (defun jao-magit-eldoc-for-commit (_callback) (when-let ((commit (magit-commit-at-point))) (with-temp-buffer (magit-git-insert "show" "--format=format:%an <%ae>, %ar" (format "--stat=%d" (window-width)) commit) (goto-char (point-min)) (put-text-property (point-min) (line-end-position) 'face 'bold) (buffer-string)))) (defun jao-magit-eldoc-setup () (add-hook 'eldoc-documentation-functions #'jao-magit-eldoc-for-commit nil t) (eldoc-mode 1)) (add-hook 'magit-log-mode-hook #'jao-magit-eldoc-setup) (add-hook 'magit-status-mode-hook #'jao-magit-eldoc-setup) (with-eval-after-load "eldoc" (eldoc-add-command 'magit-next-line) (eldoc-add-command 'magit-previous-line) (eldoc-add-command 'magit-section-forward) (eldoc-add-command 'magit-section-backward)) ;;; outline mode for notmuch tree view (defun jao-notmuch-tree--msg-prefix (msg) (insert (propertize (if (plist-get msg :first) "> " " ") 'display " "))) (defun jao-notmuch-tree--mode-setup () (setq-local outline-regexp "^> \\|^En") (outline-minor-mode t)) (defun jao-notmuch-tree-hide-others (&optional and-show) (interactive) (outline-hide-body) (outline-show-entry) (when and-show (notmuch-tree-show-message nil))) (defsubst jao-notmuch-tree--message-open () (and (buffer-live-p notmuch-tree-message-buffer) (get-buffer-window notmuch-tree-message-buffer))) (defsubst jao-notmuch--get-prop (prop &optional props) (or (and props (plist-get props prop)) (notmuch-tree-get-prop prop) (notmuch-show-get-prop prop))) (defun jao-notmuch--looking-at-match-p () (and (jao-notmuch--get-prop :match) (equal (jao-notmuch--get-prop :orig-tags) (jao-notmuch--get-prop :tags)))) (defun jao-notmuch-tree--next (prev thread no-exit &optional ignore-new) (let ((line-move-ignore-invisible nil)) (cond ((and (not ignore-new) (jao-notmuch--looking-at-match-p) (not (jao-notmuch-tree--message-open)))) (thread (notmuch-tree-next-thread prev) (unless (or (not (notmuch-tree-get-message-properties)) (jao-notmuch--looking-at-match-p)) (notmuch-tree-matching-message prev (not no-exit)))) (t (notmuch-tree-matching-message prev (not no-exit))))) (when (notmuch-tree-get-message-id) (jao-notmuch-tree-hide-others t)) (when prev (forward-char 2))) (defvar jao-notmuch-tree--prefix-map (let ((m (make-keymap "Thread operations"))) (define-key m (kbd "TAB") #'outline-cycle) (define-key m (kbd "t") #'outline-toggle-children) (define-key m (kbd "s") #'outline-show-entry) (define-key m (kbd "S") #'outline-show-all) (define-key m (kbd "h") #'outline-hide-entry) (define-key m (kbd "H") #'outline-hide-body) (define-key m (kbd "o") #'jao-notmuch-tree-hide-others) (define-key m (kbd "n") #'outline-hide-other) m)) (defun jao-notmuch-tree-outline-setup (&optional prefix) (define-key notmuch-tree-mode-map (kbd (or prefix "T")) jao-notmuch-tree--prefix-map) (define-key notmuch-tree-mode-map (kbd "TAB") #'outline-cycle) (define-key notmuch-tree-mode-map (kbd "M-TAB") #'outline-cycle-buffer) (add-hook 'notmuch-tree-mode-hook #'jao-notmuch-tree--mode-setup) (advice-add 'notmuch-tree-insert-msg :before #'jao-notmuch-tree--msg-prefix)) (defun jao-notmuch-tree-next (thread &optional no-exit) "Next message or thread in forest, taking care of thread visibility." (interactive "P") (jao-notmuch-tree--next nil thread no-exit)) (defun jao-notmuch-tree-next-thread (&optional exit) "Next thread in forest, taking care of thread visibility." (interactive "P") (jao-notmuch-tree--next nil t exit)) (defun jao-notmuch-tree-previous (thread) "Previous message or thread in forest, taking care of thread visibility." (interactive "P") (jao-notmuch-tree--next t thread t)) (defun jao-notmuch-tree-previous-thread (&optional exit) "Previous thread in forest, taking care of thread visibility." (interactive "P") (jao-notmuch-tree--next t t exit)) ;;; elpher/gemini (use-package elpher :ensure t) (defun jao-elpher--browse (url &rest _) (elpher-go url)) (add-to-list 'browse-url-handlers '("^\\(gemini\\|gopher\\)://.*" . jao-elpher--browse)) ;;; fontsets (defun jao--set-fontsets (_f) (when (and (display-graphic-p) (fboundp 'set-fontset-font)) (set-fontset-font t 64257 "Quivira" nil) (set-fontset-font t 'egyptian "Noto Sans Egyptian Hieroglyphs" nil) (set-fontset-font t 'hangul "NanumGothicCoding" nil) (set-fontset-font t 'unicode (face-attribute 'default :family) nil) (set-fontset-font t 'unicode-bmp (face-attribute 'default :family) nil) (set-fontset-font t 'symbol "Symbola-10" nil) (set-fontset-font t 'greek "GFS Didot" nil) (set-fontset-font t 'mathematical "FreeSerif" nil) (set-fontset-font t 'emoji "Noto Color Emoji" nil) ;; boxes (set-fontset-font t '(9472 . 9599) "Source Code Pro" nil) ;; variation selector-16 (set-fontset-font t 65039 "BabelStone Modern-1" nil))) (jao--set-fontsets nil) (add-to-list 'after-make-frame-functions 'jao--set-fontsets) ;;; eshell history completion to allow !$ ;; This is done by advising eshell-history-reference to expand !$ ;; into !!:$ which works... (defadvice jao-eshell-history-reference (before ben-fix-eshell-history) "Fixes eshell history to allow !$ as abbreviation for !!:$" (when (string= (ad-get-arg 0) "!$") (ad-set-arg 0 "!!:$"))) (ad-activate 'jao-eshell-history-reference) (add-hook 'eshell-expand-input-functions #'eshell-expand-history-references) ;;; enwc (use-package enwc :ensure t :custom ((enwc-default-backend 'nm) (enwc-wired-device "wlp164s0") (enwc-wireless-device "wlp164s0") (enwc-display-mode-line nil))) ;;; tidal/mpc (defconst jao-mpc--search-cmd "-f '%%album%% - %%artist%% :::%%file%%' search %s '%s'|grep :::tidal:album") (defun jao-mpc--search-albums (query) (let* ((cmd (format jao-mpc--search-cmd "any" query)) (str (jao-mpc--cmd cmd)) (res (split-string str "\n" t))) (message "%s" str) (mapcar (lambda (s) (split-string s ":::" t " ")) res))) (defun jao-mpc-select-tidal-album (&optional query port) (interactive "sSearch terms: ") (let* ((jao-mpc-port (or port jao-mpc-port)) (resa (jao-mpc--search-albums query))) (if (null resa) (user-error "No results") (when-let* ((a (completing-read "Play album: " resa nil t)) (s (car (alist-get a resa nil nil 'string=)))) (jao-mpc--add-and-play s port t))))) ;;; dogears (use-package dogears :ensure t :enabled nil :bind (:map global-map ("M-g d" . dogears-go) ("M-g M-b" . dogears-back) ("M-g M-f" . dogears-forward) ("M-g M-d" . dogears-list) ("M-g M-D" . dogears-sidebar))) (dogears-mode) ;;; pulsar (use-package pulsar :ensure t :demand t :diminish :custom ((pulsar-pulse t) (pulsar-delay 0.1) (pulsar-iterations 10) (pulsar-face 'pulsar-yellow) (pulsar-highlight-face 'jao-themes--hilite)) :config (dolist (f '(jao-prev-window jao-tracking-next-buffer smartscan-symbol-go-forward smartscan-symbol-go-backward)) (add-to-list 'pulsar-pulse-functions f)) :hook ((jao-afio-switch . pulsar-pulse-line) (consult-after-jump . pulsar-reveal-entry) (imenu-after-jump . pulsar-reveal-entry) (next-error . pulsar-pulse-line))) (pulsar-global-mode 1) ;;;; mouse (use-package disable-mouse :ensure t :diminish ((disable-mouse-global-mode . ""))) (global-disable-mouse-mode) ;;; tmr (use-package tmr :ensure t :init (setq tmr-sound-file "/usr/share/sounds/freedesktop/stereo/message.oga")) ;;; pdf-tools (use-package pdf-tools :ensure t :demand t :init (add-hook 'after-init-hook (lambda () (setq pdf-view-midnight-colors (cons (frame-parameter nil 'foreground-color) (frame-parameter nil 'background-color))))) :hook ((pdf-view-mode . jao-doc-session-mark)) :config (pdf-tools-install) :diminish ((pdf-view-midnight-minor-mode . "")) :bind (:map pdf-view-mode-map (("C-c C-d" . pdf-view-midnight-minor-mode) ("j" . pdf-view-next-line-or-next-page) ("J" . pdf-view-scroll-up-or-next-page) ("k" . pdf-view-previous-line-or-previous-page) ("K" . pdf-view-scroll-down-or-previous-page)))) ;;; slack (eval-and-compile (defvar jao-slack-dir (expand-file-name "emacs-slack" jao-local-lisp-dir))) (use-package slack :commands (slack-start) :vc t :load-path jao-slack-dir :init (setq slack-alert-icon (jao-data-file "slack.svg") slack-buffer-emojify nil slack-buffer-create-on-notify t slack-display-team-name t slack-typing-visibility 'buffer ;; 'never, 'buffer, 'frame slack-thread-also-send-to-room t slack-profile-image-file-directory "/tmp/slack-imgs/" slack-image-file-directory "/tmp/slack-imgs/" slack-file-dir "~/var/download/slack/" slack-prefer-current-team t slack-message-tracking-faces '(warning) slack-log-level 'warn slack-message-custom-notifier (lambda (_msg room _team) room)) :bind (:map slack-mode-map (("@" . slack-message-embed-mention) ("#" . slack-message-embed-channel)) :map slack-message-buffer-mode-map (("C-c C-e" . slack-message-edit) ("C-c C-a" . slack-file-upload))) :hook ((slack-file-info-buffer-mode . view-mode)) :config (defun my-slack-nobreak-mrkdwn () "Return non-nil (don't break line) if point is in markdown code face." (seq-find (lambda (ov) (eq 'slack-mrkdwn-code-block-face (overlay-get ov 'face))) (overlays-at (point)))) (add-hook 'slack-message-buffer-mode-hook (lambda () (add-hook 'fill-nobreak-predicate #'my-slack-nobreak-mrkdwn nil 'local))) (dolist (f (list slack-file-dir slack-image-file-directory)) (when (not (file-exists-p f)) (make-directory f))) (jao-shorten-modes 'slack-message-buffer-mode 'slack-thread-message-buffer-mode) (jao-tracking-faces 'warning) (jao-tracking-cleaner "logstash-\\([^-]+\\)-\\(.+\\)" "\\2-\\1") (jao-tracking-cleaner "^\\*Slack - .*? : \\(MPIM: \\)?\\([^ ]+\\)\\( \\(T\\)\\)?.*" "\\2\\4") (jao-define-attached-buffer "\\*Slack .+ Edit Message [0-9].+" 20)) ;;; snippets (defun jao-org-notes-open-tags () "Search for a note file, matching all tags with completion." (let* ((tags (jao-org-notes--read-tags)) (fn (lambda () (prog1 (jao-org-notes--find-tag (car tags)) (setq tags (cdr tags))))) (res (funcall fn))) (while (and res tags) (setq res (seq-intersection res (funcall fn)))) (unless res (user-error "No notes found")) (when-let (f (completing-read "Select file: " (mapcar #'car res))) (find-file (cadr (assoc f res)))))) (defun jao-sway-run-or-focus-tidal () (interactive) (if (jao-shell-running-p "tidal-hifi") (jao-swaymsg "[app_id=tidal-hifi] scratchpad show") (let ((c "tidal-hifi --enable-features=UseOzonePlatform --ozone-platform=wayland &")) (start-process-shell-command "tidal-hifi" nil c)) (jao-sway-run-or-focus-tidal))) ;; (defun jao-afio--set-mode-line () (when (and window-system (fboundp 'jao-mode-line-hide-inactive)) (if (string= "docs" (jao-afio-frame-name)) (jao-mode-line-show-inactive nil) (jao-mode-line-hide-inactive nil)))) (unless jao-modeline-in-minibuffer (add-hook 'jao-afio-switch-hook #'jao-afio--set-mode-line)) ;; (defun jao-word-definition-lookup () "Look up the word under cursor in a browser." (interactive) (require 'thingatpt) (browse-url (concat "http://www.wordnik.com/words/" ;; "http://www.answers.com/main/ntquery?s=" (thing-at-point 'word)))) ;; (defun jao-notmuch-format-author (width msg) (let* ((headers (plist-get msg :headers)) (auth (notmuch-tree-clean-address (plist-get headers :From))) (awidth (string-width auth)) (auth (if (> awidth width) (substring auth 0 width) (concat auth (make-string (- width awidth) 32)))) (face (if (plist-get msg :match) 'notmuch-tree-match-author-face 'notmuch-tree-no-match-author-face))) (propertize auth 'face face)))