diff options
Diffstat (limited to '.emacs.d')
41 files changed, 1761 insertions, 0 deletions
diff --git a/.emacs.d/early-init.el b/.emacs.d/early-init.el new file mode 100644 index 0000000..495c277 --- /dev/null +++ b/.emacs.d/early-init.el @@ -0,0 +1,20 @@ +(menu-bar-mode -1) +(tool-bar-mode -1) +(scroll-bar-mode -1) + +;; Initialise installed packages +(setq package-enable-at-startup t) + +;; Do not report warning errors +(setq native-comp-async-report-warnings-errors 'silent) + +;; Truly maximize screen +(setq frame-resize-pixelwise t) + +;; Start maximized +(add-to-list 'default-frame-alist '(fullscreen . maximized)) + +;; No need for titlebar +(modify-frame-parameters nil '((undecorated . t))) + +;;; early-init.el ends here diff --git a/.emacs.d/init.el b/.emacs.d/init.el new file mode 100644 index 0000000..5d0996e --- /dev/null +++ b/.emacs.d/init.el @@ -0,0 +1,178 @@ +;; Debian packages: elpa-use-package elpa-fill-column-indicator fonts-hack + +(require 'package) +(require 'fill-column-indicator) + +;; This is only needed once, near the top of the file +(eval-when-compile + (require 'use-package)) + +(add-to-list 'package-archives + '("melpa-stable" . "https://stable.melpa.org/packages/") t) + +(add-to-list 'package-archives + '("melpa" . "https://melpa.org/packages/") t) + +;; Do not persist customizations +(setq custom-file (make-temp-file "emacs-custom-")) + +;; Place backups in ~/.backups/ directory, like a civilized program. +;; ------ +(if (file-directory-p "~/.backup") + (setq backup-directory-alist '(("." . "~/.backup"))) + (message "Directory does not exist: ~/.backup")) + +(filesets-init) + +(setq backup-by-copying t ; Don't delink hardlinks + delete-old-versions t ; Clean up the backups + version-control t ; Use version numbers on backups, + kept-new-versions 3 ; keep some new versions + kept-old-versions 2) ; and some old ones, too + +;; --------- +;; Generic keybindings +;; --------- +(global-set-key (kbd "C-c d") 'diff-buffer-with-file) +(global-set-key (kbd "C-c R") 'revert-buffer) + +;; compile +(global-set-key [f12] 'compile) + +(defun help/insert-em-dash () + "Inserts an EM-DASH (not a HYPEN, not an N-DASH)" + (interactive) + (insert "—")) + +(global-set-key (kbd "C--") #'help/insert-em-dash) + +;; ------ +;; General config +;; ------ + +(setq fill-column 79) + +;; Make sure that pressing middle mouse button pastes right at point, +;; not where the mouse cursor is. +(setq mouse-yank-at-point t) + +(setq column-number-mode 1) +(setq line-number-mode 1) +(setq-default indent-tabs-mode nil) +(setq-default tab-width 4) + +(setq-default c-basic-offset 4) + +;; ------ +;; Initialize environment +;; ------ + + +(setenv "TMPDIR" (concat (getenv "HOME") "/tmp")) +(server-start) + +;; ------ +;; Helper for compilation. +;; ------ +;; Close the compilation window if there was no error at all. +(defun compilation-exit-autoclose (status code msg) + ;; If M-x compile exists with a 0 + (when (and (eq status 'exit) (zerop code)) + ;; then bury the *compilation* buffer, so that C-x b doesn't go there + (bury-buffer) + ;; and delete the *compilation* window + (delete-window (get-buffer-window (get-buffer "*compilation*")))) + ;; Always return the anticipated result of compilation-exit-message-function + (cons msg code)) + +;; Specify my function (maybe I should have done a lambda function) +(setq compilation-exit-message-function 'compilation-exit-autoclose) +(setq compilation-read-command nil) + +;; Themes +(add-to-list 'custom-theme-load-path "~/.emacs.d/themes/") + +;; Remove scratch message +(setq initial-scratch-message "") + +;; Ask y or n instead of yes or no +(defalias 'yes-or-no-p 'y-or-n-p) + +;; Fancier buffer selection +(global-set-key (kbd "C-x C-b") 'bs-show) + +;;; Stefan Monnier <foo at acm.org>. It is the opposite of fill-paragraph +(defun unfill-paragraph (&optional region) + "Takes a multi-line paragraph and makes it into a single line of text." + (interactive (progn (barf-if-buffer-read-only) '(t))) + (let ((fill-column (point-max)) + ;; This would override `fill-column' if it's an integer. + (emacs-lisp-docstring-fill-column t)) + (fill-paragraph nil region))) + +;; Handy key definition +(define-key global-map "\M-Q" 'unfill-paragraph) + +;; Only flash the mode line +(setq ring-bell-function + (lambda () + (let ((orig-fg (face-foreground 'mode-line))) + (set-face-foreground 'mode-line "#F2804F") + (run-with-idle-timer 0.1 nil + (lambda (fg) (set-face-foreground 'mode-line fg)) + orig-fg)))) + +;; Highlight parens +(setq show-paren-delay 0) +(show-paren-mode 1) + +;; Save what you enter into minibuffer prompts +(setq history-length 25) +(savehist-mode 1) + +;; Remember and restore cursor information +(save-place-mode 1) + +;; Set this to t if you don't understand what it means +(setq vc-follow-symlinks nil) + +(dolist (path '("~/.emacs.d/rul-lisp/config" "~/.emacs.d/rul-lisp/packages")) + (add-to-list 'load-path path)) + +(require 'rul-org) +(require 'rul-org-roam) +(require 'rul-elfeed) +(require 'rul-dart) + +(load-file "~/.emacs.d/rul-init.d/fonts.el") +(load-file "~/.emacs.d/rul-init.d/themes.el") + +;; Init parts (will be deprecated in favor of packages) +(load-file "~/.emacs.d/rul-init.d/auto-fill.el") +;(load-file "~/.emacs.d/rul-init.d/doom-modeline.el") +(load-file "~/.emacs.d/rul-init.d/flycheck.el") +(load-file "~/.emacs.d/rul-init.d/flyspell.el") +(load-file "~/.emacs.d/rul-init.d/go-lang.el") +(load-file "~/.emacs.d/rul-init.d/hydra.el") +(load-file "~/.emacs.d/rul-init.d/ibuffer.el") +(load-file "~/.emacs.d/rul-init.d/imenu.el") +;(load-file "~/.emacs.d/rul-init.d/ivy.el") +(load-file "~/.emacs.d/rul-init.d/latex.el") +(load-file "~/.emacs.d/rul-init.d/logos.el") +(load-file "~/.emacs.d/rul-init.d/mail-mode.el") +(load-file "~/.emacs.d/rul-init.d/markdown.el") +(load-file "~/.emacs.d/rul-init.d/magit.el") +(load-file "~/.emacs.d/rul-init.d/notmuch.el") +(load-file "~/.emacs.d/rul-init.d/projectile.el") +(load-file "~/.emacs.d/rul-init.d/tabbar.el") +(load-file "~/.emacs.d/rul-init.d/which-key.el") +(load-file "~/.emacs.d/rul-init.d/writeroom.el") +(load-file "~/.emacs.d/rul-init.d/vterm.el") + +(load-file "~/.emacs.d/rul-init.d/staging.el") + +(when-let* ((file (locate-user-emacs-file "rul-pre-init.el")) + ((file-exists-p file))) + (load-file file)) + +;; init.el ends here diff --git a/.emacs.d/rul-init.d/auto-fill.el b/.emacs.d/rul-init.d/auto-fill.el new file mode 100644 index 0000000..dad2831 --- /dev/null +++ b/.emacs.d/rul-init.d/auto-fill.el @@ -0,0 +1,4 @@ +;; auto-fill mode +(add-hook 'text-mode-hook 'turn-on-auto-fill) +(global-set-key (kbd "C-c q") 'auto-fill-mode) + diff --git a/.emacs.d/rul-init.d/company.el b/.emacs.d/rul-init.d/company.el new file mode 100644 index 0000000..d52000e --- /dev/null +++ b/.emacs.d/rul-init.d/company.el @@ -0,0 +1,42 @@ +(use-package company + :ensure t + :defer t + :init (global-company-mode) + :config + (progn + ;; Use Company for completion + (bind-key [remap completion-at-point] #'company-complete company-mode-map) + + (setq company-tooltip-align-annotations t + ;; Easy navigation to candidates with M-<n> + company-show-numbers t) + (setq company-dabbrev-downcase nil)) + + (setq company-idle-delay 0) + (setq company-minimum-prefix-length 1) + :diminish company-mode) + +(use-package company-quickhelp ; Documentation popups for Company + :ensure t + :defer t + :init (add-hook 'global-company-mode-hook #'company-quickhelp-mode)) + +(use-package company-go + :ensure t + :defer t + :init + (with-eval-after-load 'company + (add-to-list 'company-backends 'company-go))) + +(use-package company-lsp + :ensure t + :commands company-lsp) + +(use-package yasnippet + :ensure t + :config + (yas-global-mode 1) + (global-set-key (kbd "C-c y") 'company-yasnippet)) + +(use-package yasnippet-snippets + :ensure t) diff --git a/.emacs.d/rul-init.d/dashboard.el b/.emacs.d/rul-init.d/dashboard.el new file mode 100644 index 0000000..7bf51a9 --- /dev/null +++ b/.emacs.d/rul-init.d/dashboard.el @@ -0,0 +1,9 @@ +(use-package dashboard + :ensure t + :diminish dashboard-mode + :config + (setq dashboard-banner-logo-title "Happy hacking!") + (setq dashboard-items '((recents . 10) + (projects . 5) + (bookmarks . 10))) + (dashboard-setup-startup-hook)) diff --git a/.emacs.d/rul-init.d/doom-modeline.el b/.emacs.d/rul-init.d/doom-modeline.el new file mode 100644 index 0000000..b212652 --- /dev/null +++ b/.emacs.d/rul-init.d/doom-modeline.el @@ -0,0 +1,11 @@ +;; Elpa packages: doom-modeline all-the-icons +;; Run: all-the-icons-install-fonts + +(use-package doom-modeline + :ensure t + :hook (after-init . doom-modeline-mode) + :config + (setq doom-modeline-height 1) + (set-face-attribute 'mode-line nil :family "Noto Sans" :height 150) + (set-face-attribute 'mode-line-inactive nil :family "Noto Sans" :height 150) +) diff --git a/.emacs.d/rul-init.d/flycheck.el b/.emacs.d/rul-init.d/flycheck.el new file mode 100644 index 0000000..6662c06 --- /dev/null +++ b/.emacs.d/rul-init.d/flycheck.el @@ -0,0 +1,20 @@ +;; Debian-packages: elpa-flycheck python3-proselint + +(flycheck-define-checker proselint + "A linter for prose." + :command ("proselint" source-inplace) + :error-patterns + ((warning line-start (file-name) ":" line ":" column ": " + (id (one-or-more (not (any " ")))) + (message) line-end)) + :modes (text-mode markdown-mode gfm-mode org-mode)) + +(add-to-list 'flycheck-checkers 'proselint) + +;; TODO: docker run --rm -p 8010:8010 erikvl87/languagetool +(use-package flycheck-languagetool + :ensure t + :hook (message-mode . flycheck-languagetool-setup) + :init + (setq flycheck-languagetool-url "http://localhost:8010") +) diff --git a/.emacs.d/rul-init.d/flyspell.el b/.emacs.d/rul-init.d/flyspell.el new file mode 100644 index 0000000..8cf27b8 --- /dev/null +++ b/.emacs.d/rul-init.d/flyspell.el @@ -0,0 +1,12 @@ +(defcustom flyspell-delayed-commands nil + "List of commands that are \"delayed\" for Flyspell mode. +After these commands, Flyspell checking is delayed for a short time, +whose length is specified by `flyspell-delay'." + :group 'flyspell + :type '(repeat (symbol))) + +(setq ispell-dictionary "en") +(setq flyspell-default-dictionary "en") + +(setq flyspell-issue-welcome-flag nil) +(setq-default ispell-list-command "list") diff --git a/.emacs.d/rul-init.d/fonts.el b/.emacs.d/rul-init.d/fonts.el new file mode 100644 index 0000000..69eb304 --- /dev/null +++ b/.emacs.d/rul-init.d/fonts.el @@ -0,0 +1,31 @@ +;; elpa-packages: fontaine + +(setq fontaine-presets + '((tiny + :default-family "Fira Code Retina" + :default-height 100) + (small + :default-family "Fira Code Retina" + :default-height 120) + (medium + :default-height 140) + (large + :default-weight semilight + :default-height 180 + :bold-weight extrabold) + (presentation + :default-weight semilight + :default-height 200 + :bold-weight extrabold) + (jumbo + :default-weight semilight + :default-height 230 + :bold-weight extrabold) + (t + :default-family "Fira Code Retina" + :default-weight regular + :default-height 140 + :variable-pitch-family "Fira Code Retina"))) + +;; Set desired style from `fontaine-presets' +(fontaine-set-preset 'medium) diff --git a/.emacs.d/rul-init.d/go-lang.el b/.emacs.d/rul-init.d/go-lang.el new file mode 100644 index 0000000..8ec678b --- /dev/null +++ b/.emacs.d/rul-init.d/go-lang.el @@ -0,0 +1,23 @@ +;; Debian packages: elpa-go-mode +;; Elpa packages: go-eldoc + +(use-package go-mode + :ensure t + :init + (progn + (bind-key [remap find-tag] #'godef-jump)) + :config + (add-hook 'go-mode-hook #'yas-minor-mode) + (add-hook 'go-mode-hook 'electric-pair-mode) + (add-hook 'go-mode-hook 'my-go-mode-hook) + (add-hook 'before-save-hook 'gofmt-before-save) +) + +(use-package go-eldoc + :ensure t + :init + (add-hook 'go-mode-hook 'go-eldoc-setup)) + +;; Define function to call when go-mode loads +(defun my-go-mode-hook () + (set 'compile-command "go build -v && go test -v && go vet")) diff --git a/.emacs.d/rul-init.d/hydra.el b/.emacs.d/rul-init.d/hydra.el new file mode 100644 index 0000000..8afe86f --- /dev/null +++ b/.emacs.d/rul-init.d/hydra.el @@ -0,0 +1,97 @@ +(use-package hydra + :defer 1) + +;; projectile +(defhydra hydra-projectile-other-window (:color teal) + "projectile-other-window" + ("f" projectile-find-file-other-window "file") + ("g" projectile-find-file-dwim-other-window "file dwim") + ("d" projectile-find-dir-other-window "dir") + ("b" projectile-switch-to-buffer-other-window "buffer") + ("q" nil "cancel" :color blue)) + +(defhydra hydra-projectile (:color teal + :hint nil) + " + PROJECTILE: %(projectile-project-root) + + Find File Search/Tags Buffers Cache +------------------------------------------------------------------------------------------ +_s-f_: file _a_: ag _i_: Ibuffer _c_: cache clear + _ff_: file dwim _g_: update gtags _b_: switch to buffer _x_: remove known project + _fd_: file curr dir _o_: multi-occur _s-k_: Kill all buffers _X_: cleanup non-existing + _r_: recent file _s_: ripgrep ^^^^_z_: cache current + _d_: dir + +" + ("a" projectile-ag) + ("b" projectile-switch-to-buffer) + ("c" projectile-invalidate-cache) + ("d" projectile-find-dir) + ("s-f" projectile-find-file) + ("ff" projectile-find-file-dwim) + ("fd" projectile-find-file-in-directory) + ("g" ggtags-update-tags) + ("s-g" ggtags-update-tags) + ("i" projectile-ibuffer) + ("K" projectile-kill-buffers) + ("s-k" projectile-kill-buffers) + ("m" projectile-multi-occur) + ("o" projectile-multi-occur) + ("s-p" projectile-switch-project "switch project") + ("p" projectile-switch-project) + ("s" projectile-save-project-buffers "save project buffers") + ("r" projectile-recentf) + ("x" projectile-remove-known-project) + ("X" projectile-cleanup-known-projects) + ("z" projectile-cache-current-file) + ("`" hydra-projectile-other-window/body "other window") + ("q" nil "cancel" :color blue)) + + +(global-set-key (kbd "C-c p") 'hydra-projectile/body) + +;; tab-bar +(defhydra hydra-tab-bar (:color amaranth) + "Tab Bar Operations" + ("t" tab-new "Create a new tab" :column "Creation" :exit t) + ("d" dired-other-tab "Open Dired in another tab") + ("f" find-file-other-tab "Find file in another tab") + ("x" tab-close "Close current tab") + ("m" tab-move "Move current tab" :column "Management") + ("r" tab-rename "Rename Tab") + ("<return>" tab-bar-select-tab-by-name "Select tab by name" :column "Navigation") + ("l" tab-next "Next Tab") + ("j" tab-previous "Previous Tab") + ("q" nil "Exit" :exit t)) + +(global-set-key (kbd "C-x t") 'hydra-tab-bar/body) + +;; Zoom +(defhydra hydra-zoom () + "zoom" + ("g" text-scale-increase "in") + ("l" text-scale-decrease "out")) + +(global-set-key (kbd "C-c z") 'hydra-zoom/body) + +;; Go +(defhydra hydra-go () + "zoom" + ("=" gofmt :exit t) + ("c" go-coverage :exit t)) + +;; vterm +(defhydra hydra-vterm () + "zoom" + ("t" multi-vterm "Open a terminal" :exit t) + ("d" multi-vterm-dedicated-open "Dedicated" :exit t) + ("p" multi-vterm-prev "Previous terminal") + ("n" multi-vterm-next "Next terminal") + ("r" multi-vterm-rename-buffer "Rename buffer" :exit t) + ) + +(global-set-key (kbd "C-c t") 'hydra-vterm/body) + + +(global-set-key (kbd "C-c m") 'hydra-go/body) diff --git a/.emacs.d/rul-init.d/ibuffer.el b/.emacs.d/rul-init.d/ibuffer.el new file mode 100644 index 0000000..d5198d8 --- /dev/null +++ b/.emacs.d/rul-init.d/ibuffer.el @@ -0,0 +1,35 @@ +;; Debian packages: elpa-ibuffer-vc + +(use-package ibuffer ; Better buffer list + :bind (([remap list-buffers] . ibuffer)) + ;; Show VC Status in ibuffer + :config (setq ibuffer-formats + '((mark modified read-only vc-status-mini " " + (name 18 18 :left :elide) + " " + (size 9 -1 :right) + " " + (mode 16 16 :left :elide) + " " + (vc-status 16 16 :left) + " " + filename-and-process) + (mark modified read-only " " + (name 18 18 :left :elide) + " " + (size 9 -1 :right) + " " + (mode 16 16 :left :elide) + " " filename-and-process) + (mark " " + (name 16 -1) + " " filename)))) + +(use-package ibuffer-vc ; Group buffers by VC project and status + :ensure t + :defer t + :init (add-hook 'ibuffer-hook + (lambda () + (ibuffer-vc-set-filter-groups-by-vc-root) + (unless (eq ibuffer-sorting-mode 'alphabetic) + (ibuffer-do-sort-by-alphabetic))))) diff --git a/.emacs.d/rul-init.d/imenu.el b/.emacs.d/rul-init.d/imenu.el new file mode 100644 index 0000000..1a2b29b --- /dev/null +++ b/.emacs.d/rul-init.d/imenu.el @@ -0,0 +1,12 @@ +;; Debian packages: elpa-imenu-list +(use-package imenu-list + :ensure t + :bind ("C-." . imenu-list-minor-mode) + :config + (setq imenu-list-focus-after-activation t) + (setq imenu-list-size 0.2) + (setq imenu-list-position 'left) + (add-hook 'go-mode-hook #'imenu-list-minor-mode)) + + + diff --git a/.emacs.d/rul-init.d/ivy.el b/.emacs.d/rul-init.d/ivy.el new file mode 100644 index 0000000..fcf69ca --- /dev/null +++ b/.emacs.d/rul-init.d/ivy.el @@ -0,0 +1,38 @@ +(use-package ivy + :diminish (ivy-mode . "") + :init (ivy-mode 1) ; globally at startup + + :bind (:map ivy-minibuffer-map + ("TAB" . ivy-alt-done)) + :config + (setq ivy-use-virtual-buffers t) + (setq ivy-height 20) + (setq ivy-count-format "%d/%d ") + (setq ivy-re-builders-alist + '((swiper . ivy--regex-plus) + (t . ivy--regex-fuzzy))) +) + +;; Override the basic Emacs commands +(use-package counsel + :bind* ; load when pressed + (("M-x" . counsel-M-x) + ("C-s" . swiper) + ("C-x C-f" . counsel-find-file) + ("C-x C-r" . counsel-recentf) ; search for recently edited + ("C-c g" . counsel-git) ; search for files in git repo + ("C-c /" . counsel-ag) ; Use ag for regexp + ("C-x l" . counsel-locate) + ("C-x C-f" . counsel-find-file) + ("<f1> f" . counsel-describe-function) + ("<f1> v" . counsel-describe-variable) + ("<f1> l" . counsel-find-library) + ("<f2> i" . counsel-info-lookup-symbol) + ("<f2> u" . counsel-unicode-char) + ("C-c C-r" . ivy-resume))) ; Resume last Ivy-based completion + +(use-package ivy-hydra + :defer t + :after hydra) + +(provide 'init-ivy) diff --git a/.emacs.d/rul-init.d/latex.el b/.emacs.d/rul-init.d/latex.el new file mode 100644 index 0000000..de4de1f --- /dev/null +++ b/.emacs.d/rul-init.d/latex.el @@ -0,0 +1,9 @@ +(add-hook 'latex-mode-hook 'flyspell-mode) +(setq TeX-PDF-mode t) + +(defun pdfevince () + (add-to-list 'TeX-output-view-style + '("^pdf$" "." "evince %o %(outpage)"))) + +(add-hook 'LaTeX-mode-hook 'pdfevince t) ; AUCTeX LaTeX mode + diff --git a/.emacs.d/rul-init.d/logos.el b/.emacs.d/rul-init.d/logos.el new file mode 100644 index 0000000..23d9937 --- /dev/null +++ b/.emacs.d/rul-init.d/logos.el @@ -0,0 +1,29 @@ +(use-package logos +:ensure t +:config + +;; If you want to use outlines instead of page breaks (the ^L) +(setq logos-outlines-are-pages t) +(setq logos-outline-regexp-alist + `((emacs-lisp-mode . "^;;;+ ") + (org-mode . "^\\*+ +") + (markdown-mode . "^\\#+ +") + )) + +;; These apply when `logos-focus-mode' is enabled. Their value is +;; buffer-local. +(setq-default logos-hide-mode-line t + logos-hide-buffer-boundaries t + logos-hide-fringe t + logos-variable-pitch nil + logos-buffer-read-only nil + logos-scroll-lock nil + logos-olivetti t) + + +(let ((map global-map)) + (define-key map [remap narrow-to-region] #'logos-narrow-dwim) + (define-key map [remap forward-page] #'logos-forward-page-dwim) + (define-key map [remap backward-page] #'logos-backward-page-dwim) + (define-key map (kbd "<f9>") #'logos-focus-mode)) +) diff --git a/.emacs.d/rul-init.d/lsp.el b/.emacs.d/rul-init.d/lsp.el new file mode 100644 index 0000000..a565b03 --- /dev/null +++ b/.emacs.d/rul-init.d/lsp.el @@ -0,0 +1,17 @@ +(use-package lsp-mode + :ensure t + :commands + (lsp lsp-deferred) + :hook + (dart-mode go-mode . lsp) + :init + (setq lsp-keymap-prefix "H-l") + ) + +(defun lsp-go-install-save-hooks () + (add-hook 'before-save-hook #'lsp-format-buffer t t) + (add-hook 'before-save-hook #'lsp-organize-imports t t)) + +(use-package lsp-ui + :ensure t + :commands lsp-ui-mode) diff --git a/.emacs.d/rul-init.d/magit.el b/.emacs.d/rul-init.d/magit.el new file mode 100644 index 0000000..cd52e67 --- /dev/null +++ b/.emacs.d/rul-init.d/magit.el @@ -0,0 +1,15 @@ +;; Debian packages: elpa-magit + +(use-package magit + :ensure t + :defer t + :bind (("C-x g" . magit-status)) + :config + (progn + (defun inkel/magit-log-edit-mode-hook () + (flyspell-mode t) + (turn-on-auto-fill)) + (defadvice magit-status (around magit-fullscreen activate) + (window-configuration-to-register :magit-fullscreen) + ad-do-it + (delete-other-windows)))) diff --git a/.emacs.d/rul-init.d/mail-mode.el b/.emacs.d/rul-init.d/mail-mode.el new file mode 100644 index 0000000..09b04f9 --- /dev/null +++ b/.emacs.d/rul-init.d/mail-mode.el @@ -0,0 +1,15 @@ +(setq auto-mode-alist (append '((".*tmp/mutt.*" . message-mode)) auto-mode-alist)) +(setq auto-mode-alist (append '((".*tmp/neomutt.*" . message-mode)) auto-mode-alist)) +(add-to-list 'auto-mode-alist '("/mutt" . mail-mode)) + +(setq mml-secure-openpgp-sign-with-sender t) + +(add-hook 'mail-mode-hook + (lambda () + (font-lock-add-keywords nil + '(("^[ \t]*>[ \t]*>[ \t]*>.*$" + (0 'compilation-error)) + ("^[ \t]*>[ \t]*>.*$" + (0 'compilation-column-number)) + ("^[ \t]*>.*$" + (0 'comint-highlight-prompt)))))) diff --git a/.emacs.d/rul-init.d/markdown.el b/.emacs.d/rul-init.d/markdown.el new file mode 100644 index 0000000..f035509 --- /dev/null +++ b/.emacs.d/rul-init.d/markdown.el @@ -0,0 +1,5 @@ +(autoload 'markdown-mode "markdown-mode.el" + "Major mode for editing Markdown files" t) + +(setq auto-mode-alist + (cons '("\\.mdwn" . markdown-mode) auto-mode-alist)) diff --git a/.emacs.d/rul-init.d/mu4e.el b/.emacs.d/rul-init.d/mu4e.el new file mode 100644 index 0000000..da8b7c8 --- /dev/null +++ b/.emacs.d/rul-init.d/mu4e.el @@ -0,0 +1,61 @@ +(require 'mu4e) + +;; sending mail +(setq message-send-mail-function 'message-send-mail-with-sendmail + sendmail-program "/home/lur/bin/te-msmtp" + user-mail-address "raul@thousandeyes.com" + user-full-name "Raúl Benencia") + +(setq mu4e-user-mail-address-list (list "raul@thousandeyes.com")) + +(setq message-kill-buffer-on-exit t) +;; Use fancy chars +(setq mu4e-use-fancy-chars t) +;; don't save message to Sent Messages, Gmail/IMAP takes care of this +(setq mu4e-sent-messages-behavior 'delete) +(setq mu4e-update-interval 60) ;; update every 5 minutes + +;; use 'fancy' non-ascii characters in various places in mu4e +;;(setq mu4e-use-fancy-chars t) + +(setq relevant-maildirs " (maildir:/INBOX OR maildir:/jira OR maildir:/news OR maildir:/git)") +(mu4e-alert-enable-notifications) +(mu4e-alert-set-default-style 'libnotify) +(setq mu4e-alert-interesting-mail-query + (concat "flag:unread" + " AND NOT flag:trashed" + " AND" relevant-maildirs)) + +(mu4e-alert-set-default-style 'libnotify) +;;(add-hook 'after-init-hook #'mu4e-alert-enable-notifications) +(add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display) + +(setq mu4e-bookmarks + `(,(make-mu4e-bookmark + :name "INBOX" + :query "maildir:/INBOX" + :key ?i) + ,(make-mu4e-bookmark + :name "Unread messages" + :query (concat "flag:unread AND NOT flag:trashed AND" relevant-maildirs) + :key ?u) + ,(make-mu4e-bookmark + :name "Today's messages" + :query (concat "date:today..now AND" relevant-maildirs) + :key ?t) + ,(make-mu4e-bookmark + :name "Last 7 days" + :query (concat "date:7d..now AND" relevant-maildirs) + :key ?w) + ,(make-mu4e-bookmark + :name "Today's unread logs " + :query (concat "date:today..now flag:unread AND NOT" relevant-maildirs) + :key ?l) + ,(make-mu4e-bookmark + :name "Today's logs " + :query (concat "date:today..now AND NOT maildir:/fim AND NOT" relevant-maildirs) + :key ?l)) +) + +;; (require 'mu4e-maildirs-extension) +;; (mu4e-maildirs-extension) diff --git a/.emacs.d/rul-init.d/notmuch.el b/.emacs.d/rul-init.d/notmuch.el new file mode 100644 index 0000000..19ea823 --- /dev/null +++ b/.emacs.d/rul-init.d/notmuch.el @@ -0,0 +1,133 @@ +;; -------- +;; notmuch mode +;; -------- +(require 'notmuch) +(require 'notmuch-indicator) +(require 'rul-config-mail) + +;;;; General UI +(setq notmuch-show-logo nil + notmuch-column-control 1.0 + notmuch-hello-auto-refresh t + notmuch-hello-recent-searches-max 20 + notmuch-hello-thousands-separator "" + notmuch-show-all-tags-list t) + +;; Keymaps +(defun rul/capture-mail() + "Capture mail to org mode." + (interactive) + (org-store-link nil) + (org-capture nil "m") + ) + +(bind-key "c" 'rul/capture-mail notmuch-show-mode-map) + +(define-key notmuch-show-mode-map "R" 'notmuch-show-reply) +(define-key notmuch-search-mode-map "R" 'notmuch-search-reply-to-thread) + +; Spam +(define-key notmuch-show-mode-map "S" + (lambda () + "mark message as spam" + (interactive) + (notmuch-show-tag (list "+spam" "-inbox" "-unread")))) + +(define-key notmuch-search-mode-map "S" + (lambda (&optional beg end) + "mark thread as spam" + (interactive (notmuch-search-interactive-region)) + (notmuch-search-tag (list "+spam" "-inbox" "-unread") beg end))) + +; Archive +(setq notmuch-archive-tags (list "-inbox" "+archive")) +(define-key notmuch-show-mode-map "A" + (lambda () + "archive" + (interactive) + (notmuch-show-tag (list "+archive" "-inbox" "-unread")) + (notmuch-refresh-this-buffer))) + +(define-key notmuch-search-mode-map "A" + (lambda (&optional beg end) + "archive thread" + (interactive (notmuch-search-interactive-region)) + (notmuch-search-tag (list "+archive" "-inbox" "-unread") beg end) + (notmuch-refresh-this-buffer))) + +; Mark as read +(define-key notmuch-search-mode-map "r" + (lambda (&optional beg end) + "mark thread as read" + (interactive (notmuch-search-interactive-region)) + (notmuch-search-tag (list "-unread") beg end) + (notmuch-search-next-thread))) + +(define-key notmuch-search-mode-map (kbd "RET") + (lambda () + "Show the selected thread with notmuch-tree if it has more +than one email. Use notmuch-show otherwise." + (interactive) + (if (= (plist-get (notmuch-search-get-result) :total) 1) + (notmuch-search-show-thread) + (notmuch-tree (notmuch-search-find-thread-id) + notmuch-search-query-string + nil + (notmuch-prettify-subject (notmuch-search-find-subject)))))) + +(defun color-inbox-if-unread () (interactive) + (save-excursion + (goto-char (point-min)) + (let ((cnt (car (process-lines "notmuch" "count" "tag:inbox and tag:unread")))) + (when (> (string-to-number cnt) 0) + (save-excursion + (when (search-forward "inbox" (point-max) t) + (let* ((overlays (overlays-in (match-beginning 0) (match-end 0))) + (overlay (car overlays))) + (when overlay + (overlay-put overlay 'face '((:inherit bold) (:foreground "green"))))))))))) + +(defvar notmuch-hello-refresh-count 0) +(defun notmuch-hello-refresh-status-message () + (let* ((new-count + (string-to-number + (car (process-lines notmuch-command "count")))) + (diff-count (- new-count notmuch-hello-refresh-count))) + (cond + ((= notmuch-hello-refresh-count 0) + (message "You have %s messages." + (notmuch-hello-nice-number new-count))) + ((> diff-count 0) + (message "You have %s more messages since last refresh." + (notmuch-hello-nice-number diff-count))) + ((< diff-count 0) + (message "You have %s fewer messages since last refresh." + (notmuch-hello-nice-number (- diff-count))))) + (setq notmuch-hello-refresh-count new-count))) + +(add-hook 'notmuch-hello-refresh-hook 'color-inbox-if-unread) +(add-hook 'notmuch-hello-refresh-hook 'notmuch-hello-refresh-status-message) + +(setq notmuch-hello-sections '(notmuch-hello-insert-saved-searches + notmuch-hello-insert-search + notmuch-hello-insert-recent-searches + notmuch-hello-insert-alltags + )) + +;; https://git.sr.ht/~tslil/dotfiles/tree/4e51afbb/emacs/notmuch-config.el#L76-82 +(defmacro make-binds (mode-map binds argfunc &rest body) + "Create keybindings in `mode-map' using a list of (keystr . arg) +pairs in `binds' of the form ( ... (argfunc arg) body)." + `(progn ,@(mapcar (lambda (pair) + `(define-key ,mode-map (kbd ,(car pair)) + (lambda () (interactive) (,argfunc ,(cdr pair)) ,@body))) + (eval binds)))) + +(defvar notmuch-hello-tree-searches '(("u" . "tag:unread") + ("i" . "tag:inbox") + ("*" . "*")) + "List of (key . query) pairs to bind in notmuch-hello.") + +(make-binds notmuch-hello-mode-map + notmuch-hello-tree-searches + notmuch-search) diff --git a/.emacs.d/rul-init.d/projectile.el b/.emacs.d/rul-init.d/projectile.el new file mode 100644 index 0000000..ed95775 --- /dev/null +++ b/.emacs.d/rul-init.d/projectile.el @@ -0,0 +1,8 @@ +;; Debian packages: elpa-projectile + +(use-package projectile + :hook + (after-init . projectile-global-mode) + :config + (setq projectile-completion-system 'ivy) +) diff --git a/.emacs.d/rul-init.d/python.el b/.emacs.d/rul-init.d/python.el new file mode 100644 index 0000000..3186c72 --- /dev/null +++ b/.emacs.d/rul-init.d/python.el @@ -0,0 +1 @@ +(add-hook 'python-mode-hook 'py-autopep8-enable-on-save) diff --git a/.emacs.d/rul-init.d/sml.el b/.emacs.d/rul-init.d/sml.el new file mode 100644 index 0000000..247d9b2 --- /dev/null +++ b/.emacs.d/rul-init.d/sml.el @@ -0,0 +1,7 @@ +;; Debian packages: elpa-smart-mode-line elpa-smart-mode-line-powerline-theme + +(use-package smart-mode-line + :ensure t + :config + (setq sml/theme 'respectful) + (sml/setup)) diff --git a/.emacs.d/rul-init.d/staging.el b/.emacs.d/rul-init.d/staging.el new file mode 100644 index 0000000..7134fc3 --- /dev/null +++ b/.emacs.d/rul-init.d/staging.el @@ -0,0 +1,158 @@ +;; Debian packages: elpa-consult elpa-orderless elpa-vertico elpa-marginalia + +;; Example configuration for Consult +(use-package consult + ;; Replace bindings. Lazily loaded due by `use-package'. + :bind (;; C-c bindings in `mode-specific-map' + ("C-c M-x" . consult-mode-command) + ("C-c h" . consult-history) + ("C-c k" . consult-kmacro) + ("C-c m" . consult-man) + ("C-c i" . consult-info) + ([remap Info-search] . consult-info) + ;; C-x bindings in `ctl-x-map' + ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command + ("C-x b" . consult-buffer) ;; orig. switch-to-buffer + ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window + ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame + ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump + ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer + ;; Custom M-# bindings for fast register access + ("M-#" . consult-register-load) + ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated) + ("C-M-#" . consult-register) + ;; Other custom bindings + ("M-y" . consult-yank-pop) ;; orig. yank-pop + ;; M-g bindings in `goto-map' + ("M-g e" . consult-compile-error) + ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck + ("M-g g" . consult-goto-line) ;; orig. goto-line + ("M-g M-g" . consult-goto-line) ;; orig. goto-line + ("M-g o" . consult-outline) ;; Alternative: consult-org-heading + ("M-g m" . consult-mark) + ("M-g k" . consult-global-mark) + ("M-g i" . consult-imenu) + ("M-g I" . consult-imenu-multi) + ;; M-s bindings in `search-map' + ("M-s d" . consult-find) + ("M-s D" . consult-locate) + ("M-s g" . consult-grep) + ("M-s G" . consult-git-grep) + ("M-s r" . consult-ripgrep) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ("M-s k" . consult-keep-lines) + ("M-s u" . consult-focus-lines) + ;; Isearch integration + ("M-s e" . consult-isearch-history) + :map isearch-mode-map + ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string + ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string + ("M-s l" . consult-line) ;; needed by consult-line to detect isearch + ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch + ;; Minibuffer history + :map minibuffer-local-map + ("M-s" . consult-history) ;; orig. next-matching-history-element + ("M-r" . consult-history)) ;; orig. previous-matching-history-element + + ;; Enable automatic preview at point in the *Completions* buffer. This is + ;; relevant when you use the default completion UI. + :hook (completion-list-mode . consult-preview-at-point-mode) + + ;; The :init configuration is always executed (Not lazy) + :init + + ;; Optionally configure the register formatting. This improves the register + ;; preview for `consult-register', `consult-register-load', + ;; `consult-register-store' and the Emacs built-ins. + (setq register-preview-delay 0.5 + register-preview-function #'consult-register-format) + + ;; Optionally tweak the register preview window. + ;; This adds thin lines, sorting and hides the mode line of the window. + (advice-add #'register-preview :override #'consult-register-window) + + ;; Use Consult to select xref locations with preview + (setq xref-show-xrefs-function #'consult-xref + xref-show-definitions-function #'consult-xref) + + ;; Configure other variables and modes in the :config section, + ;; after lazily loading the package. + :config + + ;; Optionally configure preview. The default value + ;; is 'any, such that any key triggers the preview. + ;; (setq consult-preview-key 'any) + ;; (setq consult-preview-key "M-.") + ;; (setq consult-preview-key '("S-<down>" "S-<up>")) + ;; For some commands and buffer sources it is useful to configure the + ;; :preview-key on a per-command basis using the `consult-customize' macro. + (consult-customize + consult-theme :preview-key '(:debounce 0.2 any) + consult-ripgrep consult-git-grep consult-grep + consult-bookmark consult-recent-file consult-xref + consult--source-bookmark consult--source-file-register + consult--source-recent-file consult--source-project-recent-file + ;; :preview-key "M-." + :preview-key '(:debounce 0.4 any)) + + ;; Optionally configure the narrowing key. + ;; Both < and C-+ work reasonably well. + (setq consult-narrow-key "<") ;; "C-+" + + ;; Optionally make narrowing help available in the minibuffer. + ;; You may want to use `embark-prefix-help-command' or which-key instead. + ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help) + + ;; By default `consult-project-function' uses `project-root' from project.el. + ;; Optionally configure a different project root function. + ;;;; 1. project.el (the default) + ;; (setq consult-project-function #'consult--default-project--function) + ;;;; 2. vc.el (vc-root-dir) + ;; (setq consult-project-function (lambda (_) (vc-root-dir))) + ;;;; 3. locate-dominating-file + ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git"))) + ;;;; 4. projectile.el (projectile-project-root) + ;; (autoload 'projectile-project-root "projectile") + ;; (setq consult-project-function (lambda (_) (projectile-project-root))) + ;;;; 5. No project support + ;; (setq consult-project-function nil) +) + +;; Enable vertico +(use-package vertico + :init + (vertico-mode) + + ;; Different scroll margin + ;; (setq vertico-scroll-margin 0) + + ;; Show more candidates + ;; (setq vertico-count 20) + + ;; Grow and shrink the Vertico minibuffer + ;; (setq vertico-resize t) + + ;; Optionally enable cycling for `vertico-next' and `vertico-previous'. + ;; (setq vertico-cycle t) + ) + +;; Enable rich annotations using the Marginalia package +(use-package marginalia + ;; Bind `marginalia-cycle' locally in the minibuffer. To make the binding + ;; available in the *Completions* buffer, add it to the + ;; `completion-list-mode-map'. + :bind (:map minibuffer-local-map + ("M-A" . marginalia-cycle)) + + ;; The :init section is always executed. + :init + + ;; Marginalia must be actived in the :init section of use-package such that + ;; the mode gets enabled right away. Note that this forces loading the + ;; package. + (marginalia-mode)) + +(setq completion-styles '(flex basic) + completion-category-defaults nil + completion-category-overrides nil) diff --git a/.emacs.d/rul-init.d/tabbar.el b/.emacs.d/rul-init.d/tabbar.el new file mode 100644 index 0000000..a492e85 --- /dev/null +++ b/.emacs.d/rul-init.d/tabbar.el @@ -0,0 +1,25 @@ +(global-set-key (kbd "C-<next>") 'tab-bar-switch-to-next-tab) +(global-set-key (kbd "C-<prior>") 'tab-bar-switch-to-prev-tab) + +(setq tab-bar-show t) + +(defun my/project-create-tab () + (interactive) + (tab-bar-new-tab) + (magit-status)) + +(setq project-switch-commands #'my/project-create-tab) + +(defun my/switch-to-tab-buffer () + (interactive) + (if (project-current) + (call-interactively #'projectile-switch-to-buffer) + (call-interactively #'switch-to-buffer))) + +(global-set-key (kbd "C-x b") #'my/switch-to-tab-buffer) + +;; Turn on tab bar mode after startup +(tab-bar-mode 1) + +;; Save the desktop session +(desktop-save-mode 1) diff --git a/.emacs.d/rul-init.d/themes.el b/.emacs.d/rul-init.d/themes.el new file mode 100644 index 0000000..c94d4a1 --- /dev/null +++ b/.emacs.d/rul-init.d/themes.el @@ -0,0 +1,47 @@ +(use-package ef-themes :ensure t) + +(setq + modus-themes-mode-line '(accented borderless padded) + modus-themes-region '(bg-only) + modus-themes-bold-constructs t + modus-themes-italic-constructs t + modus-themes-paren-match '(bold intense) + modus-themes-headings (quote ((1 . (rainbow variable-pitch 1.3)) + (2 . (rainbow 1.1)) + (t . (rainbow)))) + modus-themes-org-blocks 'tinted + ) + +(use-package dbus) +(defun mf/set-theme-from-dbus-value (value) + "Set the appropiate theme according to the color-scheme setting value." + (message "value is %s" value) + (if (equal value '1) + (progn (message "Switch to dark theme") + (modus-themes-select 'modus-vivendi-tinted)) + (progn (message "Switch to light theme") + (modus-themes-select 'modus-operandi-tinted)))) + +(defun mf/color-scheme-changed (path var value) + "DBus handler to detect when the color-scheme has changed." + (when (and (string-equal path "org.freedesktop.appearance") + (string-equal var "color-scheme")) + (mf/set-theme-from-dbus-value (car value)) + )) + +;; Register for future changes +(dbus-register-signal + :session "org.freedesktop.portal.Desktop" + "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Settings" + "SettingChanged" + #'mf/color-scheme-changed) + +;; Request the current color-scheme +(dbus-call-method-asynchronously + :session "org.freedesktop.portal.Desktop" + "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Settings" + "Read" + (lambda (value) (mf/set-theme-from-dbus-value (caar value))) + "org.freedesktop.appearance" + "color-scheme" + ) diff --git a/.emacs.d/rul-init.d/vterm.el b/.emacs.d/rul-init.d/vterm.el new file mode 100644 index 0000000..54abbbf --- /dev/null +++ b/.emacs.d/rul-init.d/vterm.el @@ -0,0 +1,6 @@ +(use-package multi-vterm +:ensure t +:config +(setq + vterm-max-scrollback 100000 +)) diff --git a/.emacs.d/rul-init.d/which-key.el b/.emacs.d/rul-init.d/which-key.el new file mode 100644 index 0000000..1d8fd8d --- /dev/null +++ b/.emacs.d/rul-init.d/which-key.el @@ -0,0 +1,5 @@ +(use-package which-key + :ensure t + :config + (which-key-mode) +) diff --git a/.emacs.d/rul-init.d/writeroom.el b/.emacs.d/rul-init.d/writeroom.el new file mode 100644 index 0000000..50780cc --- /dev/null +++ b/.emacs.d/rul-init.d/writeroom.el @@ -0,0 +1,34 @@ +(use-package writeroom-mode + :defer t + :config + (setq writeroom-width 140 + writeroom-mode-line nil + writeroom-global-effects '(writeroom-set-bottom-divider-width + writeroom-set-internal-border-width + (lambda (arg) + (let ((langs '("python" + "emacs-lisp" + "common-lisp" + "js" + "ruby"))) + (cond + ((= arg 1) + (progn + (setq org-src-block-faces + (mapcar (lambda (lang) (list lang '(:family "Source Code Pro" :height 0.8))) langs)) + (normal-mode) + (variable-pitch-mode))) + ((= arg -1) + (progn + (setq org-src-block-faces + (mapcar (lambda (lang) (list lang '(:family "Source Code Pro" :height 1.0))) langs)) + (normal-mode) + (variable-pitch-mode) +(variable-pitch-mode))))))))) + +(use-package olivetti + :defer t + :config + (setq + olivetti-body-width 86 + )) diff --git a/.emacs.d/rul-lisp/config/rul-config-elfeed.el b/.emacs.d/rul-lisp/config/rul-config-elfeed.el new file mode 100644 index 0000000..026c325 --- /dev/null +++ b/.emacs.d/rul-lisp/config/rul-config-elfeed.el @@ -0,0 +1,2 @@ +(setq elfeed-feeds '("https://planet.debian.org/rss10.xml")) +(provide 'rul-config-elfeed) diff --git a/.emacs.d/rul-lisp/config/rul-config-mail.el b/.emacs.d/rul-lisp/config/rul-config-mail.el new file mode 100644 index 0000000..240f8be --- /dev/null +++ b/.emacs.d/rul-lisp/config/rul-config-mail.el @@ -0,0 +1 @@ +(provide 'rul-config-mail) diff --git a/.emacs.d/rul-lisp/config/rul-config-org.el b/.emacs.d/rul-lisp/config/rul-config-org.el new file mode 100644 index 0000000..bf538dc --- /dev/null +++ b/.emacs.d/rul-lisp/config/rul-config-org.el @@ -0,0 +1,22 @@ +(setq + org-agenda-files '("~/org/") + org-agenda-custom-commands + '(("x" agenda) + ("y" agenda*) + ("w" todo "WAITING") + ("W" todo-tree "WAITING") + ) + org-journal-file-type 'yearly + org-journal-dir "~/org/journal/" + org-journal-file-format "%Y.org" + org-journal-time-prefix "* " + org-journal-time-format "" + org-refile-path "~/refile.org" + org-roam-directory "~/org/roam/" + + org-agenda-private-local-path "/tmp/example.ics" + org-agenda-private-remote-path "/sshx:user@host:example.ics" + ) + +(provide 'rul-config-org) + diff --git a/.emacs.d/rul-lisp/packages/rul-dart.el b/.emacs.d/rul-lisp/packages/rul-dart.el new file mode 100644 index 0000000..36bfd47 --- /dev/null +++ b/.emacs.d/rul-lisp/packages/rul-dart.el @@ -0,0 +1,7 @@ +(setq dart-server-format-on-save t) +(add-hook 'dart-mode-hook 'lsp) + +(setq gc-cons-threshold (* 100 1024 1024) + read-process-output-max (* 1024 1024)) + +(provide 'rul-dart) diff --git a/.emacs.d/rul-lisp/packages/rul-elfeed.el b/.emacs.d/rul-lisp/packages/rul-elfeed.el new file mode 100644 index 0000000..34713db --- /dev/null +++ b/.emacs.d/rul-lisp/packages/rul-elfeed.el @@ -0,0 +1,5 @@ +(use-package elfeed + :config + (require 'rul-config-elfeed)) + +(provide 'rul-elfeed) diff --git a/.emacs.d/rul-lisp/packages/rul-org-agenda.el b/.emacs.d/rul-lisp/packages/rul-org-agenda.el new file mode 100644 index 0000000..7b6c4b4 --- /dev/null +++ b/.emacs.d/rul-lisp/packages/rul-org-agenda.el @@ -0,0 +1,417 @@ +(require 'org) + +(global-set-key (kbd "<f12>") #'org-agenda) +(global-set-key (kbd "C-c a") #'org-agenda) + +(defun bh/is-project-p () + "Any task with a todo keyword subtask" + (save-restriction + (widen) + (let ((has-subtask) + (subtree-end (save-excursion (org-end-of-subtree t))) + (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1))) + (save-excursion + (forward-line 1) + (while (and (not has-subtask) + (< (point) subtree-end) + (re-search-forward "^\*+ " subtree-end t)) + (when (member (org-get-todo-state) org-todo-keywords-1) + (setq has-subtask t)))) + (and is-a-task has-subtask)))) + +(defun bh/is-project-subtree-p () + "Any task with a todo keyword that is in a project subtree. +Callers of this function already widen the buffer view." + (let ((task (save-excursion (org-back-to-heading 'invisible-ok) + (point)))) + (save-excursion + (bh/find-project-task) + (if (equal (point) task) + nil + t)))) + +(defun bh/is-task-p () + "Any task with a todo keyword and no subtask" + (save-restriction + (widen) + (let ((has-subtask) + (subtree-end (save-excursion (org-end-of-subtree t))) + (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1))) + (save-excursion + (forward-line 1) + (while (and (not has-subtask) + (< (point) subtree-end) + (re-search-forward "^\*+ " subtree-end t)) + (when (member (org-get-todo-state) org-todo-keywords-1) + (setq has-subtask t)))) + (and is-a-task (not has-subtask))))) + +(defun bh/is-subproject-p () + "Any task which is a subtask of another project" + (let ((is-subproject) + (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1))) + (save-excursion + (while (and (not is-subproject) (org-up-heading-safe)) + (when (member (nth 2 (org-heading-components)) org-todo-keywords-1) + (setq is-subproject t)))) + (and is-a-task is-subproject))) + +(defun bh/list-sublevels-for-projects-indented () + "Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks. + This is normally used by skipping functions where this variable is already local to the agenda." + (if (marker-buffer org-agenda-restrict-begin) + (setq org-tags-match-list-sublevels 'indented) + (setq org-tags-match-list-sublevels nil)) + nil) + +(defun bh/list-sublevels-for-projects () + "Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks. + This is normally used by skipping functions where this variable is already local to the agenda." + (if (marker-buffer org-agenda-restrict-begin) + (setq org-tags-match-list-sublevels t) + (setq org-tags-match-list-sublevels nil)) + nil) + +(defvar bh/hide-scheduled-and-waiting-next-tasks t) + +(defun bh/toggle-next-task-display () + (interactive) + (setq bh/hide-scheduled-and-waiting-next-tasks (not bh/hide-scheduled-and-waiting-next-tasks)) + (when (equal major-mode 'org-agenda-mode) + (org-agenda-redo)) + (message "%s WAITING and SCHEDULED NEXT Tasks" (if bh/hide-scheduled-and-waiting-next-tasks "Hide" "Show"))) + +(defun bh/skip-stuck-projects () + "Skip trees that are not stuck projects" + (save-restriction + (widen) + (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) + (if (bh/is-project-p) + (let* ((subtree-end (save-excursion (org-end-of-subtree t))) + (has-next )) + (save-excursion + (forward-line 1) + (while (and (not has-next) (< (point) subtree-end) (re-search-forward "^\\*+ NEXT " subtree-end t)) + (unless (member "WAITING" (org-get-tags-at)) + (setq has-next t)))) + (if has-next + nil + next-headline)) ; a stuck project, has subtasks but no next task + nil)))) + +(defun bh/skip-non-stuck-projects () + "Skip trees that are not stuck projects" + ;; (bh/list-sublevels-for-projects-indented) + (save-restriction + (widen) + (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) + (if (bh/is-project-p) + (let* ((subtree-end (save-excursion (org-end-of-subtree t))) + (has-next )) + (save-excursion + (forward-line 1) + (while (and (not has-next) (< (point) subtree-end) (re-search-forward "^\\*+ NEXT " subtree-end t)) + (unless (member "WAITING" (org-get-tags-at)) + (setq has-next t)))) + (if has-next + next-headline + nil)) ; a stuck project, has subtasks but no next task + next-headline)))) + +(defun bh/skip-non-projects () + "Skip trees that are not projects" + ;; (bh/list-sublevels-for-projects-indented) + (if (save-excursion (bh/skip-non-stuck-projects)) + (save-restriction + (widen) + (let ((subtree-end (save-excursion (org-end-of-subtree t)))) + (cond + ((bh/is-project-p) + nil) + ((and (bh/is-project-subtree-p) (not (bh/is-task-p))) + nil) + (t + subtree-end)))) + (save-excursion (org-end-of-subtree t)))) + +(defun bh/skip-non-tasks () + "Show non-project tasks. +Skip project and sub-project tasks, habits, and project related tasks." + (save-restriction + (widen) + (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) + (cond + ((bh/is-task-p) + nil) + (t + next-headline))))) + +(defun bh/skip-project-trees-and-habits () + "Skip trees that are projects" + (save-restriction + (widen) + (let ((subtree-end (save-excursion (org-end-of-subtree t)))) + (cond + ((bh/is-project-p) + subtree-end) + ((org-is-habit-p) + subtree-end) + (t + nil))))) + +(defun bh/skip-projects-and-habits-and-single-tasks () + "Skip trees that are projects, tasks that are habits, single non-project tasks" + (save-restriction + (widen) + (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) + (cond + ((org-is-habit-p) + next-headline) + ((and bh/hide-scheduled-and-waiting-next-tasks + (member "WAITING" (org-get-tags-at))) + next-headline) + ((bh/is-project-p) + next-headline) + ((and (bh/is-task-p) (not (bh/is-project-subtree-p))) + next-headline) + (t + nil))))) + +(defun bh/skip-project-tasks-maybe () + "Show tasks related to the current restriction. +When restricted to a project, skip project and sub project tasks, habits, NEXT tasks, and loose tasks. +When not restricted, skip project and sub-project tasks, habits, and project related tasks." + (save-restriction + (widen) + (let* ((subtree-end (save-excursion (org-end-of-subtree t))) + (next-headline (save-excursion (or (outline-next-heading) (point-max)))) + (limit-to-project (marker-buffer org-agenda-restrict-begin))) + (cond + ((bh/is-project-p) + next-headline) + ((org-is-habit-p) + subtree-end) + ((and (not limit-to-project) + (bh/is-project-subtree-p)) + subtree-end) + ((and limit-to-project + (bh/is-project-subtree-p) + (member (org-get-todo-state) (list "NEXT"))) + subtree-end) + (t + nil))))) + +(defun bh/skip-project-tasks () + "Show non-project tasks. +Skip project and sub-project tasks, habits, and project related tasks." + (save-restriction + (widen) + (let* ((subtree-end (save-excursion (org-end-of-subtree t)))) + (cond + ((bh/is-project-p) + subtree-end) + ((org-is-habit-p) + subtree-end) + ((bh/is-project-subtree-p) + subtree-end) + (t + nil))))) + +(defun bh/skip-non-project-tasks () + "Show project tasks. +Skip project and sub-project tasks, habits, and loose non-project tasks." + (save-restriction + (widen) + (let* ((subtree-end (save-excursion (org-end-of-subtree t))) + (next-headline (save-excursion (or (outline-next-heading) (point-max))))) + (cond + ((bh/is-project-p) + next-headline) + ((org-is-habit-p) + subtree-end) + ((and (bh/is-project-subtree-p) + (member (org-get-todo-state) (list "NEXT"))) + subtree-end) + ((not (bh/is-project-subtree-p)) + subtree-end) + (t + nil))))) + +(defun bh/skip-projects-and-habits () + "Skip trees that are projects and tasks that are habits" + (save-restriction + (widen) + (let ((subtree-end (save-excursion (org-end-of-subtree t)))) + (cond + ((bh/is-project-p) + subtree-end) + ((org-is-habit-p) + subtree-end) + (t + nil))))) + +(defun bh/skip-non-subprojects () + "Skip trees that are not projects" + (let ((next-headline (save-excursion (outline-next-heading)))) + (if (bh/is-subproject-p) + nil + next-headline))) + +;; CLOCKING ;; +;; Resume clocking task when emacs is restarted +(org-clock-persistence-insinuate) +;; +;; Show lot of clocking history so it's easy to pick items off the C-F11 list +(setq org-clock-history-length 23) +;; Resume clocking task on clock-in if the clock is open +(setq org-clock-in-resume t) +;; Change tasks to NEXT when clocking in +(setq org-clock-in-switch-to-state 'bh/clock-in-to-next) +;; Separate drawers for clocking and logs +(setq org-drawers (quote ("PROPERTIES" "LOGBOOK"))) +;; Save clock data and state changes and notes in the LOGBOOK drawer +(setq org-clock-into-drawer t) +;; Sometimes I change tasks I'm clocking quickly - this removes clocked tasks with 0:00 duration +(setq org-clock-out-remove-zero-time-clocks t) +;; Clock out when moving task to a done state +(setq org-clock-out-when-done t) +;; Save the running clock and all clock history when exiting Emacs, load it on startup +(setq org-clock-persist t) +;; Do not prompt to resume an active clock +(setq org-clock-persist-query-resume nil) +;; Enable auto clock resolution for finding open clocks +(setq org-clock-auto-clock-resolution (quote when-no-clock-is-running)) +;; Include current clocking task in clock reports +(setq org-clock-report-include-clocking-task t) + + +(setq bh/keep-clock-running nil) + +(defun bh/clock-in-to-next (kw) + "Switch a task from TODO to NEXT when clocking in. +Skips capture tasks, projects, and subprojects. +Switch projects and subprojects from NEXT back to TODO" + (when (not (and (boundp 'org-capture-mode) org-capture-mode)) + (cond + ((and (member (org-get-todo-state) (list "TODO")) + (bh/is-task-p)) + "NEXT") + ((and (member (org-get-todo-state) (list "NEXT")) + (bh/is-project-p)) + "TODO")))) + +(defun bh/find-project-task () + "Move point to the parent (project) task if any" + (save-restriction + (widen) + (let ((parent-task (save-excursion (org-back-to-heading 'invisible-ok) (point)))) + (while (org-up-heading-safe) + (when (member (nth 2 (org-heading-components)) org-todo-keywords-1) + (setq parent-task (point)))) + (goto-char parent-task) + parent-task))) + +(defun bh/punch-in (arg) + "Start continuous clocking and set the default task to the +selected task. If no task is selected set the Organization task +as the default task." + (interactive "p") + (setq bh/keep-clock-running t) + (if (equal major-mode 'org-agenda-mode) + ;; + ;; We're in the agenda + ;; + (let* ((marker (org-get-at-bol 'org-hd-marker)) + (tags (org-with-point-at marker (org-get-tags-at)))) + (if (and (eq arg 4) tags) + (org-agenda-clock-in '(16)) + (bh/clock-in-organization-task-as-default))) + ;; + ;; We are not in the agenda + ;; + (save-restriction + (widen) + ; Find the tags on the current task + (if (and (equal major-mode 'org-mode) (not (org-before-first-heading-p)) (eq arg 4)) + (org-clock-in '(16)) + (bh/clock-in-organization-task-as-default))))) + +(defun bh/punch-out () + (interactive) + (setq bh/keep-clock-running nil) + (when (org-clock-is-active) + (org-clock-out)) + (org-agenda-remove-restriction-lock)) + +(defun bh/clock-in-default-task () + (save-excursion + (org-with-point-at org-clock-default-task + (org-clock-in)))) + +(defun bh/clock-in-parent-task () + "Move point to the parent (project) task if any and clock in" + (let ((parent-task)) + (save-excursion + (save-restriction + (widen) + (while (and (not parent-task) (org-up-heading-safe)) + (when (member (nth 2 (org-heading-components)) org-todo-keywords-1) + (setq parent-task (point)))) + (if parent-task + (org-with-point-at parent-task + (org-clock-in)) + (when bh/keep-clock-running + (bh/clock-in-default-task))))))) + +(defvar bh/organization-task-id "eb155a82-92b2-4f25-a3c6-0304591af2f9") + +;; https://stackoverflow.com/a/10091330 +(defun zin/org-agenda-skip-tag (tag &optional others) + "Skip all entries that correspond to TAG. + +If OTHERS is true, skip all entries that do not correspond to TAG." + (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))) + (current-headline (or (and (org-at-heading-p) + (point)) + (save-excursion (org-back-to-heading))))) + (if others + (if (not (member tag (org-get-tags-at current-headline))) + next-headline + nil) + (if (member tag (org-get-tags-at current-headline)) + next-headline + nil)))) + +(defun bh/clock-in-organization-task-as-default () + (interactive) + (org-with-point-at (org-id-find bh/organization-task-id 'marker) + (org-clock-in '(16)))) + +(defun bh/clock-out-maybe () + (when (and bh/keep-clock-running + (not org-clock-clocking-in) + (marker-buffer org-clock-default-task) + (not org-clock-resolving-clocks-due-to-idleness)) + (bh/clock-in-parent-task))) + +(add-hook 'org-clock-out-hook 'bh/clock-out-maybe 'append) + +;; AGENDA VIEW ;; + +;; Do not dim blocked tasks +(setq org-agenda-compact-blocks nil) +(setq org-agenda-dim-blocked-tasks nil) +(setq org-agenda-block-separator 61) + +;; Agenda log mode items to display (closed and state changes by default) +(setq org-agenda-log-mode-items (quote (closed state))) + +; For tag searches ignore tasks with scheduled and deadline dates +(setq org-agenda-tags-todo-honor-ignore-options t) + +(setq org-icalendar-include-body nil) +(setq org-icalendar-include-bbdb-anniversaries t) +(setq org-icalendar-include-todo t) +(setq org-icalendar-use-scheduled '(todo-start event-if-not-todo event-if-todo-not-done)) + +(provide 'rul-org-agenda) diff --git a/.emacs.d/rul-lisp/packages/rul-org-journal.el b/.emacs.d/rul-lisp/packages/rul-org-journal.el new file mode 100644 index 0000000..9d30e00 --- /dev/null +++ b/.emacs.d/rul-lisp/packages/rul-org-journal.el @@ -0,0 +1,11 @@ +(use-package org-journal + :ensure t + :init + ;; Change default prefix key; needs to be set before loading org-journal + (setq org-journal-prefix-key "C-c j ") + (setq org-journal-enable-agenda-integration t) + :config + (require 'rul-config-org) + (setq org-journal-date-format "%A, %d %B %Y")) + +(provide 'rul-org-journal) diff --git a/.emacs.d/rul-lisp/packages/rul-org-notify.el b/.emacs.d/rul-lisp/packages/rul-org-notify.el new file mode 100644 index 0000000..50c35a0 --- /dev/null +++ b/.emacs.d/rul-lisp/packages/rul-org-notify.el @@ -0,0 +1,9 @@ +(use-package org-notify + :ensure t + :config + (org-notify-start) + (org-notify-add 'default + '(:time "-1s" :period "20s" :duration 10 + :actions (-message -ding)) + '(:time "1d" :actions -notify/window + :duration 60))) diff --git a/.emacs.d/rul-lisp/packages/rul-org-roam.el b/.emacs.d/rul-lisp/packages/rul-org-roam.el new file mode 100644 index 0000000..f30e86e --- /dev/null +++ b/.emacs.d/rul-lisp/packages/rul-org-roam.el @@ -0,0 +1,24 @@ +(use-package org-roam + :ensure t + :after rul-org + :init + (setq org-roam-v2-ack t) + :bind (("C-c n l" . org-roam-buffer-toggle) + ("C-c n f" . org-roam-node-find) + ("C-c n i" . org-roam-node-insert)) + :config + (require 'rul-config-org) + (org-roam-setup) + (add-to-list 'display-buffer-alist + '("\\*org-roam\\*" + (display-buffer-in-direction) + (direction . right) + (window-width . 0.33) + (window-height . fit-window-to-buffer))) + (setq org-roam-mode-section-functions + (list #'org-roam-backlinks-section + #'org-roam-reflinks-section + #'org-roam-unlinked-references-section)) + ) + +(provide 'rul-org-roam) diff --git a/.emacs.d/rul-lisp/packages/rul-org.el b/.emacs.d/rul-lisp/packages/rul-org.el new file mode 100644 index 0000000..0727239 --- /dev/null +++ b/.emacs.d/rul-lisp/packages/rul-org.el @@ -0,0 +1,156 @@ +;; Debian packages: elpa-org elpa-org-bullets +;; Elpa packages: org-modern +(require 'org) +(require 'org-capture) +(require 'org-protocol) +(require 'org-habit) +(require 'org-bullets) + +(require 'org-modern) + +(require 'rul-config-org) + +(require 'rul-org-agenda) +(require 'rul-org-journal) +(require 'rul-org-roam) + +(setq org-cycle-separator-lines 0) +(setq org-startup-indented t) +(setq org-hide-leading-stars nil) + +(add-hook 'org-mode-hook 'turn-off-auto-fill) +(add-hook 'org-mode-hook 'visual-line-mode) + +(setq org-startup-indented t + org-bullets-bullet-list '(" ") ;; no bullets, needs org-bullets package + org-pretty-entities nil + org-hide-emphasis-markers t + ;; show actually italicized text instead of /italicized text/ + org-fontify-whole-heading-line t + org-fontify-done-headline t + org-fontify-quote-and-verse-blocks t) + +;; ORG BINDINGS ;; +(global-set-key (kbd "C-c l") #'org-store-link) +(global-set-key (kbd "C-c c") #'org-capture) +(global-set-key (kbd "C-c s") #'org-schedule) + +;; ORG STATES ;; +(setq org-todo-keywords + (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)") + (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" "MEETING")))) + +(setq org-use-fast-todo-selection t) + +(setq org-todo-state-tags-triggers + (quote (("CANCELLED" ("CANCELLED" . t)) + ("WAITING" ("WAITING" . t)) + ("HOLD" ("WAITING") ("HOLD" . t)) + (done ("WAITING") ("HOLD")) + ("TODO" ("WAITING") ("CANCELLED") ("HOLD")) + ("NEXT" ("WAITING") ("CANCELLED") ("HOLD")) + ("DONE" ("WAITING") ("CANCELLED") ("HOLD"))))) + +(setq org-enforce-todo-dependencies t) +(setq org-log-done (quote time)) +(setq org-log-redeadline (quote time)) +(setq org-log-reschedule (quote time)) + +;; CAPTURE ;; +(setq org-default-notes-file org-refile-path) +(setq org-capture-templates + (quote + ( + ("w" "Todo" entry (file+headline org-refile-path "Tasks") + "* TODO " + :empty-lines 1) + ("m" + "Capture incoming email" + entry + (file+headline org-refile-path "Incoming") + "* TODO Re: %:description\n\n Source: %u, %a\n" + :empty-lines 1) + ))) + +(add-hook 'org-capture-mode-hook 'delete-other-windows) +(setq org-protocol-flag nil) + +(defadvice org-capture-finalize (after delete-frame-at-end activate) + "Delete frame at remember finalization" + (progn (if org-protocol-flag (delete-frame)) + (setq org-protocol-flag nil))) + +(defadvice org-capture-kill (after delete-frame-at-end activate) + "Delete frame at remember abort" + (progn (if org-protocol-flag (delete-frame)) + (setq org-protocol-flag nil))) + +(defadvice org-protocol-capture (before set-org-protocol-flag activate) + (setq org-protocol-flag t)) + + +;; REFILE ;; + +; Targets include this file and any file contributing to the agenda - up to 3 levels deep +(setq org-refile-targets + '((nil :maxlevel . 3) + (org-agenda-files :maxlevel . 3))) + +; Targets complete directly with IDO +(setq org-outline-path-complete-in-steps nil) + +; Allow refile to create parent tasks with confirmation +(setq org-refile-allow-creating-parent-nodes (quote confirm)) + + + +;; ORG REPORTS ;; +; Set default column view headings: Task Effort Clock_Summary +(setq org-columns-default-format "%80ITEM(Task) %10Effort(Effort){:} %10CLOCKSUM") + +(defun my-org-clocktable-indent-string (level) + (if (= level 1) + "" + (let ((str "^")) + (while (> level 2) + (setq level (1- level) + str (concat str "--"))) + (concat str "-> ")))) + +(advice-add 'org-clocktable-indent-string :override #'my-org-clocktable-indent-string) + +(setq org-clock-clocktable-default-properties '(:maxlevel 4 :scope file :formula %)) + +; global Effort estimate values +; global STYLE property values for completion +(setq org-global-properties (quote (("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00") + ("STYLE_ALL" . "habit")))) + +;; TAGS ;; +; Tags with fast selection keys +(setq org-tag-alist (quote ((:startgroup) + ("@errand" . ?e) + ("@office" . ?o) + ("@home" . ?H) + (:endgroup) + ("WAITING" . ?w) + ("HOLD" . ?h) + ("PERSONAL" . ?P) + ("WORK" . ?W) + ("ORG" . ?O) + ("NOTE" . ?n) + ("CANCELLED" . ?c) + ("FLAGGED" . ??)))) + +(setq org-stuck-projects + '("+LEVEL=2+PROJECT/-MAYBE-DONE" ("NEXT") ("@shop") + "\\<IGNORE\\>")) + +; Allow setting single tags without the menu +(setq org-fast-tag-selection-single-key (quote expert)) + +;; org-modern +(add-hook 'org-mode-hook 'org-modern-mode) +(add-hook 'org-agenda-finalize-hook #'org-modern-agenda) + +(provide 'rul-org) |