aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.org12
-rw-r--r--emacs.org3206
-rw-r--r--imgs/modeline.pngbin0 -> 10050 bytes
-rw-r--r--solarized-light.css316
-rw-r--r--solarized-light.min.css1
5 files changed, 3535 insertions, 0 deletions
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..eaf4845
--- /dev/null
+++ b/README.org
@@ -0,0 +1,12 @@
+#+TITLE Dotfiles
+
+#+begin_quote
+there's no place like ~/
+#+end_quote
+
+This is my attempt at a literate configuration for all my stuff.
+There are various =*.org= files that will tangle to config files in
+various places.
+
+Files:
+ - [[file:emacs.org][emacs configuration]]
diff --git a/emacs.org b/emacs.org
new file mode 100644
index 0000000..5e762e8
--- /dev/null
+++ b/emacs.org
@@ -0,0 +1,3206 @@
+# -*- indent-tabs-mode: nil; lexical-binding: t; -*-
+#+TITLE: My Emacs configuration
+#+PROPERTY: header-args :tangle ~/.emacs.d/init.el
+#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="solarized-light.css" />
+
+Behold, this is my emacs configuration. Here be dragons.
+
+#+begin_src emacs-lisp
+ ;; -*- lexical-binding: t; -*-
+
+ (require 'cl-lib)
+
+ (message "here be dragons")
+#+end_src
+
+A bit of /clojure-ness/ is always accepted! I copied the idea of a
+=comment= macro from there. In elisp there is a =ignore= function,
+but that evaluates its argument, yielding always nil, whereas
+=comment= doesn't evaluate its body. It's useful to temporary disable
+bits of code.
+
+#+begin_src emacs-lisp
+ (defmacro comment (&rest _body)
+ "Ignore BODY, just like `ignore', but this is a macro."
+ '())
+#+end_src
+
+* early-init.el
+ It's strange to start with the top of the =init.el= and then talk
+ about the =early-init.el=, isn't it? Well, that's how thing goes.
+
+ #+begin_src emacs-lisp :tangle ~/.emacs.d/early-init.el
+ (when (fboundp 'menu-bar-mode)
+ (menu-bar-mode -1))
+ (when (fboundp 'tool-bar-mode)
+ (tool-bar-mode -1))
+ (when (fboundp 'scroll-bar-mode)
+ (scroll-bar-mode -1))
+ (when (fboundp 'horizontal-scroll-bar-mode)
+ (horizontal-scroll-bar-mode -1))
+
+ (add-to-list 'load-path
+ (expand-file-name "lisp" user-emacs-directory))
+
+ (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")
+
+ (require 'my-modeline)
+ (load-theme 'minimal-light t)
+
+ ;; Resizing the Emacs frame can be a terribly expensive part of
+ ;; changing the font. By inhibiting this, we easily halve startup
+ ;; times with fonts that are larger than the system default.
+ (setq frame-inhibit-implied-resize t)
+
+ ;; save the current gc vars, bump them to infinite and restore on
+ ;; emacs-startup-hook
+
+ (defvar my/default-gc-cons-threshold gc-cons-threshold
+ "Backup of the default GC threshold.")
+
+ (defvar my/default-gc-cons-percentage gc-cons-percentage
+ "Backup of the default GC cons percentage.")
+
+ ;; boost the gc during the load
+ (setq gc-cons-threshold most-positive-fixnum ; 2^61 bytes
+ gc-cons-percentage 0.6)
+
+ ;; and reset it to "normal" when done
+ (add-hook 'emacs-startup-hook
+ (lambda ()
+ (setq gc-cons-threshold my/default-gc-cons-threshold
+ gc-cons-percentage my/default-gc-cons-percentage)))
+ #+end_src
+
+* straight
+ I'm currently using =straight.el= and =use-package= to manage
+ external packages. It makes easy to pull packages from git, so one
+ can do local modifications and test right away. I don't know if
+ it's better than other similar tools (cask, qelpa, ...), it's the
+ first one I tried and so far I like it.
+
+ #+begin_src emacs-lisp
+ (defvar bootstrap-version)
+ (defvar straight-use-package-by-default t)
+ (defvar straight-disable-native-compile t)
+ (let ((bootstrap-file
+ (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
+ (bootstrap-version 5))
+ (unless (file-exists-p bootstrap-file)
+ (with-current-buffer
+ (url-retrieve-synchronously
+ "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
+ 'silent 'inhibit-cookies)
+ (goto-char (point-max))
+ (eval-print-last-sexp)))
+ (load bootstrap-file nil 'nomessage))
+
+ (straight-use-package 'use-package)
+ #+end_src
+
+* org
+ One day I'll split this manegeable chunks, but today it's not that
+ day.
+
+ #+begin_src emacs-lisp
+ (use-package org
+ :straight nil
+ :bind (("C-c c" . org-capture)
+ ("C-c a" . org-agenda)
+ ("<f7> s" . org-store-link)
+ :map org-src-mode-map
+ ("C-x w" . org-edit-src-exit)
+ ("C-x C-s" . org-edit-src-exit))
+ :hook ((org-mode . op/org-setup)
+ (org-mode . variable-pitch-mode))
+ :custom ((org-todo-keywords '((sequence "TODO" "WAITING" "|" "DONE")
+ (sequence "REPORT" "BUG" "KNOWCAUSE" "|" "FIXED")
+ (sequence "|" "CANCELLED")))
+ (org-capture-templates '(("n" "annotate something" entry (file "~/org/personal.org")
+ "* %? :note:\n %a")
+ ("t" "something to do" entry (file "~/org/personal.org")
+ "* TODO %?\n %a")
+ ("b" "bug" entry (file "~/org/personal.org")
+ "* REPORT %?\n %a")))
+ (org-ellipsis " [+]")
+ (org-imenu-depth 4)
+ (org-startup-folded t)
+ (org-startup-with-inline-images t)
+ (org-fontify-quote-and-verse-blocks t)
+ (org-use-speed-commands t)
+ (org-src-window-setup 'current-window)
+ (org-directory "~/org")
+ (org-agenda-files '("~/org"))
+ (org-refile-use-outline-path t)
+ (org-outline-path-complete-in-steps nil)
+ (org-refile-targets '((nil :maxlevel . 3)
+ (org-agenda-files :maxlevel . 3)))
+ (org-src-fontify-natively t)
+ (org-clock-out-remove-zero-time-clocks t)
+ (org-clock-out-when-done t)
+ (org-clock-auto-clock-resolution '(when-no-clock-is-running))
+ (org-clock-report-include-clocking-task t)
+ (org-time-stamp-rounding-minutes '(1 1))
+ (org-clock-history-length 23)
+ (org-clock-in-resume t)
+ (org-confirm-babel-evaluate nil))
+ :config
+ (require 'org-protocol)
+
+ (setq org-publish-project-alist '(("dots-org"
+ :base-directory "~/dots"
+ :base-extension "org"
+ :publishing-directory "~/w/blog/resources/dots/"
+ :recursive t
+ :publishing-function org-html-publish-to-html)
+ ("dots-static"
+ :base-directory "~/dots"
+ :base-extension "css\\|png\\|jpg\\|jpeg"
+ :publishing-directory "~/w/blog/resources/dots/"
+ :recursive t
+ :publishing-function org-publish-attachment)
+ ("dots" :components ("dots-org" "dots-static"))))
+
+ (defun op/org-setup ()
+ (hl-line-mode +1)
+ (auto-fill-mode +1)
+ (whitespace-mode -1)
+ (setq-local cursor-type 'bar)
+ (setq-local delete-trailing-lines t)
+ (add-hook 'before-save-hook #'delete-trailing-whitespace nil t))
+
+ (org-link-set-parameters "gemini"
+ :follow (lambda (p) (elpher-go (concat "gemini:" p)))
+ :display 'full)
+
+ (org-babel-do-load-languages
+ 'org-babel-load-languages
+ '((emacs-lisp . t)
+ (C . t)
+ (R . t)
+ (sql . t)
+ (shell . t)
+ (python . t)
+ (gnuplot . t)))
+
+ ;; NOTE: needs sqlite3
+ (use-package org-roam
+ :custom ((org-roam-directory "~/org-roam"))
+ :hook ((after-init . org-roam-mode))
+ :bind (("<f6> o" . org-roam-capture)
+ ("<f6> u" . org-roam-find-file)
+ ("<f6> e" . org-roam)
+ :map org-roam-mode-map
+ ("<f6> O" . org-roam-insert))
+ :config
+ (comment
+ (make-directory org-roam-directory))))
+ #+end_src
+
+ I'm having some problems with org, in particular =C-c C-e ...=
+ doesn't export. Probably it's because I'm ending up with =org= from
+ Emacs and not from straight, or something like that. This seems to
+ fix the problem, but I'd like to avoid this workaround
+
+ #+begin_src emacs-lisp
+ (add-hook 'after-init-hook
+ #'org-reload)
+ #+end_src
+
+ Org uses htmlize to prettify the code when exporting:
+ #+begin_src emacs-lisp
+ (use-package htmlize)
+ #+end_src
+
+ To fix some "alignment" problem with unicode characters in tables
+ (but not also) there is a =valign= package!
+
+ #+begin_src emacs-lisp
+ (use-package valign
+ :straight (:type git :host github :repo "casouri/valign")
+ :defer t
+ :hook ((org-mode . valign-mode))
+ :custom ((valign-fancy-bar t)))
+ #+end_src
+
+**** TODO wasn't valign included into ELPA?
+
+* Misc
+ The following are some misc customizations. They can't be split in
+ their own blocks, either because are variables defined in C or are
+ defined in lisp files that we can't =require=. Either the way, it's
+ probably self-explanatory.
+
+ #+begin_src emacs-lisp
+ (use-package emacs
+ :straight nil
+ :custom ((use-dialog-box nil)
+ (x-stretch-cursor t)
+ (sentence-end-double-space t)
+ (require-final-newline t)
+ (visible-bell nil)
+ (load-prefer-newer t))
+ :bind (("M-z" . zap-up-to-char))
+ :config
+ ;; free the C-z key
+ (define-key global-map (kbd "C-z") nil)
+
+ ;; these becomes buffer-local when set
+ (setq-default scroll-up-aggressively 0.0
+ scroll-down-aggressively 0.0
+ scroll-preserve-screen-position t
+ next-screen-context-lines 1)
+
+ ;; fix hangs due to pasting from xorg -- workaround, not a solution :/
+ (setq x-selection-timeout 1)
+ (add-hook 'after-make-frame-functions
+ (lambda (_frame)
+ (setq x-selection-timeout 1)))
+
+ (fset 'yes-or-no-p 'y-or-n-p))
+ #+end_src
+
+ I'm using a custom keyboard layout, where the numbers are actually
+ symbols, and to type numbers I have to hold shift. Normally, this
+ is not a problem, I type symbols more frequently than numbers
+ anyway, but it's handy to have a quick shortcut for =C-u 0=, instead
+ of doing =C-u s-!= or =C-s-!= (0 is =s-!= here). Introducing =C-!=
+
+ #+begin_src emacs-lisp
+ (defun op/digit-argument-zero ()
+ "Like `digit-argument', but set the arg to 0 unconditionally."
+ (interactive)
+ (prefix-command-preserve-state)
+ (setq prefix-arg 0))
+
+ (define-key global-map (kbd "C-!") #'op/digit-argument-zero)
+ #+end_src
+
+** tab-bar
+ I initially thought I would never used the =tab-bar=, but now here
+ we are. How ironic. Anyway, please don't show the tab-bar when
+ there is only one tab:
+
+ #+begin_src emacs-lisp
+ (setq tab-bar-show 1)
+ #+end_src
+
+** bookmarks
+ Emacs lets one keep bookmarks on various places (usually files)
+ to quickly jump around.
+
+ #+begin_src emacs-lisp
+ (use-package bookmark
+ :straight nil
+ :bind (("C-z b b" . bookmark-jump)
+ ("C-z b a" . bookmark-set)
+ ("C-z b l" . list-bookmarks)))
+ #+end_src
+
+** save the place
+ =save-place-mode= remembers the position of the point in a buffer
+ and, when re-opening it, restores the point. I don't know how it
+ handles the fact that a buffer can be viewed in different window,
+ each one with its point, but anyway it seems handy.
+
+ #+begin_src emacs-lisp
+ (use-package saveplace
+ :straight nil
+ :config (save-place-mode 1))
+ #+end_src
+
+** history
+ =savehist= is similar to =saveplace=, but save history. I don't
+ know exactly what histories it saves, but when it doubt, save it!
+
+ #+begin_src emacs-lisp
+ (use-package savehist
+ :straight nil
+ :config (savehist-mode))
+ #+end_src
+
+** Uniquify
+ Buffer names must be unique. This package permits to tweak the
+ rules that Emacs uses to /uniquify/ those names. The following
+ seems pretty handy, especially wrt project structures like Clojure
+
+ #+begin_src emacs-lisp
+ (use-package uniquify
+ :straight nil
+ :custom ((uniquify-buffer-name-style 'forward)
+ (uniquify-strip-common-suffix t)))
+ #+end_src
+** Hydra
+ I use hydra for various thing, hence why it's in the "misc"
+ section.
+
+ These are some general hydras that I find useful. They are used
+ mostly to quickly "repeat" the last command.
+
+ #+begin_src emacs-lisp
+ (use-package hydra
+ :config
+ (defhydra hydra-windowsize (global-map "C-x")
+ ("{" shrink-window-horizontally)
+ ("}" enlarge-window-horizontally))
+
+ (defhydra hydra-grep-like (global-map "M-g")
+ ("n" next-error "next")
+ ("p" previous-error "prev")
+ ("RET" nil :exit t)
+ ("C-l" recenter-top-bottom)
+ ("q" nil :exit t))
+
+ (defhydra hydra-other-window (global-map "C-x")
+ ("o" other-window "next window")
+ ("O" (other-window -1) "previous window"))
+ (hydra-set-property 'hydra-other-window :verbosity 0)
+
+ (defhydra hydra-other-tab (global-map "C-x t")
+ ("o" tab-next)
+ ("O" tab-previous)
+ ("q" nil :exit t))
+ (hydra-set-property 'hydra-other-tab :verbosity 0))
+ #+end_src
+** desktop.el
+ The desktop package saves and restore the emacs session. This is
+ especially useful when using the emacs daemon. Truth to be told,
+ I'm thinking of getting rid of this in favour of something like =recentf=.
+
+ #+begin_src emacs-lisp
+ (use-package desktop
+ :straight nil
+ :hook ((after-init . desktop-read)
+ (after-init . desktop-save-mode))
+ :custom ((desktop-base-file-name ".desktop")
+ (desktop-base-lock-name ".desktop.lock")
+ (desktop-restore-eager 8)
+ (desktop-restore-frames nil)))
+ #+end_src
+** Gemini for =thingatpoint=
+ I don't exactly remember why, but this should enable the
+ =gemini://= scheme in some kind of buffers.
+ #+begin_src emacs-lisp
+ (use-package thingatpt
+ :config
+ (add-to-list 'thing-at-point-uri-schemes "gemini://"))
+ #+end_src
+** browse-url
+ Browse URLs, and add Gemini support.
+
+ #+begin_src emacs-lisp
+ (use-package browse-url
+ :bind ("<f9>" . browse-url)
+ :config
+ (add-to-list 'browse-url-default-handlers
+ '("\\`gemini:" . op/browse-url-elpher))
+ (defun op/browse-url-elpher (url &rest _args)
+ "Open URL with `elpher-go'."
+ (elpher-go url)))
+ #+end_src
+* Minibuffer
+ #+begin_quote
+ all hail the minibuffer
+ #+end_quote
+
+ This allows to launch a command that uses the minibuffer while
+ already inside the minibuffer.
+ #+begin_src emacs-lisp
+ (setq enable-recursive-minibuffers t)
+ #+end_src
+
+ Misc enhancement to the minibuffer behaviour.
+ #+begin_src emacs-lisp
+ ;; add prompt inidcator to `completing-read-multiple'.
+ (defun op/crm-indicator (args)
+ (cons (concat "[CRM] " (car args))
+ (cdr args)))
+ (advice-add #'completing-read-multiple :filter-args #'op/crm-indicator)
+
+ (setq minibuffer-prompt-properties
+ '(read-only true cursor-intangible t face minibuffer-prompt))
+ (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
+ #+end_src
+
+** Marginalia
+ Enhances the minibuffer completions with additional informations
+ #+begin_src emacs-lisp
+ (use-package marginalia
+ :custom (marginalia-annotators
+ '(marginalia-annotators-heavy marginalia-annotators-light nil))
+ :init (marginalia-mode))
+ #+end_src
+** Orderless
+ Controls the sorting of the minibuffer completions. I still have
+ to tweak it a little bit, but I'm overall happy.
+
+ #+begin_src emacs-lisp
+ (use-package orderless
+ :custom ((completion-styles '(substring orderless))
+ (completion-category-defaults nil)
+ (completion-category-overrides '((file (styles . (partial-completion)))))))
+ #+end_src
+** Consult
+ Consult enhances various command by using the minibuffer.
+ #+begin_src emacs-lisp
+ (use-package consult
+ :bind (("C-c h" . consult-history)
+ ("C-c m" . consult-mode-command)
+ ("C-c b" . consult-bookmark)
+ ("C-c k" . consult-kmacro)
+ ("C-x M-:" . consult-complex-command)
+ ("C-x b" . consult-buffer)
+ ("C-x 4 b" . consult-buffer-other-window)
+ ("C-x 5 b" . consult-buffer-other-frame)
+ ("M-#" . consult-register-load)
+ ("M-'" . consult-register-store)
+ ("C-M-#" . consult-register)
+ ("M-g e" . consult-compile-error)
+ ("M-g g" . consult-goto-line)
+ ("M-g M-g" . consult-goto-line)
+ ("M-g o" . consult-outline)
+ ("M-g m" . consult-mark)
+ ("M-g k" . consult-global-mark)
+ ("M-g i" . consult-imenu)
+ ("M-g I" . consult-project-imenu)
+ ("M-s f" . consult-find)
+ ("M-s g" . consult-grep)
+ ("M-s l" . consult-line)
+ ("M-s k" . consult-keep-lines)
+ ("M-s u" . consult-focus-lines)
+ ("M-s e" . consult-isearch))
+ :custom ((register-preview-delay 0)
+ (register-preview-function #'consult-register-format)
+ ;; use consult to select xref locations with preview
+ (xref-show-xrefs-function #'consult-xref)
+ (xref-show-definitions-function #'consult-xref)
+ (consult-narrow-key "<")
+ (consult-project-root #'project-roots))
+ :init
+ (advice-add #'register-preview :override #'consult-register-window)
+
+ :config
+ ;; make narrowing help available in the minibuffer.
+ (define-key consult-narrow-map (vconcat consult-narrow-key "?")
+ #'consult-narrow-help))
+ #+end_src
+** Affe
+ This is a new-ish package from the same author of consult and
+ marginalia. Honestly, I still have to use it, so this is more a
+ remainder of its existance.
+
+ #+begin_src emacs-lisp
+ (use-package affe
+ :straight (:type git :host github :repo "minad/affe")
+ :after orderless
+ :custom ((affe-regexp-function #'orderless-pattern-compiler)
+ (affe-highlight-function #'orderless-highlight-matches)))
+ #+end_src
+** Vertico
+ Vertico is just like selectrum or icomplete-vertical. It's written
+ by the same author of consult, so at this point I thought of
+ keeping the streak and using this
+
+ #+begin_src emacs-lisp
+ (use-package vertico
+ :config (vertico-mode))
+ #+end_src
+** embark
+ Embark provides custom actions on the minibuffer (technically
+ everywhere, but I only use it in the minibuffer.)
+
+ =embark-become= is a command I should use more. It provides a way
+ to "change" the minibuffer while retaining the input. For
+ instance, I often do =C-x b <something>= just to see that I haven't
+ a buffer, and then =C-x C-f= to open it. With =embark-become= I
+ can /transform/ the =switch-buffer= command to the =find-file=
+ command without the abort =C-g= in between and retain the input.
+
+ #+begin_src emacs-lisp
+ (use-package embark
+ :straight (:type git :host github :repo "oantolin/embark")
+ :bind (:map minibuffer-local-completion-map
+ ("M-t" . embark-act)
+ ("M-h" . embark-become)
+ :map minibuffer-local-map
+ ("M-t" . embark-act)
+ ("M-h" . embark-become)))
+ #+end_src
+* Completions
+ I'm trying corfu at the moment. It has still some bugs for me, but
+ I haven't found a way to reproduce, so I can't report them.
+
+ #+begin_src emacs-lisp
+ (use-package corfu
+ :custom (corfu-cycle t)
+ :config
+ (corfu-global-mode +1))
+ #+end_src
+* Window management
+ This is a bit topic for me, and the only thing that I'm not
+ completely happy with. Fortunately, as time goes, I'm less annoyed
+ with it, bit by bit.
+** The window package
+ This does a lot of stuff, from the split logic to customising the
+ thresholds. One of these days I'll split in multiple pieces.
+
+ #+begin_src emacs-lisp
+ (use-package window
+ :straight nil
+ :bind (("C-x +" . balance-windows-area))
+ :custom
+ ((window-combination-resize t)
+ (even-window-sizes 'heigth-only)
+ (window-sides-vertical nil)
+ (switch-to-buffer-in-dedicated-window 'pop)
+ (split-height-threshold 160)
+ (split-width-threshold 80)
+ (split-window-preferred-function #'op/split-window-sensibly))
+ :config
+ (defun op/split-window-prefer-horizontal (&optional window)
+ "Based on `split-window-sensibly', but designed to prefer a horizontal split.
+ It prefers windows tiled side-by-side. Taken from
+ emacs.stackexchange.com. Optional argument WINDOW is the current
+ window."
+ (let ((window (or window (select-window))))
+ (or (and (window-splittable-p window t)
+ ;; split window horizontally
+ (with-selected-window window
+ (split-window-right))))
+ (and (window-splittable-p window)
+ ;; split window vertically
+ (with-selected-window window
+ (split-window-below)))
+ (and
+ ;; if window is the only usable window on its frame and is not
+ ;; the minibuffer window, try to split it horizontally
+ ;; disregarding the value of `split-height-threshold'.
+ (let ((frame (window-frame window)))
+ (or (eq window (frame-root-window frame))
+ (catch 'done
+ (walk-window-tree (lambda (w)
+ (unless (or (eq w window)
+ (window-dedicated-p w))
+ (throw 'done nil)))
+ frame)
+ t)))
+ (not (window-minibuffer-p window))
+ (let ((split-width-threshold 0))
+ (when (window-splittable-p window t)
+ (with-selected-window window
+ (split-window-right)))))))
+
+ (defun op/split-window-sensibly (&optional window)
+ "Splitting window function.
+ Intended to use as `split-window-preferred-function'. Also taken
+ from stackexchange with edits. Optional argument WINDOW is the
+ window."
+ (let ((window (or window (selected-window))))
+ (with-selected-window window
+ (if (> (window-total-width window)
+ (* 2 (window-total-width window)))
+ (op/split-window-sensibly window)
+ (split-window-sensibly window))))))
+ #+end_src
+** Placement
+ #+begin_src emacs-lisp
+ (use-package shackle
+ :custom
+ ((shackle-rules
+ (let ((repls "\\*\\(cider-repl\\|sly-mrepl\\|ielm\\)")
+ (godot "\\*godot - .*\\*")
+ (vcs "\\*\\(Flymake\\|Package-Lint\\|vc-\\(git\\|got\\) :\\).*")
+ (elfeed "\\*elfeed-entry\\*")
+ (vmd "\\*vmd console .*"))
+ `((compilation-mode :noselect t
+ :align above
+ :size 0.3)
+ ("*Async Shell Command*" :ignore t)
+ (,repls :regexp t
+ :align below
+ :size 0.3)
+ (,godot :regexp t
+ :align t
+ :size 0.3)
+ (occur-mode :select t
+ :align right
+ :size 0.3)
+ (diff-mode :select t)
+ (help-mode :select t
+ :align left
+ :size 0.3)
+ (,vcs :regexp t
+ :align above
+ :size 0.3
+ :select t)
+ (,elfeed :regexp t
+ :align t
+ :select t
+ :size 0.75)
+ (,vmd :regexp t
+ :align below
+ :select t
+ :size 0.3))))
+ (shackle-default-rule '(:inhibit-window-quit t)))
+ :config (shackle-mode))
+ #+end_src
+** History
+ Winner saves the window placement and allows to travel back and
+ forth in time. Also add an hydra for that for extra comfort.
+
+ #+begin_src emacs-lisp
+ (use-package winner
+ :straight nil
+ :config
+ (winner-mode 1)
+ (defhydra hydra-winner (winner-mode-map "C-c")
+ ("<left>" (progn (winner-undo)
+ (setq this-command 'winner-undo))
+ "undo")
+ ("h" (progn (winner-undo)
+ (setq this-command 'winner-undo))
+ "undo")
+ ("<right>" winner-redo "redo")
+ ("l" winner-redo "redo")
+ ("q" nil :exit nil)))
+ #+end_src
+** Switch window
+ The builtin windmove package provides function to move between
+ windows in the same frame easily. Unfortunately, I don't use this
+ package often enough, I usually =C-x o=.
+
+ #+begin_src emacs-lisp
+ (defhydra hydra-windmove (global-map "M-r")
+ ("h" windmove-left)
+ ("j" windmove-down)
+ ("k" windmove-up)
+ ("l" windmove-right)
+ ("q" nil :exit nil))
+ (hydra-set-property 'hydra-windmove :verbosity 0)
+ #+end_src
+** Layouts
+ =transpose-frame= provides various function to change the window
+ layout in the current frame. Since my memory is pretty limited, an
+ hydra is needed.
+
+ #+begin_src emacs-lisp
+ (use-package transpose-frame
+ :bind ("C-#" . my/hydra-window/body)
+ :commands (transpose-frame flip-frame flop-frame
+ rotate-frame rotate-frame-clockwise
+ rotate-frame-anti-anticlockwise)
+ :config
+ (defhydra hydra-window (:hint nil)
+ "
+ ^File/Buffer^ ^Movements^ ^Misc^ ^Transpose^
+ ^^^^^^^^------------------------------------------------------------------------------
+ _b_ switch buffer ^ ^ hjkl _0_ delete _t_ transpose frame
+ _f_ find file _o_ other window _1_ delete other _M-f_ flip frame
+ _s_ save conf _O_ OTHER window _2_ split below _M-C-f_ flop frame
+ _r_ reload conf ^ ^ _3_ split right _M-s_ rotate frame
+ ^ ^ ^ ^ _SPC_ balance _M-r_ rotate clockw.
+ ^^^^------------------------------- _v_ split horiz. _M-C-r_ rotate anti clockw.
+ _?_ toggle help ^ ^ _-_ split vert.
+ ^ ^ ^ ^ _C-l_ recenter line
+ "
+ ("?" (hydra-set-property 'hydra-window :verbosity
+ (if (= (hydra-get-property 'hydra-window :verbosity) 1)
+ 0 1)))
+
+ ("b" switch-to-buffer)
+ ("f" (call-interactively #'find-file))
+
+ ("s" window-configuration-to-register)
+ ("r" jump-to-register)
+
+ ("k" windmove-up)
+ ("j" windmove-down)
+ ("h" windmove-left)
+ ("l" windmove-right)
+
+ ("o" (other-window 1))
+ ("O" (other-window -1))
+
+ ("C-l" recenter-top-bottom)
+
+ ("0" delete-window)
+ ("1" delete-other-windows)
+ ("2" split-window-below)
+ ("3" split-window-right)
+
+ ;; v is like a |, no?
+ ("v" split-window-horizontally)
+ ("-" split-window-vertically)
+
+ ("SPC" balance-windows)
+
+ ("t" transpose-frame)
+ ("M-f" flip-frame)
+ ("M-C-f" flop-frame)
+ ("M-s" rotate-frame)
+ ("M-r" rotate-frame-clockwise)
+ ("M-C-r" rotate-frame-anti-anticlockwise)
+
+ ("q" nil :exit nil)
+ ("RET" nil :exit nil)
+ ("C-g" nil :exit nil))
+
+ (defun my/hydra-window/body ()
+ (interactive)
+ (hydra-set-property 'hydra-window :verbosity 0)
+ (hydra-window/body)))
+ #+end_src
+* Text editing
+** Misc
+ Usually I don't need to waste space for a column with the line
+ numbers, it's something that it's just not useful. Anyway, there
+ are specific times where this is handy, so reserve a key for it.
+
+ #+begin_src emacs-lisp
+ (define-key global-map (kbd "C-z n") #'display-line-numbers-mode)
+ #+end_src
+
+ Better defaults
+ #+begin_src emacs-lisp
+ (define-key global-map (kbd "M-SPC") #'cycle-spacing)
+ (define-key global-map (kbd "M-u") #'upcase-dwim)
+ (define-key global-map (kbd "M-l") #'downcase-dwim)
+ (define-key global-map (kbd "M-c") #'capitalize-dwim)
+
+ #+end_src
+
+** imenu
+ Imenu is a mean of navigation in a buffer. It can act like a TOC,
+ for instance.
+
+ Prevent stale entries by always rescan the buffer
+ #+begin_src emacs-lisp
+ (setq imenu-auto-rescan t)
+ #+end_src
+** Filling
+ This is a useful function copied from somewhere I don't remember,
+ sorry unknown author!
+
+ It makes =fill-paragraph= "toggable": =M-q= once to fill, =M-q=
+ again to un-fill!
+
+ #+begin_src emacs-lisp
+ (defun op/fill-or-unfill (fn &optional justify region)
+ "Meant to be an adviced :around `fill-paragraph'.
+ FN is the original `fill-column'. If `last-command' is
+ `fill-paragraph', unfill it, fill it otherwise. Inspired from a
+ post on endless parentheses. Optional argument JUSTIFY and
+ REGION are passed to `fill-paragraph'."
+ (let ((fill-column
+ (if (eq last-command 'fill-paragraph)
+ (progn (setq this-command nil)
+ (point-max))
+ fill-column)))
+ (funcall fn justify region)))
+ (advice-add 'fill-paragraph :around #'op/fill-or-unfill)
+ #+end_src
+
+** Transpose
+ This is an idea that I stole from prot' dotemacs. It augments the
+ various =transpose-*= commands so they respect the region: if
+ =(use-region-p)= then transpose the /thing/ at the extremes of the
+ region, otherwise operates as usual.
+
+ (the code is somewhat different from prot, but the idea is the
+ same)
+
+ #+begin_src emacs-lisp
+ (defmacro op/deftranspose (name scope key doc)
+ "Macro to produce transposition functions.
+ NAME is the function's symbol. SCOPE is the text object to
+ operate on. Optional DOC is the function's docstring.
+
+ Transposition over an active region will swap the object at
+ mark (region beginning) with the one at point (region end).
+
+ It can optionally define a key for the defined function in the
+ `global-map' if KEY is passed.
+
+ Originally from protesilaos' dotemacs."
+ (declare (indent defun))
+ `(progn
+ (defun ,name (arg)
+ ,doc
+ (interactive "p")
+ (let ((x (intern (format "transpose-%s" ,scope))))
+ (if (use-region-p)
+ (funcall x 0)
+ (funcall x arg))))
+ ,(when key
+ `(define-key global-map (kbd ,key) #',name))))
+
+ (op/deftranspose op/transpose-lines "lines" "C-x C-t"
+ "Transpose lines or swap over active region.")
+
+ (op/deftranspose op/transpose-paragraphs "paragraphs" "C-S-t"
+ "Transpose paragraph or swap over active region.")
+
+ (op/deftranspose op/transpose-sentences "sentences" "C-x M-t"
+ "Transpose sentences or swap over active region.")
+
+ (op/deftranspose op/transpose-sexps "sexps" "C-M-t"
+ "Transpose sexps or swap over active region.")
+
+ (op/deftranspose op/transpose-words "words" "M-t"
+ "Transpose words or swap over active region.")
+ #+end_src
+
+ A command I have to try to use more is =transpose-regions=
+
+ #+begin_src emacs-lisp
+ (define-key global-map (kbd "C-x C-M-t") #'transpose-regions)
+ #+end_src
+
+*** TODO [[https://depp.brause.cc/cycle-region/][cycle-region]] is worth a try
+
+** Narrow to what I mean
+
+ Narrowing is really a powerful mechanism of Emacs. It lets one
+ show only a part of a buffer. Unfortunately, the default keys
+ aren't that great, and there's space for a /do what I mean/
+ command. The following is adapted from a post on endless
+ parentheses.
+
+ #+begin_src emacs-lisp
+ (defun op/narrow-or-widen-dwim (p)
+ "Widen if the buffer is narrowed, narrow-dwim otherwise.
+ Dwim means: region, org-src-block, org-subtree or defun,
+ whichever applies first. Narrowing to org-src-blocks actually
+ calls `org-edit-src-code'.
+
+ With prefix P, don't widen, just narrow even if buffer is already
+ narrowed. With P being -, narrow to page instead of to defun.
+
+ Taken from endless parentheses."
+ (interactive "P")
+ (declare (interactive-only))
+ (cond ((and (buffer-narrowed-p) (not p)) (widen))
+ ((region-active-p)
+ (narrow-to-region (region-beginning)
+ (region-end)))
+ ((derived-mode-p 'org-mode)
+ ;; `org-edit-src-code' isn't a real narrowing
+ (cond ((ignore-errors (org-edit-src-code) t))
+ ((ignore-errors (org-narrow-to-block) t))
+ (t (org-narrow-to-subtree))))
+ ((eql p '-) (narrow-to-page))
+ (t (narrow-to-defun))))
+
+ (define-key global-map (kbd "C-c w") #'op/narrow-or-widen-dwim)
+ #+end_src
+
+** White spaces
+
+ Nothing bothers me more than trailing white spaces, so enable
+ =whitespace-mode= for programming and text buffers.
+
+ Also, I like to use =TAB= to trigger the =completions-at-point=,
+ and while there customize tab behaviours.
+
+ Furthermore, use hard tabs by default; =op/disable-tabs= will be
+ added as mode hook for buffers that needs "soft" tabs.
+
+ #+begin_src emacs-lisp
+ (use-package whitespace
+ :straight nil
+ :custom ((whitespace-style '(face trailing))
+ (backward-delete-char-untabify-method 'hungry)
+ (tab-always-indent 'complete)
+ (tab-width 8))
+ :hook ((conf-mode . op/enable-tabs)
+ (text-mode . op/enable-tabs)
+ (prog-mode . op/enable-tabs)
+ (prog-mode . whitespace-mode)
+ (text-mode . whitespace-mode))
+ :config
+ (setq-default indent-tabs-mode t)
+
+ (defun op/enable-tabs ()
+ "Enable `indent-tabs-mode' in the current buffer."
+ (interactive)
+ (setq-local indent-tabs-mode t))
+
+ (defun op/disable-tabs ()
+ "Disable `indent-tabs-mode' in the current buffer."
+ (interactive)
+ (setq-local indent-tabs-mode nil))
+
+ ;; TODO: remove
+ (dolist (hook '(emacs-lisp-mode-hook))
+ (add-hook hook 'op/disable-tabs)))
+ #+end_src
+
+** Version Control
+*** Backups
+ Albeit not exactly a version control system, the backup system is
+ indeed very usefuly. By defaults backup are created alongside the
+ original files. I don't like that, and prefer to move everything
+ into a separate backup directory.
+
+ By the way, it's incredibly useful to keep backups. I once deleted
+ a file, and manage to recover it because of Emacs' backups!
+
+ #+begin_src emacs-lisp
+ (defconst op/backup-dir
+ (expand-file-name "backups" user-emacs-directory))
+
+ (unless (file-exists-p op/backup-dir)
+ (make-directory op/backup-dir))
+
+ (setq backup-directory-alist `(("." . ,op/backup-dir)))
+ #+end_src
+*** Log
+ It's handy to have =auto-fill-mode= enabled while writing the
+ commit message inside a =log-edit-mode= buffer. It saves a few
+ =M-q=
+ #+begin_src emacs-lisp
+ (use-package log-edit
+ :straight nil
+ :hook ((log-edit-mode . auto-fill-mode)))
+ #+end_src
+*** Got
+ [[https://gameoftrees.org/][Game of Trees]] is a version control system written by Stefan
+ Sperling.
+
+ #+begin_quote
+ Game of Trees (Got) is a version control system which prioritizes
+ ease of use and simplicity over flexibility.
+
+ Got is still under development; it is being developed on OpenBSD
+ and its main target audience are OpenBSD developers.
+
+ Got uses Git repositories to store versioned data. Git can be used
+ for any functionality which has not yet been implemented in
+ Got. It will always remain possible to work with both Got and Git
+ on the same repository.
+ #+end_quote
+
+ I'm trying to complete [[https://github.com/omar-polo/vc-got/][=vc-got=]], a VC backend for Got.
+
+ #+begin_src emacs-lisp
+ (use-package vc-got
+ :straight nil
+ :load-path "~/w/vc-got/"
+ :defer t
+ :init
+ (add-to-list 'vc-handled-backends 'Got)
+ (add-to-list 'vc-directory-exclusion-list ".got"))
+ #+end_src
+** Dired
+ By default dired will show, other than the files, also various
+ other data about every file (like owner, permissions, ...) in a
+ format similar to =ls -lah=. This is indeed useful, but usually I
+ don't need to see all that informations, and they steal precious
+ space, hence =dired-hide-details-mode=.
+
+ In the same spite, most of the time I'm not interested in certain
+ kinds of files (like object files or similar garbage), so hide them
+ too by default with =dired-omit-mode=.
+
+ Finally, =wdired= is awesome, reserve a key for it!
+
+ #+begin_src emacs-lisp
+ (use-package dired
+ :straight nil
+ :hook ((dired-mode . dired-hide-details-mode)
+ (dired-mode . dired-omit-mode))
+ :bind (:map dired-mode-map
+ ("C-c w" . wdired-change-to-wdired-mode))
+ :config
+ (require 'dired-x)
+ (setq dired-listing-switches "-lahF"
+ dired-dwim-target t
+ dired-deletion-confirmer 'y-or-n-p
+ dired-omit-files "\\`[.]?#\\|\\`[.][.]?\\'\\|*\\.o\\`\\|*\\.log\\`"))
+ #+end_src
+
+** Project
+ This is a bulit-in package to manage "projects" (that is, directory
+ trees commonly called "projects")
+
+ It provides various commands that operate on the project, like
+ =project-find-file= and =project-query-replace-regexp=.
+
+ By default a project is something that is managed by a VCS, such as
+ =git=. However, sometimes is useful to mark something as a project
+ without actually create a repo for it. This code, adapted from
+ something that I found online I don't remember where, adds another
+ implementation for the project backend that consider a project
+ something that has a =.project= file.
+
+ #+begin_src emacs-lisp
+ (with-eval-after-load 'project
+ (defun op/project-try-local (dir)
+ "Determine if DIR is a local project.
+ DIR must include a .project file to be considered a project."
+ (when-let (root (locate-dominating-file dir ".project"))
+ (cons 'local root)))
+ (add-to-list 'project-find-functions #'op/project-try-local)
+
+ (cl-defmethod project-root ((project (head local)))
+ (cdr project)))
+ #+end_src
+
+*** TODO add some mechanism to ignore files
+** Scratchpads
+ Scratchpads are useful. I wrote a [[*Scratchpads][small package]] to create
+ custom scratchpads on-the-fly. By default it creates a
+ =*scratch*<n>= buffer in the current =major-mode=, but the starting
+ mode can be chosen by invoking =scratchpad-new-scratchpad= with a
+ prefix argument.
+
+ #+begin_src emacs-lisp
+ (use-package scratchpads
+ :bind ("C-z s" . scratchpads-new-scratchpad)
+ :straight nil)
+ #+end_src
+** Occur & loccur
+ Occur is a grep-like functionality for Emacs. It populates the
+ =*occur*= buffer with the lines matching a certain regexp in the
+ current buffer. It's super-useful.
+
+ #+begin_src emacs-lisp
+ (use-package replace
+ :straight nil
+ :bind (("C-c o" . occur)))
+ #+end_src
+
+ =loccur= is similar, but instead of using a separate buffer, it
+ visually hides all the non-matching lines, also super useful!
+
+ #+begin_src emacs-lisp
+ (use-package loccur
+ :bind (("C-c O" . loccur)))
+ #+end_src
+** hideshow
+ Hideshow is a built-in package to fold section of code. It has
+ some really awkward keybindings under =C-c @=, but otherwise is
+ nice, sometimes.
+
+ #+begin_src emacs-lisp
+ (add-hook 'prog-mode-hook #'hs-minor-mode)
+ #+end_src
+** Smartparens
+ Smartparens has become my go-to package for managing parethesis and
+ the like. The peculiar thing is that, unlike packages such as
+ paredit, it works on any language, not only lisp-y ones.
+
+ #+begin_src emacs-lisp
+ (use-package smartparens
+ :bind (:map smartparens-mode-map
+ ("C-M-f" . sp-forward-sexp)
+ ("C-M-b" . sp-backward-sexp)
+
+ ("C-M-a" . sp-beginning-of-sexp)
+ ("C-M-e" . sp-end-of-sexp)
+ ("C-M-n" . sp-next-sexp)
+ ("C-M-p" . sp-previous-sexp)
+
+ ("C-(" . sp-forward-barf-sexp)
+ ("C-)" . sp-forward-slurp-sexp)
+ ("C-{" . sp-backward-barf-sexp)
+ ("C-}" . sp-backward-slurp-sexp)
+
+ ("C-k" . sp-kill-hybrid-sexp)
+
+ ("C-," . sp-rewrap-sexp)
+
+ :map emacs-lisp-mode-map
+ (";" . sp-comment)
+
+ :map lisp-mode-map
+ (";" . sp-comment))
+ :hook ((prog-mode . turn-on-smartparens-strict-mode)
+ (web-mode . op/sp-web-mode)
+ (LaTeX-mode . turn-on-smartparens-strict-mode))
+ :custom ((sp-highlight-pair-overlay nil))
+ :config
+ (require 'smartparens-config)
+
+ (with-eval-after-load 'clojure-mode
+ (define-key clojure-mode-map ";" #'sp-comment))
+
+ (with-eval-after-load 'scheme-mode
+ (define-key scheme-mode-map ";" #'sp-comment))
+
+ (sp-with-modes 'org-mode
+ (sp-local-pair "=" "=" :wrap "C-="))
+
+ (bind-key [remap c-electric-backspace] #'sp-backward-delete-char
+ smartparens-strict-mode-map)
+
+ (sp-local-pair 'log-edit-mode "`" "'")
+
+ (defun op/sp-web-mode ()
+ (setq web-mode-enable-auto-pairing nil))
+
+ (defun op/newline-indent (&rest _ignored)
+ (split-line)
+ (indent-for-tab-command))
+
+ (let ((c-like '(awk-mode c++mode cc-mode c-mode css-mode go-mode java-mode
+ js-mode json-mode python-mode web-mode es-mode
+ perl-mode)))
+ (dolist (x `(("{" . ,c-like)
+ ("[" . ,c-like)
+ ("(" . (sql-mode ,@c-like))))
+ (dolist (mode (cdr x))
+ (sp-local-pair mode (car x) nil :post-handlers
+ '((op/newline-indent "RET")
+ (op/newline-indent "<return>"))))))
+
+ (defun op/inside-comment-or-string-p ()
+ "T if point is inside a string or comment."
+ (let ((s (syntax-ppss)))
+ (or (nth 4 s) ;comment
+ (nth 3 s))))
+
+ (defun op/current-line-str ()
+ "Return the current line as string."
+ (buffer-substring-no-properties (line-beginning-position)
+ (line-end-position)))
+
+ (defun op/maybe-add-semicolon-paren (_id action _ctx)
+ "Insert semicolon after parens when appropriat.
+ Mainly useful in C and derived, and only when ACTION is insert."
+ (when (eq action 'insert)
+ (save-excursion
+ ;; caret is between parens (|)
+ (forward-char)
+ (let ((line (op/current-line-str)))
+ (when (and (looking-at "\\s-*$")
+ (not (string-match-p
+ (regexp-opt '("if" "else" "switch" "for" "while"
+ "do" "define")
+ 'words)
+ line))
+ (string-match-p "[\t ]" line)
+ (not (op/inside-comment-or-string-p)))
+ (insert ";"))))))
+
+ (let ((c-like-modes-list '(c-mode c++-mode java-mode perl-mode)))
+ (sp-local-pair c-like-modes-list "(" nil
+ :post-handlers
+ '(:add op/maybe-add-semicolon-paren)))
+
+ (defhydra hydra-sp (:hint nil)
+ "
+ Moving^^^^ Slurp & Barf^^ Wrapping^^ Sexp juggling^^^^ Destructive
+ ------------------------------------------------------------------------------------------------------------------------
+ [_a_] beginning [_n_] down [_h_] bw slurp [_R_] rewrap [_S_] split [_t_] transpose [_c_] change inner [_w_] copy
+ [_e_] end [_N_] bw down [_H_] bw barf [_u_] unwrap [_s_] splice [_A_] absorb [_C_] change outer
+ [_f_] forward [_p_] up [_l_] slurp [_U_] bw unwrap [_r_] raise [_E_] emit [_k_] kill [_g_] quit
+ [_b_] backward [_P_] bw up [_L_] barf [_(__{__[_] wrap (){}[] [_j_] join [_o_] convolute [_K_] bw kill [_q_] quit"
+ ("?" (hydra-set-property 'hydra-sp :verbosity 1))
+
+ ;; moving
+ ("a" sp-beginning-of-sexp)
+ ("e" sp-end-of-sexp)
+ ("f" sp-forward-sexp)
+ ("b" sp-backward-sexp)
+ ("n" sp-down-sexp)
+ ("N" sp-backward-down-sexp)
+ ("p" sp-up-sexp)
+ ("P" sp-backward-up-sexp)
+
+ ;; slurping & barfing
+ ("h" sp-backward-slurp-sexp)
+ ("H" sp-backward-barf-sexp)
+ ("l" sp-forward-slurp-sexp)
+ ("L" sp-forward-barf-sexp)
+
+ ;; wrapping
+ ("R" sp-rewrap-sexp)
+ ("u" sp-unwrap-sexp)
+ ("U" sp-backward-unwrap-sexp)
+ ("(" sp-wrap-round)
+ ("[" sp-wrap-square)
+ ("{" sp-wrap-curly)
+
+ ;; sexp juggling
+ ("S" sp-split-sexp)
+ ("s" sp-splice-sexp)
+ ("r" sp-raise-sexp)
+ ("j" sp-join-sexp)
+ ("t" sp-transpose-sexp)
+ ("A" sp-absorb-sexp)
+ ("E" sp-emit-sexp)
+ ("o" sp-convolute-sexp)
+
+ ;; destructive editing
+ ("c" sp-change-inner :exit t)
+ ("C" sp-change-enclosing :exit t)
+ ("k" sp-kill-sexp)
+ ("K" sp-backward-kill-sexp)
+ ("w" sp-copy-sexp)
+
+ ("q" nil)
+ ("g" nil))
+
+ (define-key global-map (kbd "s-c")
+ (lambda ()
+ (interactive)
+ (hydra-set-property 'hydra-sp :verbosity 0)
+ (hydra-sp/body))))
+ #+end_src
+
+*** TODO the configuration is quite long, can it be made modular?
+
+** Flymake
+ Flymake marks errors in buffer, using various means. [[*eglot][LSP]] is one of
+ those. For starters, enable it for every =prog-mode= buffer
+
+ #+begin_src emacs-lisp
+ (add-hook 'prog-mode-hook #'flymake-mode)
+ #+end_src
+
+ Tweak its settings a bit
+ #+begin_src emacs-lisp
+ (setq flymake-fringe-indicator-position 'left-fringe
+ flymake-suppress-zero-counters t
+ flymake-start-on-flymake-mode t
+ flymake-no-changes-timeout nil
+ flymake-start-on-save-buffer t
+ flymake-proc-compilation-prevents-syntax-check t
+ flymake-wrap-around nil)
+ #+end_src
+
+ and make a hydra for it
+
+ #+begin_src emacs-lisp
+ (with-eval-after-load 'flymake
+ (defhydra hydra-flymake (flymake-mode-map "C-c !")
+ ("n" flymake-goto-next-error)
+ ("p" flymake-goto-prev-error)
+ ("RET" nil :exit t)
+ ("q" nil :exit t)))
+ #+end_src
+** Flyspell and friends
+ Flyspell is Flymake, but for natural languages! /s
+
+ #+begin_src emacs-lisp
+ (add-hook 'text-mode-hook #'flyspell-mode)
+ #+end_src
+*** guess language
+ One annoying thing of not being a native English speaker is that I
+ need Emacs to handle more than one language. That means
+ constantly =M-x ispell-change-dictionary=, or one cane use
+ =guess-language=!
+
+ It uses a statistical method to detect the language, which seems
+ to work pretty well for English and Italian. It even supports
+ multiple languages in the same buffer (as long as they appear in
+ different paragraphs). The only drawback is that sometimes Emacs
+ gets stuck executing =ispell=, but a =pkill -USR2= on the server
+ pid fixes it.
+
+ #+begin_src emacs-lisp
+ (use-package guess-language
+ :hook (text-mode . guess-language-mode)
+ :config
+ (setq guess-language-langcodes '((en . ("en_GB" "English"))
+ (it . ("it" "Italian")))
+ guess-language-languages '(en it)
+ guess-language-min-paragraph-length 45))
+ #+end_src
+** Typo(graphical stuff)
+ Typo transforms certain character into their "typographical"
+ counterpart. I like to use it when writing in my blog, so enable
+ it for =gemini-mode=.
+
+ #+begin_src emacs-lisp
+ (use-package typo
+ :hook ((gemini-mode . typo-mode))
+ :config
+ (push '("Italian" "“" "”" "‘" "’" "«" "»")
+ typo-quotation-marks))
+ #+end_src
+
+ Olivetti mode "centers" the buffer, it's nice when writing text:
+ #+begin_src emacs-lisp
+ (use-package olivetti
+ :hook ((gemini-mode . olivetti-mode)
+ (org-mode . olivetti-mode)
+ (markdown-mode . olivetti-mode)))
+ #+end_src
+
+ I also do typos pretty often, and abbrev is handy for those
+ occasions and accents (like "perchè" instead of "perché").
+
+ [[*my-abbrev][=my-abbrev=]] is a package-like file where I store the abbreviations
+ I need.
+ #+begin_src emacs-lisp
+ (use-package my-abbrev
+ :straight nil)
+ #+end_src
+** hippie expand
+ This is a "dumb" completion method. It tries a couple of method to
+ complete the word before the cursor. Turns out, for how
+ rudimentary it may be, it's often precise.
+
+ #+begin_src emacs-lisp
+ (define-key global-map (kbd "M-/") #'hippie-expand)
+
+ (setq hippie-expand-try-functions-list
+ '(try-expand-dabbrev
+ try-expand-dabbrev-all-buffers
+ try-expand-dabbrev-from-kill
+ try-complete-file-name-partially
+ try-complete-file-name
+ try-expand-all-abbrevs
+ try-expand-list
+ try-expand-line
+ try-complete-lisp-symbol-partially
+ try-complete-lisp-symbol))
+ #+end_src
+** isearch
+ Some very small tweaks for isearch
+ #+begin_src emacs-lisp
+ (setq isearch-lazy-count t
+ search-whitespace-regexp ".*?"
+ isearch-allow-scroll 'unlimited)
+ #+end_src
+** etags
+ Reload tags without asking
+ #+begin_src emacs-lisp
+ (setq tags-revert-without-query 1)
+ #+end_src
+** view mode
+ Sometimes it's handy to make a buffer read-only. Also, define some
+ key to easily navigate in read-only buffers.
+
+ #+begin_src emacs-lisp
+ (use-package view
+ :straight nil
+ :bind (("C-x C-q" . view-mode)
+ :map view-mode-map
+ ("n" . next-line)
+ ("p" . previous-line)
+ ("l" . recenter-top-bottom)))
+ #+end_src
+** avy
+ I definitely need to use it more. It allows to quickly jump
+ around, both in the same and in other buffers.
+
+ #+begin_src emacs-lisp
+ (use-package avy
+ :custom ((avy-keys '(?s ?n ?t ?h ?d ?i ?u ?e ?o ?a)))
+ :bind (("M-g c" . avy-goto-char)
+ ("M-g C" . avy-goto-char-2)
+ ("M-g w" . avy-goto-word-1)
+ ("M-g f" . avy-goto-line)
+ :map isearch-mode-map
+ ("C-'" . avy-isearch)))
+ #+end_src
+** iedit
+ I tried to use =multiple-cursor=, but I just fail. =iedit= does
+ 99% of what I need.
+
+ The following is a small tweak for it, maybe it's unnecessary as I
+ haven't read the documentation in depth.
+
+ #+begin_src emacs-lisp
+ (use-package iedit
+ :bind (("C-;" . op/iedit-dwim))
+ :config
+ (defun op/iedit-dwim (arg)
+ "Start iedit but do what I mean.
+ With a prefix (i.e. non-nil ARG) just execute `iedit-mode'; if
+ the region is active start iedit in the current defun (as by
+ `narrow-to-defun') with the current selection as replacement
+ search string. if a region is not active, do the same but with
+ `current-word'. Inspired, but modified, by the
+ masteringemacs.org article."
+ (interactive "P")
+ (if arg
+ (iedit-mode)
+ (let (beg end)
+ (save-excursion
+ (save-restriction
+ (widen)
+ (narrow-to-defun)
+ (setq beg (point-min)
+ end (point-max))))
+ (cond (iedit-mode (iedit-done))
+ ((use-region-p) (iedit-start (regexp-quote
+ (buffer-substring-no-properties (mark)
+ (point)))
+ beg end))
+ (t (iedit-start (concat "\\<"
+ (regexp-quote (current-word))
+ "\\>")
+ beg end)))))))
+ #+end_src
+** Languages
+*** LISP-y stuff
+*** jump to matching paren
+ The idea behind this is really cool. Pressing =%= with the cursor
+ on (or before) a parenthesis (of any kind) will jump to the other
+ side. Unfortunately, it doesn't play well with Clojure, where =%=
+ is used for the "terse" lambda syntax (i.e. =#(assoc foo :bar %)=)
+
+ #+begin_src emacs-lisp
+ (use-package paren
+ :straight nil
+ ;; :bind (("%" . op/match-paren))
+ :config
+ (show-paren-mode +1)
+
+ ;; thanks, manual
+ (defun op/match-paren (arg)
+ "Go to the matchig paren if on a paren; otherwise self-insert."
+ (interactive "p")
+ (cond ((looking-at "\\s(") (forward-list 1) (backward-char 1))
+ ((looking-at "\\s)") (forward-char 1) (backward-list 1))
+ (t (self-insert-command (or arg 1))))))
+ #+end_src
+*** eglot
+ LSP stands for =Language Something Protocol=, developed by M$ for
+ vs-code, but − trust me, it's weird to say it − it seems a
+ /decent/ idea.
+
+ There are two major implementations for emacs: =lsp-mode= and
+ =eglot=. lsp-mode is too noisy for me, I prefer =eglot= as it's
+ less intrusive
+
+ #+begin_src emacs-lisp
+ (use-package eglot
+ :bind (:map eglot-mode-map
+ ("<f1>" . eglot-code-actions)
+ ("<f2>" . eglot-format))
+ :config
+ (add-to-list 'eglot-server-programs
+ '(c-mode . ("clangd" "--header-insertion=never")))
+
+ ;; get rid of the highlighting when I'm hovering a symbol
+ (add-to-list 'eglot-ignored-server-capabilites
+ :documentHighlightProvider))
+ #+end_src
+
+ =clangd= has an annoying "feature": it automatically adds include
+ when it thinks they're needed.
+
+ Protip: when working on a C project, one needs a
+ =compile-commands.json= file. But, most of the time, a simple
+ =compile_flags.txt= with the =$CFLAGS= one per line is enough.
+ See gmid Makefile for instance, but usually this is enough:
+ #+begin_src makefile :tangle no
+ compile_commands.txt:
+ printf "%s\n" ${CFLAGS} > $@
+ #+end_src
+*** prog-mode
+ Enable auto-fill for comments in =prog-mode= buffers:
+ #+begin_src emacs-lisp
+ (defun op/auto-fill-comment ()
+ "Enable auto-fill for comments."
+ (setq-local comment-auto-fill-only-comments t)
+ (auto-fill-mode))
+ (add-hook 'prog-mode-hook #'op/auto-fill-comment)
+ #+end_src
+*** text-mode
+ Enable abbrev-mode in text buffers:
+ #+begin_src emacs-lisp
+ (add-hook 'text-mode-hook #'abbrev-mode)
+ #+end_src
+*** elisp
+ Enable prettify and checkdock in emacs lisp mode: the former
+ transforms =lambda= into =λ=, and the latter enables style warning
+ for elisp packages
+
+ #+begin_src emacs-lisp
+ (add-hook 'emacs-lisp-mode-hook #'checkdoc-minor-mode)
+ (add-hook 'emacs-lisp-mode-hook #'prettify-symbols-mode)
+ #+end_src
+
+ Bind a key to run all the tests and to spawn ielm:
+ #+begin_src emacs-lisp
+ (defun op/ert-all ()
+ "Run all ert tests."
+ (interactive)
+ (ert t))
+
+ (defun op/ielm-repl (arg)
+ "Pop up a ielm buffer."
+ (interactive "P")
+ (let ((buf (get-buffer-create "*ielm*")))
+ (if arg
+ (switch-to-buffer buf)
+ (pop-to-buffer buf))
+ (ielm)))
+
+ (let ((map emacs-lisp-mode-map))
+ (define-key map (kbd "C-c C-k") #'eval-buffer)
+ (define-key map (kbd "C-c k") #'op/ert-all)
+ (define-key map (kbd "C-c C-z") #'op/ielm-repl))
+ #+end_src
+
+ Eros is a nice little package that renders the output of
+ =eval-last-sexp= in a small overlay right after the cursor, just
+ like CIDER!
+
+ #+begin_src emacs-lisp
+ (use-package eros
+ :config (eros-mode 1))
+ #+end_src
+
+ Emacs-lisp doesn't have namespaces, so usually there's this
+ convention of prefixing every symbol of a package with the package
+ name. Nameless helps with this. It binds =_= to insert the name
+ of the package, and it visually replace it with =:=. It's pretty
+ cool.
+
+ #+begin_src emacs-lisp
+ (use-package nameless
+ :hook (emacs-lisp-mode . nameless-mode)
+ :custom ((nameless-private-prefix t)
+ (nameless-affect-indentation-and-filling nil))
+ :bind (:map emacs-lisp-mode-map
+ ("_" . nameless-insert-name-or-self-insert)))
+ #+end_src
+*** Common LISP
+ I'm trying to use this convention for repls:
+ - =C-c C-z= opens a repl at the bottom of the window
+ - =C-u C-c C-z= opens the repl in the current buffer
+
+ #+begin_src emacs-lisp
+ (use-package sly
+ :hook ((lisp-mode . prettify-symbols-mode)
+ (lisp-mode . op/disable-tabs)
+ ;; (lisp-mode . sly-symbol-completion-mode)
+ )
+ :custom (inferior-lisp-program "sbcl")
+ :bind (:map sly-mode-map
+ ("C-c C-z" . op/sly-mrepl))
+ :config
+ (defun op/sly-mrepl (arg)
+ "Find or create the first useful REPL for the default connection in a side window."
+ (interactive "P")
+ (save-excursion
+ (sly-mrepl nil))
+ (let ((buf (sly-mrepl--find-create (sly-current-connection))))
+ (if arg
+ (switch-to-buffer buf)
+ (pop-to-buffer buf))))
+
+ (use-package sly-mrepl
+ :straight nil ;; it's part of sly!
+ :bind (:map sly-mrepl-mode-map
+ ("M-r" . comint-history-isearch-backward))))
+ #+end_src
+*** Clojure
+ Load =clojure-mode= from MELPA (I guess, or is it ELPA?)
+
+ #+begin_src emacs-lisp
+ (use-package clojure-mode
+ :mode (("\\.clj" . clojure-mode)
+ ("\\.cljs" . clojurescript-mode)
+ ("\\.cljc" . clojurec-mode)
+ ("\\.edn" . clojure-mode))
+ :hook ((clojure-mode . subword-mode)
+ (clojurec-mode . subword-mode)
+ (clojurescript-mode . subword-mode)
+
+ (clojure-mode . op/disable-tabs)
+ (clojurec-mode . op/disable-tabs)
+ (clojurescript-mode . op/disable-tabs)
+
+ (clojure-mode . abbrev-mode)
+ (clojurec-mode . abbrev-mode)
+ (clojurescript-mode . abbrev-mode))
+ :config
+ (put-clojure-indent 'doto-cond '(1 nil nil (1))))
+ #+end_src
+
+ =doto-cond= is a macro I wrote some time ago, I don't remember
+ where, but anyway.
+
+ CIDER is the Clojure Interactive Development Environment that
+ Rocks, aka the best thing for clojure. Just like with ielm and
+ sly, use my convention for =C-c C-z= behaviour wrt prefix
+ argument, but tweak also the key so the repl behaves more like a
+ comint buffer.
+
+ #+begin_src emacs-lisp
+ (use-package cider
+ :custom (cider-repl-display-help-banner nil)
+ :bind (:map cider-repl-mode-map
+ ;; more like comint
+ ("C-c M-o" . cider-repl-clear-buffer)
+ ("C-c C-l" . cider-repl-switch-to-other)
+ :map cider-mode-map
+ ("C-c C-z" . op/cider-repl))
+ :config
+ (defun op/cider-repl (arg)
+ "Switch to repl buffer in side window.
+ With non-nil ARG use `display-buffer' ignoring the rules in
+ `display-buffer-alist'."
+ (interactive "P")
+ (when-let (buf (cider-current-repl))
+ (call-interactively #'cider-repl-set-ns)
+ (let ((display-buffer-alist (if arg
+ ()
+ display-buffer-alist)))
+ (pop-to-buffer buf '(display-buffer-reuse-window))))))
+ #+end_src
+*** Scheme
+ Geiser works for any scheme IIRC, but needs a tweak to find
+ =guile= in my system.
+
+ #+begin_src emacs-lisp
+ (use-package geiser
+ :config
+ (setq geiser-guile-binary "guile3.0"))
+ #+end_src
+*** Elastic search mode
+ =es-mode= let one write kibana-like queries and execute them from
+ Emacs.
+
+ #+begin_src emacs-lisp
+ (use-package es-mode
+ :mode "\\.es\\'")
+ #+end_src
+*** SQL
+ =op/visit-new-migration-file= prompts for a name and creates an
+ associated migration file, named after =$date-$name.sql=.
+
+ #+begin_src emacs-lisp
+ (defun op/visit-new-migration-file (name)
+ "Visit a new SQL migration file named after NAME."
+ (interactive "Mname: ")
+ (let* ((name (replace-regexp-in-string " " "-" (string-trim name)))
+ (f (format "%s-%s.sql"
+ (format-time-string "%Y%m%d%H%M")
+ name)))
+ (find-file f)))
+ #+end_src
+
+ To please my muscle memory:
+ #+begin_src emacs-lisp
+ (defalias 'psql #'sql-postgres)
+ #+end_src
+
+ Sometimes I need to connect to a PostgreSQL database over a
+ non-standard port, so here's a quick function to do that
+ #+begin_src emacs-lisp
+ (defun op/psql-params (port)
+ "Easily connect to a psql on a non-standard PORT."
+ (interactive "nPort: ")
+ (let ((sql-port port))
+ (psql)))
+ #+end_src
+
+ I don't particularly like how the =electric-indent= behaves in SQL
+ buffers, so try to tame it
+ #+begin_src emacs-lisp
+ (defun op/sql-sane-electric-indent-mode ()
+ "Fix function `electric-indent-mode' behaviour locally."
+ (interactive)
+ (setq-local electric-indent-inhibit nil))
+
+ (add-hook 'sql-mode-hook #'op/sql-sane-electric-indent-mode)
+ #+end_src
+
+ The lines in the interactive SQL buffer can get long, and
+ truncation makes them look awful.
+ #+begin_src emacs-lisp
+ (add-hook 'sql-interactive-mode-hook #'toggle-truncate-lines)
+ #+end_src
+
+ Finally, define some handy keys to open a connection
+ #+begin_src emacs-lisp
+ (define-key global-map (kbd "C-z a s") #'psql)
+ (define-key global-map (kbd "C-z a S") #'op/psql-params)
+ #+end_src
+*** web
+ =web-mode= provides font-lock, indentation and stuff for various
+ "web-related" file types.
+
+ By enabling =web-mode-enable-engine-detection= it became possible
+ to define =web-mode-engines-alist= and having =web-mode= selecting
+ the engine from that alist.
+
+ #+begin_src emacs-lisp
+ (use-package web-mode
+ :mode (("\\.erb\\'" . web-mode)
+ ("\\.mustache\\'" . web-mode)
+ ("\\.html\\'" . web-mode))
+ :custom ((web-mode-markup-indent-offset 2)
+ (web-mode-css-indent-offset 2)
+ (web-mode-code-indent-offset 2)
+ (web-mode-style-padding 0)
+ (web-mode-enable-engine-detection t))
+ :hook ((web-mode . op/disable-tabs)))
+ #+end_src
+
+ It's useful to use a =.dir-locals.el= file to customize the engine
+ selection, but that unfortunately doesn't work out-of-the-box.
+ The following hack is needed:
+
+ #+begin_src emacs-lisp
+ (with-eval-after-load 'web-mode
+ (defun op/web-mode-fix-dir-locals ()
+ (when (derived-mode-p major-mode 'web-mode)
+ (web-mode-guess-engine-and-content-type)))
+ (add-hook 'hack-local-variables-hook #'op/web-mode-fix-dir-locals))
+ #+end_src
+*** CSS
+ I don't use =web-mode= for CSS, emacs bulit in mode works pretty
+ well. Just disable hard tabs:
+ #+begin_src emacs-lisp
+ (use-package css-mode
+ :hook (css-mode . op/disable-tabs))
+ #+end_src
+*** javascript
+ Just load some useful modes and disable tabs
+ #+begin_src emacs-lisp
+ (use-package js
+ :straight nil
+ :hook ((js-mode . abbrev-mode)
+ (js-mode . subword-mode)
+ (js-mode . op/disable-tabs)))
+ #+end_src
+*** C
+ Usually I follow the [[https://man.openbsd.org/style][OpenBSD KNF style(9)]] guidelines when writing
+ C.
+
+ #+begin_src emacs-lisp
+ (setq c-basic-offset 8
+ c-default-style "K&R")
+
+ (defun op/c-indent ()
+ (interactive)
+ (c-set-offset 'arglist-intro '+)
+ (c-set-offset 'arglist-cont-nonempty '*))
+
+ (add-hook 'c-mode-hook #'op/c-indent)
+ #+end_src
+
+ Subword and abbrev mode are particularly useful. With abbrev I
+ can easily fix typos like =#inculde= → =#include=, and subword is
+ useful for camelCase/PascalCase function name (fortunately enough,
+ they aren't widespread in C)
+
+ #+begin_src emacs-lisp
+ (dolist (hook '(c-mode-hook c++-mode-hook))
+ (add-hook hook #'abbrev-mode)
+ (add-hook hook #'subword-mode))
+ #+end_src
+
+ [[*Smartparens][My smartparens configuration]] automatically adds semicolon when
+ appropriate (well, most of the times). While it's useful, typing
+ a line of code soon becomes a matter of typing the code and then
+ =C-e RET= to go to the next line. Fortunately we can optimise it:
+
+ #+begin_src emacs-lisp
+ (defun op/open-line-under ()
+ "Like `open-line', but under."
+ (interactive)
+ (move-end-of-line 1)
+ (newline)
+ (c-indent-line))
+
+ (with-eval-after-load 'c-mode
+ (define-key c-mode-map (kbd "M-RET") #'op/open-line-under))
+ #+end_src
+
+ Use some similar (but slightly different) smartparens key and
+ reserve a key for =recompile=.
+ #+begin_src emacs-lisp
+ (with-eval-after-load 'c-mode-map
+ (let ((map c-mode-map))
+ (define-key map (kbd "<tab>") #'indent-for-tab-command)
+ (define-key map (kbd "TAB") #'indent-for-tab-command)
+ (define-key map (kbd "C-M-a") #'sp-beginning-of-sexp)
+ (define-key map (kbd "C-M-e") #'sp-end-of-sexp)
+ (define-key map (kbd "C-M-p") #'beginning-of-defun)
+ (define-key map (kbd "C-M-n") #'end-of-defun)
+ (define-key map (kbd "C-c M-c") #'recompile)))
+ #+end_src
+
+ I used to use =irony=, but now I mostly use [[*eglot][eglot]] if I really need
+ "advanced" support. Just for history sake, here's my old
+ configuration (this is *not* tangled)
+
+ #+begin_src emacs-lisp :tangle no
+ (use-package irony
+ :hook ((c++-mode . irony-mode)
+ (c-mode . irony-mode)
+ (obj-mode . irony-mode)))
+ #+end_src
+*** Go
+ My go configuration is simple: just load =go-mode=!
+ #+begin_src emacs-lisp
+ (use-package go-mode
+ :mode "\\.go\\'"
+ :hook ((go-mode . subword-mode)))
+ #+end_src
+*** Perl
+ Just require =perl-mode= and ensure we indent with hard tabs
+ #+begin_src emacs-lisp
+ (use-package perl-mode
+ :straight nil
+ :custom ((perl-indent-level 8)))
+ #+end_src
+*** Python
+ Load =python-mode= and disable hard tabs:
+ #+begin_src emacs-lisp
+ (use-package python
+ :hook ((python-mode . op/disable-tabs)))
+ #+end_src
+*** sh-mode
+ Simple stuff, set the tab width and fix the indentation
+ #+begin_src emacs-lisp
+ (use-package sh-script
+ :straight nil
+ :custom ((sh-basic-offset 8)
+ (sh-indent-after-loop-construct 8)
+ (sh-indent-after-continuation nil)))
+ #+end_src
+**** TODO fix smartparens and sh' =case=
+ In a case statement, we have un-paired closed parethesis that
+ require =C-q= to be typed because of =sp-strict-mode=.
+*** Lua
+ Nothing fancy, just load the package
+ #+begin_src emacs-lisp
+ (use-package lua-mode
+ :mode "\\.lua\\'")
+ #+end_src
+*** GDScript
+ GDScript is the scripting language of the Godot game engine. The
+ =gdscript-mode= provides also format (via a python program) and
+ integration with Godot:
+ #+begin_src emacs-lisp
+ (use-package gdscript-mode
+ :mode "\\.gd\\'"
+ :custom (gdscript-gdformat-save-and-format t))
+ #+end_src
+
+ It needs an external program for the formatting:
+ #+begin_src shell :tangle no
+ pip3 install --user gdtoolkit
+ #+end_src
+
+ but see the [[https://github.com/godotengine/emacs-gdscript-mode][repository on GitHub]] for more informations!
+*** YAML
+ Yet another simple block for Yet Another Markup Language.
+
+ Disable flyspell in yaml. It inherits from =text-mode= but most
+ of the time grammar check doesn't yield anything useful.
+ #+begin_src emacs-lisp
+ (use-package yaml-mode
+ :mode "\\.yml\\'"
+ :hook ((yaml-mode . turn-off-flyspell)))
+ #+end_src
+*** TOML
+ #+begin_src emacs-lisp
+ (use-package toml-mode
+ :mode "\\.toml\\'")
+ #+end_src
+*** Gemini (text/gemini)
+ Fetch =gemini-mode= package. Also, I like to write text/gemini
+ with a nice proportial font and a "bar" as cursor, just like I do
+ with org-mode!
+
+ #+begin_src emacs-lisp
+ (use-package gemini-mode
+ :hook ((gemini-mode . op/gemini-setup)
+ (gemini-mode . variable-pitch-mode))
+ :config
+ (defun op/gemini-setup ()
+ (setq-local cursor-type 'bar)))
+ #+end_src
+
+*** Markdown
+ Install =markdown-mode= and enable auto fill.
+
+ #+begin_src emacs-lisp
+ (use-package markdown-mode
+ :mode "\\.md\\'"
+ :hook ((markdown-mode . auto-fill-mode)))
+ #+end_src
+* Applications
+ Here, configuration for various non text editing related stuff.
+
+ #+begin_src emacs-lisp
+ (defun op/tigervnc->chiaki ()
+ "Connects to chiaki over tigervnc."
+ (interactive)
+ (async-shell-command "vncviewer.tigervnc 192.168.1.11"))
+
+ (define-key global-map (kbd "C-z a c") #'op/tigervnc->chiaki)
+ #+end_src
+
+** bins
+ It's useful to send a buffer, or part of it, to a bin online and
+ then send the corresponding link to someone. The [[*clbin][clbin]] package
+ does that, in a DWIM manner: send the current region (if any) or
+ the whole buffer, and save the corresponding url in the kill-ring.
+
+ #+begin_src emacs-lisp
+ (use-package clbin
+ :straight nil
+ :bind ("C-z w" . clbin-dwim))
+ #+end_src
+
+** sam for the rescue!
+ I like the design of various plan9 stuff, even if I haven't used
+ the system. [[https://github.com/omar-polo/sam.el][=sam.el=]] is my ongoing (and slow) attempt at emulating
+ sam
+
+ #+begin_src emacs-lisp
+ (use-package sam
+ :straight nil
+ :load-path "~/w/sam/master/")
+ #+end_src
+
+** eshell
+ Eshell is the Emacs Shell. It's a strange combo, because it isn't
+ a full-blown elisp REPL like ielm, but neither a UNIX shell like
+ =shell=. It seems fun to use though.
+
+ I like to sync the =$PWD= with the buffer name, so an eshell in my
+ home has as buffer name =*eshell /home/op*=.
+
+ =cl= is a better clear function, because the built in =clear= is a
+ joke.
+
+ =C-c C-B= uses the minibuffer to select a buffer and insert the
+ eshell code to append that command output to the selected buffer.
+
+ #+begin_src emacs-lisp
+ (use-package eshell
+ :bind (("C-c e" . op/eshell))
+ :hook (eshell-mode . op/setup-eshell)
+ :custom ((eshell-compl-dir-ignore
+ "\\`\\(\\.\\.?\\|CVS\\|\\.svn\\|\\.git\\|\\.got\\)/\\'")
+ (eshell-save-history-on-exit t)
+ (eshell-prompt-function (lambda () "$ ")))
+ :config
+ (defun eshell/emacs (&rest args)
+ "Open a file in emacs (from the wiki)."
+ (if (null args)
+ (bury-buffer)
+ (mapc #'find-file
+ (mapcar #'expand-file-name
+ (eshell-flatten-list (nreverse args))))))
+
+ (defalias 'eshell/less #'find-file)
+
+ (defun eshell/dired ()
+ (dired (eshell/pwd)))
+
+ (defun eshell/cl ()
+ "Clear the eshell buffer."
+ (let ((inhibit-read-only t))
+ (erase-buffer)))
+
+ (defun op/eshell-bufname (dir)
+ (concat "*eshell " (expand-file-name dir) "*"))
+
+ (defun op/eshell-after-cd (&rest _)
+ (rename-buffer (op/eshell-bufname default-directory) t))
+ (advice-add #'eshell/cd :after #'op/eshell-after-cd)
+
+ (defun op/eshell (arg)
+ "Run eshell in the current project (or directory if ARG.)"
+ (interactive "P")
+ (let* ((proj (project-current))
+ (dir (cond (arg default-directory)
+ (proj (project-root proj))
+ (t default-directory)))
+ (default-directory dir)
+ (eshell-buffer-name (op/eshell-bufname dir)))
+ (eshell)))
+
+ (defun op/append-to-buffer (buf)
+ (interactive "Bbuffer: ")
+ (insert ">>> " "#<" buf ">"))
+
+ ;; eshell has this thing where sets eshell-mode-map as
+ ;; buffer-local... just why?
+ (defun op/setup-eshell ()
+ (define-key eshell-mode-map (kbd "C-c C-B") #'op/append-to-buffer)))
+ #+end_src
+
+
+*** TODO make =>>>= ignore =stderr=
+
+** vterm ― when eshell isn't enough
+ vterm is a full-blown terminal emulator for Emacs, powered by the
+ same library that GNOME terminal uses (IIRC)
+
+ #+begin_src emacs-lisp
+ (use-package vterm
+ :bind (:map vterm-mode-map
+ ("C-<backspace>" . op/vterm-fix-c-backspace)
+ ("C-c M-t" . vterm-copy-mode)
+ :map vterm-copy-mode-map
+ ("C-c M-t" . vterm-copy-mode))
+ :bind-keymap ("C-z v" . op/vterm-map)
+ :custom ((vterm-buffer-name-string "*vterm %s*"))
+ :config
+ (defun op/vterm-fix-c-backspace ()
+ (interactive)
+ (vterm-send-key (kbd "C-w")))
+
+ (defvar *op/hostname*
+ (with-temp-buffer
+ (process-file "hostname" nil (current-buffer))
+ (string-trim (buffer-string) "" "\n"))
+ "The hostname of this machine.")
+
+ (defun op/project-vterm ()
+ "Spawn a vterm in the current project."
+ (interactive)
+ (let* ((project-current (project-current))
+ (default-directory (if project-current
+ (project-root project-current)
+ default-directory)))
+ (vterm)))
+
+ (define-prefix-command 'op/vterm-map)
+ (define-key op/vterm-map (kbd "v") #'vterm)
+ (define-key op/vterm-map (kbd "v") #'op/project-vterm))
+ #+end_src
+** ibuffer
+ Ibuffer is a package to list, filter and do stuff on the buffers.
+
+ #+begin_src emacs-lisp
+ (define-key global-map (kbd "C-x C-b") #'ibuffer)
+ #+end_src
+** help
+ Help buffers are, no pun intended, helpful. But they are so more
+ with a bit o' visual lines:
+ #+begin_src emacs-lisp
+ (add-hook 'help-mode-hook #'visual-line-mode)
+ #+end_src
+** gdb
+ I like the "many buffer" view of gdb, so let's enable it
+ #+begin_src emacs-lisp
+ (setq gdb-many-windows t)
+ #+end_src
+
+ And to aid my muscle memory from the shell, define an =egdb= alias
+ #+begin_src emacs-lisp
+ (defalias 'egdb #'gdb)
+ #+end_src
+** Literate calc-mode
+ This is pretty awesome. It provides a major mode where we can type
+ expression and do math inline. Truly awesome.
+
+ #+begin_src emacs-lisp
+ (use-package literate-calc-mode)
+ #+end_src
+** keycast
+ Sometimes is nice. It adds the key and the command in the
+ modeline, useful during records.
+
+ #+begin_src emacs-lisp
+ (use-package keycast
+ :custom (keycast-insert-after 'mode-line-misc-info))
+ #+end_src
+** email
+ *NB*: =mu4e= is installed from ports, not via straight.
+ #+begin_src sh :tangle no
+ doas pkg_add mu4e
+ #+end_src
+
+*** TODO split the following into pieces
+
+ #+begin_src emacs-lisp
+ (use-package emacs
+ :straight nil
+ :bind ("<f12>" . mu4e)
+ :custom ((message-citation-line-format "On %a, %b %d %Y, %f wrote\n")
+ (message-default-charset 'utf8)
+ (gnus-treat-display-smileys nil)
+ (user-mail-address "op@omarpolo.com")
+
+ (mu4e-maildir (expand-file-name "~/Maildir"))
+ (mu4e-get-mail-command "mbsync -a")
+ (mu4e-update-interval 180)
+ (mu4e-headers-auto-update t)
+ (mu4e-compose-signature-auto-include nil)
+ (mu4e-compose-format-flowed nil)
+ (mu4e-compose-in-new-frame t)
+ (mu4e-change-filenames-when-moving t) ;; don't break mbsync!
+ (mu4e-attachment-dir "~/Downloads")
+ (mu4e-compose-dont-reply-to-self t)
+ (mu4e-view-show-addresses t)
+ (mu4e-confirm-quit nil)
+ (mu4e-context-policy 'pick-first)
+ (mu4e-compose-context-policy 'always-ask)
+ (message-kill-buffer-on-exit t)
+ (mu4e-view-show-images t)
+
+ (mu4e~view-beginning-of-url-regexp
+ "https?\\://\\|mailto:\\|gemini://")
+
+ ;; use the ``tech preview''. mu4e uses gnus article-mode to
+ ;; show the messages.
+ (mu4e-view-use-gnus t)
+
+ ;; use localhost to send mails
+ (message-send-mail-function 'smtpmail-send-it)
+ (smtpmail-smtp-server "localhost")
+ (smtpmail-default-smtp-server "localhost"))
+ :hook (mu4e-view-mode . visual-line-mode)
+ :config
+ (require 'mu4e)
+
+ (add-to-list 'mu4e-view-actions
+ '("ViewInBrowser" . mu4e-action-view-in-browser) t)
+
+ (defun op/mu4e-change-headers ()
+ (interactive)
+ (setq mu4e-headers-fields
+ `((:human-date . 10)
+ (:flags . 4)
+ (:mailing-list . 8)
+ (:from . 20)
+ (:to . 20)
+ (:thread-subject))))
+ (add-hook 'mu4e-headers-mode-hook #'op/mu4e-change-headers)
+
+ (defun op/mu4e-do-compose-stuff ()
+ (interactive)
+ (set-fill-column 72)
+ (auto-fill-mode)
+ (flyspell-mode))
+ (add-hook 'mu4e-compose-mode-hook #'op/mu4e-do-compose-stuff)
+
+ (defun op/mu4e-make-bookmarks (mdir)
+ (list (make-mu4e-bookmark :name "Global Unread Messages"
+ :query "flag:unread and not flag:trashed"
+ :key ?u)
+ (make-mu4e-bookmark :name "Local Unread Messages"
+ :query (concat "maildir:" mdir "/* and flag:unread and not flag:trashed")
+ :key ?l)
+ (make-mu4e-bookmark :name "Today's Messages"
+ :query (concat "maildir:" mdir "/* and date:today..now")
+ :key ?t)
+ (make-mu4e-bookmark :name "Big Messages"
+ :query (concat "maildir:" mdir "/* and size:1M..500")
+ :key ?b)
+ (make-mu4e-bookmark :name "Sent"
+ :query (concat "maildir:" mdir "/Sent")
+ :key ?s)
+ (make-mu4e-bookmark :name "Drafts"
+ :query (concat "maildir:" mdir "/Drafts")
+ :key ?d)))
+
+ (defun op/mu4e-match-fn (mdir)
+ (lambda (msg)
+ (when msg
+ (string-prefix-p mdir (mu4e-message-field msg :maildir)))))
+
+ ;; loading the mu4e configuration without halting emacs if its not found
+ ;;
+ ;; the my-mu4e-config.el file contains:
+ ;; (setq mu4e-contexts
+ ;; (list
+ ;; (make-mu4e-context
+ ;; :name "foobar"
+ ;; :match-func (my/mu4e-match-fn "/maildir-name")
+ ;; :vars `((user-mail.address . "")
+ ;; (user-full-name . "")
+ ;; (mu4e-sent-folder . "")
+ ;; (mu4e-drafts-folder . "")
+ ;; (mu4e-trash-folder . "")
+ ;; (mu4e-compose-format-flowed . t)
+ ;; (mu4e-maildir-shortcuts . (("/foo/bar" . ?f)
+ ;; ...))
+ ;; (mu4e-bookmarks . ,(my/mu4e-make-bookmars "/maildir-name"))))))
+ ;;
+ (condition-case nil
+ (progn
+ (message "before load")
+ (load "my-mu4e-config")
+ (message "after load"))
+ (error (message "NOT loading mu4e configuration. It's missing!"))))
+ #+end_src
+** vmd
+ I wrote a [[https://github.com/omar-polo/vmd.el/][small package]] to interact with =vmd(8)= on OpenBSD. It's
+ kinda nice, if I may say so.
+
+ #+begin_src emacs-lisp
+ (use-package vmd
+ :straight nil
+ :load-path "~/.emacs.d/lisp/vmd/"
+ :custom (vmd-console-function #'op/vmd-vterm)
+ :bind ("C-z a v" . vmd)
+ :config
+ (defun op/vmd-vterm (name cmd)
+ (let ((vterm-shell (mapconcat #'shell-quote-argument cmd " "))
+ (vterm-buffer-name (concat "*" name "*")))
+ (vterm))))
+ #+end_src
+** sndio
+ I wrote also a package to interact with =sndio(8)=. Yep, I like
+ these small package.
+
+ #+begin_src emacs-lisp
+ (use-package sndio
+ :straight nil
+ :load-path "~/.emacs.d/lisp/sndio.el/"
+ :bind ("C-z a a" . sndio))
+ #+end_src
+** EMMS
+ Emacs Multi Media System is cool. It only needs a decent UI, which
+ I provide via hydra.
+
+ There's a bit of hackery to make it see and play =opus= files, it's
+ probably not needed anymore but who knows.
+
+ Also, I should split into pieces
+ #+begin_src emacs-lisp
+ (use-package emms
+ :commands (emms)
+ :bind ("C-z e" . hydra-emms/body)
+ :config
+ (setq emms-source-file-default-directory "~/music/"
+ emms-mode-line-format "「%s」"
+ emms-browser-covers 'emms-browser-cache-thumbnail-async)
+
+ (require 'emms-setup)
+ (emms-all)
+ (emms-default-players)
+ (emms-playing-time-disable-display)
+
+ (add-to-list 'emms-player-base-format-list "opus")
+ ;; re-compute the regxp for mpv
+ (emms-player-set emms-player-mpv 'regex
+ (apply #'emms-player-simple-regexp emms-player-base-format-list))
+
+ ;; save on quit and recover on startup
+ (require 'emms-history)
+ (emms-history-load)
+
+ ;; use libtag to extract tracks info.
+ ;;
+ ;; XXX: this needs to be compiled from sources
+ ;; (~/.emacs.d/straight/repos/emms/) and cp emms-print-metadata
+ ;; ~/bin.
+ (require 'emms-info)
+ (require 'emms-info-libtag)
+ (setq emms-info-functions '(emms-info-libtag))
+ (setq emms-info-libtag-known-extensions
+ (regexp-opt '("opus" "mp3" "mp4" "m4a" "ogg" "flac" "spx" "wma")))
+
+ ;; use my backend for sndioctl
+ (require 'emms-volume-sndioctl)
+ (setq-default emms-volume-change-function 'emms-volume-sndioctl-change)
+
+ (defun my/tick-symbol (x)
+ "Return a tick if X is true-ish."
+ (if x "x" " "))
+
+ (defun my/emms-player-status ()
+ "Return the state of the EMMS player: `not-active', `playing', `paused' or `dunno'.
+
+ Modeled after `emms-player-pause'."
+ (cond ((not emms-player-playing-p)
+ ;; here we should return 'not-active. The fact is that
+ ;; when i change song, there is a short amount of time
+ ;; where we are ``not active'', and the hydra is rendered
+ ;; always during that short amount of time. So we cheat a
+ ;; little.
+ 'playing)
+
+ (emms-player-paused-p
+ (let ((resume (emms-player-get emms-player-playing-p 'resume))
+ (pause (emms-player-get emms-player-playing-p 'pause)))
+ (cond (resume 'paused)
+ (pause 'playing)
+ (t 'dunno))))
+ (t (let ((pause (emms-player-get emms-player-playing-p 'pause)))
+ (if pause 'playing 'dunno)))))
+
+ (defun my/emms-toggle-time-display ()
+ "Toggle the display of time information in the modeline"
+ (interactive)
+ (if emms-playing-time-display-p
+ (emms-playing-time-disable-display)
+ (emms-playing-time-enable-display)))
+
+ (defun my/emms-select-song ()
+ "Select and play a song from the current EMMS playlist."
+ (interactive)
+ (with-current-emms-playlist
+ (emms-playlist-mode-center-current)
+ (let* ((current-line-number (line-number-at-pos))
+ (lines (cl-loop
+ with min-line-number = (line-number-at-pos (point-min))
+ with buffer-text-lines = (split-string (buffer-string) "\n")
+ with lines = nil
+ for l in buffer-text-lines
+ for n = min-line-number then (1+ n)
+ do (push (cons l n)
+ lines)
+ finally return (nreverse lines)))
+ (selected-line (completing-read "Song: " lines)))
+ (when selected-line
+ (let ((line (cdr (assoc selected-line lines))))
+ (goto-line line)
+ (emms-playlist-mode-play-smart)
+ (emms-playlist-mode-center-current))))))
+
+ (defhydra hydra-emms (:hint nil)
+ "
+ %(my/emms-player-status) %(emms-track-description (emms-playlist-current-selected-track))
+
+ ^Volume^ ^Controls^ ^Playback^ ^Misc^
+ ^^^^^^^^----------------------------------------------------------------
+ _+_: inc _n_: next _r_: repeat one [% s(my/tick-symbol emms-repeat-track)] _t_oggle modeline
+ _-_: dec _p_: prev _R_: repeat all [% s(my/tick-symbol emms-repeat-playlist)] _T_oggle only time
+ ^ ^ _<_: seek bw _#_: shuffle _s_elect
+ ^ ^ _>_: seek fw _%_: sort _g_oto EMMS buffer
+ ^ ^ _SPC_: play/pause _l_yrics
+ ^ ^ _DEL_: restart
+ "
+ ("+" emms-volume-raise)
+ ("-" emms-volume-lower)
+ ("n" emms-next)
+ ("p" emms-previous)
+ ("<" emms-seek-backward)
+ (">" emms-seek-forward)
+ ("SPC" emms-pause)
+ ("DEL" (emms-player-seek-to 0))
+ ("<backspace>" (emms-player-seek-to 0))
+ ("r" emms-toggle-repeat-track)
+ ("R" emms-toggle-repeat-playlist)
+ ("#" emms-shuffle)
+ ("%" emms-sort)
+ ("t" (progn (my/emms-toggle-time-display)
+ (emms-mode-line-toggle)))
+ ("T" my/emms-toggle-time-display)
+ ("s" my/emms-select-song)
+ ("g" (progn (emms)
+ (with-current-emms-playlist
+ (emms-playlist-mode-center-current))))
+ ("l" my/emms-current-lyrics)
+
+ ("q" nil :exit t)))
+ #+end_src
+** eww
+ The infamous Emacs Web Wrowser. Nothing fancy, just don't store
+ cookie please!
+
+ #+begin_src emacs-lisp
+ (use-package eww
+ :straight nil
+ :custom ((url-cookie-trusted-urls nil)
+ (url-cookie-untrusted-urls '(".*"))))
+ #+end_src
+** elpher
+ Elpher is a Gemini/Gopher browser for Emacs.
+ #+begin_src emacs-lisp
+ (use-package elpher
+ :custom ((elpher-ipv4-always nil)
+ (elpher-default-url-type "gemini"))
+ :commands (elpher elpher-go elpher-jump))
+ #+end_src
+** pq
+ =pq= is a postgres library module for elisp. Sound crazy, hu?
+
+ #+begin_src emacs-lisp
+ (use-package pq
+ :straight nil
+ :load-path "/home/op/build/emacs-libpq/")
+ #+end_src
+** nov.el
+ =nov= is a package to read epub from Emacs. It's really cool, and
+ integrates with both bookmarks and org-mode.
+
+ #+begin_src emacs-lisp
+ (use-package nov
+ :mode ("\\.epub\\'" . nov-mode)
+ :hook (nov-mode . op/novel-setup)
+ :config
+ (defun op/novel-setup ()
+ (interactive)
+ (variable-pitch-mode +1)
+ (olivetti-mode +1)
+ (setq-local cursor-type 'bar)))
+ #+end_src
+** Toxe
+ Toxe is my try at writing a tox client in elisp. One day will be
+ functional, I promise!
+
+ #+begin_src emacs-lisp
+ (use-package toxe
+ :straight nil
+ :load-path "~/w/toxe/")
+ #+end_src
+** rcirc
+ rcirc is the first (and only) Emacs IRC client I tried, and the
+ only IRC client I use. I really like it out-of-the-box!
+
+ #+begin_src emacs-lisp
+ (use-package rcirc
+ :straight nil
+ :bind (("C-z i e" . op/rcirc-emph-nick)
+ ("C-z i d" . op/rcirc-de-emph-nick)
+ ("C-z i i" . rcirc))
+ :custom ((rcirc-buffer-maximum-lines 1000)
+ (rcirc-log-flag t)
+ (rcirc-omit-responses '("JOIN" "PART" "QUIT"
+ "NICK" "AWAY"))
+ (rcirc-fill-column 72)
+ (rcirc-keywords '("godot" "poedit" "mu4e")))
+ :config
+ (rcirc-track-minor-mode)
+ (add-hook 'rcirc-mode-hook
+ (lambda ()
+ (flyspell-mode 1)
+ (rcirc-omit-mode)))
+
+ (defun op/rcirc-emph-nick (nick)
+ "Emphasize NICK on the fly."
+ (interactive (list
+ (if (eq major-mode 'rcirc-mode)
+ (completing-read "Nick: " (caddr (rcirc-completion-at-point)))
+ (completing-read "Nick: " nil))))
+ (add-to-list 'rcirc-bright-nicks nick))
+
+ (defun op/rcirc-de-emph-nick (nick)
+ "De-emphasize NICK on the fly (but don't dim it)"
+ (interactive (list (completing-read "Nick: " rcirc-bright-nicks)))
+ (setq rcirc-bright-nicks
+ (cl-delete nick rcirc-bright-nicks
+ :test #'string=)))
+
+ ;; contains something along the lines of
+ ;;
+ ;; (setq rcirc-authinfo
+ ;; '(("irc.freenode.net" nickserv
+ ;; "USERNAME" "PASSWORD")))
+ (condition-case nil
+ (load "my-rcirc-conf")
+ (error (message "Missing rcirc configuration!"))))
+ #+end_src
+** telega
+ Telega is *the best* telegram client. period.
+
+ I'm using a non-standard recipe because the standard one doesn't
+ include the =contrib/*= stuff (in particular I'm interested in the
+ org integration).
+
+ #+begin_src emacs-lisp
+ (use-package telega
+ :straight (:type git
+ :flavor melpa
+ :files (:defaults "etc" "server" "Makefile" "telega-pkg.el"
+ "contrib/ol-telega.el"
+ "contrib/telega-url-shorten.el")
+ :branch "master"
+ :host github
+ :repo "zevlg/telega.el")
+ :custom ((telega-chat-input-markups '(nil "markdown1" "markdown2"))
+ (telega-use-images t)
+ (telega-emoji-font-family "Noto Color Emoji"))
+ :hook ((telega-root-mode . telega-notifications-mode)
+ ;; (telega-chat-mode . op/telega-enable-company)
+ (telega-load-hook . global-telega-url-shorten-mode))
+ :bind-keymap ("C-c t" . telega-prefix-map)
+ :config
+ ;; if put in the :custom will fail, and now I have other things to
+ ;; do.
+ (setq telega-completing-read-function #'completing-read)
+
+ (comment
+ (defun op/telega-enable-company ()
+ (interactive)
+ (company-mode +1)
+ (set (make-local-variable 'company-backends)
+ (append '(telega-company-emoji
+ telega-company-username
+ telega-company-hashtag)
+ (when (telega-chat-bot-p telega-chatbuf--chat)
+ '(telega-company-botcmd))))))
+
+ ;; for telega-url-shorten
+ (use-package all-the-icons))
+ #+end_src
+** elfeed
+ Elfeed is a RSS reader for Emacs that rocks!
+
+ #+begin_src emacs-lisp
+ (use-package elfeed
+ :bind (("C-x w" . elfeed)
+ :map elfeed-show-mode-map
+ ("q" . delete-window))
+ :custom (elfeed-feeds
+ '("https://undeadly.org/cgi?action=rss&full=yes&items=10"
+ "http://www.tedunangst.com/flak/rss"
+ "https://www.dragonflydigest.com/feed"
+ "https://www.mirbsd.org/news.rss"
+ "https://www.mirbsd.org/announce.rss"
+ "https://bentsukun.ch/index.xml"
+ "https://drewdevault.com/feed.xml"
+ "https://www.cambus.net/atom.xml"
+ "https://dataswamp.org/~solene/rss.xml"
+ "https://briancallahan.net/blog/feed.xml"
+ "https://www.poolp.org/index.xml"
+ "https://jcs.org/rss"
+ "https://sanctum.geek.nz/arabesque/feed/"
+ "https://tech.toryanderson.com/"
+ "https://alexschroeder.ch/wiki?action=journal;search=-tag:rpg -tag:rsp;lang=en;title=English Diary without RPG Pages"
+ "http://boston.conman.org/bostondiaries.rss"
+ "https://emacsninja.com/feed.atom"
+ "https://bsdly.blogspot.com/feeds/posts/default"
+ "https://crawshaw.io/atom.xml"
+ "https://nullprogram.com/feed/"
+ "http://pragmaticemacs.com/feed/"
+ "https://emacsnotes.wordpress.com/feed/"
+ "https://metaredux.com/feed.xml"
+ "https://emacsredux.com/atom.xml"
+ "https://endlessparentheses.com/atom.xml"
+ "https://www.masteringemacs.org/feed"
+ "https://cestlaz.github.io/rss.xml"
+ "https://utcc.utoronto.ca/~cks/space/blog/?atom"
+ "https://irreal.org/blog/?feed=rss2"
+ "https://planet.lisp.org/rss20.xml"
+ "https://insideclojure.org/feed.xml"
+ "https://tech.toryanderson.com/index.xml"
+ "https://vermaden.wordpress.com/feed/"
+ "https://www.arp242.net/feed.xml"
+ "https://tymoon.eu/api/reader/atom"
+ "https://venam.nixers.net/blog/feed.xml"
+ "https://www.omarpolo.com/rss.xml"
+ "https://owarisubs.lacumpa.biz/feed/"
+ "https://asenshi.moe/feed/"
+ "https://godotengine.org/rss.xml"
+ "https://github.com/yshui/picom/releases.atom"
+ "https://github.com/vslavik/poedit/releases.atom"
+ "https://github.com/TokTok/c-toxcore/releases.atom"
+ "https://github.com/alexander-akhmetov/python-telegram/releases.atom"
+ "https://github.com/paul-nameless/tg/releases.atom"
+ "https://github.com/YACReader/yacreader/releases.atom"
+
+ "https://www.crimsonmagic.me/feed/"
+ "https://fullybookedtls.wordpress.com/feed/"))
+ :config
+ (setq elfeed-show-entry-switch #'pop-to-buffer))
+ #+end_src
+*** TODO find a way to integrate elfeed-org with this document
+ it would be sick!
+** firefox integration
+ There is a package, =edit-server=, that let me edit textareas from
+ Emacs. I'm still trying to decide if it's worth or not.
+
+ #+begin_src emacs-lisp
+ (use-package edit-server
+ :init (if after-init-time
+ (edit-server-start)
+ (add-hook 'after-init-hook #'edit-server-start))
+ :custom (edit-server-new-frame-alist '((name . "Edit with Emacs")
+ (minibuffer . t))))
+ #+end_src
+* "smol" packages
+ These are stuff that I keep in =emacs-user-directory/lisp=, but that
+ isn't useful enough to submit a package for it.
+
+** Scratchpads
+
+ Creates a new =*scratchpad*= buffer on demand. When called with a
+ prefix argument, choose the mode!
+ #+begin_src emacs-lisp :tangle ~/.emacs.d/lisp/scratchpads.el
+ ;;; scratchpads.el --- create scratchpads -*- lexical-binding: t; -*-
+
+ ;; Copyright (C) 2021 Omar Polo
+
+ ;; Author: Omar Polo <op@omarpolo.com>
+ ;; Keywords: convenience
+
+ ;; This program is free software; you can redistribute it and/or modify
+ ;; it under the terms of the GNU General Public License as published by
+ ;; the Free Software Foundation, either version 3 of the License, or
+ ;; (at your option) any later version.
+
+ ;; This program is distributed in the hope that it will be useful,
+ ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ;; GNU General Public License for more details.
+
+ ;; You should have received a copy of the GNU General Public License
+ ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+ ;;; Commentary:
+
+ ;; Quickly create temp scratch buffer
+
+ ;;; Code:
+
+ (require 'cl-lib)
+
+ (defun scratchpads--list-major-modes ()
+ "List all the major modes.
+ Inspired from ieure/scratch-el. Naïve probably."
+ (cl-loop for sym the symbols of obarray
+ for name = (symbol-name sym)
+ when (and (functionp sym)
+ (not (member sym minor-mode-alist))
+ (string-match "-mode$" name)
+ (not (string-match "--" name)))
+ collect name))
+
+ (defun scratchpads--select-mode ()
+ "Select an appropriate major mode."
+ (if current-prefix-arg
+ (intern (concat (completing-read
+ "Major Mode: "
+ (scratchpads--list-major-modes)
+ nil t nil nil)))
+ major-mode))
+
+ ;;;###autoload
+ (defun scratchpads-new-scratchpad (mode)
+ "Create a new *scratch* buffer for the MODE."
+ (interactive (list (scratchpads--select-mode)))
+ (let ((buf (generate-new-buffer "*scratch*")))
+ (pop-to-buffer buf)
+ (funcall mode)))
+
+ (provide 'scratchpads)
+ ;;; scratchpads.el ends here
+ #+end_src
+
+** clbin
+ Do you know what NIH syndrome is? It's the definition of this
+ package!
+
+ #+begin_src emacs-lisp :tangle ~/.emacs.d/lisp/clbin.el
+ ;;; clbin.el --- post stuff to clbin -*- lexical-binding: t; -*-
+
+ ;; Copyright (C) 2021 Omar Polo
+
+ ;; Author: Omar Polo <op@omarpolo.com>
+ ;; Keywords: comm
+
+ ;; This program is free software; you can redistribute it and/or modify
+ ;; it under the terms of the GNU General Public License as published by
+ ;; the Free Software Foundation, either version 3 of the License, or
+ ;; (at your option) any later version.
+
+ ;; This program is distributed in the hope that it will be useful,
+ ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ;; GNU General Public License for more details.
+
+ ;; You should have received a copy of the GNU General Public License
+ ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+ ;;; Commentary:
+
+ ;; clbin.el posts something to clbin and saves the corresponding link
+ ;; to the kill ring.
+
+ ;;; Code:
+
+ (defun clbin--do (start end)
+ "Send the region from START to END to clbin, add the link to the `kill-ring'."
+ (let ((temp-buffer (generate-new-buffer " *clbin*" t)))
+ (unwind-protect
+ (let ((res (call-process-region start end
+ "curl" nil temp-buffer nil
+ "-Fclbin=<-" "https://clbin.com")))
+ (cond ((zerop res)
+ (with-current-buffer temp-buffer
+ (goto-char (point-max))
+ (forward-line -1)
+ (let ((start (line-beginning-position))
+ (end (line-end-position)))
+ (kill-ring-save start end)
+ (message "%s" (buffer-substring start end)))))
+ (t
+ (error "Subprocess (curl) failed"))))
+ (and (buffer-name temp-buffer)
+ (kill-buffer temp-buffer)))))
+
+ ;;;###autoload
+ (defun clbin-dwim ()
+ "Send to clbin the current (narrowed) buffer or the active region."
+ (interactive)
+ (if mark-active
+ (clbin--do (point) (mark))
+ (clbin--do (point-min) (point-max))))
+
+ (provide 'clbin)
+ ;;; clbin.el ends here
+ #+end_src
+** my-abbrev
+ Where I store the abbreviation I need.
+
+ #+begin_src emacs-lisp :tangle ~/.emacs.d/lisp/my-abbrev.el
+ ;;; my-abbrev --- Abbrev stuff -*- lexical-binding: t; -*-
+ ;;; Commentary:
+
+ ;; This adds various abbrevs for various modes. Abbrevs are useful to
+ ;; avoid typos, for instance. To prevent the expansion, type ``word
+ ;; C-q SPC'' instead of ``word SPC''.
+
+ ;;; Code:
+
+ (clear-abbrev-table global-abbrev-table)
+
+ (define-abbrev-table 'global-abbrev-table
+ '(("prots@" "ports@")
+
+ ("supprots" "supports")
+
+ ("het" "the")
+ ("teh" "the")
+
+ ("wehn" "when")
+
+ ("perchè" "perché")
+ ("perche" "perché")
+ ("nonchè" "nonché")
+ ("nonche" "nonché")
+ ("quetse" "queste")
+ ("sovlgimento" "svolgimento")
+ ("sovlgere" "svolgere")
+ ("sbagilo" "sbaglio")
+ ("caffe" "caffè")))
+
+ (when (boundp 'text-mode-abbrev-table)
+ (clear-abbrev-table text-mode-abbrev-table))
+
+ (define-abbrev-table 'text-mode-abbrev-table
+ '(("hw" "hardware")
+ ("sw" "software")))
+
+ (when (boundp 'clojure-mode-abbrev-table)
+ (clear-abbrev-table clojure-mode-abbrev-table))
+
+ (define-abbrev-table 'clojure-mode-abbrev-table
+ '(("erq" "req")))
+
+ (when (boundp 'c-mode-abbrev-table)
+ (clear-abbrev-table c-mode-abbrev-table))
+
+ (define-abbrev-table 'c-mode-abbrev-table
+ '(("inculde" "include")))
+
+ ;; turn on abbrev mode globally
+ (setq-default abbrev-mode t)
+
+ (provide 'my-abbrev)
+ ;;; my-abbrev.el ends here
+ #+end_src
+** my-modeline
+ This is my customized modeline. It hides informations that I don't
+ find particularly interesting. It looks like this
+
+ #+CAPTION: screenshot of the modeline
+ #+NAME: fig:modeline
+ [[./imgs/modeline.png]]
+
+ #+begin_src emacs-lisp :tangle ~/.emacs.d/lisp/my-modeline.el
+ ;; -*- lexical-binding: t; -*-
+
+ (defvar my/show-minor-modes nil
+ "Show the minor mode list in the modeline.")
+
+ ;; probably this list can be deleted and simply check, in
+ ;; my/alt-mode-p if the current major-mode is special or not.
+ (defvar my/mode-line-alt-mode '(rcirc-mode mu4e-main-mode mu4e-headers-mode
+ mu4e-view-mode
+ sql-interactive-mode
+ telega-chat-mode
+ telega-root-mode)
+ "List of modes that get an alternative mode-line.")
+
+ (defun my/alt-mode-p ()
+ "Return t if the current `major-mode' is within `my/mode-line-alt-mode'."
+ (member major-mode my/mode-line-alt-mode))
+
+ (defun my/mode-line-toggle-minor-modes ()
+ "Toggle the list of minor modes in modeline."
+ (interactive)
+ (setq my/show-minor-modes (not my/show-minor-modes)))
+
+ (setq-default
+ mode-line-format
+ `(
+ ;; coding system and stuff
+ (:eval (when (not (my/alt-mode-p))
+ `(" " ,mode-line-mule-info)))
+
+ ;; buffer name
+ " %b "
+
+ ;; major mode
+ ;; " %m "
+
+ ;; recursion level
+ (:eval
+ (let ((l (recursion-depth)))
+ (when (> l 1)
+ (list (format "level:%s" (recursion-depth))
+ " "))))
+
+ ;; misc info
+ (:eval (unless (my/alt-mode-p)
+ `(,mode-line-remote ,mode-line-client ,mode-line-modified)))
+
+ ;; vc can be useful
+ (:eval (when-let (vc vc-mode)
+ `(" " ,vc " ")))
+
+ ;; percentage, line number and narrow
+ (:eval (if (my/alt-mode-p)
+ " "
+ " %p L%l %n ")) ; add C%c for the chars
+
+ ;; show minor modes (eventually)
+ (:eval (when my/show-minor-modes
+ mode-line-modes))
+
+ mode-line-misc-info ;; rcirc, mu4e...
+ ))
+
+ (provide 'my-modeline)
+ ;;; my-modeline.el ends here
+ #+end_src
+** minimal-light-theme
+ =minimal-light-theme= is my personal fork of [[https://github.com/anler/minimal-theme][anler/minimal-theme]].
+ Since I change it pretty often, I don't see how it could be useful
+ to others.
+
+ #+begin_src emacs-lisp :tangle ~/.emacs.d/minimal-light-theme.el
+ ;;; minimal-light-theme.el --- A light/dark minimalistic Emacs 27 theme. -*- lexical-binding: t; -*-
+
+ ;; Copyright (C) 2020 Omar Polo
+ ;; Copyright (C) 2014 Anler Hp
+
+ ;; Author: Anler Hp <anler86 [at] gmail.com>
+ ;; Keywords: color, theme, minimal
+ ;; X-URL: http://github.com/ikame/minimal-theme
+ ;; URL: http://github.com/ikame/minimal-theme
+
+ ;; This program is free software; you can redistribute it and/or modify
+ ;; it under the terms of the GNU General Public License as published by
+ ;; the Free Software Foundation, either version 3 of the License, or
+ ;; (at your option) any later version.
+
+ ;; This program is distributed in the hope that it will be useful,
+ ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ;; GNU General Public License for more details.
+
+ ;; You should have received a copy of the GNU General Public License
+ ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ ;;; Commentary:
+ ;;
+ ;; A minimalistic color theme to avoid distraction with
+ ;; colors. Based on monochrome theme.
+
+ ;;; Code:
+ (deftheme minimal-light "minimal light theme.")
+
+ (let* ((class '((class color) (min-colors 89)))
+ (foreground "#586e75")
+ (background "#ffffff") ;; "#fdf6e3"
+ (cursor "#333")
+ (border "grey90")
+ (minibuffer cursor)
+ (region "grey85")
+ (comment-delimiter "grey55")
+ (comment "grey30")
+ (constant foreground)
+ (string "grey40")
+ (modeline-foreground foreground)
+ (modeline-background "#e1dbca")
+ (modeline-foreground-inactive comment)
+ (modeline-background-inactive background)
+ (modeline-border-color "white")
+ (isearch-background modeline-background)
+ (hl-background "grey94")
+ (hl-face-background nil)
+ (failure "red")
+ (org-background "grey98"))
+ (custom-theme-set-faces
+ 'minimal-light
+
+ ;; basic stuff
+ `(default ((,class (:background ,background :foreground ,foreground))))
+ `(fringe ((,class (:inherit default))))
+ `(cursor ((,class (:background ,cursor :inverse-video t))))
+ `(vertical-border ((,class (:foreground ,border))))
+
+ ;; minibuffer
+ `(minibuffer-prompt ((,class (:foreground ,minibuffer :weight bold))))
+
+ ;; region
+ `(region ((,class (:background ,region))))
+ `(secondary-selection ((,class (:background ,region))))
+
+ ;; faces
+ `(font-lock-builtin-face ((,class (:foreground ,foreground :weight bold))))
+ `(font-lock-constant-face ((,class (:foreground ,foreground :weight bold))))
+ `(font-lock-keyword-face ((,class (:foreground ,foreground :weight bold))))
+ `(font-lock-type-face ((,class (:foreground ,foreground :slant italic))))
+ `(font-lock-function-name-face ((,class (:foreground ,foreground :weight bold))))
+ `(font-lock-variable-name-face ((,class (:foreground ,foreground))))
+
+ `(font-lock-comment-delimiter-face ((,class (:foreground ,comment-delimiter))))
+ `(font-lock-comment-face ((,class (:foreground ,comment
+ :slant italic))))
+ `(font-lock-doc-face ((,class (:inherit (font-lock-comment-face)))))
+ `(font-lock-string-face ((,class (:foreground ,foreground :foreground ,string))))
+
+ ;; faces used by isearch
+ `(isearch ((,class (:foreground ,foreground :background ,isearch-background :weight normal))))
+ `(isearch-fail ((,class (:foreground ,failure :bold t))))
+ `(lazy-highlight
+ ((,class (:foreground ,foreground :background ,region))))
+
+ ;; flymake-error
+ `(flymake-error ((,class :underline (:style line :color "Red1"))))
+
+ ;; ido-mode
+ `(ido-subdir ((,class (:foreground ,foreground :weight bold))))
+ `(ido-only-match ((,class (:foreground ,foreground :weight bold))))
+
+ ;; show-paren
+ `(show-paren-match
+ ((,class (:inherit highlight
+ :underline (:color ,foreground :style line)))))
+ `(show-paren-mismatch
+ ((,class (:foreground ,failure :weight bold))))
+ `(show-paren-match-expression
+ ((,class (:inherit default :background "#eee"))))
+
+ ;; highlight-sexp
+ `(hl-sexp-face
+ ((,class (:inherit show-paren-match-expression))))
+
+ ;; tab-bar
+ `(tab-bar ((,class :background ,modeline-background)))
+ `(tab-bar-tab ((,class :background ,modeline-background-inactive
+ :box (:line-width 4 :color ,modeline-background-inactive))))
+ `(tab-bar-tab-inactive ((,class :background ,modeline-background
+ :box (:line-width 4 :color ,modeline-background))))
+
+ ;; help. Don't print funny boxes around keybindings
+ `(help-key-binding ((,class)))
+
+ ;; modeline
+ `(mode-line
+ ((,class (:inverse-video unspecified
+ :overline ,border
+ :underline nil
+ :foreground ,modeline-foreground
+ :background ,modeline-background
+ :overline ,border
+ :box (:line-width 3 :color ,modeline-background)))))
+ `(mode-line-buffer-id ((,class (:weight bold))))
+ `(mode-line-inactive
+ ((,class (:inverse-video unspecified
+ :overline ,border
+ :underline nil
+ :foreground ,modeline-foreground-inactive
+ :background ,modeline-background-inactive
+ :box (:line-width 3 :color ,background)))))
+
+ ;; hl-line-mode
+ `(hl-line ((,class (:background ,hl-background))))
+ `(hl-line-face ((,class (:background ,hl-face-background))))
+
+ ;; highlight-stages-mode
+ `(highlight-stages-negative-level-face ((,class (:foreground ,failure))))
+ `(highlight-stages-level-1-face ((,class (:background ,org-background))))
+ `(highlight-stages-level-2-face ((,class (:background ,region))))
+ `(highlight-stages-level-3-face ((,class (:background ,region))))
+ `(highlight-stages-higher-level-face ((,class (:background ,region))))
+
+ ;; org-mode
+ `(org-level-1 ((,class (:foreground ,foreground :height 1.6))))
+ `(org-level-2 ((,class (:foreground ,foreground :height 1.5))))
+ `(org-level-3 ((,class (:foreground ,foreground :height 1.4))))
+ `(org-level-4 ((,class (:foreground ,foreground :height 1.3))))
+ `(org-level-5 ((,class (:foreground ,foreground :height 1.2))))
+ `(org-level-6 ((,class (:foreground ,foreground :height 1.1))))
+ `(org-level-7 ((,class (:foreground ,foreground))))
+ `(org-level-8 ((,class (:foreground ,foreground))))
+
+ `(org-block ((,class (:inherit fixed-pitch))))
+ `(org-table ((,class (:inherit fixed-pitch))))
+ `(org-meta-line ((,class (:inherit (font-lock-comment-face fixed-pitch)))))
+ `(org-property-value ((,class (:inherit fixed-pitch))) t)
+ `(org-verbatim ((,class (:inherit (shadow fixed-pitch)))))
+ `(org-quote ((,class (:slant italic))))
+
+ ;; outline
+ `(outline-1 ((,class (:inherit org-level-1))))
+ `(outline-2 ((,class (:inherit org-level-2))))
+ `(outline-3 ((,class (:inherit org-level-3))))
+ `(outline-4 ((,class (:inherit org-level-4))))
+ `(outline-5 ((,class (:inherit org-level-5))))
+ `(outline-6 ((,class (:inherit org-level-6))))
+ `(outline-7 ((,class (:inherit org-level-7))))
+ `(outline-8 ((,class (:inherit org-level-8))))
+
+ `(org-document-title ((,class (:foreground ,foreground))))
+
+ `(org-link ((,class (:foreground ,foreground :underline t))))
+ `(org-tag ((,class (:background ,org-background :foreground ,foreground))))
+ `(org-warning ((,class (:background ,region :foreground ,foreground :weight bold))))
+ `(org-todo ((,class (:weight bold))))
+ `(org-done ((,class (:weight bold))))
+ `(org-headline-done ((,class (:foreground ,foreground))))
+
+ `(org-table ((,class (:background ,org-background))))
+ `(org-code ((,class (:background ,org-background))))
+ `(org-date ((,class (:background ,org-background :underline t))))
+ `(org-block ((,class (:background ,org-background))))
+ `(org-block-background ((,class (:background ,org-background :foreground ,foreground))))
+ `(org-block-begin-line
+ ((,class (:background ,org-background :foreground ,comment-delimiter :weight bold))))
+ `(org-block-end-line
+ ((,class (:background ,org-background :foreground ,comment-delimiter :weight bold))))
+
+ ;; js2-mode
+ `(js2-external-variable ((,class (:inherit base-faces :weight bold))))
+ `(js2-function-param ((,class (:inherit base-faces))))
+ `(js2-instance-member ((,class (:inherit base-faces))))
+ `(js2-jsdoc-html-tag-delimiter ((,class (:inherit base-faces))))
+ `(js2-jsdoc-html-tag-name ((,class (:inherit base-faces))))
+ `(js2-jsdoc-tag ((,class (:inherit base-faces))))
+ `(js2-jsdoc-type ((,class (:inherit base-faces :weight bold))))
+ `(js2-jsdoc-value ((,class (:inherit base-faces))))
+ `(js2-magic-paren ((,class (:underline t))))
+ `(js2-private-function-call ((,class (:inherit base-faces))))
+ `(js2-private-member ((,class (:inherit base-faces))))
+
+ ;; sh-mode
+ `(sh-heredoc ((,class (:inherit base-faces :slant italic))))
+
+ ;; telega
+ `(telega-msg-heading ((,class (:inherit base-faces :underline ,comment-delimiter
+ :foreground ,comment))))
+ `(telega-msg-user-title ((,class (:inherit telega-msg-heading))))
+ `(telega-msg-inline-reply ((,class (:inherit telega-msg-heading
+ :slant italic))))
+
+ ;; objed
+ `(objed-hl ((,class (:background ,region))))))
+
+ ;;;###autoload
+ (when (and (boundp 'custom-theme-load-path) load-file-name)
+ (add-to-list 'custom-theme-load-path
+ (file-name-as-directory (file-name-directory load-file-name))))
+
+ (provide-theme 'minimal-light)
+ ;;; minimal-light-theme.el ends here
+
+ #+end_src
+** emms-volume-sndioctl
+ It's a EMMS volume backend for =sndioctl(1)=. I should probably
+ send it upstream.
+
+ #+begin_src emacs-lisp :tangle ~/.emacs.d/lisp/emms-volume-sndioctl.el
+ ;;; emms-volume-sndioctl.el --- a mode for changing volume using sndioctl
+
+ ;; This file is NOT part of EMMS.
+
+ ;;;###autoload
+ (defun emms-volume-sndioctl-change (amount)
+ "Change sndioctl output.level by AMOUNT."
+ (message "Playback channels: %s"
+ (with-temp-buffer
+ (when (zerop
+ (call-process "sndioctl" nil (current-buffer) nil
+ "-n"
+ (format "output.level=%s%f"
+ (if (> amount 0) "+" "")
+ (/ (float amount) 100))))
+ (replace-regexp-in-string "\n" "" (buffer-string))))))
+
+ (provide 'emms-volume-sndioctl)
+ #+end_src
+** even-more-cua
+ Even more cua is something I wrote for someone who wanted to try
+ Emacs but needed "CUA" keys. It extends a bit cua-mode, and it's a
+ bit opinionated. I don't have a use for it anymore, but anyway.
+
+ #+begin_src emacs-lisp :tangle ~/.emacs.d/lisp/even-more-cua.el
+ ;; -*- lexical-binding: t; -*-
+
+ (cua-mode +1)
+
+ (setq mouse-drag-and-drop-region t)
+
+ (define-key global-map (kbd "C-s") 'save-buffer)
+ (define-key global-map (kbd "C-f") 'isearch-forward)
+ (define-key global-map (kbd "C-q") 'save-buffers-kill-terminal)
+
+ (define-key global-map (kbd "S-<mouse-1>")
+ (lambda (arg e)
+ (interactive "P\ne")
+ (unless (region-active-p)
+ (set-mark-command arg))
+ (mouse-set-point e)))
+
+ (defun emc--move-text-impl (arg)
+ "Move text up or down by ARG lines.
+
+ If ARG is negative, the text will be moved up. ``text'' means the
+ current line or the current region if its active. The region is
+ automatically extended to the beginning of the first line and to the
+ end of the last line."
+ (atomic-change-group
+ (cond
+ ((and mark-active transient-mark-mode)
+ (if (> (point) (mark))
+ (exchange-point-and-mark))
+
+ ;; extend the region to the beginning of the first line/end of
+ ;; the last
+ (when (not (bolp))
+ (move-beginning-of-line nil))
+ (exchange-point-and-mark)
+ (when (not (bolp))
+ (next-line)
+ (move-beginning-of-line nil))
+ (exchange-point-and-mark)
+
+ (let ((column (current-column))
+ (text (delete-and-extract-region (point) (mark))))
+ (forward-line arg)
+ (move-to-column column t)
+ (set-mark (point))
+ (insert text)
+ (exchange-point-and-mark)
+ (setq deactivate-mark nil)))
+ (t
+ (let ((column (current-column)))
+ (beginning-of-line)
+ (when (or (> arg 0) (not (bobp)))
+ (forward-line)
+ (when (or (< arg 0) (not (eobp)))
+ (transpose-lines arg))
+ (forward-line -1))
+ (move-to-column column t))))))
+
+ (define-key global-map (kbd "C-S-<up>")
+ (lambda (arg)
+ (interactive "*p")
+ (emc--move-text-impl (- arg))))
+
+ (define-key global-map (kbd "C-S-<down>")
+ (lambda (arg)
+ (interactive "*p")
+ (emc--move-text-impl arg)))
+
+ (provide 'even-more-cua)
+ ;;; even-more-cua.el ends here
+ #+end_src
diff --git a/imgs/modeline.png b/imgs/modeline.png
new file mode 100644
index 0000000..6a8d4fa
--- /dev/null
+++ b/imgs/modeline.png
Binary files differ
diff --git a/solarized-light.css b/solarized-light.css
new file mode 100644
index 0000000..7f01709
--- /dev/null
+++ b/solarized-light.css
@@ -0,0 +1,316 @@
+@import url(http://fonts.googleapis.com/css?family=Inconsolata);
+@import url(http://fonts.googleapis.com/css?family=PT+Sans);
+@import url(http://fonts.googleapis.com/css?family=PT+Sans+Narrow:400,700);
+
+article, aside, details, figcaption,
+figure, footer, header, hgroup, nav,
+section, summary {
+ display: block;
+}
+
+audio, canvas, video {
+ display: inline-block;
+}
+
+audio:not([controls]) {
+ display:none;
+ height:0
+}
+
+[hidden] {
+ display:none
+}
+
+html {
+ font-family:sans-serif;
+ -webkit-text-size-adjust:100%;
+ -ms-text-size-adjust:100%
+}
+
+body {
+ margin:0;
+}
+
+a:focus {
+ outline: thin dotted;
+}
+
+a:active, a:hover {
+ outline: 0;
+}
+
+h1 {
+ font-size: 2em;
+}
+
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+
+b, strong {
+ font-weight: bold;
+}
+
+dfn {
+ font-style: italic;
+}
+
+mark {
+ background:#ff0;
+ color:#000;
+}
+
+code, kbd, pre, samp {
+ font-family: monospace;
+ font-size: 1em;
+}
+
+pre {
+ white-space: pre-wrap;
+ word-wrap: break-word
+}
+
+q {
+ quotes: "\201C" "\201D" "\2018" "\2019";
+}
+
+small {
+ font-size: 80%;
+}
+
+sub, sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+img {
+ border: 0;
+ max-width: 100%;
+}
+
+svg:not(:root) {
+ overflow: hidden;
+}
+
+figure {
+ margin: 0;
+}
+
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: .35em .625em .75em;
+}
+
+legend {
+ border: 0;
+ padding:0;
+}
+
+button, input, select, textarea {
+ font-family: inherit;
+ font-size: 100%;
+ margin: 0;
+}
+
+button, input {
+ line-height: normal;
+}
+
+button, html input[type="button"], input[type="reset"], input[type="submit"] {
+ -webkit-appearance: button;
+ cursor: pointer;
+}
+
+button[disabled], input[disabled] {
+ cursor: default;
+}
+
+input[type="checkbox"], input[type="radio"] {
+ box-sizing: border-box;
+ padding: 0;
+}
+
+input[type="search"] {
+ -webkit-appearance: textfield;
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+textarea {
+ overflow: auto;
+ vertical-align: top;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+html {
+ font-family: 'PT Sans', sans-serif;
+}
+
+pre, code {
+ font-family: 'Inconsolata', monospace;
+}
+
+h1, h2, h3,h4, h5, h6 {
+ font-family: 'PT Sans Narrow', sans-serif;
+ font-weight: 700;
+}
+
+html {
+ background-color: #eee8d5;
+ color: #657b83;
+ margin: 1em;
+}
+
+body {
+ background-color: #fdf6e3;
+ margin: 0 auto;
+ max-width: 23cm;
+ border: 1pt solid #93a1a1;
+ padding: 1em;
+}
+
+code {
+ background-color: #eee8d5;
+ padding: 2px;
+ border-radius: 4px;
+}
+
+a {
+ color: #b58900;
+}
+
+a:visited {
+ color: #cb4b16;
+}
+
+a:hover {
+ color: #cb4b16;
+}
+
+h1 {
+ color: #d33682;
+}
+
+h2, h3, h4, h5, h6 {
+ color: #859900;
+}
+
+pre {
+ background-color: #fdf6e3;
+ color: #657b83;
+ border: 1pt solid #93a1a1;
+ padding: 1em;
+ box-shadow: 5pt 5pt 8pt #eee8d5;
+}
+
+pre code {
+ background-color: #fdf6e3;
+}
+
+h1 {
+ font-size: 2.8em;
+}
+
+h2 {
+ font-size: 2.4em;
+}
+
+h3 {
+ font-size: 1.8em;
+}
+
+h4 {
+ font-size: 1.4em;
+}
+
+h5 {
+ font-size: 1.3em;
+}
+
+h6 {
+ font-size: 1.15em;
+}
+
+.tag {
+ background-color: #eee8d5;
+ color: #d33682;
+ padding: 0 .2em;
+}
+
+.todo, .next, .done {
+ color: #fdf6e3;
+ background-color: #dc322f;
+ padding: 0 .2em;
+}
+
+.tag {
+ border-radius:.35em;
+}
+
+.TODO {
+ border-radius: .2em;
+ background-color: #2aa198;
+}
+
+.NEXT {
+ border-radius: .2em;
+ background-color: #268bd2;
+}
+
+.ACTIVE {
+ border-radius: .2em;
+ background-color: #268bd2;
+}
+
+.DONE {
+ border-radius: .2em;
+ background-color: #859900;
+}
+
+.WAITING {
+ border-radius: .2em;
+ background-color: #cb4b16;
+}
+
+.HOLD {
+ border-radius: .2em;
+ background-color:#d33682;
+}
+
+.NOTE {
+ border-radius: .2em;
+ background-color: #d33682;
+}
+
+.CANCELLED {
+ border-radius: .2em;
+ background-color: #859900;
+}
+
diff --git a/solarized-light.min.css b/solarized-light.min.css
new file mode 100644
index 0000000..40511c1
--- /dev/null
+++ b/solarized-light.min.css
@@ -0,0 +1 @@
+@import url(http://fonts.googleapis.com/css?family=Inconsolata);@import url(http://fonts.googleapis.com/css?family=PT+Sans);@import url(http://fonts.googleapis.com/css?family=PT+Sans+Narrow:400,700);article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap;word-wrap:break-word}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}html{font-family:'PT Sans',sans-serif}pre,code{font-family:'Inconsolata',sans-serif}h1,h2,h3,h4,h5,h6{font-family:'PT Sans Narrow',sans-serif;font-weight:700}html{background-color:#eee8d5;color:#657b83;margin:1em}body{background-color:#fdf6e3;margin:0 auto;max-width:23cm;border:1pt solid #93a1a1;padding:1em}code{background-color:#eee8d5;padding:2px}a{color:#b58900}a:visited{color:#cb4b16}a:hover{color:#cb4b16}h1{color:#d33682}h2,h3,h4,h5,h6{color:#859900}pre{background-color:#fdf6e3;color:#657b83;border:1pt solid #93a1a1;padding:1em;box-shadow:5pt 5pt 8pt #eee8d5}pre code{background-color:#fdf6e3}h1{font-size:2.8em}h2{font-size:2.4em}h3{font-size:1.8em}h4{font-size:1.4em}h5{font-size:1.3em}h6{font-size:1.15em}.tag{background-color:#eee8d5;color:#d33682;padding:0 .2em}.todo,.next,.done{color:#fdf6e3;background-color:#dc322f;padding:0 .2em}.tag{-webkit-border-radius:.35em;-moz-border-radius:.35em;border-radius:.35em}.TODO{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#2aa198}.NEXT{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#268bd2}.ACTIVE{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#268bd2}.DONE{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#859900}.WAITING{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#cb4b16}.HOLD{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#d33682}.NOTE{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#d33682}.CANCELLED{-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;background-color:#859900} \ No newline at end of file