commit 87219d8702b9a1dab9d137ddcab1ac18a20d2236
parent 86032cc79804fd516655992b611f6a721a0085e5
Author: Alex Balgavy <alex@balgavy.eu>
Date: Wed, 16 Mar 2022 19:13:02 +0100
emacs: sync org agenda via caldav (and some reorganising)
Diffstat:
M | emacs/config.org | | | 1555 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
1 file changed, 801 insertions(+), 754 deletions(-)
diff --git a/emacs/config.org b/emacs/config.org
@@ -191,40 +191,17 @@ This lets me jump to any position in Emacs rather quickly, sometimes it's useful
(("C-:" . 'avy-goto-char-timer)))
#+end_src
-** org
-In org mode, I want to use bullets instead of stars, so I also install ~org-bullets~.
-Furthermore, tags were getting cut off, so I manually set the best column to display them.
-
-#+begin_src emacs-lisp
- (defun za/settings-org-mode ()
- "My settings for org mode"
- (org-bullets-mode 1)
- (za/toggle-wrap t)
- (org-indent-mode)
- (setq org-tags-column (- 10 (window-total-width)))
- ;; Realign tags
- (org-set-tags-command '(4)))
-
- (defun za/settings-org-agenda-mode ()
- "My settings for org agenda mode"
- (setq org-agenda-tags-column (- 10 (window-total-width))))
-#+end_src
+** Org
+*** Installation
+Install Org and require additional components that I use.
#+begin_src emacs-lisp
(use-package org
:config
- (unless (package-installed-p 'org-bullets)
- (package-refresh-contents)
- (package-install 'org-bullets))
- (use-package org-bullets)
(require 'org-tempo)
(require 'org-habit)
(require 'org-agenda)
- :init
- (add-hook 'org-mode-hook #'za/settings-org-mode)
- (add-hook 'org-agenda-mode-hook #'za/settings-org-agenda-mode)
-
:bind
(("C-c a" . org-agenda)
("C-c n" . org-capture)
@@ -232,1108 +209,1178 @@ Furthermore, tags were getting cut off, so I manually set the best column to dis
("C-c l" . org-store-link)))
#+end_src
-To be able to link to emails via notmuch, I use ol-notmuch:
+*** Nicer bullets
+In org mode, I want to use bullets instead of stars, so I also install ~org-bullets~.
#+begin_src emacs-lisp
- (use-package ol-notmuch :quelpa)
+ (use-package org-bullets)
#+end_src
-** calfw
-Basically provides a way to show the org agenda as a standard GUI calendar app would.
+*** Org mode settings
+Furthermore, tags were getting cut off, so I manually set the best column to display them.
#+begin_src emacs-lisp
- (use-package calfw
- :config
- (use-package calfw-org)
- (setq cfw:org-overwrite-default-keybinding t)
- (setq calendar-week-start-day 1))
+ (defun za/settings-org-mode ()
+ "My settings for org mode"
+ (org-bullets-mode 1)
+ (za/toggle-wrap t)
+ (org-indent-mode)
+ (setq org-tags-column (- 10 (window-total-width)))
+ ;; Realign tags
+ (org-set-tags-command '(4)))
+
+ (add-hook 'org-mode-hook #'za/settings-org-mode)
#+end_src
-** lean-mode
-Specifically for the Lean prover.
-I also install company-lean and helm-lean, which are suggested on the [[https://github.com/leanprover/lean-mode][Github page]].
-Then I map company-complete only for lean-mode.
+*** Enable linking to email via notmuch
+To be able to link to emails via notmuch, I use ol-notmuch:
#+begin_src emacs-lisp
- (use-package lean-mode
- :config
- (use-package company-lean)
- :hook
- (lean-mode . (lambda () (define-key lean-mode-map (kbd "S-SPC") #'company-complete))))
+ (use-package ol-notmuch :quelpa)
#+end_src
-** magit
+*** Agenda & GTD
+**** Agenda mode settings
+Fix tag display by dynamically calculating the column.
+
#+begin_src emacs-lisp
- (use-package magit)
-#+end_src
+ (defun za/settings-org-agenda-mode ()
+ "My settings for org agenda mode"
+ (setq org-agenda-tags-column (- 10 (window-total-width))))
-** vterm
-Emacs has a bunch of built-in terminal emulators.
-And they all suck.
-(OK not really, eshell is alright, but not for interactive terminal programs like newsboat/neomutt)
+ (add-hook 'org-agenda-mode-hook #'za/settings-org-agenda-mode)
+#+end_src
-Also use emacsclient inside vterm as an editor, because that'll open documents in the existing Emacs session.
-And I'm not gonna be a heretic and open Vim inside of Emacs.
+**** Set file locations
+Which files should be included in the agenda (I have to use ~list~ to evaluate the variables, because org-agenda-files expects strings):
#+begin_src emacs-lisp
- (use-package vterm
- :hook
- (vterm-mode . (lambda () (unless server-process (server-start)))))
+ (setq org-agenda-files (list za/org-life-main
+ za/org-life-inbox
+ za/org-life-tickler))
#+end_src
-I'll bind a key to start a vterm or switch to the running vterm:
+Convenience functions to make opening the main file faster:
#+begin_src emacs-lisp
- (defun switch-to-vterm () "Switch to a running vterm, or start one and switch to it."
- (interactive)
- (if (get-buffer vterm-buffer-name)
- (switch-to-buffer vterm-buffer-name)
- (vterm)))
- (global-set-key (kbd "C-c t") 'switch-to-vterm)
+ (defun gtd () "GTD: main file" (interactive) (find-file za/org-life-main))
+ (defun gtd-inbox () "GTD: inbox" (interactive) (find-file za/org-life-inbox))
+ (defun gtd-archive () "GTD: archive" (interactive) (find-file za/org-life-archive))
+ (defun gtd-someday () "GTD: someday" (interactive) (find-file za/org-life-someday))
+ (defun gtd-reference () "GTD: reference" (interactive) (find-file za/org-life-reference))
+ (defun gtd-tickler () "GTD: tickler" (interactive) (find-file za/org-life-tickler))
#+end_src
-** sr-speedbar
-Make speed bar show in the current frame.
+Bind keys to those functions:
#+begin_src emacs-lisp
- (use-package sr-speedbar
- :config
- (setq sr-speedbar-right-side nil)
- (define-key speedbar-mode-map (kbd "q") 'sr-speedbar-close))
-
+ (global-set-key (kbd "C-c g i") 'gtd-inbox)
+ (global-set-key (kbd "C-c g g") 'gtd)
+ (global-set-key (kbd "C-c g a") 'gtd-archive)
+ (global-set-key (kbd "C-c g s") 'gtd-someday)
+ (global-set-key (kbd "C-c g r") 'gtd-reference)
+ (global-set-key (kbd "C-c g t") 'gtd-tickler)
#+end_src
-
-Jump to speedbar. sr-speedbar-exist-p can be void, so I check if it's bound first.
-If it's not bound, or if it's false, first open the speedbar.
-Then, select it.
+**** Refiling & archiving
+Where I want to be able to move subtrees (doesn't include inbox because I never refile to that, and the archive has its own keybining):
#+begin_src emacs-lisp
- (global-set-key (kbd "C-c F") (lambda () (interactive)
- (if (or (not (boundp 'sr-speedbar-exist-p))
- (not (sr-speedbar-exist-p)))
- (sr-speedbar-open))
- (sr-speedbar-select-window)))
+ (setq org-refile-targets `((,za/org-life-main :maxlevel . 3)
+ (,za/org-life-someday :level . 1)
+ (,za/org-life-tickler :maxlevel . 2)
+ (,za/org-life-reference :maxlevel . 2)))
#+end_src
-** expand-region
-Expand the selected region semantically.
+I want to archive to a specific file, in a date tree:
#+begin_src emacs-lisp
- (use-package expand-region
- :bind ("C-=" . er/expand-region))
+ (setq org-archive-location (concat za/org-life-archive "::datetree/"))
#+end_src
-** flycheck
-Install flycheck, and enable it by default in certain major modes:
+Include the destination file as an element in the path to a heading, and to use the full paths as completion targets rather than just the heading text itself:
#+begin_src emacs-lisp
- (use-package flycheck
- :hook (sh-mode . flycheck-mode))
+ (setq org-refile-use-outline-path 'file)
#+end_src
-** anki-editor
-Some extra keybindings that are not set up by default.
-anki-editor doesn't provide a keymap so I have to set one up here:
+Tell Org that I don’t want to complete in steps; I want Org to generate all of the possible completions and present them at once (necessary for Helm/Ivy):
#+begin_src emacs-lisp
- (use-package anki-editor
- :config
- (defvar anki-editor-mode-map (make-sparse-keymap))
- (add-to-list 'minor-mode-map-alist (cons 'anki-editor-mode
- anki-editor-mode-map))
+ (setq org-outline-path-complete-in-steps nil)
+#+end_src
- (setq anki-editor-use-math-jax t)
+Allow me to tack new heading names onto the end of my outline path, and if I am asking to create new ones, make me confirm it:
- :hook
- (anki-editor-mode . (lambda ()
- (define-key anki-editor-mode-map (kbd "C-c t") #'org-property-next-allowed-value)
- (define-key anki-editor-mode-map (kbd "C-c i") #'anki-editor-insert-note)
- (define-key anki-editor-mode-map (kbd "C-c p") #'anki-editor-push-notes)
- (define-key anki-editor-mode-map (kbd "C-c c") #'anki-editor-cloze-dwim))))
+#+begin_src emacs-lisp
+ (setq org-refile-allow-creating-parent-nodes 'confirm)
#+end_src
-** rainbow-mode
-'rainbow-mode' lets you visualise hex colors:
+**** Quick capture
+Quick capture lets me send something to my inbox very quickly, without thinking about where it should go.
+The inbox is processed later.
+
+Templates for quick capture:
#+begin_src emacs-lisp
- (use-package rainbow-mode
- :hook (emacs-lisp-mode . rainbow-mode))
+ (setq org-capture-templates `(("t" "Todo [inbox]" entry
+ (file ,za/org-life-inbox)
+ "* TODO %i%?")
+
+ ("s" "Save for read/watch/listen" entry
+ (file+headline ,za/org-life-tickler "Read/watch/listen")
+ "* TODO %?[[%^{link}][%^{description}]] %^G")))
#+end_src
-** pdf-tools
-A better replacement for DocView:
+**** Todo & custom agenda views
+Todo keywords based on the GTD system (pipe separates incomplete from complete).
+Apart from the logging-on-done configured [[*Logging][below]], I also want to log a note & timestamp when I start waiting on something.
+In ~org-todo-keywords~, ~@~ means note+timestamp, ~!~ means timestamp, ~@/!~ means note+timestamp on state entry and timestamp on leave.
#+begin_src emacs-lisp
- (use-package pdf-tools
- :config
- (setq-default pdf-annot-default-annotation-properties '((t
- (label . "Alex Balgavy"))
- (text
- (icon . "Note")
- (color . "#0088ff"))
- (highlight
- (color . "yellow"))
- (squiggly
- (color . "orange"))
- (strike-out
- (color . "red"))
- (underline
- (color . "blue"))))
- :hook
- (pdf-annot-list-mode . pdf-annot-list-follow-minor-mode)
- (pdf-annot-edit-contents-minor-mode . org-mode)
- (pdf-view-mode . (lambda () (display-line-numbers-mode 0)))
- (pdf-view-mode . (lambda () (define-key pdf-isearch-minor-mode-map (kbd "C-s") #'isearch-forward))))
- (pdf-tools-install)
+ (setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "WAITING(w@)" "SOMEDAY(s)" "|" "DONE(d)" "CANCELLED(c)"))
+ org-todo-keyword-faces '(("TODO" . org-todo)
+ ("NEXT" . org-todo)
+ ("WAITING" . org-todo)
+ ("SOMEDAY" . org-todo)
+ ("DONE" . org-done)
+ ("CANCELLED" . org-done)))
#+end_src
-** virtualenvwrapper
-Like virtualenvwrapper.sh, but for Emacs.
+I decided that projects will not be TODO items, but their progress will be tracked with a progress cookie ([x/y]). This function converts an item to a project: it adds a PROJECT tag, sets the progress indicator to count all checkboxes in sub-items (only TODO items), and removes any existing TODO keywords. Finally, PROJECT tags shouldn't be inherited (i.e. subtasks shouldn't be marked as projects).
#+begin_src emacs-lisp
- (use-package virtualenvwrapper
- :config
- (venv-initialize-interactive-shells)
- (venv-initialize-eshell)
- (setq venv-location "~/.config/virtualenvs"))
+ (defun za/mark-as-project ()
+ "This function makes sure that the current heading has
+ (1) the tag PROJECT
+ (2) the property COOKIE_DATA set to \"todo recursive\"
+ (3) a leading progress indicator"
+ (interactive)
+ (org-set-property "TODO" "")
+ (org-toggle-tag "PROJECT" 'on)
+ (org-set-property "COOKIE_DATA" "todo recursive")
+ (org-back-to-heading t)
+ (forward-whitespace 1)
+ (insert "[/] ")
+ (org-update-statistics-cookies nil))
#+end_src
-** org-ref
+Only the top-level project headlines should be tagged as projects, so disable inheritance of that tag:
+
#+begin_src emacs-lisp
- (use-package org-ref)
+ (setq org-tags-exclude-from-inheritance '("PROJECT"))
#+end_src
-** org-noter
+Define a function to skip items if they're part of a project (i.e. one of their parents has a "PROJECT" tag).
+The problem is, the "PROJECT" tag isn't inherited. So, we temporarily disable excluding from inheritance, just for the ~org-get-tags~ call. Then check if "PROJECT" is one of the tags.
+
#+begin_src emacs-lisp
- (use-package org-noter)
+ (defun za/skip-if-in-project ()
+ "Skip items that are part of a project"
+ (let ((subtree-end (save-excursion (org-end-of-subtree t)))
+ (item-tags (let ((org-tags-exclude-from-inheritance nil)) (org-get-tags))))
+ (if (member "PROJECT" item-tags)
+ subtree-end
+ nil)))
+
#+end_src
-** hl-todo
-I want to highlight TODO keywords in comments:
+Also, define a function to skip tasks (trees) that are not habits (i.e. don't have the STYLE property ~habit~):
#+begin_src emacs-lisp
- (use-package hl-todo
- :custom-face
- (hl-todo ((t (:inherit hl-todo :underline t))))
- :config
- (setq hl-todo-keyword-faces
- '(("TODO" . "#ff7060")
- ("FIXME" . "#caa000")))
- (global-hl-todo-mode t))
+ (defun za/skip-unless-habit ()
+ "Skip trees that are not habits"
+ (let ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (if (string= (org-entry-get nil "STYLE") "habit")
+ nil
+ subtree-end)))
#+end_src
-** undo-tree
-Sometimes it's better to look at undo history as a tree:
+
+And one to skip tasks that /are/ habits:
#+begin_src emacs-lisp
- (use-package undo-tree
- :config
- (global-undo-tree-mode))
-#+end_src
-** ledger
-#+begin_src emacs-lisp
- (use-package ledger-mode
- :mode ("\\.ledger\\'")
- :config
- (setq ledger-clear-whole-transactions t
- ledger-reconcile-default-commodity "eur"))
+ (defun za/skip-if-habit ()
+ "Skip trees that are not habits"
+ (let ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (if (string= (org-entry-get nil "STYLE") "habit")
+ subtree-end
+ nil)))
#+end_src
-Custom reports:
+
+And another function, to skip tasks that are blocked:
#+begin_src emacs-lisp
- (custom-set-variables
- '(ledger-reports
- '(("budget-last-month" "ledger -f %(ledger-file) --period \"last month\" budget ^expenses -X eur")
- ("budget-this-month" "ledger -f %(ledger-file) --period \"this month\" budget ^expenses -X eur")
- ("expenses-this-month-vs-budget" "ledger -f %(ledger-file) --period \"this month\" --period-sort \"(amount)\" bal ^expenses -X eur --budget")
- ("expenses-last-month-vs-budget" "ledger -f %(ledger-file) --period \"last month\" --period-sort \"(amount)\" bal ^expenses -X eur --budget")
- ("expenses-vs-income-last-month" "ledger -f %(ledger-file) --period \"last month\" --period-sort \"(amount)\" bal ^expenses ^income -X eur")
- ("expenses-last-month" "ledger -f %(ledger-file) --period \"last month\" --period-sort \"(amount)\" bal ^expenses -X eur")
- ("expenses-this-month" "ledger -f %(ledger-file) --period \"this month\" --period-sort \"(amount)\" bal ^expenses -X eur")
- ("expenses-vs-income-this-month" "ledger -f %(ledger-file) --period \"this month\" --period-sort \"(amount)\" bal ^income ^expenses -X eur")
- ("expenses-this-month" "ledger -f %(ledger-file) --period \"this month\" --period-sort \"(amount)\" bal ^income ^expenses -X eur")
- ("bal-assets-czk" "ledger [[ledger-mode-flags]] -f %(ledger-file) bal Assets Liabilities -X czk")
- ("bal-assets" "ledger [[ledger-mode-flags]] -f %(ledger-file) bal Assets Liabilities")
- ("bal" "ledger [[ledger-mode-flags]] -f %(ledger-file) bal -B")
- ("bal-assets-eur" "ledger [[ledger-mode-flags]] -f %(ledger-file) bal Assets Liabilities -X eur")
- ("reg" "%(binary) -f %(ledger-file) reg")
- ("payee" "%(binary) -f %(ledger-file) reg @%(payee)")
- ("account" "%(binary) -f %(ledger-file) reg %(account)")))
- )
+ (defun za/skip-if-blocked ()
+ "Skip trees that are blocked by previous tasks"
+ (let ((subtree-end (save-excursion (org-end-of-subtree t))))
+ (if (org-entry-blocked-p)
+ subtree-end
+ nil)))
#+end_src
-** osm
-#+begin_src emacs-lisp
- (use-package osm
- :bind (("C-c M h" . osm-home)
- ("C-c M s" . osm-search)
- ("C-c M v" . osm-server)
- ("C-c M t" . osm-goto)
- ("C-c M x" . osm-gpx-show)
- ("C-c M j" . osm-bookmark-jump))
- :custom
- ;; Take a look at the customization group `osm' for more options.
- (osm-server 'default) ;; Configure the tile server
- (osm-copyright nil) ;; Display the copyright information
+Create custom agenda view based on those keywords.
+Agenda views are made up of blocks, appearing in the order that you declare them.
+The first two strings are what shows up in the agenda dispatcher (the key to press and the description).
- :init
- ;; Load Org link support
- (with-eval-after-load 'org
- (require 'osm-ol)))
-#+end_src
-* Interface
-** Start debugger on error
#+begin_src emacs-lisp
- ;; (toggle-debug-on-error t)
-#+end_src
+ (setq org-agenda-custom-commands
+ '(("n" "Next actions"
+ ((todo "NEXT" ((org-agenda-overriding-header "Next actions:")))))
+ ("W" "Waiting"
+ ((todo "WAITING" ((org-agenda-overriding-header "Waiting:")))))
+ ("S" . "Saved for later...")
+ ("Sw" "Saved to watch"
+ ((tags-todo "WATCH" ((org-agenda-overriding-header "To watch:")))))
+ ("Sr" "Saved to read"
+ ((tags-todo "READ" ((org-agenda-overriding-header "To read:")))))
+ ("Sl" "Saved to listen"
+ ((tags-todo "LISTEN" ((org-agenda-overriding-header "To listen:")))))
-** Messages
-Hide some messages I don't need, and add a list of recent files.
+ ("a" . "Agenda with schedule only...")
+ ("aw" "This week"
+ ((agenda "" ((org-agenda-span 'week)))))
+ ("ad" "Today"
+ ((agenda "" ((org-agenda-span 'day)))))
+ ("at" "Tomorrow"
+ ((agenda "" ((org-agenda-span 'day)
+ (org-agenda-start-day "+1d")))))
-#+begin_src emacs-lisp
- (recentf-mode)
- (setq inhibit-startup-message t
- initial-major-mode #'org-mode
- initial-scratch-message
- (concat "Welcome to Emacs\n\n"
- "Recent:\n"
- (mapconcat
- (lambda (x) (format "- [[%s]]" x)) recentf-list "\n")
- "\n\nELISP Evaluation area:\n#+begin_src emacs-lisp\n\n#+end_src"))
+ ("w" "Week Agenda + Next Actions"
+ ((agenda "" ((org-agenda-overriding-header "Week agenda:")))
+ (todo "NEXT" ((org-agenda-overriding-header "Next actions:")))))
-#+end_src
+ ("o" "Month agenda"
+ ((agenda "" ((org-agenda-overriding-header "Month agenda:")
+ (org-agenda-span 'month)))))
-** Appearance
-*** Cursor line
-Highlight the current line:
+ ("d" "Day Agenda + Next Actions + Habits"
+ ((agenda "" ((org-agenda-overriding-header "Day:")
+ (org-agenda-span 'day)
+ (org-habit-show-habits nil)))
+ (todo "NEXT" ((org-agenda-overriding-header "Next actions:")))
+ (agenda "" ((org-agenda-overriding-header "Habits:")
+ (org-agenda-span 'day)
+ (org-agenda-use-time-grid nil)
+ (org-agenda-skip-function 'za/skip-unless-habit)
+ (org-habit-show-habits t) (org-habit-show-habits-only-for-today nil)
+ (org-habit-show-all-today t)))
+ (todo "WAITING" ((org-agenda-overriding-header "Waiting:")))))
-#+begin_src emacs-lisp
- (global-hl-line-mode)
-#+end_src
-*** Matching parentheses
-Don't add a delay to show matching parenthesis.
-Must come before show-paren-mode enable.
+ ("p" "Projects"
+ ((tags "PROJECT" ((org-agenda-overriding-header "Projects:")
+ (org-agenda-prefix-format '((tags . " %i %-22(let ((deadline (org-entry-get nil \"DEADLINE\"))) (if deadline deadline \"\"))")))
+ (org-agenda-sorting-strategy '((tags deadline-up alpha-down)))))))
-#+begin_src emacs-lisp
- (setq show-paren-delay 0)
+ ("f" "Finished tasks that aren't in a project"
+ ((tags "TODO=\"DONE\"|TODO=\"CANCELLED\"" ((org-agenda-overriding-header "Finished tasks:")
+ (org-agenda-skip-function 'za/skip-if-in-project)))))
+
+ ;; Useful thread for opening calfw: https://github.com/kiwanami/emacs-calfw/issues/18
+ ("c" "Calendar view" (lambda (&rest _)
+ (interactive)
+ (let ((org-agenda-skip-function 'za/skip-if-habit))
+ (cfw:open-org-calendar))))))
#+end_src
-Show matching parentheses:
+In calfw, I don't want to show habits:
#+begin_src emacs-lisp
- (show-paren-mode t)
+ (add-hook 'cfw:calendar-mode-hook (setq-local org-agenda-skip-function 'za/skip-if-habit))
#+end_src
-*** Cursor
-The default box cursor isn't really accurate, because the cursor is actually between letters, not on a letter.
-So, I want a bar instead of a box:
+**** Logging for tasks
+I want to log into the LOGBOOK drawer (useful when I want to take quick notes):
#+begin_src emacs-lisp
- (setq-default cursor-type '(bar . 4)
- cursor-in-non-selected-windows 'hollow)
+ (setq org-log-into-drawer "LOGBOOK")
#+end_src
-(I use ~setq-default~ here because cursor-type is automatically buffer-local when it's set)
-
-*** Line numbers
-Relative line numbers:
+I also want to log when I finish a task (useful for archiving).
+Furthermore, when I'm done, I want to add a note (any important
+workarounds/tips). And when I reschedule, I want to know the reason.
+I can disable logging on state change for a specific task by adding ~:LOGGING: nil~ to the ~:PROPERTIES:~ drawer.
#+begin_src emacs-lisp
- (setq display-line-numbers-type 'relative)
- (global-display-line-numbers-mode)
+ (setq org-log-done 'note
+ org-log-reschedule 'note)
#+end_src
-Function to hide them:
+I want to hide drawers on startup. This variable has options:
+- 'overview': Top-level headlines only.
+- 'content': All headlines.
+- 'showall': No folding on any entry.
+- 'show2levels: Headline levels 1-2.
+- 'show3levels: Headline levels 1-3.
+- 'show4levels: Headline levels 1-4.
+- 'show5levels: Headline levels 1-5.
+- 'showeverything: Show even drawer contents.
#+begin_src emacs-lisp
- (defun za/hide-line-numbers ()
- "Hide line numbers"
- (display-line-numbers-mode 0))
+ (setq org-startup-folded 'content)
#+end_src
-Don't display them in specific modes. For each of the modes in
-'mode-hooks', add a function to hide line numbers when the mode
-activates (which triggers the 'mode'-hook).
+
+**** Task ordering
+Some tasks should be ordered, i.e. they should be done in steps.
+Those have the ~:ORDERED: t~ setting in ~:PROPERTIES:~, and it should be enforced:
#+begin_src emacs-lisp
- (let ((mode-hooks '(doc-view-mode-hook vterm-mode-hook mpc-status-mode-hook mpc-tagbrowser-mode-hook)))
- (mapc
- (lambda (mode-name)
- (add-hook mode-name #'za/hide-line-numbers))
- mode-hooks))
+ (setq org-enforce-todo-dependencies t)
#+end_src
-*** Modeline
-I want to show the time and date in the modeline:
+
+Furthermore, tasks that are ordered and can't be done yet because of previous steps should be dimmed in the agenda:
#+begin_src emacs-lisp
- (setq display-time-day-and-date t ; also the date
- display-time-default-load-average nil ; don't show load average
- display-time-format "%I:%M%p %e %b (%a)") ; "HR:MIN(AM/PM) day-of-month Month (Day)"
- (display-time-mode 1) ; enable time mode
+ (setq org-agenda-dim-blocked-tasks t)
#+end_src
-And to set the modeline format:
+I might also want to set ~org-enforce-todo-checkbox-dependencies~, but not convinced on that one yet.
+
+**** Time tracking & effort
+Time tracking should be done in its own drawer:
#+begin_src emacs-lisp
- (setq-default mode-line-format '("%e" mode-line-front-space mode-line-mule-info mode-line-client mode-line-modified mode-line-remote mode-line-frame-identification mode-line-buffer-identification " " mode-line-position
- (vc-mode vc-mode)
- " " mode-line-modes mode-line-misc-info mode-line-end-spaces))
+ (setq org-clock-into-drawer "CLOCK")
#+end_src
-I want to hide certain modes from the modeline, they're always on:
+And to customize how clock tables work:
#+begin_src emacs-lisp
- (use-package diminish
- :config
- (let ((modes-to-hide '(ivy-mode counsel-mode which-key-mode hl-todo-mode undo-tree-mode ivy-posframe-mode)))
- (mapc (lambda (mode-name) (diminish mode-name)) modes-to-hide))
- (diminish 'view-mode " 👓"))
+ (setq org-clocktable-defaults '(:lang "en" :scope agenda-with-archives :wstart 1 :mstart 1 :compact t :maxlevel nil))
+ (setq org-agenda-clockreport-parameter-plist '(:link t :maxlevel nil))
#+end_src
-*** Tab bar
-Only show tab bar if there's more than 1 tab:
+
+I want to set effort in hours:minutes:
#+begin_src emacs-lisp
- (setq tab-bar-show 1)
+ (add-to-list 'org-global-properties '("Effort_ALL" . "0:05 0:10 0:15 0:20 0:30 0:45 1:00 1:30 2:00 4:00 6:00 8:00"))
#+end_src
-** Buffer displaying
+I want column view to look like this:
-So, this is a bit hard to grok. But basically the alist contains a
-regular expression to match a buffer name, then a list of functions to
-use in order for displaying the list, and then options for those functions (each of which is an alist).
+| To do | Task | Tags | Sum of time elapsed | Sum of time estimated (effort) |
+|--------------+-----------+------+---------------------+--------------------------------|
+| todo keyword | task name | tags | sum of clock | sum of estimated time |
+| ... | ... | ... | ... | ... |
#+begin_src emacs-lisp
- (setq
- ;; Maximum number of side-windows to create on (left top right bottom)
- window-sides-slots '(0 ;; left
- 1 ;; top
- 3 ;; right
- 1 ) ;; bottom
+ (setq org-columns-default-format "%7TODO (To Do) %32ITEM(Task) %TAGS(Tags) %11CLOCKSUM_T(Clock) %8Effort(Effort){:}")
+#+end_src
- display-buffer-alist '(
- ;; Right side
- ("\\*Help\\*"
- (display-buffer-reuse-window display-buffer-in-side-window)
- (side . right)
- (slot . -1)
- (inhibit-same-window . t))
- ("\\*Async Shell Command\\*"
- (display-buffer-reuse-window display-buffer-in-side-window)
- (side . right)
- (slot . 0)
- (inhibit-same-window . t))
- ("magit-process: .*"
- (display-buffer-reuse-window display-buffer-in-side-window)
- (side . right)
- (slot . 0)
- (inhibit-same-window . t))
+Fix column alignment in agenda. Unfortunately that means I have to
+decrease the size of the date-today font. But I don't have any other
+solution atm.
- ;; Top side
- ("\\*Info\\*"
- (display-buffer-reuse-window display-buffer-in-side-window)
- (side . top)
- (slot . 0))
- ("\\*Man .*\\*"
- (display-buffer-reuse-window display-buffer-in-side-window)
- (side . top)
- (slot . 0))
+#+begin_src emacs-lisp
+ (set-face-attribute 'org-column nil
+ :height (face-attribute 'default :height)
+ :family (face-attribute 'default :family))
+ (set-face-attribute 'org-agenda-date-today nil
+ :height (face-attribute 'default :height))
+#+end_src
- ;; Bottom
- ("\\*Flycheck errors\\*"
- (display-buffer-reuse-window display-buffer-in-side-window)
- (side . bottom)
- (slot . 0))))
+**** Calculate time since timestamp
+#+begin_src emacs-lisp
+ (defun za/org-time-since ()
+ "Print the amount of time between the timestamp at point and the current date and time."
+ (interactive)
+ (unless (org-at-timestamp-p 'lax)
+ (user-error "Not at timestamp"))
+
+ (when (org-at-timestamp-p 'lax)
+ (let ((timestamp (match-string 0)))
+ (with-temp-buffer
+ (insert timestamp
+ "--"
+ (org-time-stamp '(16)))
+ (org-evaluate-time-range)))))
#+end_src
-And a way to toggle those side windows:
+*** Custom functions
+**** Get number of headlines in a file
+#+begin_src emacs-lisp
+ (defun za/org-count-headlines-in-file (level filename)
+ "Count number of level LEVEL headlines in FILENAME. If LEVEL is 0, count all."
+ (let ((headline-str (cond ((zerop level) "^\*+")
+ (t (format "^%s " (apply 'concat (make-list level "\\*")))))))
+ (save-mark-and-excursion
+ (with-temp-buffer
+ (insert-file-contents filename)
+ (count-matches headline-str (point-min) (point-max))))))
+#+end_src
+**** Yank URL
#+begin_src emacs-lisp
- (global-set-key (kbd "C-c w") (lambda () (interactive) (window-toggle-side-windows)))
+ (defun org-yank-link-url ()
+ (interactive)
+ (kill-new (org-element-property :raw-link (org-element-context))))
+
+ (define-key org-mode-map (kbd "C-c M-y") 'org-yank-link-url)
#+end_src
-* Emacs file locations
-** Auto-Save files
-By default, auto-save files ("#file#") are placed in the same directory as the file itself.
-I want to put this all in some unified place:
+*** Tempo expansions
#+begin_src emacs-lisp
- (let ((saves-directory "~/.local/share/emacs/saves/"))
- (unless (file-directory-p saves-directory)
- (make-directory saves-directory))
- (setq auto-save-file-name-transforms
- `((".*" ,saves-directory t))))
+ (add-to-list 'org-structure-template-alist '("se" . "src emacs-lisp"))
+ (add-to-list 'org-structure-template-alist '("sb" . "src bibtex"))
+ (add-to-list 'org-structure-template-alist '("ss" . "src sh"))
#+end_src
-** Backup files
-By default, backup files (those with a tilde) are saved in the same directory as the currently edited file.
-This setting puts them in ~/.local/share/emacs/backups.
+*** Catch invisible edits
+Sometimes when text is folded away, I might accidentally edit text inside of it.
+This option prevents that.
+I wanted to do 'smart', but that has a 'fixme' so it might change in the future...
+Instead, show what's being edited, but don't perform the edit.
#+begin_src emacs-lisp
- (let ((backups-directory "~/.local/share/emacs/backups"))
- (unless (file-directory-p backups-directory)
- (make-directory backups-directory))
- (setq backup-directory-alist `(("." . ,backups-directory)))
- (setq backup-by-copying t))
+ (setq org-catch-invisible-edits 'show-and-error)
#+end_src
-** Custom settings file
-Both commands are necessary.
-First one tells Emacs where to save customizations.
-The second one actually loads them.
+*** Notification
+macOS doesn't have dbus. So I use terminal-notifier for functions like org-notify:
#+begin_src emacs-lisp
- (setq custom-file (expand-file-name (concat user-emacs-directory "custom.el")))
- (load custom-file)
+ (if (and (eq system-type 'darwin)
+ (executable-find "terminal-notifier"))
+ (setq org-show-notification-handler
+ (lambda (str) (start-process "terminal-notifier" nil (executable-find "terminal-notifier")
+ "-title" "Timer done"
+ "-message" str
+ "-group" "org.gnu.Emacs"
+ "-sender" "org.gnu.Emacs"))))
#+end_src
-* Editor
-** Overwrite selection on typing
-Normally, when I select something and start typing, Emacs clears the selection, i.e. it deselects and inserts text after the cursor.
-I want to replace the selection.
+*** org-caldav
+This lets me sync my Org agenda to my CalDAV server.
+The main reason is because Orgzly doesn't have a calendar view and can't (yet) search for events on a specific day, so if someone asks "are you free on that day", it's a bit hard for me to answer if I don't have my computer with me.
+This way, I can just check my calendar.
#+begin_src emacs-lisp
- (delete-selection-mode t)
+ (use-package org-caldav)
#+end_src
-** Strip trailing whitespace
-You can show trailing whitespace by setting show-trailing-whitespace to 't'.
-But I want to automatically strip trailing whitespace.
-Luckily there's already a function for that, I just need to call it in a hook:
+A lot of these variables are from my secret.el file, they're not something I can share publicly.
+I use ~/.authinfo.gpg to store authorization info for the server.
#+begin_src emacs-lisp
- (add-hook 'before-save-hook #'delete-trailing-whitespace)
+ (setq org-caldav-url za/caldav-url
+ org-caldav-calendar-id za/caldav-org-calendar-id
+ za/org-life-calendar-inbox (concat za/org-life-dir "calendar-inbox.org")
+ org-caldav-inbox za/org-life-calendar-inbox
+ org-caldav-files (cons (car (split-string org-archive-location "::")) org-agenda-files)
+ org-icalendar-include-todo 'all
+ org-icalendar-use-deadline '(event-if-todo event-if-not-todo todo-due)
+ org-icalendar-use-scheduled '(todo-start event-if-todo event-if-not-todo))
+
#+end_src
-** Formatting & indentation
-Show a tab as 8 spaces:
+I don't want to export habits, because those will just clutter up my calendar.
+The calendar is supposed to be for one-off stuff, or rarely repeating stuff.
+Yes, I have to manually add the "HABIT" tag to every habit.
+Perhaps nicer would be to exclude based on the property ~:STYLE: habit~, but I haven't figured that one out yet.
#+begin_src emacs-lisp
- (setq-default tab-width 8)
+ (setq org-caldav-exclude-tags '("HABIT")))
#+end_src
-Never insert tabs with indentation by default:
+Maybe check [[https://old.reddit.com/r/orgmode/comments/8rl8ep/making_orgcaldav_useable/e0sb5j0/][this]] for a way to sync on save.
+** calfw
+Basically provides a way to show the org agenda as a standard GUI calendar app would.
#+begin_src emacs-lisp
- (setq-default indent-tabs-mode nil)
+ (use-package calfw
+ :config
+ (use-package calfw-org)
+ (setq cfw:org-overwrite-default-keybinding t)
+ (setq calendar-week-start-day 1))
#+end_src
-Allow switching between the two easily:
+** lean-mode
+Specifically for the Lean prover.
+I also install company-lean and helm-lean, which are suggested on the [[https://github.com/leanprover/lean-mode][Github page]].
+Then I map company-complete only for lean-mode.
#+begin_src emacs-lisp
- (defun indent-tabs ()
- (interactive)
- (setq indent-tabs-mode t))
- (defun indent-spaces ()
- (interactive)
- (setq indent-tabs-mode nil))
+ (use-package lean-mode
+ :config
+ (use-package company-lean)
+ :hook
+ (lean-mode . (lambda () (define-key lean-mode-map (kbd "S-SPC") #'company-complete))))
#+end_src
-Indentation for various modes:
-
+** magit
#+begin_src emacs-lisp
- (setq-default sh-basic-offset 2
- c-basic-offset 4)
+ (use-package magit)
#+end_src
-** Wrapping
+** vterm
+Emacs has a bunch of built-in terminal emulators.
+And they all suck.
+(OK not really, eshell is alright, but not for interactive terminal programs like newsboat/neomutt)
-A function to toggle wrapping:
+Also use emacsclient inside vterm as an editor, because that'll open documents in the existing Emacs session.
+And I'm not gonna be a heretic and open Vim inside of Emacs.
#+begin_src emacs-lisp
- (make-variable-buffer-local 'za/wrapping) ; wrapping changes per buffer
-
- (defun za/toggle-wrap (&optional enable)
- "Toggle line wrapping settings. With ENABLE a positive number, enable wrapping. If ENABLE is negative or zero, disable wrapping."
- (interactive "P") ; prefix arg in raw form
+ (use-package vterm
+ :hook
+ (vterm-mode . (lambda () (unless server-process (server-start)))))
+#+end_src
- ;; If an argument is provided, prefix or otherwise
- (if enable
- (let ((enable (cond ((numberp enable)
- enable)
- ((booleanp enable)
- (if enable 1 0))
- ((or (listp enable) (string= "-" enable))
- (prefix-numeric-value enable)))))
- ;; If zero or negative, we want to disable wrapping, so pretend it's currently enabled.
- ;; And vice versa.
- (cond ((<= enable 0) (setq za/wrapping t))
- ((> enable 0) (setq za/wrapping nil)))))
+I'll bind a key to start a vterm or switch to the running vterm:
+#+begin_src emacs-lisp
+ (defun switch-to-vterm () "Switch to a running vterm, or start one and switch to it."
+ (interactive)
+ (if (get-buffer vterm-buffer-name)
+ (switch-to-buffer vterm-buffer-name)
+ (vterm)))
+ (global-set-key (kbd "C-c t") 'switch-to-vterm)
+#+end_src
- (let ((disable-wrapping (lambda ()
- (visual-line-mode -1)
- (toggle-truncate-lines t)))
- (enable-wrapping (lambda ()
- (toggle-truncate-lines -1)
- (visual-line-mode))))
+** sr-speedbar
+Make speed bar show in the current frame.
- ;; If za/wrapping is not locally set, infer its values from the enabled modes
- (unless (boundp 'za/wrapping)
- (setq za/wrapping (and visual-line-mode
- (not truncate-lines))))
+#+begin_src emacs-lisp
+ (use-package sr-speedbar
+ :config
+ (setq sr-speedbar-right-side nil)
+ (define-key speedbar-mode-map (kbd "q") 'sr-speedbar-close))
- ;; Toggle wrapping based on current value
- (cond (za/wrapping
- (funcall disable-wrapping)
- (setq za/wrapping nil)
- (message "Wrapping disabled."))
- (t
- (funcall enable-wrapping)
- (setq za/wrapping t)
- (message "Wrapping enabled.")))))
#+end_src
-And a keybinding to toggle wrapping:
+Jump to speedbar. sr-speedbar-exist-p can be void, so I check if it's bound first.
+If it's not bound, or if it's false, first open the speedbar.
+Then, select it.
#+begin_src emacs-lisp
- (global-set-key (kbd "C-c q w") #'za/toggle-wrap)
+ (global-set-key (kbd "C-c F") (lambda () (interactive)
+ (if (or (not (boundp 'sr-speedbar-exist-p))
+ (not (sr-speedbar-exist-p)))
+ (sr-speedbar-open))
+ (sr-speedbar-select-window)))
#+end_src
-I want to wrap text at window boundary for some modes:
+** expand-region
+Expand the selected region semantically.
#+begin_src emacs-lisp
- (defun za/settings-help-mode ()
- "Help mode settings"
- (za/toggle-wrap t))
+ (use-package expand-region
+ :bind ("C-=" . er/expand-region))
#+end_src
+** flycheck
+Install flycheck, and enable it by default in certain major modes:
+
#+begin_src emacs-lisp
- (add-hook 'help-mode-hook #'za/settings-help-mode)
+ (use-package flycheck
+ :hook (sh-mode . flycheck-mode))
#+end_src
-** Pulse line
-When you switch windows, Emacs can flash the cursor briefly to guide your eyes; I like that.
-Set some options for pulsing:
+** anki-editor
+Some extra keybindings that are not set up by default.
+anki-editor doesn't provide a keymap so I have to set one up here:
#+begin_src emacs-lisp
- (setq pulse-iterations 10)
- (setq pulse-delay 0.05)
+ (use-package anki-editor
+ :config
+ (defvar anki-editor-mode-map (make-sparse-keymap))
+ (add-to-list 'minor-mode-map-alist (cons 'anki-editor-mode
+ anki-editor-mode-map))
+
+ (setq anki-editor-use-math-jax t)
+
+ :hook
+ (anki-editor-mode . (lambda ()
+ (define-key anki-editor-mode-map (kbd "C-c t") #'org-property-next-allowed-value)
+ (define-key anki-editor-mode-map (kbd "C-c i") #'anki-editor-insert-note)
+ (define-key anki-editor-mode-map (kbd "C-c p") #'anki-editor-push-notes)
+ (define-key anki-editor-mode-map (kbd "C-c c") #'anki-editor-cloze-dwim))))
#+end_src
-Define the pulse function:
+** rainbow-mode
+'rainbow-mode' lets you visualise hex colors:
#+begin_src emacs-lisp
- (defun pulse-line (&rest _)
- "Pulse the current line."
- (pulse-momentary-highlight-one-line (point)))
+ (use-package rainbow-mode
+ :hook (emacs-lisp-mode . rainbow-mode))
#+end_src
-Run it in certain cases: scrolling up/down, recentering, switching windows.
-'dolist' binds 'command' to each value in the list in turn, and runs the body.
-'advice-add' makes the pulse-line function run after 'command'.
+** pdf-tools
+A better replacement for DocView:
#+begin_src emacs-lisp
- (dolist (command '(scroll-up-command scroll-down-command recenter-top-bottom other-window))
- (advice-add command :after #'pulse-line))
+ (use-package pdf-tools
+ :config
+ (setq-default pdf-annot-default-annotation-properties '((t
+ (label . "Alex Balgavy"))
+ (text
+ (icon . "Note")
+ (color . "#0088ff"))
+ (highlight
+ (color . "yellow"))
+ (squiggly
+ (color . "orange"))
+ (strike-out
+ (color . "red"))
+ (underline
+ (color . "blue"))))
+ :hook
+ (pdf-annot-list-mode . pdf-annot-list-follow-minor-mode)
+ (pdf-annot-edit-contents-minor-mode . org-mode)
+ (pdf-view-mode . (lambda () (display-line-numbers-mode 0)))
+ (pdf-view-mode . (lambda () (define-key pdf-isearch-minor-mode-map (kbd "C-s") #'isearch-forward))))
+ (pdf-tools-install)
#+end_src
-And set the pulse color:
+** virtualenvwrapper
+Like virtualenvwrapper.sh, but for Emacs.
#+begin_src emacs-lisp
- (custom-set-faces '(pulse-highlight-start-face ((t (:background "CadetBlue2")))))
+ (use-package virtualenvwrapper
+ :config
+ (venv-initialize-interactive-shells)
+ (venv-initialize-eshell)
+ (setq venv-location "~/.config/virtualenvs"))
#+end_src
-** Pager toggle keybinding
-M-x view-mode enables pager behavior.
-I want read-only files to automatically use pager mode:
+** org-ref
+#+begin_src emacs-lisp
+ (use-package org-ref)
+#+end_src
+** org-noter
#+begin_src emacs-lisp
- (setq view-read-only t)
+ (use-package org-noter)
#+end_src
-** Mail mode for neomutt
-When editing a message from neomutt, I want to use mail mode.
-Even though I won't be sending the email from there, I like the syntax highlighting :)
+
+** hl-todo
+I want to highlight TODO keywords in comments:
#+begin_src emacs-lisp
- (add-to-list 'auto-mode-alist '("/neomutt-" . mail-mode))
+ (use-package hl-todo
+ :custom-face
+ (hl-todo ((t (:inherit hl-todo :underline t))))
+ :config
+ (setq hl-todo-keyword-faces
+ '(("TODO" . "#ff7060")
+ ("FIXME" . "#caa000")))
+ (global-hl-todo-mode t))
#+end_src
-** Zap up to char
-It's more useful for me to be able to delete up to a character instead of to and including a character:
+** undo-tree
+Sometimes it's better to look at undo history as a tree:
#+begin_src emacs-lisp
- (global-set-key (kbd "M-z") 'zap-up-to-char)
+ (use-package undo-tree
+ :config
+ (global-undo-tree-mode))
#+end_src
-** Expansion/completion
-Use hippie expand instead of dabbrev-expand:
+** ledger
+#+begin_src emacs-lisp
+ (use-package ledger-mode
+ :mode ("\\.ledger\\'")
+ :config
+ (setq ledger-clear-whole-transactions t
+ ledger-reconcile-default-commodity "eur"))
+#+end_src
+
+Custom reports:
#+begin_src emacs-lisp
- (global-set-key (kbd "M-/") 'hippie-expand)
+ (custom-set-variables
+ '(ledger-reports
+ '(("budget-last-month" "ledger -f %(ledger-file) --period \"last month\" budget ^expenses -X eur")
+ ("budget-this-month" "ledger -f %(ledger-file) --period \"this month\" budget ^expenses -X eur")
+ ("expenses-this-month-vs-budget" "ledger -f %(ledger-file) --period \"this month\" --period-sort \"(amount)\" bal ^expenses -X eur --budget")
+ ("expenses-last-month-vs-budget" "ledger -f %(ledger-file) --period \"last month\" --period-sort \"(amount)\" bal ^expenses -X eur --budget")
+ ("expenses-vs-income-last-month" "ledger -f %(ledger-file) --period \"last month\" --period-sort \"(amount)\" bal ^expenses ^income -X eur")
+ ("expenses-last-month" "ledger -f %(ledger-file) --period \"last month\" --period-sort \"(amount)\" bal ^expenses -X eur")
+ ("expenses-this-month" "ledger -f %(ledger-file) --period \"this month\" --period-sort \"(amount)\" bal ^expenses -X eur")
+ ("expenses-vs-income-this-month" "ledger -f %(ledger-file) --period \"this month\" --period-sort \"(amount)\" bal ^income ^expenses -X eur")
+ ("expenses-this-month" "ledger -f %(ledger-file) --period \"this month\" --period-sort \"(amount)\" bal ^income ^expenses -X eur")
+ ("bal-assets-czk" "ledger [[ledger-mode-flags]] -f %(ledger-file) bal Assets Liabilities -X czk")
+ ("bal-assets" "ledger [[ledger-mode-flags]] -f %(ledger-file) bal Assets Liabilities")
+ ("bal" "ledger [[ledger-mode-flags]] -f %(ledger-file) bal -B")
+ ("bal-assets-eur" "ledger [[ledger-mode-flags]] -f %(ledger-file) bal Assets Liabilities -X eur")
+ ("reg" "%(binary) -f %(ledger-file) reg")
+ ("payee" "%(binary) -f %(ledger-file) reg @%(payee)")
+ ("account" "%(binary) -f %(ledger-file) reg %(account)")))
+ )
#+end_src
+** osm
+#+begin_src emacs-lisp
+ (use-package osm
+ :bind (("C-c M h" . osm-home)
+ ("C-c M s" . osm-search)
+ ("C-c M v" . osm-server)
+ ("C-c M t" . osm-goto)
+ ("C-c M x" . osm-gpx-show)
+ ("C-c M j" . osm-bookmark-jump))
-** Prefer newer file loading
+ :custom
+ ;; Take a look at the customization group `osm' for more options.
+ (osm-server 'default) ;; Configure the tile server
+ (osm-copyright nil) ;; Display the copyright information
+
+ :init
+ ;; Load Org link support
+ (with-eval-after-load 'org
+ (require 'osm-ol)))
+#+end_src
+* Interface
+** Start debugger on error
#+begin_src emacs-lisp
- (setq load-prefer-newer t)
+ ;; (toggle-debug-on-error t)
#+end_src
-** Automatically find tags file
-When opening a file in a git repo, try to discover the etags file:
+** Messages
+Hide some messages I don't need, and add a list of recent files.
#+begin_src emacs-lisp
- (defun current-tags-file ()
- "Get current tags file"
- (let* ((tagspath ".git/etags")
- (git-root (locate-dominating-file (buffer-file-name) tagspath)))
- (if git-root
- (expand-file-name tagspath git-root))))
+ (recentf-mode)
+ (setq inhibit-startup-message t
+ initial-major-mode #'org-mode
+ initial-scratch-message
+ (concat "Welcome to Emacs\n\n"
+ "Recent:\n"
+ (mapconcat
+ (lambda (x) (format "- [[%s]]" x)) recentf-list "\n")
+ "\n\nELISP Evaluation area:\n#+begin_src emacs-lisp\n\n#+end_src"))
- (setq default-tags-table-function #'current-tags-file)
#+end_src
-There's probably a better way to write this. I need to ask Reddit for feedback at some point.
+** Appearance
+*** Cursor line
+Highlight the current line:
-** Semantic mode
-Set default submodes:
+#+begin_src emacs-lisp
+ (global-hl-line-mode)
+#+end_src
+*** Matching parentheses
+Don't add a delay to show matching parenthesis.
+Must come before show-paren-mode enable.
#+begin_src emacs-lisp
- (setq semantic-default-submodes '(global-semantic-idle-scheduler-mode ; reparse buffer when idle
- global-semanticdb-minor-mode ; maintain database
- global-semantic-idle-summary-mode)) ; show information (e.g. types) about tag at point
- ;; global-semantic-stickyfunc-mode)) ; show current func in header line
+ (setq show-paren-delay 0)
#+end_src
-Add some keybindings:
+Show matching parentheses:
#+begin_src emacs-lisp
- (with-eval-after-load 'semantic
- (define-key semantic-mode-map (kbd "C-c , .") #'semantic-ia-show-summary))
+ (show-paren-mode t)
#+end_src
-SemanticDB is written into ~/.emacs.d/semanticdb/.
-
-Enable semantic mode for major modes:
+*** Cursor
+The default box cursor isn't really accurate, because the cursor is actually between letters, not on a letter.
+So, I want a bar instead of a box:
#+begin_src emacs-lisp
- (defun za/settings-c-mode ()
- "C mode settings"
- (semantic-mode 1))
+ (setq-default cursor-type '(bar . 4)
+ cursor-in-non-selected-windows 'hollow)
#+end_src
+
+(I use ~setq-default~ here because cursor-type is automatically buffer-local when it's set)
+
+*** Line numbers
+Relative line numbers:
+
#+begin_src emacs-lisp
- (let ((mode-hooks [c-mode-common-hook]))
- (mapc (lambda (mode-name)
- (add-hook mode-name #'za/settings-c-mode))
- mode-hooks))
+ (setq display-line-numbers-type 'relative)
+ (global-display-line-numbers-mode)
#+end_src
-** Forward-word and forward-to-word
-Change M-f to stop at the start of the word:
+Function to hide them:
+
+#+begin_src emacs-lisp
+ (defun za/hide-line-numbers ()
+ "Hide line numbers"
+ (display-line-numbers-mode 0))
+#+end_src
+Don't display them in specific modes. For each of the modes in
+'mode-hooks', add a function to hide line numbers when the mode
+activates (which triggers the 'mode'-hook).
#+begin_src emacs-lisp
- (global-set-key (kbd "M-f") 'forward-to-word)
+ (let ((mode-hooks '(doc-view-mode-hook vterm-mode-hook mpc-status-mode-hook mpc-tagbrowser-mode-hook)))
+ (mapc
+ (lambda (mode-name)
+ (add-hook mode-name #'za/hide-line-numbers))
+ mode-hooks))
#+end_src
-
-Bind C-M-S-F to the old functionality of M-f (stop at end of word)
+*** Modeline
+I want to show the time and date in the modeline:
#+begin_src emacs-lisp
- (global-set-key (kbd "C-M-S-F") 'forward-word)
+ (setq display-time-day-and-date t ; also the date
+ display-time-default-load-average nil ; don't show load average
+ display-time-format "%I:%M%p %e %b (%a)") ; "HR:MIN(AM/PM) day-of-month Month (Day)"
+ (display-time-mode 1) ; enable time mode
#+end_src
-** Rectangle insert string
+And to set the modeline format:
+
#+begin_src emacs-lisp
- (global-set-key (kbd "C-x r I") 'string-insert-rectangle)
- (global-set-key (kbd "C-x r R") 'replace-rectangle)
+ (setq-default mode-line-format '("%e" mode-line-front-space mode-line-mule-info mode-line-client mode-line-modified mode-line-remote mode-line-frame-identification mode-line-buffer-identification " " mode-line-position
+ (vc-mode vc-mode)
+ " " mode-line-modes mode-line-misc-info mode-line-end-spaces))
#+end_src
-** End sentences with one space
-Emacs uses the rather old-fashioned convention of treating a period followed by double spaces as end of sentence. However, it is more common these days to end sentences with a period followed by a single space.
-Let a period followed by a single space be treated as end of sentence:
+I want to hide certain modes from the modeline, they're always on:
#+begin_src emacs-lisp
- (setq sentence-end-double-space nil)
+ (use-package diminish
+ :config
+ (let ((modes-to-hide '(ivy-mode counsel-mode which-key-mode hl-todo-mode undo-tree-mode ivy-posframe-mode)))
+ (mapc (lambda (mode-name) (diminish mode-name)) modes-to-hide))
+ (diminish 'view-mode " 👓"))
#+end_src
+*** Tab bar
+Only show tab bar if there's more than 1 tab:
-* Org mode
-** Get number of headlines in a file
#+begin_src emacs-lisp
- (defun za/org-count-headlines-in-file (level filename)
- "Count number of level LEVEL headlines in FILENAME. If LEVEL is 0, count all."
- (let ((headline-str (cond ((zerop level) "^\*+")
- (t (format "^%s " (apply 'concat (make-list level "\\*")))))))
- (save-mark-and-excursion
- (with-temp-buffer
- (insert-file-contents filename)
- (count-matches headline-str (point-min) (point-max))))))
+ (setq tab-bar-show 1)
#+end_src
-** Agenda & GTD
-*** Set file locations
-Which files should be included in the agenda (I have to use ~list~ to evaluate the variables, because org-agenda-files expects strings):
+** Buffer displaying
+
+So, this is a bit hard to grok. But basically the alist contains a
+regular expression to match a buffer name, then a list of functions to
+use in order for displaying the list, and then options for those functions (each of which is an alist).
#+begin_src emacs-lisp
- (setq org-agenda-files (list za/org-life-main
- za/org-life-inbox
- za/org-life-tickler))
-#+end_src
+ (setq
+ ;; Maximum number of side-windows to create on (left top right bottom)
+ window-sides-slots '(0 ;; left
+ 1 ;; top
+ 3 ;; right
+ 1 ) ;; bottom
-Convenience functions to make opening the main file faster:
+ display-buffer-alist '(
+ ;; Right side
+ ("\\*Help\\*"
+ (display-buffer-reuse-window display-buffer-in-side-window)
+ (side . right)
+ (slot . -1)
+ (inhibit-same-window . t))
+ ("\\*Async Shell Command\\*"
+ (display-buffer-reuse-window display-buffer-in-side-window)
+ (side . right)
+ (slot . 0)
+ (inhibit-same-window . t))
+ ("magit-process: .*"
+ (display-buffer-reuse-window display-buffer-in-side-window)
+ (side . right)
+ (slot . 0)
+ (inhibit-same-window . t))
-#+begin_src emacs-lisp
- (defun gtd () "GTD: main file" (interactive) (find-file za/org-life-main))
- (defun gtd-inbox () "GTD: inbox" (interactive) (find-file za/org-life-inbox))
- (defun gtd-archive () "GTD: archive" (interactive) (find-file za/org-life-archive))
- (defun gtd-someday () "GTD: someday" (interactive) (find-file za/org-life-someday))
- (defun gtd-reference () "GTD: reference" (interactive) (find-file za/org-life-reference))
+ ;; Top side
+ ("\\*Info\\*"
+ (display-buffer-reuse-window display-buffer-in-side-window)
+ (side . top)
+ (slot . 0))
+ ("\\*Man .*\\*"
+ (display-buffer-reuse-window display-buffer-in-side-window)
+ (side . top)
+ (slot . 0))
+
+ ;; Bottom
+ ("\\*Flycheck errors\\*"
+ (display-buffer-reuse-window display-buffer-in-side-window)
+ (side . bottom)
+ (slot . 0))))
#+end_src
-Bind keys to those functions:
+And a way to toggle those side windows:
#+begin_src emacs-lisp
- (global-set-key (kbd "C-c g i") 'gtd-inbox)
- (global-set-key (kbd "C-c g g") 'gtd)
- (global-set-key (kbd "C-c g a") 'gtd-archive)
- (global-set-key (kbd "C-c g s") 'gtd-someday)
- (global-set-key (kbd "C-c g r") 'gtd-reference)
+ (global-set-key (kbd "C-c w") (lambda () (interactive) (window-toggle-side-windows)))
#+end_src
-*** Refiling & archiving
-Where I want to be able to move subtrees (doesn't include inbox because I never refile to that, and the archive has its own keybining):
+* Emacs file locations
+** Auto-Save files
+By default, auto-save files ("#file#") are placed in the same directory as the file itself.
+I want to put this all in some unified place:
#+begin_src emacs-lisp
- (setq org-refile-targets `((,za/org-life-main :maxlevel . 3)
- (,za/org-life-someday :level . 1)
- (,za/org-life-tickler :maxlevel . 2)
- (,za/org-life-reference :maxlevel . 2)))
+ (let ((saves-directory "~/.local/share/emacs/saves/"))
+ (unless (file-directory-p saves-directory)
+ (make-directory saves-directory))
+ (setq auto-save-file-name-transforms
+ `((".*" ,saves-directory t))))
#+end_src
-I want to archive to a specific file, in a date tree:
+** Backup files
+By default, backup files (those with a tilde) are saved in the same directory as the currently edited file.
+This setting puts them in ~/.local/share/emacs/backups.
#+begin_src emacs-lisp
- (setq org-archive-location (concat za/org-life-archive "::datetree/"))
+ (let ((backups-directory "~/.local/share/emacs/backups"))
+ (unless (file-directory-p backups-directory)
+ (make-directory backups-directory))
+ (setq backup-directory-alist `(("." . ,backups-directory)))
+ (setq backup-by-copying t))
#+end_src
-Include the destination file as an element in the path to a heading, and to use the full paths as completion targets rather than just the heading text itself:
+** Custom settings file
+Both commands are necessary.
+First one tells Emacs where to save customizations.
+The second one actually loads them.
#+begin_src emacs-lisp
- (setq org-refile-use-outline-path 'file)
+ (setq custom-file (expand-file-name (concat user-emacs-directory "custom.el")))
+ (load custom-file)
#+end_src
-Tell Org that I don’t want to complete in steps; I want Org to generate all of the possible completions and present them at once (necessary for Helm/Ivy):
+* Editor
+** Overwrite selection on typing
+Normally, when I select something and start typing, Emacs clears the selection, i.e. it deselects and inserts text after the cursor.
+I want to replace the selection.
#+begin_src emacs-lisp
- (setq org-outline-path-complete-in-steps nil)
+ (delete-selection-mode t)
#+end_src
-Allow me to tack new heading names onto the end of my outline path, and if I am asking to create new ones, make me confirm it:
+** Strip trailing whitespace
+You can show trailing whitespace by setting show-trailing-whitespace to 't'.
+But I want to automatically strip trailing whitespace.
+Luckily there's already a function for that, I just need to call it in a hook:
#+begin_src emacs-lisp
- (setq org-refile-allow-creating-parent-nodes 'confirm)
+ (add-hook 'before-save-hook #'delete-trailing-whitespace)
#+end_src
+** Formatting & indentation
-*** Quick capture
-Quick capture lets me send something to my inbox very quickly, without thinking about where it should go.
-The inbox is processed later.
-
-Templates for quick capture:
+Show a tab as 8 spaces:
#+begin_src emacs-lisp
- (setq org-capture-templates `(("t" "Todo [inbox]" entry
- (file ,za/org-life-inbox)
- "* TODO %i%?")
-
- ("s" "Save for read/watch/listen" entry
- (file+headline ,za/org-life-tickler "Read/watch/listen")
- "* TODO %?[[%^{link}][%^{description}]] %^G")))
+ (setq-default tab-width 8)
#+end_src
-*** Todo & custom agenda views
-Todo keywords based on the GTD system (pipe separates incomplete from complete).
-Apart from the logging-on-done configured [[*Logging][below]], I also want to log a note & timestamp when I start waiting on something.
-In ~org-todo-keywords~, ~@~ means note+timestamp, ~!~ means timestamp, ~@/!~ means note+timestamp on state entry and timestamp on leave.
+Never insert tabs with indentation by default:
#+begin_src emacs-lisp
- (setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "WAITING(w@)" "SOMEDAY(s)" "|" "DONE(d)" "CANCELLED(c)"))
- org-todo-keyword-faces '(("TODO" . org-todo)
- ("NEXT" . org-todo)
- ("WAITING" . org-todo)
- ("SOMEDAY" . org-todo)
- ("DONE" . org-done)
- ("CANCELLED" . org-done)))
+ (setq-default indent-tabs-mode nil)
#+end_src
-I decided that projects will not be TODO items, but their progress will be tracked with a progress cookie ([x/y]). This function converts an item to a project: it adds a PROJECT tag, sets the progress indicator to count all checkboxes in sub-items (only TODO items), and removes any existing TODO keywords. Finally, PROJECT tags shouldn't be inherited (i.e. subtasks shouldn't be marked as projects).
+Allow switching between the two easily:
#+begin_src emacs-lisp
- (defun za/mark-as-project ()
- "This function makes sure that the current heading has
- (1) the tag PROJECT
- (2) the property COOKIE_DATA set to \"todo recursive\"
- (3) a leading progress indicator"
+ (defun indent-tabs ()
(interactive)
- (org-set-property "TODO" "")
- (org-toggle-tag "PROJECT" 'on)
- (org-set-property "COOKIE_DATA" "todo recursive")
- (org-back-to-heading t)
- (forward-whitespace 1)
- (insert "[/] ")
- (org-update-statistics-cookies nil))
+ (setq indent-tabs-mode t))
+ (defun indent-spaces ()
+ (interactive)
+ (setq indent-tabs-mode nil))
#+end_src
-Only the top-level project headlines should be tagged as projects, so disable inheritance of that tag:
+Indentation for various modes:
#+begin_src emacs-lisp
- (setq org-tags-exclude-from-inheritance '("PROJECT"))
+ (setq-default sh-basic-offset 2
+ c-basic-offset 4)
#+end_src
-Define a function to skip items if they're part of a project (i.e. one of their parents has a "PROJECT" tag).
-The problem is, the "PROJECT" tag isn't inherited. So, we temporarily disable excluding from inheritance, just for the ~org-get-tags~ call. Then check if "PROJECT" is one of the tags.
+** Wrapping
+
+A function to toggle wrapping:
#+begin_src emacs-lisp
- (defun za/skip-if-in-project ()
- "Skip items that are part of a project"
- (let ((subtree-end (save-excursion (org-end-of-subtree t)))
- (item-tags (let ((org-tags-exclude-from-inheritance nil)) (org-get-tags))))
- (if (member "PROJECT" item-tags)
- subtree-end
- nil)))
+ (make-variable-buffer-local 'za/wrapping) ; wrapping changes per buffer
+
+ (defun za/toggle-wrap (&optional enable)
+ "Toggle line wrapping settings. With ENABLE a positive number, enable wrapping. If ENABLE is negative or zero, disable wrapping."
+ (interactive "P") ; prefix arg in raw form
+
+ ;; If an argument is provided, prefix or otherwise
+ (if enable
+ (let ((enable (cond ((numberp enable)
+ enable)
+ ((booleanp enable)
+ (if enable 1 0))
+ ((or (listp enable) (string= "-" enable))
+ (prefix-numeric-value enable)))))
+ ;; If zero or negative, we want to disable wrapping, so pretend it's currently enabled.
+ ;; And vice versa.
+ (cond ((<= enable 0) (setq za/wrapping t))
+ ((> enable 0) (setq za/wrapping nil)))))
+
+
+ (let ((disable-wrapping (lambda ()
+ (visual-line-mode -1)
+ (toggle-truncate-lines t)))
+ (enable-wrapping (lambda ()
+ (toggle-truncate-lines -1)
+ (visual-line-mode))))
+
+ ;; If za/wrapping is not locally set, infer its values from the enabled modes
+ (unless (boundp 'za/wrapping)
+ (setq za/wrapping (and visual-line-mode
+ (not truncate-lines))))
+ ;; Toggle wrapping based on current value
+ (cond (za/wrapping
+ (funcall disable-wrapping)
+ (setq za/wrapping nil)
+ (message "Wrapping disabled."))
+ (t
+ (funcall enable-wrapping)
+ (setq za/wrapping t)
+ (message "Wrapping enabled.")))))
#+end_src
-Also, define a function to skip tasks (trees) that are not habits (i.e. don't have the STYLE property ~habit~):
+And a keybinding to toggle wrapping:
#+begin_src emacs-lisp
- (defun za/skip-unless-habit ()
- "Skip trees that are not habits"
- (let ((subtree-end (save-excursion (org-end-of-subtree t))))
- (if (string= (org-entry-get nil "STYLE") "habit")
- nil
- subtree-end)))
+ (global-set-key (kbd "C-c q w") #'za/toggle-wrap)
#+end_src
-And one to skip tasks that /are/ habits:
+I want to wrap text at window boundary for some modes:
#+begin_src emacs-lisp
- (defun za/skip-if-habit ()
- "Skip trees that are not habits"
- (let ((subtree-end (save-excursion (org-end-of-subtree t))))
- (if (string= (org-entry-get nil "STYLE") "habit")
- subtree-end
- nil)))
+ (defun za/settings-help-mode ()
+ "Help mode settings"
+ (za/toggle-wrap t))
#+end_src
-
-And another function, to skip tasks that are blocked:
-
#+begin_src emacs-lisp
- (defun za/skip-if-blocked ()
- "Skip trees that are blocked by previous tasks"
- (let ((subtree-end (save-excursion (org-end-of-subtree t))))
- (if (org-entry-blocked-p)
- subtree-end
- nil)))
+ (add-hook 'help-mode-hook #'za/settings-help-mode)
#+end_src
-Create custom agenda view based on those keywords.
-Agenda views are made up of blocks, appearing in the order that you declare them.
-The first two strings are what shows up in the agenda dispatcher (the key to press and the description).
+** Pulse line
+When you switch windows, Emacs can flash the cursor briefly to guide your eyes; I like that.
+Set some options for pulsing:
#+begin_src emacs-lisp
- (setq org-agenda-custom-commands
- '(("n" "Next actions"
- ((todo "NEXT" ((org-agenda-overriding-header "Next actions:")))))
- ("W" "Waiting"
- ((todo "WAITING" ((org-agenda-overriding-header "Waiting:")))))
- ("S" . "Saved for later...")
- ("Sw" "Saved to watch"
- ((tags-todo "WATCH" ((org-agenda-overriding-header "To watch:")))))
- ("Sr" "Saved to read"
- ((tags-todo "READ" ((org-agenda-overriding-header "To read:")))))
- ("Sl" "Saved to listen"
- ((tags-todo "LISTEN" ((org-agenda-overriding-header "To listen:")))))
-
- ("a" . "Agenda with schedule only...")
- ("aw" "This week"
- ((agenda "" ((org-agenda-span 'week)))))
- ("ad" "Today"
- ((agenda "" ((org-agenda-span 'day)))))
- ("at" "Tomorrow"
- ((agenda "" ((org-agenda-span 'day)
- (org-agenda-start-day "+1d")))))
-
- ("w" "Week Agenda + Next Actions"
- ((agenda "" ((org-agenda-overriding-header "Week agenda:")))
- (todo "NEXT" ((org-agenda-overriding-header "Next actions:")))))
-
- ("o" "Month agenda"
- ((agenda "" ((org-agenda-overriding-header "Month agenda:")
- (org-agenda-span 'month)))))
-
- ("d" "Day Agenda + Next Actions + Habits"
- ((agenda "" ((org-agenda-overriding-header "Day:")
- (org-agenda-span 'day)
- (org-habit-show-habits nil)))
- (todo "NEXT" ((org-agenda-overriding-header "Next actions:")))
- (agenda "" ((org-agenda-overriding-header "Habits:")
- (org-agenda-span 'day)
- (org-agenda-use-time-grid nil)
- (org-agenda-skip-function 'za/skip-unless-habit)
- (org-habit-show-habits t) (org-habit-show-habits-only-for-today nil)
- (org-habit-show-all-today t)))
- (todo "WAITING" ((org-agenda-overriding-header "Waiting:")))))
-
- ("p" "Projects"
- ((tags "PROJECT" ((org-agenda-overriding-header "Projects:")
- (org-agenda-prefix-format '((tags . " %i %-22(let ((deadline (org-entry-get nil \"DEADLINE\"))) (if deadline deadline \"\"))")))
- (org-agenda-sorting-strategy '((tags deadline-up alpha-down)))))))
-
- ("f" "Finished tasks that aren't in a project"
- ((tags "TODO=\"DONE\"|TODO=\"CANCELLED\"" ((org-agenda-overriding-header "Finished tasks:")
- (org-agenda-skip-function 'za/skip-if-in-project)))))
-
- ;; Useful thread for opening calfw: https://github.com/kiwanami/emacs-calfw/issues/18
- ("c" "Calendar view" (lambda (&rest _)
- (interactive)
- (let ((org-agenda-skip-function 'za/skip-if-habit))
- (cfw:open-org-calendar))))))
+ (setq pulse-iterations 10)
+ (setq pulse-delay 0.05)
#+end_src
-In calfw, I don't want to show habits:
+Define the pulse function:
#+begin_src emacs-lisp
- (add-hook 'cfw:calendar-mode-hook (setq-local org-agenda-skip-function 'za/skip-if-habit))
+ (defun pulse-line (&rest _)
+ "Pulse the current line."
+ (pulse-momentary-highlight-one-line (point)))
#+end_src
-*** Logging
-I want to log into the LOGBOOK drawer (useful when I want to take quick notes):
+Run it in certain cases: scrolling up/down, recentering, switching windows.
+'dolist' binds 'command' to each value in the list in turn, and runs the body.
+'advice-add' makes the pulse-line function run after 'command'.
#+begin_src emacs-lisp
- (setq org-log-into-drawer "LOGBOOK")
+ (dolist (command '(scroll-up-command scroll-down-command recenter-top-bottom other-window))
+ (advice-add command :after #'pulse-line))
#+end_src
-I also want to log when I finish a task (useful for archiving).
-Furthermore, when I'm done, I want to add a note (any important
-workarounds/tips). And when I reschedule, I want to know the reason.
-I can disable logging on state change for a specific task by adding ~:LOGGING: nil~ to the ~:PROPERTIES:~ drawer.
+And set the pulse color:
#+begin_src emacs-lisp
- (setq org-log-done 'note
- org-log-reschedule 'note)
+ (custom-set-faces '(pulse-highlight-start-face ((t (:background "CadetBlue2")))))
#+end_src
-I want to hide drawers on startup. This variable has options:
-- 'overview': Top-level headlines only.
-- 'content': All headlines.
-- 'showall': No folding on any entry.
-- 'show2levels: Headline levels 1-2.
-- 'show3levels: Headline levels 1-3.
-- 'show4levels: Headline levels 1-4.
-- 'show5levels: Headline levels 1-5.
-- 'showeverything: Show even drawer contents.
+** Pager toggle keybinding
+M-x view-mode enables pager behavior.
+I want read-only files to automatically use pager mode:
#+begin_src emacs-lisp
- (setq org-startup-folded 'content)
+ (setq view-read-only t)
#+end_src
-
-*** Task ordering
-Some tasks should be ordered, i.e. they should be done in steps.
-Those have the ~:ORDERED: t~ setting in ~:PROPERTIES:~, and it should be enforced:
+** Mail mode for neomutt
+When editing a message from neomutt, I want to use mail mode.
+Even though I won't be sending the email from there, I like the syntax highlighting :)
#+begin_src emacs-lisp
- (setq org-enforce-todo-dependencies t)
+ (add-to-list 'auto-mode-alist '("/neomutt-" . mail-mode))
#+end_src
-
-Furthermore, tasks that are ordered and can't be done yet because of previous steps should be dimmed in the agenda:
+** Zap up to char
+It's more useful for me to be able to delete up to a character instead of to and including a character:
#+begin_src emacs-lisp
- (setq org-agenda-dim-blocked-tasks t)
+ (global-set-key (kbd "M-z") 'zap-up-to-char)
#+end_src
-
-I might also want to set ~org-enforce-todo-checkbox-dependencies~, but not convinced on that one yet.
-
-*** Time tracking & effort
-Time tracking should be done in its own drawer:
+** Expansion/completion
+Use hippie expand instead of dabbrev-expand:
#+begin_src emacs-lisp
- (setq org-clock-into-drawer "CLOCK")
+ (global-set-key (kbd "M-/") 'hippie-expand)
#+end_src
-And to customize how clock tables work:
-
+** Prefer newer file loading
#+begin_src emacs-lisp
- (setq org-clocktable-defaults '(:lang "en" :scope agenda-with-archives :wstart 1 :mstart 1 :compact t :maxlevel nil))
- (setq org-agenda-clockreport-parameter-plist '(:link t :maxlevel nil))
+ (setq load-prefer-newer t)
#+end_src
-I want to set effort in hours:minutes:
+** Automatically find tags file
+When opening a file in a git repo, try to discover the etags file:
#+begin_src emacs-lisp
- (add-to-list 'org-global-properties '("Effort_ALL" . "0:05 0:10 0:15 0:20 0:30 0:45 1:00 1:30 2:00 4:00 6:00 8:00"))
+ (defun current-tags-file ()
+ "Get current tags file"
+ (let* ((tagspath ".git/etags")
+ (git-root (locate-dominating-file (buffer-file-name) tagspath)))
+ (if git-root
+ (expand-file-name tagspath git-root))))
+
+ (setq default-tags-table-function #'current-tags-file)
#+end_src
-I want column view to look like this:
+There's probably a better way to write this. I need to ask Reddit for feedback at some point.
-| To do | Task | Tags | Sum of time elapsed | Sum of time estimated (effort) |
-|--------------+-----------+------+---------------------+--------------------------------|
-| todo keyword | task name | tags | sum of clock | sum of estimated time |
-| ... | ... | ... | ... | ... |
+** Semantic mode
+Set default submodes:
#+begin_src emacs-lisp
- (setq org-columns-default-format "%7TODO (To Do) %32ITEM(Task) %TAGS(Tags) %11CLOCKSUM_T(Clock) %8Effort(Effort){:}")
+ (setq semantic-default-submodes '(global-semantic-idle-scheduler-mode ; reparse buffer when idle
+ global-semanticdb-minor-mode ; maintain database
+ global-semantic-idle-summary-mode)) ; show information (e.g. types) about tag at point
+ ;; global-semantic-stickyfunc-mode)) ; show current func in header line
#+end_src
-Fix column alignment in agenda. Unfortunately that means I have to
-decrease the size of the date-today font. But I don't have any other
-solution atm.
+Add some keybindings:
#+begin_src emacs-lisp
- (set-face-attribute 'org-column nil
- :height (face-attribute 'default :height)
- :family (face-attribute 'default :family))
- (set-face-attribute 'org-agenda-date-today nil
- :height (face-attribute 'default :height))
+ (with-eval-after-load 'semantic
+ (define-key semantic-mode-map (kbd "C-c , .") #'semantic-ia-show-summary))
#+end_src
-*** Calculate time since timestamp
-#+begin_src emacs-lisp
- (defun za/org-time-since ()
- "Print the amount of time between the timestamp at point and the current date and time."
- (interactive)
- (unless (org-at-timestamp-p 'lax)
- (user-error "Not at timestamp"))
+SemanticDB is written into ~/.emacs.d/semanticdb/.
- (when (org-at-timestamp-p 'lax)
- (let ((timestamp (match-string 0)))
- (with-temp-buffer
- (insert timestamp
- "--"
- (org-time-stamp '(16)))
- (org-evaluate-time-range)))))
+Enable semantic mode for major modes:
+
+#+begin_src emacs-lisp
+ (defun za/settings-c-mode ()
+ "C mode settings"
+ (semantic-mode 1))
+#+end_src
+#+begin_src emacs-lisp
+ (let ((mode-hooks [c-mode-common-hook]))
+ (mapc (lambda (mode-name)
+ (add-hook mode-name #'za/settings-c-mode))
+ mode-hooks))
#+end_src
-** Tempo expansions
+** Forward-word and forward-to-word
+Change M-f to stop at the start of the word:
#+begin_src emacs-lisp
- (add-to-list 'org-structure-template-alist '("se" . "src emacs-lisp"))
- (add-to-list 'org-structure-template-alist '("sb" . "src bibtex"))
- (add-to-list 'org-structure-template-alist '("ss" . "src sh"))
+ (global-set-key (kbd "M-f") 'forward-to-word)
#+end_src
-** Yank URL
-#+begin_src emacs-lisp
- (defun org-yank-link-url ()
- (interactive)
- (kill-new (org-element-property :raw-link (org-element-context))))
+Bind C-M-S-F to the old functionality of M-f (stop at end of word)
- (define-key org-mode-map (kbd "C-c M-y") 'org-yank-link-url)
+#+begin_src emacs-lisp
+ (global-set-key (kbd "C-M-S-F") 'forward-word)
#+end_src
-** Catch invisible edits
-Sometimes when text is folded away, I might accidentally edit text inside of it.
-This option prevents that.
-I wanted to do 'smart', but that has a 'fixme' so it might change in the future...
-Instead, show what's being edited, but don't perform the edit.
-
+** Rectangle insert string
#+begin_src emacs-lisp
- (setq org-catch-invisible-edits 'show-and-error)
+ (global-set-key (kbd "C-x r I") 'string-insert-rectangle)
+ (global-set-key (kbd "C-x r R") 'replace-rectangle)
#+end_src
+** End sentences with one space
+Emacs uses the rather old-fashioned convention of treating a period followed by double spaces as end of sentence. However, it is more common these days to end sentences with a period followed by a single space.
-** Notification
-macOS doesn't have dbus. So I use terminal-notifier for functions like org-notify:
+Let a period followed by a single space be treated as end of sentence:
#+begin_src emacs-lisp
- (if (and (eq system-type 'darwin)
- (executable-find "terminal-notifier"))
- (setq org-show-notification-handler
- (lambda (str) (start-process "terminal-notifier" nil (executable-find "terminal-notifier")
- "-title" "Timer done"
- "-message" str
- "-group" "org.gnu.Emacs"
- "-sender" "org.gnu.Emacs"))))
+ (setq sentence-end-double-space nil)
#+end_src
* Markdown