2313 lines
84 KiB
Org Mode
2313 lines
84 KiB
Org Mode
#+TITLE: My Emacs Configuration
|
||
#+PROPERTY: header-args :tangle yes
|
||
#+PROPERTY: header-args:emacs-lisp :lexical yes :tangle yes :comments nil :padline yes
|
||
|
||
|
||
* Literate Configuration for Emacs
|
||
** Goals
|
||
- Manage *init.el* as an org-file
|
||
- Use a simple robust tangling solution which works together with
|
||
Crafted Emacs
|
||
- Leverage inclusion of ~use-package~ in Emacs29 for configuration and
|
||
loading
|
||
- Reduce dependencies : read evaluate the value a package brings
|
||
before including it
|
||
- Refactor existing configuration
|
||
** Notes on Elpaca and dev versions of emacs.
|
||
Elpaca needs the build date of emacs to compare to package versions or
|
||
something. However it does not support all dev versions.
|
||
|
||
For guix emacs-next packages you can find the date with: ( <C-c C-c>
|
||
in the source block below:
|
||
|
||
#+BEGIN_SRC shell
|
||
stat /gnu/store/*emacs-next-[23]*.drv | rg Birth | cut -d' ' -f3 | tr -d '-'
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| 20240727 |
|
||
| 20240727 |
|
||
|
||
It is possible there are more so probably the most recent one is the
|
||
one to use.
|
||
|
||
** Inspiration Sources
|
||
|
||
- [[https://pages.sachachua.com/.emacs.d/Sacha.html][Sacha Chua's Emacs config]]
|
||
- [[https://github.com/jwiegley/use-package][GitHub repo for use-package]]
|
||
- [[https://github.com/jwiegley/dot-emacs/blob/master/init.org][John Wiegley's .emacs file]]
|
||
- [[https://github.com/TheBB/dotemacs/blob/master/init.el][Eivind Fonn's init.el]]
|
||
|
||
* First Things First
|
||
|
||
** Make tangled file read-only
|
||
|
||
Add a preamble to the file to ensure some global settings, most
|
||
importantly make the file read-only to avoid editing the tangled file by
|
||
accident instead of the source in the org-mode file.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; init.el --- Literate configuration for Emacs -*- lexical-binding: t; read-only-mode: t; -*-
|
||
;;
|
||
;;; Commentary:
|
||
;;
|
||
;; DO NOT EDIT!!!
|
||
;;
|
||
;; This file is automatically generated from the source in *init.org*.
|
||
;;
|
||
#+END_SRC
|
||
|
||
Also immediately set lexical binding mode.
|
||
** Set the garbage collector threshold, to avoid collections
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq gc-cons-percentage 0.5
|
||
gc-cons-threshold (* 128 1024 1024))
|
||
#+end_src
|
||
|
||
** Report time spent loading the configuration
|
||
|
||
#+begin_src emacs-lisp
|
||
(defconst emacs-start-time (current-time))
|
||
|
||
(defun report-time-since-load (&optional suffix)
|
||
"Report the time since the file was init script was started.
|
||
|
||
If SUFFIX is provided, it is appended to the message."
|
||
(message "%.3fs: %s"
|
||
(float-time (time-subtract (current-time) emacs-start-time))
|
||
suffix))
|
||
|
||
(add-hook 'after-init-hook
|
||
#'(lambda () (report-time-since-load " [after-init]"))
|
||
t)
|
||
(report-time-since-load "start init file")
|
||
#+end_src
|
||
|
||
When looking for where the time goes, the `report-time-since-load`
|
||
with a string indicating the location in the init process can report
|
||
on the time since start.
|
||
|
||
*** Save customizations in a separate file
|
||
|
||
By default customization settings are saved at the end of the *init.el*
|
||
file. This wreaks havoc with managing the files in git and will not
|
||
work with the tangled version anyway as it will be removed/overwritten
|
||
each time the file is regenerated.
|
||
Here we set the location of the file to save the customizations and
|
||
then load it.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;;; Code:
|
||
(setq custom-file (concat user-emacs-directory "custom.el"))
|
||
(when (and custom-file
|
||
(file-exists-p custom-file))
|
||
(load custom-file nil :nomessage))
|
||
#+END_SRC
|
||
|
||
*** Load the General
|
||
|
||
Needs to load early so the ~:general~ keyword is available for ~use-package~.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package general
|
||
:ensure t
|
||
:demand t)
|
||
#+END_SRC
|
||
|
||
|
||
** Utility Functions
|
||
|
||
|
||
*** Reload dir local variables
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun pti-reload-dir-locals-for-current-buffer ()
|
||
"Reload dir locals for the current buffer."
|
||
(interactive)
|
||
(let ((enable-local-variables :all))
|
||
(hack-dir-local-variables-non-file-buffer)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: pti-reload-dir-locals-for-current-buffer
|
||
|
||
|
||
** Get latest version of a github released project
|
||
|
||
Many projects nowadays use github to release their software. However
|
||
there is no easy way to get the latest version of a project
|
||
provided. This functions uses the releases API to get the latest
|
||
metadata and get the version number from the JSON.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(require 'url)
|
||
(defun pti-latest-github-release (repo)
|
||
"Return the latest version of the releases for REPO.
|
||
|
||
The repo should be in the form of `owner/repo'."
|
||
(with-temp-buffer
|
||
(url-insert-file-contents
|
||
(format "https://api.github.com/repos/%s/releases/latest" repo))
|
||
(let ((result (json-read)))
|
||
(cdr (assoc 'name result)))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: pti-latest-github-release
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no :results value
|
||
(pti-latest-github-release "plantuml/plantuml")
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: v1.2024.6
|
||
|
||
* Crafted Emacs
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Crafted Emacs")
|
||
#+END_SRC
|
||
|
||
|
||
When I could not solve the persistent slowness I experienced on
|
||
Windows using Doom Emacs, I wanted to switch to a more hands-on
|
||
minimal Emacs configuration. I just watched the 'Rational Emacs'
|
||
episode and that seemed as good a starting point as any. Then in
|
||
August '22 it was renamed to Crafted Emacs which prompted a
|
||
(minor) refactoring.
|
||
I used for a quite a long term Doom on Linux and Rational/Crafted on
|
||
Windows till I was comfortable enough and not started Doom anymore.
|
||
|
||
** Initialize Crafted Emacs
|
||
|
||
Loads the Crafted Emacs initialization configuration:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; initialize crafted-emacs
|
||
(setq crafted-startup-inhibit-splash t)
|
||
(load "~/.local/share/crafted-emacs/modules/crafted-init-config")
|
||
#+END_SRC
|
||
|
||
|
||
** Add and configure packages and load crafted and custom modules
|
||
|
||
Lists and installs a variety of packages and includes custom configurations for them:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(require 'crafted-completion-packages)
|
||
(require 'crafted-evil-packages)
|
||
(require 'crafted-lisp-packages)
|
||
(require 'crafted-writing-packages)
|
||
(require 'crafted-ui-packages)
|
||
|
||
(report-time-since-load "loading packages")
|
||
(crafted-package-install-selected-packages)
|
||
(elpaca-wait)
|
||
|
||
(require 'crafted-defaults-config)
|
||
(require 'crafted-evil-config)
|
||
(require 'crafted-completion-config)
|
||
(require 'crafted-org-config)
|
||
(require 'crafted-lisp-config)
|
||
(require 'crafted-writing-config)
|
||
(require 'crafted-ui-config):
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: pti-org-config
|
||
|
||
* Integration with Environment
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Integration with Environment")
|
||
#+END_SRC
|
||
|
||
** Set default Coding System to Use UTF-8 Everywhere
|
||
|
||
Ensures UTF-8 is the default coding system everywhere.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(set-default-coding-systems 'utf-8)
|
||
(set-language-environment 'utf-8)
|
||
(setq locale-coding-system 'utf-8)
|
||
(prefer-coding-system 'utf-8)
|
||
|
||
|
||
;; Treat clipboard input as UTF-8 string first; compound text next, etc.
|
||
(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))
|
||
#+END_SRC
|
||
|
||
Here’s a breakdown of what each line does:
|
||
|
||
1. *(set-default-coding-systems 'utf-8)*
|
||
|
||
This line sets the default coding system for new buffers. When you
|
||
create a new buffer or open a file, Emacs will use UTF-8 encoding
|
||
by default. It will also set the default terminal and keyboard
|
||
coding systems. This applies to all internal operations where a
|
||
specific coding system has not been specified.
|
||
|
||
2. *(set-language-environment 'utf-8)*
|
||
|
||
This sets the language environment to UTF-8. Emacs uses the
|
||
language environment to guess the preferred coding systems for
|
||
reading and writing files and for other operations. Setting this to
|
||
UTF-8 ensures that UTF-8 is preferred in all language-related
|
||
contexts.
|
||
|
||
3. *(setq locale-coding-system 'utf-8)*
|
||
|
||
This sets the coding system for locale data, such as environment
|
||
variables and system messages. It ensures that Emacs correctly
|
||
interprets UTF-8 encoded data coming from the operating system.
|
||
|
||
4. *(prefer-coding-system 'utf-8)*
|
||
|
||
This makes UTF-8 the preferred coding system for any situation
|
||
where Emacs needs to choose an encoding. It ensures that Emacs
|
||
prefers UTF-8 over other encodings.
|
||
|
||
5. *(setq x-select-request-type ...)*
|
||
Treat clipboard input as UTF8_STRING first, compound text next,
|
||
etc... .
|
||
|
||
|
||
** Set Path from shell configuration
|
||
In order to get the paths in Emacs to be consistent with the ones in
|
||
the terminals we get them from a started shell instead of the current
|
||
environment which can be considerably different in X, Wayland or on
|
||
Mac because the shell initialization scripts have not run yet.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; set path from shell when started in UI-RELATED
|
||
(use-package exec-path-from-shell
|
||
:ensure t
|
||
:defer 1
|
||
:if (or (daemonp) (memq window-system '(mac ns x)))
|
||
:config (exec-path-from-shell-initialize))
|
||
#+END_SRC
|
||
** Setup backup directories
|
||
Configures where backup files are stored:
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; setup backup directories
|
||
;; see https://www.emacswiki.org/emacs/BackupDirectory
|
||
(setq backup-directory-alist
|
||
`(("." . ,(file-name-concat user-emacs-directory "backups"))))
|
||
#+END_SRC
|
||
|
||
** Enable integration with the Unix Password Store aka *pass*
|
||
The *pass* command gives a super practical way to store secrets
|
||
encrypted using *gpg* and use them in *.envrc* files, batch scripts on the
|
||
command line and, of course, in *Emacs*.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; enable unix password-store
|
||
;;(use-package epg)
|
||
;;(setq epg-pinentry-mode 'loopback)
|
||
(auth-source-pass-enable)
|
||
#+END_SRC
|
||
|
||
This enables *pass* secrets to be used for all subsystems supporting
|
||
*auth-source* (which are probably all of them nowadays). It does require
|
||
some finagling to map the parts on the name in the pass system.
|
||
|
||
- [[https://www.passwordstore.org/][Pass Website]]
|
||
- [[info:auth#The Unix password store][auth#The Unix password store in the info pages]]
|
||
|
||
*** Use of Pass Secrets in ELisp
|
||
|
||
It is very convenient to get secrets from this store (once gpg is set
|
||
up, which a totally different can of worms). A function
|
||
`auth-source-pass-get` is provided :
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(auth-source-pass-get 'secret "dummy/password")
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: shht!secret
|
||
|
||
** GUIX support
|
||
|
||
[[https://gitlab.com/emacs-guix/emacs-guix][Emacs-guix]] is a module to interact with the guix system and help
|
||
manage packages and profiles. It also offers support for creating
|
||
additional profiles and packages for Guix.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package guix
|
||
:ensure t
|
||
:defer 5)
|
||
#+END_SRC
|
||
|
||
I find it a bit a confusing module.
|
||
|
||
It provides a minor-mode
|
||
|
||
** Add a fully featured terminal emulator
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package eat
|
||
:ensure t
|
||
:defer 5)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26285 20099 685413 nil elpaca-process-queues nil nil 626000 nil]
|
||
|
||
|
||
** Enable editing textareas in browsers with Emacs
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package edit-server
|
||
:ensure t
|
||
:commands edit-server-start
|
||
:init (if after-init-time
|
||
(edit-server-start)
|
||
(add-hook 'after-init-hook
|
||
#'(lambda() (edit-server-start))))
|
||
:config (setq edit-server-new-frame-alist
|
||
'((name . "Edit with Emacs FRAME")
|
||
(top . 200)
|
||
(left . 200)
|
||
(width . 80)
|
||
(height . 25)
|
||
(minibuffer . t)
|
||
(menu-bar-lines . t)
|
||
(window-system . x))))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26383 36877 803383 nil elpaca-process-queues nil nil 768000 nil]
|
||
|
||
|
||
* Editor Features
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Editor Features")
|
||
#+END_SRC
|
||
|
||
|
||
|
||
** Evil Vim Keybindings
|
||
|
||
I configure all my apps to use vim keybindings if possible to maximise
|
||
muscle memory.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; some provided automations
|
||
(crafted-evil-discourage-arrow-keys)
|
||
(crafted-evil-vim-muscle-memory)
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
|
||
;; set leader keys
|
||
(evil-set-leader nil (kbd "C-SPC"))
|
||
(evil-set-leader 'normal (kbd "SPC"))
|
||
(evil-set-leader 'normal "," t) ;; set localleader
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; make vc commands available via leader key
|
||
(evil-define-key 'normal 'global
|
||
(kbd "<leader>v") vc-prefix-map)
|
||
(keymap-global-set "C-c v")
|
||
#+END_SRC
|
||
|
||
#+RESULTS
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; make project commands available via leader key
|
||
(evil-define-key 'normal 'global
|
||
(kbd "<leader>p") project-prefix-map)
|
||
(keymap-global-set "C-c p" project-prefix-map)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: (keymap (2 . project-list-buffers) (111 . project-any-command) (120 . project-execute-extended-command) (114 . project-query-replace-regexp) (71 . project-or-external-find-regexp) (103 . project-find-regexp) (112 . project-switch-project) (107 . project-kill-buffers) (101 . project-eshell) (99 . project-compile) (118 . magit-status) (68 . project-dired) (100 . project-find-dir) (115 . project-shell) (98 . project-switch-to-buffer) (70 . project-or-external-find-file) (102 . project-find-file) (38 . project-async-shell-command) (33 . project-shell-command))
|
||
|
||
#+RESULTS
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(evil-define-key 'normal 'global
|
||
(kbd "<leader>ff") #'find-file
|
||
(kbd "<leader>fo") #'recentf
|
||
(kbd "<leader>fr") #'revert-buffer
|
||
(kbd "<leader>fd") #'diff-buffer-with-file)
|
||
(keymap-global-set "C-c ff") #'find-file
|
||
(keymap-global-set "C-c fo") #'recentf
|
||
(keymap-global-set "C-c fr") #'revert-buffer
|
||
(keymap-global-set "C-c fd") #'diff-buffer-with-file)
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; dired integration
|
||
(evil-collection-dired-setup)
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package harpoon
|
||
:ensure t
|
||
:init
|
||
(setq pti-harpoon-map (make-sparse-keymap))
|
||
(keymap-set pti-harpoon-map (kbd "h") 'harpoon-toggle-quick-menu)
|
||
(keymap-set pti-harpoon-map (kbd "a") 'harpoon-add-file)
|
||
(keymap-set pti-harpoon-map (kbd "f") 'harpoon-toggle-file)
|
||
(keymap-set pti-harpoon-map (kbd "j") 'harpoon-go-to-1)
|
||
(keymap-set pti-harpoon-map (kbd "k") 'harpoon-go-to-2)
|
||
(keymap-set pti-harpoon-map (kbd "l") 'harpoon-go-to-3)
|
||
(keymap-set pti-harpoon-map (kbd ";") 'harpoon-go-to-4)
|
||
(keymap-set pti-harpoon-map (kbd "h") 'harpoon-toggle-quick-menu)
|
||
:bind
|
||
(("<leader>h" . 'pti-harpoon-map)))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26284 54919 318035 nil elpaca-process-queues nil nil 339000 nil]
|
||
|
||
** User Interface
|
||
*** Do not show default splash screen
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; Show splash screen
|
||
(unless crafted-startup-inhibit-splash
|
||
(setq initial-buffer-choice #'crafted-startup-screen))
|
||
#+END_SRC
|
||
|
||
****
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; Profile emacs startup
|
||
(add-hook 'emacs-startup-hook
|
||
(lambda ()
|
||
(message "Emacs started in %s."
|
||
(emacs-init-time))))
|
||
#+END_SRC
|
||
*** Configure Fonts with Fontaine
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;;
|
||
;; Font configuration
|
||
;;
|
||
|
||
(defun pti-find-installed-font (fonts)
|
||
"Find the first font in FONTS which is installed on this system."
|
||
(seq-find
|
||
(lambda (f) (find-font (font-spec :family f)))
|
||
fonts))
|
||
|
||
(defun pti-configure-fonts (&optional frame)
|
||
"Set configuration of fonts based on display size.
|
||
|
||
The FRAME argument makes it possible to set the fonts for new frames by
|
||
adding this function to `after-make-frame-functions' which must have
|
||
this argument."
|
||
(defvar pti-font-size
|
||
(if (> (display-pixel-height) 1600) 22 14))
|
||
|
||
;; set startup and default fontsets to make org-bullet work
|
||
|
||
(set-fontset-font "fontset-startup" nil "DejaVu Sans Mono" nil)
|
||
(set-fontset-font "fontset-default" nil "DejaVu Sans Mono" nil)
|
||
|
||
|
||
(setq fontaine-presets
|
||
`((t
|
||
:default-family ,(pti-find-installed-font
|
||
'("FiraCode NF" "FiraCode Nerd Font"))
|
||
:default-weight regular
|
||
:default-height ,pti-font-size
|
||
:variable-pitch-family ,(pti-find-installed-font
|
||
'("Lato" "Literation Sans" "FiraCode NF Propo" "FiraCode Nerd Font Propo" "DejaVu Sans"))
|
||
:variable-pitch-height 1.33
|
||
:bold-weight heavy
|
||
:line-spacing 1
|
||
)
|
||
(regular
|
||
:default-height 100)
|
||
(small
|
||
:default-height 75)
|
||
(medium
|
||
:default-height 125)
|
||
(large
|
||
:default-height 150)))
|
||
|
||
(fontaine-set-preset 'regular))
|
||
|
||
(use-package fontaine
|
||
:ensure t
|
||
:if (display-graphic-p)
|
||
:demand t
|
||
:config (progn
|
||
(pti-configure-fonts)
|
||
(add-hook 'after-make-frame-functions #'pti-configure-fonts)))
|
||
#+END_SRC
|
||
|
||
*** Update configuration of each created frame
|
||
|
||
When running as daemon there is no graphical context. This means that
|
||
all graphical related settings cannot be set properly at initial
|
||
startup if we need to interrogate the capabilities of the current
|
||
screen.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;;
|
||
;; remove chrome from the frames
|
||
;; also support client frames
|
||
;;
|
||
(defun pti-display-tweaks (&optional frame)
|
||
"Configure a newly created FRAME."
|
||
(interactive)
|
||
(unless frame
|
||
(setq frame (selected-frame)))
|
||
(when (display-graphic-p)
|
||
(with-selected-frame frame
|
||
;; remove chrome from UI
|
||
(menu-bar-mode -1)
|
||
(tool-bar-mode -1)
|
||
(scroll-bar-mode -1)
|
||
)))
|
||
|
||
(add-hook 'after-make-frame-functions #'pti-display-tweaks)
|
||
;; run it in the current frame, because the hooks have already fired
|
||
(pti-display-tweaks)
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
*** Set Theme
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; Set theme
|
||
|
||
(use-package catppuccin-theme
|
||
:ensure t
|
||
:demand t)
|
||
(use-package modus-themes
|
||
:ensure t
|
||
:demand t
|
||
:custom
|
||
(modus-themes-italic-constructs t)
|
||
(modus-themes-bold-constructs t)
|
||
(modus-themes-mixed-fonts t "enable mixed fonts in org and markdown et al.")
|
||
|
||
(modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted))
|
||
|
||
(modus-themes-completions '((matches . (extrabold background intense))
|
||
(selection . (semibold accented intense))))
|
||
(modus-themes-org-blocks 'tinted-background)
|
||
(modus-themes-mixed-fonts t)
|
||
|
||
(modus-themes-headings '((1 . (monochrome extrabold background overline variable-pitch 1.6))
|
||
(2 . (monochrome bold overline 1.4))
|
||
(3 . (monochrome semibold overline 1.3))
|
||
(4 . (monochrome 1.2))
|
||
(5 . (monochrome 1.1))
|
||
(agenda-date . (semilight 1.5))
|
||
(agenda-structure . (variable-pitch light 1.9))
|
||
(t . (monochrome light))))
|
||
:config
|
||
(load-theme 'modus-operandi-tinted :no-confirm)
|
||
:bind
|
||
(("<f5>" . #'modus-themes-toggle)))
|
||
#+END_SRC
|
||
|
||
There is a keybinding on *<F5>* to toggle between light and dark mode.
|
||
|
||
#+RESULTS:
|
||
: modus-themes-toggle
|
||
|
||
*** Limit Height of Selected Popup Windows
|
||
#+BEGIN_SRC emacs-lisp
|
||
(push '("\\*Occur\\*"
|
||
;; display-buffer functions, first one that succeeds is used
|
||
(display-buffer-reuse-mode-window
|
||
display-buffer-below-selected)
|
||
;; Parameters
|
||
(window-height . 10))
|
||
display-buffer-alist)
|
||
(push '("\\*Warnings\\*"
|
||
;; display-buffer functions, first one that succeeds is used
|
||
(display-buffer-reuse-mode-window
|
||
display-buffer-below-selected)
|
||
;; Parameters
|
||
(window-height . 10))
|
||
display-buffer-alist)
|
||
(push '("\\*Geiser Debug\\*"
|
||
;; display-buffer functions, first one that succeeds is used
|
||
(display-buffer-reuse-mode-window
|
||
display-buffer-below-selected)
|
||
;; Parameters
|
||
(window-height . 10))
|
||
display-buffer-alist)
|
||
|
||
;; set custom info folder
|
||
(add-to-list 'Info-default-directory-list "~/.local/share/info/")
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| ~/.local/share/info/ |
|
||
|
||
a quick way to test this it to generate a warning with
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(warn "This is a warning")
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: t
|
||
|
||
** Yasnippet configuration
|
||
Enables and configures Yasnippet, a template system for Emacs:
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure yasnippet
|
||
(use-package yasnippet
|
||
:ensure nil
|
||
:defer 5
|
||
:config
|
||
(yas-global-mode 1))
|
||
#+END_SRC
|
||
|
||
** Enable LLM access with Ellama
|
||
Configures access to language models using Ellama. I don't know
|
||
whether to put it under writing, comms or programming as it is equally
|
||
/useful(?)/ for either activity. So I promoted it to an Editor feature.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package llm
|
||
:ensure t
|
||
:commands (llm-chat llm-ask-about llm-ask-line llm-ask-selection)
|
||
)
|
||
(use-package llm-openai
|
||
:ensure nil
|
||
:commands (make-llm-openai)
|
||
:after llm)
|
||
;; enable LLM access with ellama
|
||
(use-package ellama
|
||
:ensure t
|
||
:commands (ellama-chat ellama-ask-about ellama-ask-line ellama-ask-selection)
|
||
:custom
|
||
(ellama-language "English")
|
||
(ellama-keymap-prefix "<leader>a")
|
||
(ellama-provider
|
||
(make-llm-openai
|
||
:key (auth-source-pass-get 'secret "snamellit/openai-api-key")
|
||
:chat-model "gpt-4o"
|
||
))
|
||
(ellama-sessions-directory (expand-file-name "~/Nextcloud/ellama-sessions"))
|
||
:config
|
||
(report-time-since-load "Ellama is available"))
|
||
#+END_SRC
|
||
|
||
It seems the *gpt-4o* model provides better responses. I should
|
||
investigate local models more.
|
||
|
||
** Dired Configuration
|
||
Enables an alternative file navigation behavior in Dired, Emacs' directory editor:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(put 'dired-find-alternate-file 'disabled nil)
|
||
#+END_SRC
|
||
** Use Magit for version control
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; default to magit for version control
|
||
(use-package magit
|
||
:ensure t
|
||
:commands (magit-status)
|
||
:bind
|
||
(("C-x p v" . magit-status)
|
||
("<leader> p v" . magit-status)))
|
||
|
||
#+END_SRC
|
||
** Better EDiff support
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; better ediff setting suggested by cwebber
|
||
(use-package ediff
|
||
:commands (ediff ediff-files ediff3 ediff-files3)
|
||
:config
|
||
(setq
|
||
ediff-window-setup-function 'ediff-setup-windows-plain
|
||
ediff-split-window-function 'split-window-horizontally))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: t
|
||
|
||
** Replace normal Buffer Support with IBuffer
|
||
#+BEGIN_SRC emacs-lisp
|
||
;;; enhance ibuffer with ibuffer-project if it is available.
|
||
(defun crafted-ide-enhance-ibuffer-with-ibuffer-project ()
|
||
"Set up integration for `ibuffer' with `ibuffer-project'."
|
||
(setq ibuffer-filter-groups (ibuffer-project-generate-filter-groups))
|
||
(unless (eq ibuffer-sorting-mode 'project-file-relative)
|
||
(ibuffer-do-sort-by-project-file-relative)))
|
||
|
||
(use-package ibuffer-project
|
||
:ensure t
|
||
:hook
|
||
(ibuffer-mode . crafted-ide-enhance-ibuffer-with-ibuffer-project)
|
||
:bind (("C-x C-b" . #'ibuffer)))
|
||
#+END_SRC
|
||
** Enable EditorConfig Standard Support
|
||
#+BEGIN_SRC emacs-lisp
|
||
;;; load editorconfig support
|
||
(use-package editorconfig
|
||
:hook
|
||
(prog-mode . (lambda() (editorconfig-mode 1))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| editorconfig-mode |
|
||
|
||
** Enable Direnv Integration
|
||
|
||
Direnv is the key to have isolated project folders which maintain their own bubble to support whatever is done in that folder.
|
||
- set environment variables
|
||
- run prep scripts or start services
|
||
- load nix or guix profiles to provide support applications
|
||
|
||
Emacs' direnv module gives first class support to Emacs projects so they use the right tools for the project.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; enable direnv mode
|
||
(use-package direnv
|
||
:ensure t
|
||
:config
|
||
(direnv-mode))
|
||
#+END_SRC
|
||
|
||
This enables direnv globally.
|
||
|
||
** Configure Embark
|
||
|
||
Currently already loaded by crafted emacs
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(use-package embark
|
||
:ensure t
|
||
:bind
|
||
(("C-m" . embark-act)
|
||
("C-;" . embark-dwim)
|
||
("C-h B" . embark-bindings))
|
||
:config
|
||
(setq prefix-help-command #'embark-prefix-help-command))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26384 64829 513923 nil elpaca-process-queues nil nil 691000 nil]
|
||
|
||
* Writing and Planning
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Writing and Planning")
|
||
#+END_SRC
|
||
|
||
** Org Mode
|
||
|
||
*** Mixed Pitch Support by Default in Org
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun pti-org-mode-config ()
|
||
"Set options for better writing in org buffers."
|
||
(mixed-pitch-mode)
|
||
(visual-line-mode)
|
||
(turn-on-auto-fill)
|
||
)
|
||
|
||
(use-package org-bullets
|
||
:ensure t
|
||
:if (display-graphic-p)
|
||
:hook (org-mode . org-bullets-mode))
|
||
|
||
(use-package mixed-pitch
|
||
:ensure t
|
||
:if (display-graphic-p)
|
||
:hook
|
||
(org-mode . mixed-pitch-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26279 36119 854408 nil elpaca-process-queues nil nil 376000 nil]
|
||
*** Org Configuration
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package org
|
||
:ensure nil
|
||
:custom
|
||
(org-agenda-skip-scheduled-if-done t)
|
||
(org-agenda-skip-deadline-if-done t)
|
||
(org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "WAITING(w)" "SOMEDAY(s)" "PROJ(p)"
|
||
"|" "DONE(d)" "CANCELED(c)")))
|
||
(org-todo-keywords-for-agenda '((sequence "NEXT(n)" "TODO(t)" "WAITING(w)" "SOMEDAY(s)" "PROJ(p)" "|" "DONE(d)" "CANCELED(c)")))
|
||
(line-spacing 0.1)
|
||
(left-margin-width 2)
|
||
(right-margin-width 2)
|
||
(org-hide-emphasis-markers t)
|
||
(org-agenda-files (list "~/Nextcloud/org/" "~/org/snamellit/" "~/org/customer/" "~/org/personal/"))
|
||
(org-refile-targets '(
|
||
(org-agenda-files . (:level . 1))
|
||
("~/org/personal/bijen.org" . (:level . 1))
|
||
("~/org/personal/fitness.org" . (:level . 1))
|
||
))
|
||
:config
|
||
;; set files for agenda views
|
||
(setq +org-capture-todo-file "~/Nextcloud/org/inbox.org"
|
||
+org-capture-notes-file "~/Nextcloud/org/inbox.org"
|
||
+org-capture-journal-file "~/Nextcloud/org/journal.org"
|
||
+org-capture-projects-file "~/Nextcloud/org/projects.org")
|
||
:hook
|
||
(org-mode . pti-org-mode-config)
|
||
:bind (
|
||
("<leader> o a" . org-agenda)
|
||
("<leader> o s" . org-store-link)
|
||
))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
*** Org Babel Support
|
||
|
||
I have a test file which has samples of babel features for easy
|
||
testing in my [[file:~/org/snamellit/testfile.org::*User Journey Graph][org babel test file.]]
|
||
|
||
**** Support REST calls in Babel Blocks
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; enable verb package to do REST calls
|
||
(use-package verb
|
||
:ensure t
|
||
:defer 3
|
||
:bind
|
||
(:map org-mode-map
|
||
("C-c C-r" . verb-command-map)))
|
||
|
||
(use-package ob-verb
|
||
:after verb
|
||
:defer 3)
|
||
#+END_SRC
|
||
**** Support Mermaid Diagrams in Babel Blocks
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; enable mermaid for org-babel
|
||
(use-package ob-mermaid
|
||
:ensure t
|
||
:defer 3)
|
||
#+END_SRC
|
||
|
||
Mermaid needs support of the mermaid-cli which is a node package. It
|
||
can be installed with
|
||
|
||
#+BEGIN_SRC shell :tangle no
|
||
npm install -g @mermaid-js/mermaid-cli
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| | | | | | |
|
||
| changed | 194 | packages | in | 6s | |
|
||
| | | | | | |
|
||
| 39 | packages | are | looking | for | funding |
|
||
| run | `npm | fund` | for | details | |
|
||
|
||
**** Support PlantUML Diagrams in Babel Blocks
|
||
|
||
Requires nothing special, other than *plantuml.jar* archive installed.
|
||
|
||
The following code block depends on [[*Get latest version of a github released project][Get latest version of a github
|
||
released project]] utility function. This block will check if there is a
|
||
plantuml.jar in the emacs config directory and if not download the
|
||
latest version from github. The `pti-download-latest-plantuml` is made
|
||
interactive to manually install the latest version if needed.
|
||
|
||
#+BEGIN_SRC emacs-lisp :lexical t
|
||
(defun pti-download-latest-plantuml ()
|
||
"Download the latest version of PlantUML.
|
||
|
||
This function is interactive to make it easy to upgrade to
|
||
the latest, current version with `M-x pti-download-latest-plantuml'."
|
||
(interactive)
|
||
(let* ((version (pti-latest-github-release "plantuml/plantuml"))
|
||
(url (format
|
||
"https://github.com/plantuml/plantuml/releases/download/%s/plantuml-%s.jar"
|
||
version (substring version 1))))
|
||
(message "Downloading PlantUML version %s from %s" version url)
|
||
(url-copy-file
|
||
url
|
||
(concat user-emacs-directory "plantuml.jar")
|
||
t)))
|
||
|
||
(let ((plantuml-jar (concat user-emacs-directory "plantuml.jar")))
|
||
(if (not (file-exists-p plantuml-jar))
|
||
(pti-download-latest-plantuml))
|
||
|
||
(setq org-plantuml-jar-path plantuml-jar))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: /home/pti/.config/emacs/plantuml.jar
|
||
|
||
**** Configure Babel Languages
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure babel languages
|
||
(use-package org-babel
|
||
:no-require
|
||
:after '(ob-verb ob-mermaid)
|
||
:config
|
||
(org-babel-do-load-languages
|
||
'org-babel-load-languages
|
||
'((emacs-lisp . t)
|
||
(shell . t)
|
||
(python . t)
|
||
(latex . t)
|
||
(verb . t)
|
||
(scheme . t)
|
||
(plantuml . t)
|
||
(mermaid . t)
|
||
(dot . t))))
|
||
#+END_SRC
|
||
|
||
**** Temporary Patches for Org Babel
|
||
|
||
***** Fix Scheme Babel Bug
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; fix a bug in ob-scheme which causes an error to be thrown when evaluating
|
||
;; a cons cell or improper list
|
||
;; see https://list.orgmode.org/87bkk3x1bu.fsf@gajsin.name/T/
|
||
(defun org-babel-scheme--table-or-string (results)
|
||
"Convert RESULTS into an appropriate elisp value.
|
||
If the results look like a list or tuple, then convert them into an
|
||
Emacs-lisp table, otherwise return the results as a string."
|
||
(let ((res (org-babel-script-escape results)))
|
||
(cond ((proper-list-p res)
|
||
(mapcar (lambda (el)
|
||
(if (or (null el) (eq el 'null))
|
||
org-babel-scheme-null-to
|
||
el))
|
||
res))
|
||
(t res))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: org-babel-scheme--table-or-string
|
||
|
||
**** TODO Move babel test file to emacs config folder
|
||
|
||
Alternatively I might add a sample after each configured block to keep
|
||
it in the same context. Hmmm.... sounds even better.
|
||
|
||
|
||
*** Org Export
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package ox
|
||
:ensure nil)
|
||
|
||
#+END_SRC
|
||
|
||
**** Org Export to Markdown
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package ox-md
|
||
:ensure nil
|
||
:after ox
|
||
)
|
||
#+END_SRC
|
||
**** Org Latex Export
|
||
***** Syntax Highlighting requires Multiple Passes
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; support for minted in LaTeX for code highlighting
|
||
(use-package ox-latex
|
||
:ensure nil
|
||
:after ox
|
||
:config
|
||
(setq org-latex-compiler "lualatex")
|
||
(setq org-latex-pdf-process
|
||
'("%latex -shell-escape -interaction nonstopmode -output-directory %o %f"
|
||
"%latex -interaction nonstopmode -output-directory %o %f"
|
||
"%latex -interaction nonstopmode -output-directory %o %f"))
|
||
)
|
||
#+END_SRC
|
||
***** Load Customer Latex Classes
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; Customer LaTeX classes
|
||
(setq mlx-latex-classes
|
||
'(
|
||
("mlx-beamer" "\\documentclass{mlx-beamer}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
("mlx-book" "\\documentclass{mlx-book}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\part{%s}" . "\\part*{%s}")
|
||
("\\chapter{%s}" . "\\chapter*{%s}")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
("mlx-report" "\\documentclass{mlx-report}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\part{%s}" . "\\part*{%s}")
|
||
("\\chapter{%s}" . "\\chapter*{%s}")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
("mlx-article" "\\documentclass{mlx-article}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
||
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
||
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
|
||
("mlx-project-charter" "\\documentclass{mlx-project-charter}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\section{%s}
|
||
\\begin{mdframed}" "\\end{mdframed}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
||
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
||
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))
|
||
#+END_SRC
|
||
***** Load My Latex Classes
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq snm-latex-classes
|
||
'(
|
||
("snm-beamer" "\\documentclass{snm-beamer}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
("snm-book" "\\documentclass{snm-book}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\part{%s}" . "\\part*{%s}")
|
||
("\\chapter{%s}" . "\\chapter*{%s}")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
("snm-report" "\\documentclass{snm-report}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\part{%s}" . "\\part*{%s}")
|
||
("\\chapter{%s}" . "\\chapter*{%s}")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
("snm-invoice" "\\documentclass{snm-invoice}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
||
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
||
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
|
||
("snm-article" "\\documentclass{snm-article}
|
||
[NO-DEFAULT-PACKAGES]
|
||
[NO-PACKAGES]"
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
||
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
||
("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))
|
||
|
||
(with-eval-after-load 'ox-latex
|
||
(dolist
|
||
(class (append mlx-latex-classes snm-latex-classes))
|
||
(add-to-list 'org-latex-classes class)))
|
||
#+END_SRC
|
||
**** Blogging with Zola
|
||
|
||
gicrisf has [[https://github.com/gicrisf/ox-zola][created a package]] to export org files to Zola.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package ox-hugo
|
||
:ensure t
|
||
:pin melpa
|
||
:after ox)
|
||
(use-package ox-zola
|
||
:ensure (ox-zola :host github :repo "gicrisf/ox-zola" :files (:defaults "*.el" "backend" "stylesheets"))
|
||
:after ox-hugo
|
||
:config
|
||
(require 'ox-hugo)
|
||
:general
|
||
("<leader> o z" 'ox-zola-export-wim-to-md))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
<<<<<<< HEAD
|
||
: [nil 26302 34728 837190 nil elpaca-process-queues nil nil 651000 nil]
|
||
||||||| parent of 48b03fc (add the general package)
|
||
: [nil 26301 59789 776924 nil elpaca-process-queues nil nil 824000 nil]
|
||
=======
|
||
>>>>>>> 48b03fc (add the general package)
|
||
|
||
It is a wrapper around ~ox-hugo~ to export org files to Zola. It
|
||
supports most features of it and directs the user to the [[https://ox-hugo.scripter.co/][the hugo
|
||
exporter manual.]]
|
||
|
||
|
||
*** Org Capture Customization
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package org-capture
|
||
:ensure nil
|
||
:config (progn
|
||
(setq org-capture-templates '())
|
||
(add-to-list 'org-capture-templates
|
||
'("t" "Todo" entry (file+headline +org-capture-todo-file "Tasks")
|
||
"* TODO %?\n %i\n %a"))
|
||
(add-to-list 'org-capture-templates
|
||
'("n" "Note" entry (file+headline +org-capture-todo-file "Notes")
|
||
"* %?\n %i\n %a"))
|
||
(add-to-list 'org-capture-templates
|
||
'("j" "Journal" entry (file+datetree +org-capture-journal-file)
|
||
"* %?\nEntered on %U\n %i\n %a"))
|
||
(add-to-list 'org-capture-templates
|
||
'("b" "Blog Post" entry
|
||
(file+headline "~/org/snamellit/blog.org" "Blog Posts")
|
||
"** %^{Blog Title:}\n:PROPERTIES:\n:EXPORT_FILE_NAME %^{Export Filename:}\n:EXPORT_DATE: %t\n:END:\n%i%?"))
|
||
|
||
;; configure support for recipes
|
||
(add-to-list 'org-capture-templates
|
||
'("c" "Cookbook" entry (file "~/org/cookbook.org")
|
||
"%(org-chef-get-recipe-from-url)"
|
||
:empty-lines 1))
|
||
(add-to-list 'org-capture-templates
|
||
'("m" "Manual Cookbook" entry (file "~/org/cookbook.org")
|
||
"* %^{Recipe title: }\n :PROPERTIES:\n :source-url:\n :servings:\n :prep-time:\n :cook-time:\n :ready-in:\n :END:\n** Ingredients\n %?\n** Directions\n\n")))
|
||
:bind (("<leader>X" . org-capture)))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: t
|
||
*** Evil Support for Org
|
||
|
||
Better keybinding for evil mode from [[https://github.com/Somelauw/evil-org-mode][the evil-org github repo]].
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package evil-org
|
||
:ensure t
|
||
:after org
|
||
:hook
|
||
(org-mode . evil-org-mode)
|
||
:config
|
||
(require 'evil-org-agenda)
|
||
(evil-org-agenda-set-keys))
|
||
#+END_SRC
|
||
|
||
Here is a snapshot of the keybindings dd <2024-07-30 Tue>.
|
||
|
||
**** Quick overview
|
||
|
||
|----------------+---------------------------|
|
||
| key | explanation |
|
||
|----------------+---------------------------|
|
||
| gh, gj, gk, gl | navigate between elements |
|
||
| vae | select an element |
|
||
|----------------+---------------------------|
|
||
|
||
**** Headings and items
|
||
|
||
|--------------+------------------------|
|
||
| key | explanation |
|
||
|--------------+------------------------|
|
||
| M-ret | insert heading |
|
||
| <tab>, g TAB | fold / unfold headings |
|
||
| M-h or << | promote a heading |
|
||
| M-l or >> | demote a heading |
|
||
| M-k | move subtree up |
|
||
| M-j | move subtree down |
|
||
| M-S-h or <aR | promote a subtree |
|
||
| M-S-l or >aR | demote a subtree |
|
||
| vaR | select a subtree |
|
||
|--------------+------------------------|
|
||
|
||
**** Tables
|
||
|
||
|-----------+--------------------------------|
|
||
| key | explanation |
|
||
|-----------+--------------------------------|
|
||
| ( | previous table cell |
|
||
| ) | next table cell |
|
||
| { | beginning of table |
|
||
| } | end of table |
|
||
| M-h / M-l | move table column left / right |
|
||
| M-k / M-j | move table column up / down |
|
||
| vae | select table cell |
|
||
| vaE | select table row |
|
||
| var | select whole table |
|
||
|-----------+--------------------------------|
|
||
|
||
**** Agenda
|
||
|
||
|-------------------------+-------------------------+-----------------------------------------------------------------------------------|
|
||
| Evil key | Emacs key | explanation |
|
||
|-------------------------+-------------------------+-----------------------------------------------------------------------------------|
|
||
| gH | | Move cursor to the top of window |
|
||
| gM | | Move cursor to the middle of window |
|
||
| gL | | Move cursor to the bottom of window |
|
||
| <tab>, S-<return> | <tab> | go to the corresponding entry at point |
|
||
| g TAB | <tab> | go to the corresponding entry at point |
|
||
| <return> | <return> | go to the Org mode file which contains the item at point |
|
||
| M-<return> | L | Display Org file and center around the item |
|
||
| <space> | <space> | scroll up |
|
||
| <delete> or <backspace> | <delete> or <backspace> | scroll down |
|
||
| j, k | n, p | next, previous line |
|
||
| gj, gk, C-j, C-k | N, P | next, previous item |
|
||
| [, ] | b, f | previous, next week |
|
||
| J, K | -, +, S-down, S-up | down, up priority |
|
||
| H, L | S-left, S-right | modify date to earlier, later |
|
||
| t | t | cycle TODO keywords |
|
||
| M-j, M-k | M-down, M-up | drag line forward, backward |
|
||
| C-S-h, C-S-l | C-S-left, C-S-right | previous, next keyword |
|
||
| u | C-_, C-/ | undo |
|
||
| dd | C-k | delete item |
|
||
| da | a | ask and archive item |
|
||
| dA | $ | archive item |
|
||
| ct | : | set tags |
|
||
| ce | e | set effort |
|
||
| cT | ; | set timer |
|
||
| i | i | insert entry in diary |
|
||
| a | z | add note |
|
||
| A | A | append to agenda |
|
||
| C | k | capture |
|
||
| m | m | mark |
|
||
| * | * | toggle all marks |
|
||
| % | % | mark regexp |
|
||
| M | U | remove all marks |
|
||
| x | B | execute action on marks |
|
||
| gr | r | refresh agenda |
|
||
| gR | g | refresh all agendas |
|
||
| ZQ | x | exit agenda |
|
||
| ZZ | Q | quit agenda |
|
||
| gD | v | tweak display (deadlines, diary, follow/log-mode, entry text, grid, day/week/year |
|
||
| ZD | # | dim blocked tasks |
|
||
| sc, sr, se, st, s^ | <, =, _, /, ^ | filter by category, regexp, effort, tag, top headline |
|
||
| S | \vert | remove all filters |
|
||
| ss | ~ | filter/limit interactively |
|
||
| I | I | clock in |
|
||
| O | O | clock out |
|
||
| cg | J | jump to the currently clocked in task within the agenda |
|
||
| cc | X | cancel the current running clock |
|
||
| cr | R | toggle clocktable mode in an agenda buffer |
|
||
| . | . | go to today's date |
|
||
| gc | c | pop up calendar |
|
||
| gC | C | pop up date converter |
|
||
| p | > | pop up date selector |
|
||
| gh | H | pop up holiday calendar |
|
||
| gm | M | pop up phases of the moon |
|
||
| gs | S | pop up sunrise/sunset times |
|
||
| gt | T | pop up tag list |
|
||
| +, - | [, ] | manipulate the query by adding a search term with positive or negative selection |
|
||
|-------------------------+-------------------------+-----------------------------------------------------------------------------------|
|
||
|
||
*** Org GCal Support
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure support for google calendar
|
||
(use-package org-gcal
|
||
:ensure t
|
||
:custom
|
||
(org-gcal-client-id (auth-source-pass-get 'secret "snamellit/org-gcal-client"))
|
||
(org-gcal-client-secret (auth-source-pass-get "id" "snamellit/org-gcal-client"))
|
||
(org-gcal-fetch-file-alist '(("pti@snamellit.com" . "~/org/schedule.org")))
|
||
:commands (org-gcal-sync org-gcal-fetch) )
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
*** Org Jira Integration
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure org-jira
|
||
(use-package org-jira
|
||
:ensure (:host github :repo "ptillemans/org-jira" :branch "pti_fix_getUsers")
|
||
:commands (org-jira-get-issues org-jira-get-projects)
|
||
:config
|
||
(setq org-jira-users '(("Peter Tillemans". "557058:bdf83521-663b-4ae6-9b71-487bb98e2add")))
|
||
(setq jiralib-agile-page-size 1000)
|
||
(setq org-jira-custom-jqls
|
||
'(
|
||
(:jql " assignee = currentUser() and (created > '2024-01-01' of updated > '2024-01-01) order by created DESC"
|
||
:limit 100 :filename "this-year")
|
||
(:jql "project = TTRK and (resolution = Unresolved OR updated>=-15d) ORDER BY priority DESC"
|
||
:limit 100 :filename "~/Projects/timetrak/jira")
|
||
))
|
||
(make-directory "~/.org-jira" 'parents)
|
||
(setq jiralib-url (auth-source-pass-get "url" "customer/jira"))
|
||
(setq org-jira-custom-jqls
|
||
'((:jql " assignee = currentUser() and project = TTRK order by priority DESC " :limit 100 :filename "~/Projects/timetrak/jira")
|
||
(:jql " assignee = currentUser() and createdDate >= '2024-01-01' order by created DESC " :limit 100 :filename "this-years-work")))
|
||
(jiralib-login
|
||
(auth-source-pass-get "user" "customer/jira")
|
||
(auth-source-pass-get 'secret "customer/jira"))
|
||
:bind (("C-c ig" . 'org-jira-get-issues)
|
||
("C-c ip" . 'org-jira-get-projects)
|
||
("C-c ij" . 'org-jira-get-issues-from-custom-jql)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26292 53013 676464 nil elpaca-process-queues nil nil 459000 nil]
|
||
|
||
It is very useful to create for each active project a custom JQL to
|
||
make a snapshot of issues just for that project in the folder of the
|
||
project to keep everything in context.
|
||
|
||
|
||
|
||
**** Keybindings
|
||
|
||
| Key | Command |
|
||
|---------+------------------------------------------|
|
||
| C-c pg | org-jira-get-projects |
|
||
| C-c bg | org-jira-get-boards |
|
||
| C-c iv | org-jira-get-issues-by-board |
|
||
| C-c ib | org-jira-browse-issue |
|
||
| C-c ig | org-jira-get-issues |
|
||
| C-c ij | org-jira-get-issues-from-custom-jql |
|
||
| C-c ih | org-jira-get-issues-headonly |
|
||
| C-c if | org-jira-get-issues-from-filter-headonly |
|
||
| C-c iF | org-jira-get-issues-from-filter |
|
||
| C-c iu | org-jira-update-issue |
|
||
| C-c iw | org-jira-progress-issue |
|
||
| C-c in | org-jira-progress-issue-next |
|
||
| C-c ia | org-jira-assign-issue |
|
||
| C-c isr | org-jira-set-issue-reporter |
|
||
| C-c ir | org-jira-refresh-issue |
|
||
| C-c iR | org-jira-refresh-issues-in-buffer |
|
||
| C-c ic | org-jira-create-issue |
|
||
| C-c ik | org-jira-copy-current-issue-key |
|
||
| C-c sc | org-jira-create-subtask |
|
||
| C-c sg | org-jira-get-subtasks |
|
||
| C-c cc | org-jira-add-comment |
|
||
| C-c cu | org-jira-update-comment |
|
||
| C-c wu | org-jira-update-worklogs-from-org-clocks |
|
||
| C-c tj | org-jira-todo-to-jira |
|
||
| C-c if | org-jira-get-issues-by-fixversion |
|
||
|
||
**** TODO add focused project support
|
||
There should be some things we can do to better integrate this with
|
||
projects:
|
||
- update issues in background when opening the project.
|
||
- run custom JQL defined in the project iso globally.
|
||
|
||
|
||
*** Daviwil's Productivity Tools
|
||
|
||
@daviwil has a set of productivity tools which are very useful. I have
|
||
|
||
|
||
**** Find all tasks with a specific tag (or without)
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(setq org-agenda-custom-commands
|
||
'(("p" "Planning" tags-todo "+@planning")))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| p | Planning | tags-todo | +@planning |
|
||
|
||
We can remove tasks with a certain tag by adding `-@work` to the pattern.
|
||
|
||
**** Find all tasks with a specific tag (or without)
|
||
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(setq org-agenda-custom-commands
|
||
'(("u" "Untagged Tasks" tags-todo "-{.*}")))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| u | Untagged Tasks | tags-todo | -{.*} |
|
||
|
||
**** Combine multiple filters
|
||
|
||
We can add a list of queries
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(setq org-agenda-custom-commands
|
||
'(("p" "Planning" ((tags-todo "+@planning")
|
||
(tags-todo "-{.*}")))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| p | Planning | ((tags-todo +@planning) (tags-todo -{.*})) |
|
||
|
||
**** We can add settings to each filter
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(setq org-agenda-custom-commands
|
||
'(("p" "Planning" ((tags-todo "+@planning"
|
||
((org-agenda-overriding-header "Planning Tasks")))
|
||
(tags-todo "-{.*}"
|
||
((org-agenda-overriding-header "Untagged Tasks")))))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| p | Planning | ((tags-todo +@planning ((org-agenda-overriding-header Planning Tasks))) (tags-todo -{.*} ((org-agenda-overriding-header Untagged Tasks)))) |
|
||
|
||
**** Add support for an input file
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-agenda-custom-commands
|
||
'(("i" "Inbox" ((todo ".*"
|
||
((org-agenda-files '("~/Nextcloud/org/inbox.org"))
|
||
(org-agenda-overriding-header "Unprocessed Inbox Items")))))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| i | Inbox | ((todo .* ((org-agenda-files '(~/Nextcloud/org/inbox.org)) (org-agenda-overriding-header Unprocessed Inbox Items)))) |
|
||
|
||
|
||
**** Daily Agenda
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-agenda-custom-commands
|
||
'(("d" "Daily Agenda"
|
||
((agenda "" ((org-agenda-span 'day)
|
||
(org-deadline-warning-days 1)
|
||
(org-agenda-overriding-header "Today's Agenda")))
|
||
(tags-todo "+PRIORITY=\"A\""
|
||
((org-agenda-overriding-header "High-Priority Unfinished Tasks")
|
||
))))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| d | Daily Agenda | ((agenda ((org-agenda-span 'day) (org-deadline-warning-days 1) (org-agenda-overriding-header Today's Agenda))) (tags-todo +PRIORITY="A" ((org-agenda-overriding-header High-priority unfinished tasks)))) |
|
||
|
||
|
||
**** Weekly Review
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-log-done 'time) ; log the time when a task is completed
|
||
(setq org-agenda-start-with-log-mode t) ; show the log mode when starting the agenda
|
||
|
||
(setq org-agenda-custom-commands
|
||
'(("w" "Weekly Review"
|
||
((agenda "" ((org-agenda-overriding-header "Completed Tasks")
|
||
(org-agenda-skip-function (org-agenda-skip-entry-if 'nottodo 'done))
|
||
(org-agenda-span 'week)))
|
||
(agenda "" ((org-agenda-overriding-header "Unfinished Scheduled Tasks")
|
||
(org-agenda-skip-function (org-agenda-skip-entry-if 'todo 'done))
|
||
(org-agenda-span 'week)))
|
||
))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| w | Weekly Review | ((agenda ((org-agenda-overriding-header Completed Tasks) (org-agenda-skip-function (org-agenda-skip-entry-if 'nottodo 'done)) (org-agenda-span 'week))) (agenda ((org-agenda-overriding-header Unfinished Scheduled Tasks) (org-agenda-skip-function (org-agenda-skip-entry-if 'todo 'done)) (org-agenda-span 'week)))) |
|
||
|
||
|
||
** Denote
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure denote
|
||
(use-package denote
|
||
:ensure t
|
||
:init
|
||
(setq denote-directory (file-name-concat (expand-file-name "~") "Nextcloud/denote"))
|
||
(setq denote-dired-directories
|
||
(list denote-directory
|
||
(expand-file-name "~/Documents/denote")))
|
||
:hook (dired-mode . denote-dired-mode-in-directories)
|
||
:bind (
|
||
("<leader> n d" . (lambda () (interactive) (dired denote-directory)))
|
||
("<leader> n n" . #'denote)
|
||
("<leader> n N" . #'denote-type)
|
||
("<leader> n c" . #'denote-link-or-create)
|
||
("<leader> n t" . #'denote-template)
|
||
("<leader> n z" . #'denote-signature)
|
||
("<leader> n l" . #'denote-link)
|
||
("<leader> n L" . #'denote-find-link)
|
||
("<leader> n k" . #'denote-keywords-add)
|
||
("<leader> n b" . #'denote-link-backlink)
|
||
("<leader> n B" . #'denote-find-backlink)
|
||
("<leader> n r" . #'denote-rename-file)
|
||
("<leader> n R" . #'denote-rename-file-using-front-matter)
|
||
("<leader> n f" . (lambda () (interactive) (consult-find denote-directory)))))
|
||
#+END_SRC
|
||
|
||
**** TODO explain what denote-dired-mode-in-directories does.
|
||
|
||
* Programming
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Programming")
|
||
#+END_SRC
|
||
|
||
** Programming Support Infrastructure
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Programming - Infrastructure")
|
||
#+END_SRC
|
||
|
||
*** Integration with LSP Servers for language support
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure eglot-mode
|
||
(use-package eglot
|
||
:config
|
||
(evil-define-key 'normal eglot-mode-map
|
||
(kbd "<leader>c a") 'eglot-code-actions
|
||
(kbd "<leader>c d") 'eglot-find-declaration
|
||
(kbd "<leader>c i") 'eglot-find-implementation
|
||
(kbd "<leader>c k") 'eglot-find-typeDefinition
|
||
(kbd "<leader>c f") 'eglot-format
|
||
(kbd "<leader>c F") 'eglot-format-buffer
|
||
(kbd "<leader>c r") 'eglot-rename
|
||
(kbd "<leader>c Q") 'eglot-shutdown
|
||
(kbd "<leader>c q") 'eglot-reconnect
|
||
(kbd "<leader>c n") 'flymake-goto-next-error
|
||
(kbd "<leader>c p") 'flymake-goto-prev-error
|
||
(kbd "]d") #'flymake-goto-next-error
|
||
(kbd "[d") #'flymake-goto-prev-error
|
||
)
|
||
|
||
;; Shutdown server when last managed buffer is killed
|
||
(setq eglot-autoshutdown t)
|
||
|
||
;; from https://www.reddit.com/r/emacs/comments/ye18nd/setting_up_eglot_for_python/
|
||
(add-to-list 'eglot-server-programs '(python-mode . ("pylsp")))
|
||
|
||
(setq-default eglot-workspace-configuration
|
||
'((:pylsp . (:configurationSources ["flake8"]
|
||
:plugins (:pycodestyle (:enabled nil)
|
||
:mccabe (:enabled nil)
|
||
:flake8 (:enabled t))))))
|
||
|
||
:hook
|
||
(go-ts-mode . eglot-ensure)
|
||
(rust-ts-mode . eglot-ensure)
|
||
(java-ts-mode . eglot-ensure)
|
||
(python-ts-mode . eglot-ensure)
|
||
(zig-mode . eglot-ensure)
|
||
)
|
||
|
||
#+END_SRC
|
||
|
||
*** Flymake Support
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun pti-flymake-evil-keybindings ()
|
||
(evil-define-key 'normal flymake-mode-map
|
||
(kbd "]d") #'flymake-goto-next-error
|
||
(kbd "[d") #'flymake-goto-prev-error))
|
||
|
||
(add-hook 'flymake-mode-hook #'pti-flymake-evil-keybindings)
|
||
#+END_SRC
|
||
|
||
|
||
*** Use Treesitter parser support
|
||
#+BEGIN_SRC emacs-lisp
|
||
|
||
;; set locations for treesitter grammars
|
||
(use-package treesit
|
||
:config
|
||
(setq treesit-language-source-alist
|
||
'((bash "https://github.com/tree-sitter/tree-sitter-bash")
|
||
(cmake "https://github.com/uyha/tree-sitter-cmake")
|
||
(css "https://github.com/tree-sitter/tree-sitter-css")
|
||
(elisp "https://github.com/Wilfred/tree-sitter-elisp")
|
||
(go "https://github.com/tree-sitter/tree-sitter-go")
|
||
(gomod "https://github.com/camdencheek/tree-sitter-go-mod.git")
|
||
(haskell "https://github.com/tree-sitter/tree-sitter-haskell" "master" "src" nil nil)
|
||
(html "https://github.com/tree-sitter/tree-sitter-html")
|
||
(java "https://github.com/tree-sitter/tree-sitter-java.git")
|
||
(javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src")
|
||
(json "https://github.com/tree-sitter/tree-sitter-json")
|
||
(lua "https://github.com/tjdevries/tree-sitter-lua")
|
||
(make "https://github.com/alemuller/tree-sitter-make")
|
||
(markdown "https://github.com/ikatyang/tree-sitter-markdown")
|
||
(ocaml "https://github.com/tree-sitter/tree-sitter-ocaml" nil "ocaml/src")
|
||
(python "https://github.com/tree-sitter/tree-sitter-python")
|
||
(toml "https://github.com/tree-sitter/tree-sitter-toml")
|
||
(tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
|
||
(typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
|
||
(yaml "https://github.com/ikatyang/tree-sitter-yaml")))
|
||
|
||
:hook
|
||
((prog . treesit-inspect-mode)))
|
||
|
||
(use-package treesit-auto
|
||
:ensure t
|
||
:defer 5
|
||
:custom
|
||
(treesit-auto-install 'prompt)
|
||
(treesit-auto-langs '(awk bash c c-sharp clojure cmake commonlisp cpp css
|
||
dart dockerfile elixir go gomod html java
|
||
javascript json julia kotlin lua make markdown nix nu
|
||
org perl proto python r ruby rust scala sql toml tsx
|
||
typescript vue yaml)) ; reduced langs list somewhat
|
||
:config
|
||
(treesit-auto-add-to-auto-mode-alist 'all)
|
||
(global-treesit-auto-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26284 45426 709595 nil elpaca-process-queues nil nil 734000 nil]
|
||
|
||
I always get errors compiling support for *janet* so I pruned the
|
||
`treesit-auto-langs` to exclude it and other things I don't use.
|
||
|
||
|
||
***** TODO figure out why Latex does not want to install
|
||
|
||
***** TODO decide whether to keep using treesitter-auto at all or just plain treesit with some support functions.
|
||
|
||
**** Recompiling all Treesitter Grammars
|
||
To recompile all treesitter grammars, execute following block with
|
||
*C-c C-c*.
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| bash | cmake | css | elisp | go | gomod | haskell | html | java | javascript | json | lua | make | markdown | ocaml | python | toml | tsx | typescript | yaml |
|
||
|
||
**** Treesitter Grammars for Windows
|
||
Windows does not come with a handy-dandy C-compiler available as *cc* or *gcc* or even *c99* and treesitter does not have a handy dandy feature to remap that to *zig cc* and similar.
|
||
|
||
However we can download it from the release page of the tree-sitter-langs repo or even better install the *tree-sitter-langs* package.
|
||
|
||
This will download the dynamic library to _user-emacs-directory_/elpa/tree-sitter-langs/bin
|
||
|
||
However they'll have the wrong filename and are not in the path where treesitter looks in.
|
||
|
||
- Open the folder with *dired*
|
||
- switch to writable with `C-xC-q` to enable *wdired*
|
||
- :%s/[a-z-]\*.dll/libtree-sitter-\\0/
|
||
- `C-cC-c` to confirm the changes if it looks ok
|
||
- `%m` to mark all files starting with libtree
|
||
- `R` them to the *~/.config/emacs/tree-sitter* folder
|
||
|
||
and they will now be installed.
|
||
|
||
Normally that should work for other OSes too, but there is no real need for it.
|
||
|
||
see also [[https://www.masteringemacs.org/article/how-to-get-started-tree-sitter][How to get started with tree sitter]] .
|
||
|
||
*** Create a folder to store downloaded LSP servers
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; LSP support
|
||
(let ((lsp_dir (file-name-concat user-emacs-directory "lsp")))
|
||
(if (not (file-exists-p lsp_dir))
|
||
(mkdir lsp_dir t)))
|
||
#+END_SRC
|
||
*** Electric Return
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; enable electric return to automatically indent code
|
||
(defvar electrify-return-match
|
||
"[\]}\)\"]"
|
||
"The text after the cursor to do an \"electric\" return.")
|
||
|
||
(defun electrify-return-if-match (arg)
|
||
"Insert a newline, and indent it when needed.
|
||
|
||
If the text after the cursor matches `electrify-return-match' then
|
||
open and indent an empty line between the cursor and the text. Move the
|
||
cursor to the new line.
|
||
|
||
With a prefix argument ARG, insert that many newlines"
|
||
(interactive "P")
|
||
(let ((case-fold-search nil))
|
||
(if (looking-at electrify-return-match)
|
||
(save-excursion (newline-and-indent)))
|
||
(newline arg)
|
||
(indent-according-to-mode)))
|
||
|
||
;; Using local-set-key in a mode-hook is a better idea.
|
||
;(global-set-key (kbd "RET") 'electrify-return-if-match)
|
||
#+END_SRC
|
||
|
||
|
||
**** TODO Evaluate if Electric Return is still useful
|
||
|
||
** Configure Selected Languages
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Programming - Selected Languages")
|
||
#+END_SRC
|
||
|
||
*** Rust Support
|
||
#+BEGIN_SRC emacs-lisp
|
||
|
||
;; configure rust support
|
||
(let* ((release-date "2023-10-30")
|
||
(os (pcase system-type
|
||
('darwin "x86_64-apple-darwin")
|
||
('gnu/linux "x86_64-unknown-linux-gnu")
|
||
('windows-nt "x86_64-pc-windows-msvc")
|
||
(_ "unknown")))
|
||
(releases-url "https://github.com/rust-lang/rust-analyzer/releases/download/")
|
||
(download-url (concat releases-url release-date "/rust-analyzer-" os ".gz"))
|
||
(rust-analyzer
|
||
(file-name-concat
|
||
user-emacs-directory
|
||
(concat "lsp/rust-analyzer" (if (eq system-type 'windows-nt) ".exe" "")))))
|
||
(if (not (file-exists-p rust-analyzer))
|
||
(let ((rust-analyzer-archive (concat rust-analyzer ".gz" )))
|
||
(message "install rust-analyzer from %s at %s" download-url rust-analyzer)
|
||
(url-copy-file download-url rust-analyzer-archive t)
|
||
(call-process "gzip" nil "*snam-install*" t "-d" (concat rust-analyzer ".gz"))
|
||
(call-process "chmod" nil "*snam-install*" t "+x" rust-analyzer)
|
||
(message "rust-analyzer installed at %s" rust-analyzer)))
|
||
)
|
||
|
||
(use-package rustic
|
||
:ensure t
|
||
:init
|
||
(setq rustic-lsp-client 'eglot))
|
||
#+END_SRC
|
||
*** OCaml Support
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure Ocaml support
|
||
(setq opam-emacs-dir (file-name-concat (expand-file-name "~") "/.opam/default/share/emacs/site-lisp"))
|
||
(use-package ocp-indent
|
||
:ensure t
|
||
:load-path opam-emacs-dir
|
||
:if (file-exists-p opam-emacs-dir))
|
||
(use-package tuareg
|
||
:ensure t
|
||
:hook
|
||
(tuareg-mode . eglot-ensure))
|
||
#+END_SRC
|
||
*** Go Support
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
;; configure go support
|
||
(use-package go-ts-mode
|
||
:init
|
||
(add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))
|
||
:hook
|
||
(go-ts . eglot-ensure))
|
||
#+END_SRC
|
||
*** Javascript, Typescript, TSC, etc...
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
;; configure typescript support
|
||
(use-package typescript-ts-mode
|
||
:init
|
||
(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode))
|
||
:hook
|
||
(typescript-ts-mode . eglot-ensure))
|
||
|
||
;; configure javascript support
|
||
(use-package javascript-ts-mode
|
||
:init
|
||
(add-to-list 'auto-mode-alist '("\\.js\\'" . js-ts-mode))
|
||
:hook
|
||
(javascript-ts-mode . eglot-ensure))
|
||
|
||
;; configure react support
|
||
(use-package tsx-ts-mode
|
||
:init
|
||
(add-to-list 'auto-mode-alist '("\\.[jt]sx\\'" . tsx-ts-mode))
|
||
:hook
|
||
(tsx-ts-mode . eglot-ensure))
|
||
#+END_SRC
|
||
*** Java and other JVM languages
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure java support
|
||
(use-package java-ts-mode
|
||
:hook
|
||
(javascript-ts-mode . eglot-ensure)
|
||
:config
|
||
(let* ((download-url"https://www.eclipse.org/downloads/download.php?file=/jdtls/snapshots/jdt-language-server-latest.tar.gz")
|
||
(jdtls-dir (file-name-concat user-emacs-directory "lsp/jdtls"))
|
||
(jdtls-prog (concat jdtls-dir "/bin/jdtls" (if (eq system-type 'windows-nt) ".bat" "")))
|
||
(archive (file-name-concat jdtls-dir "jdtls.tgz")))
|
||
(if (not (file-exists-p jdtls-dir))
|
||
(mkdir jdtls-dir t))
|
||
(if (not (file-exists-p jdtls-prog))
|
||
(progn
|
||
(message "install jdtls at %s" jdtls-dir)
|
||
(if (not (file-exists-p archive))
|
||
(url-copy-file download-url archive))
|
||
(call-process "tar" nil "*snam-install*" t "-C" jdtls-dir "-xzvf" archive )
|
||
(report-time-since-load "jdtls installed at %s" jdtls-prog)))
|
||
(with-eval-after-load 'eglot
|
||
(progn
|
||
(setenv "JAVA_HOME" (getenv "GUIX_PROFILE"))
|
||
(add-to-list 'eglot-server-programs `((java-mode java-ts-mode) ,jdtls-prog))))))
|
||
|
||
#+END_SRC
|
||
*** Lisp and Scheme Support
|
||
**** Enable ParEdit in lispy modes
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; Lisp support
|
||
(use-package paredit
|
||
:ensure nil
|
||
:commands (enable-paredit-mode)
|
||
:init
|
||
(dolist (mode '(emacs-lisp-mode-hook
|
||
lisp-interaction-mode-hook
|
||
lisp-mode-hook
|
||
scheme-mode-hook))
|
||
(add-hook mode #'enable-paredit-mode)))
|
||
(use-package evil-paredit
|
||
:ensure t
|
||
:commands (evil-paredit-mode)
|
||
:init
|
||
(dolist (mode '(emacs-lisp-mode-hook
|
||
lisp-interaction-mode-hook
|
||
lisp-mode-hook
|
||
scheme-mode-hook))
|
||
(add-hook mode #'evil-paredit-mode)))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
***** TODO FIx paredit bug related to obsolete macro
|
||
|
||
evil-paredit relies on an obsolete (and no longer available method)
|
||
`evil-called-interactively-p`. So I define it here till evil-paredit
|
||
has implemented the new method.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defmacro evil-called-interactively-p ()
|
||
"Wrapper for `called-interactively-p'.
|
||
In older versions of Emacs, `called-interactively-p' takes
|
||
no arguments. In Emacs 23.2 and newer, it takes one argument."
|
||
(called-interactively-p 'any))
|
||
(make-obsolete 'evil-called-interactively-p
|
||
"please use (called-interactively-p 'any) instead."
|
||
"Git commit 222b791")
|
||
|
||
#+END_SRC
|
||
|
||
**** Enable Geiser Mode in Scheme Mode
|
||
|
||
Configure Geiser and Scheme
|
||
- map .scm file by default to Guile
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package geiser
|
||
:ensure nil
|
||
:defer t
|
||
:commands (geiser-mode)
|
||
:config
|
||
(setq geiser-implementation-alist
|
||
,(add-to-list 'geiser-implementation-alist '((regexp "\\.scm\\'") guile))))
|
||
(use-package scheme-mode
|
||
:ensure nil
|
||
:defer t
|
||
:commands (scheme-mode)
|
||
:hook (scheme-mode . geiser-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| evil-paredit-mode | geiser-mode | enable-paredit-mode | aggressive-indent-mode | geiser-mode--maybe-activate |
|
||
|
||
*** Terraform Support
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure terraform support
|
||
(use-package terraform-mode
|
||
:ensure t
|
||
:config
|
||
(setq
|
||
terraform-indent-level 2
|
||
terraform-format-on-save t)
|
||
(evil-define-key 'normal terraform-mode-map
|
||
(kbd "<leader>c k") #'terraform-open-doc
|
||
(kbd "<leader>c f") #'terraform-format
|
||
(kbd "<leader>c F") #'terraform-format-buffer
|
||
(kbd "<leader>c n") 'flymake-goto-next-error
|
||
(kbd "<leader>c p") 'flymake-goto-prev-error
|
||
(kbd "]d") #'flymake-goto-next-error
|
||
(kbd "[d") #'flymake-goto-prev-error))
|
||
#+END_SRC
|
||
|
||
Map the keymap consistently to the eglot mappings.
|
||
|
||
*** Zig Support
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure zig support
|
||
(use-package zig-mode
|
||
:ensure t
|
||
:hook
|
||
(zig-mode . eglot-ensure))
|
||
|
||
#+END_SRC
|
||
|
||
|
||
** Debugger Support
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Programming - Debugger Support")
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; install DAP servers
|
||
(setq pti-vscode-js-debug-dir (file-name-concat user-emacs-directory "dape/vscode-js-debug"))
|
||
(defun pti-install-vscode-js-debug ()
|
||
"Run installation procedure to install JS debugging support."
|
||
(interactive)
|
||
(mkdir pti-vscode-js-debug-dir t)
|
||
(let ((default-directory (expand-file-name pti-vscode-js-debug-dir)))
|
||
|
||
(vc-git-clone "https://github.com/microsoft/vscode-js-debug.git" "." nil)
|
||
(report-time-since-load "git repository created")
|
||
(call-process "npm" nil "*pti-install*" t "install")
|
||
(report-time-since-load "npm dependencies installed")
|
||
(call-process "npx" nil "*pti-install*" t "gulp" "dapDebugServer")
|
||
(report-time-since-load "vscode-js-debug installed")))
|
||
|
||
(setq pti-codelldb-dir (file-name-concat user-emacs-directory "dape/codelldb"))
|
||
(defun pti-install-codelldb ()
|
||
"Install Vadimcn.Vscode-Lldb DAP server for C/C++/RUST."
|
||
(interactive)
|
||
(let* ((default-directory pti-codelldb-dir)
|
||
(arch (car (split-string system-configuration "-" nil nil)))
|
||
(os (pcase system-type
|
||
('windows-nt "windows")
|
||
('gnu/linux "linux")
|
||
('darwin "darwin")
|
||
(_ "unknown")))
|
||
(version "1.10.0")
|
||
(release-url (concat "https://github.com/vadimcn/codelldb/releases/download/v" version "/codelldb-" arch "-" os ".vsix")))
|
||
(mkdir default-directory t)
|
||
(url-copy-file release-url "codelldb.zip" t)
|
||
(report-time-since-load "codelldb archive downloaded")
|
||
(call-process "unzip" nil "*pti-install*" t "codelldb.zip")
|
||
(report-time-since-load "codelldb installed")
|
||
))
|
||
|
||
;; configure dape (dap-mode)
|
||
(use-package dape
|
||
:ensure (dape :host github :repo "svaante/dape" :wait t)
|
||
:defer 5
|
||
:config (progn
|
||
;; Use n for next etc. in REPL
|
||
;; (setq dape-repl-use-shorthand t)
|
||
|
||
;; By default dape uses gdb keybinding prefix
|
||
;; (setq dape-key-prefix "<space>d")
|
||
(evil-define-key 'normal 'global (kbd "<leader>d") dape-global-map)
|
||
|
||
;; Kill compile buffer on build success
|
||
;; (add-hook 'dape-compile-compile-hooks 'kill-buffer)
|
||
|
||
;; Projectile users
|
||
;; (setq dape-cwd-fn 'projectile-project-root))
|
||
(add-to-list 'dape-configs
|
||
`(vscode-js-node
|
||
modes (js-mode js-ts-mode typescript-mode typescript-ts-mode)
|
||
host "localhost"
|
||
port 8123
|
||
command "node"
|
||
command-cwd ,(file-name-concat pti-vscode-js-debug-dir "dist")
|
||
command-args ("src/dapDebugServer.js" "8123")
|
||
:type "pwa-node"
|
||
:request "launch"
|
||
:cwd dape-cwd-fn
|
||
:program dape-find-file-buffer-default
|
||
:outputCapture "console"
|
||
:sourceMapRenames t
|
||
:pauseForSourceMap nil
|
||
:enableContentValidation t
|
||
:autoAttachChildProcesses t
|
||
:console "internalConsole"
|
||
:killBehavior "forceful"))
|
||
(add-to-list 'dape-configs
|
||
`(delve
|
||
modes (go-mode go-ts-mode)
|
||
command "dlv"
|
||
command-args ("dap" "--listen" "127.0.0.1:55878")
|
||
command-cwd dape-cwd-fn
|
||
host "127.0.0.1"
|
||
port 55878
|
||
:type "debug" ;; needed to set the adapterID correctly as a string type
|
||
:request "launch"
|
||
:cwd dape-cwd-fn
|
||
:program dape-cwd-fn))
|
||
(add-to-list 'dape-configs
|
||
`(codelldb
|
||
modes (c-mode c-ts-mode
|
||
c++-mode c++-ts-mode
|
||
rust-ts-mode rust-mode)
|
||
;; Replace vadimcn.vscode-lldb with the vsix directory you just extracted
|
||
command ,(expand-file-name
|
||
(file-name-concat
|
||
pti-codelldb-dir
|
||
(concat "extension/adapter/codelldb"
|
||
(if (eq system-type 'windows-nt)
|
||
".exe"
|
||
""))))
|
||
host "localhost"
|
||
port 5818
|
||
command-args ("--port" "5818")
|
||
:type "lldb"
|
||
:request "launch"
|
||
:cwd dape-cwd-fn
|
||
:program dape-find-file))
|
||
(add-to-list 'dape-configs
|
||
`(debugpy
|
||
modes (python-ts-mode python-mode)
|
||
command "python"
|
||
command-args ("-m" "debugpy.adapter")
|
||
:type "executable"
|
||
:request "launch"
|
||
:cwd dape-cwd-fn
|
||
:program dape-find-file-buffer-default))
|
||
|
||
))
|
||
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
** Copilot Support
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Programming - Copilot Support")
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package copilot
|
||
:ensure (:host github :repo "zerolfx/copilot.el"
|
||
:branch "main"
|
||
:files ("dist" "*.el"))
|
||
:bind
|
||
(:map evil-insert-state-map
|
||
("C-y" . copilot-accept-completion))
|
||
:config
|
||
(add-to-list 'copilot-indentation-alist '(scheme-mode . 2))
|
||
(add-to-list 'copilot-indentation-alist '(emacs-lisp-mode . 2))
|
||
(add-to-list 'copilot-indentation-alist '(lisp-mode . 2))
|
||
:hook
|
||
(prog-mode . copilot-mode)
|
||
(org-mode . copilot-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26280 62042 380168 nil elpaca-process-queues nil nil 930000 nil]
|
||
|
||
*** TODO move scheme configuration to the scheme section
|
||
or leave it here but move it to config, whatever makes most sense at
|
||
the moment.
|
||
|
||
** Gitlab Support
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Programming - Gitlab Support")
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package gitlab-ci-mode
|
||
:ensure (:host gitlab :repo "ptillemans/gitlab-ci-mode" :branch "fixes_2024")
|
||
:mode "\\.gitlab-ci\\.yml\\'"
|
||
:custom
|
||
(gitlab-ci-url "https://gitlab.melexis.com")
|
||
(gitlab-ci-api-token (auth-source-pass-get 'secret "snamellit/gitlab/token")))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26292 38256 549743 nil elpaca-process-queues nil nil 11000 nil]
|
||
|
||
* Communication and Interwebs
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Communication and Interwebs")
|
||
#+END_SRC
|
||
|
||
** ERC configuration
|
||
Set up ERC, an IRC client for Emacs, to automatically join specific channels and log in:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package erc
|
||
:ensure t
|
||
:defer 5
|
||
:config
|
||
(erc-services-mode 1)
|
||
(erc-autojoin-mode 1)
|
||
(erc-update-modules)
|
||
(setq erc-autojoin-channels-alist
|
||
'(('znc
|
||
"#emacs" "#erc" "#spritely" "#guix"
|
||
"#systemcrafters" "#systemcrafters-guix" "#systemcrafters-emacs")))
|
||
(custom-set-variables
|
||
'(erc-fool-highlight-type 'all)
|
||
'(erc-fools '("Marvin2"))
|
||
'(erc-keyword-highlight-type 'all)
|
||
'(erc-pal-highlight-type 'all)
|
||
'(erc-pals '("Fade" "daviwil" "alternateved" "trev" "talos" "dthompson"))
|
||
'(erc-prompt-for-password nil)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26384 63712 203392 nil elpaca-process-queues nil nil 785000 nil]
|
||
|
||
Setup all channels which are joined by default.
|
||
|
||
|
||
*** ERC connect function
|
||
|
||
Define a function to login to ERC when needed. In principle ERC
|
||
reconnects after suspend, and sometimes it even works, but mostly I
|
||
run this function again to reconnect, which will update the buffers
|
||
with the rooms I joined.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun snam-erc ()
|
||
"Login to znc bouncer and join rooms."
|
||
(interactive)
|
||
(erc-tls :id 'znc
|
||
:server (auth-source-pass-get "server" "snamellit/znc")
|
||
:port (auth-source-pass-get "port" "snamellit/znc")
|
||
:user (auth-source-pass-get "user" "snamellit/znc")
|
||
:nick (auth-source-pass-get "nick" "snamellit/znc")
|
||
:password (auth-source-pass-get 'secret "snamellit/znc")))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: snam-erc
|
||
|
||
In practice this has proven to be a very useful function to reconnect
|
||
in all kind of weird situation.
|
||
|
||
**** TODO Figure out how to make ERC reconnect more reliably
|
||
Although running `snam-erc` is not a big deal, it should not be needed
|
||
so much. I should figure out in what cases reconnects fail or dropping
|
||
connections fail to be recognized.
|
||
I often see that ERC is _reconnecting_ however this seems to silently
|
||
fail. I dunno, there is something fishy here...
|
||
|
||
*** Integrate ERC with i3 Desktops
|
||
|
||
I like to have my IRC channels on workspace 5 in *i3*.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun my-erc ()
|
||
"Create new frame and move to ws5 and launch erc."
|
||
(interactive)
|
||
(let ((frame (make-frame))
|
||
(channel "#systemcrafters"))
|
||
(call-process "i3" nil nil nil "move window to workspace 5")
|
||
(snam-erc)
|
||
(call-process "i3" nil nil nil "workspace 5")
|
||
(report-time-since-load "Waiting to connect to IRC...")
|
||
(dotimes (i 12)
|
||
(unless (member channel (mapcar 'buffer-name (erc-channel-list nil)))
|
||
(sleep-for 0.25)))
|
||
(set-window-buffer
|
||
(frame-selected-window frame) channel)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: my-erc
|
||
|
||
|
||
** Elfeed configuration
|
||
Configures Elfeed, an RSS feed reader for Emacs:
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure elfeed
|
||
(use-package elfeed
|
||
:ensure t
|
||
:defer 5
|
||
:config
|
||
(setq elfeed-feeds
|
||
'("https://www.spritelyproject.org/feed.xml"
|
||
;; "https://www.emacswiki.org/emacs?action=rss"
|
||
;; "https://www.reddit.com/r/emacs.rss"
|
||
;; "https://www.reddit.com/r/guix.rss"
|
||
;; "https://www.reddit.com/r/linux.rss"
|
||
"https://sachachua.com/blog/feed/"
|
||
"https://blog.benoitj.ca/posts/index.xml"
|
||
"https://tylerdback.com/index.xml"
|
||
"https://www.snamellit.com/rss.xml"
|
||
"https://richarddavis.xyz/en/blog/rss.xml"
|
||
"https://protesilaos.com/master.xml"
|
||
)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26279 35504 577569 nil elpaca-process-queues nil nil 161000 nil]
|
||
|
||
|
||
*** TODO OPML To Elfeed
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package opml-to-elfeed-feeds
|
||
:ensure (:host "codeberg.org" :repo "kakafarm/emacs-opml-to-elfeed-feeds"
|
||
:main "opml-to-elfeed-feeds.el")
|
||
:custom (o2e-opml-list . (("https://craftering.systemcrafters.net/Craftering.opml" blog craftering opml-to-elfeed-feeds)))
|
||
:commands (o2e-opml-to-elfeed--update-o2e-efleed-feeds)
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26289 17361 350243 nil elpaca-process-queues nil nil 817000 nil]
|
||
|
||
This does all kind of weird things. Apparently elpaca or use-package
|
||
magically namespaces all the functions and for some reason it fails
|
||
to require 'elfeed'. ... The weird thing is that the emacs lips
|
||
feature is used to expand o2e to opml-to-elfeed-feeds, by setting the
|
||
`read-symbol-shorthands` variable to `(("o2e"
|
||
. "opml-to-elfeed-feeds"))`.
|
||
|
||
Agreed with cow_2000 to create an issue on the repo with my
|
||
recommendation for public API.
|
||
|
||
|
||
** Enable 0x0 to share snippets and files easily
|
||
Configures the ~0x0~ package for easily sharing code snippets and files:
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure 0x0
|
||
(use-package 0x0
|
||
:ensure t
|
||
:config (setq 0x0-use-curl t))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26279 35497 183387 nil elpaca-process-queues nil nil 963000 nil]
|
||
|
||
The commands this package offers:
|
||
|
||
- ~0x0-dwim~ chooses the right one of the following, based on location or previous commands.
|
||
- ~0x0-upload-text~ will upload the active region or if not existent the entire buffer
|
||
- ~0x0-upload-file~ will upload a selected file
|
||
- ~0x0-upload-kill-ring~ uploads the content of the kill ring
|
||
- ~0x0-popup~ creates and displays a buffer, lets you upload its contents later
|
||
- ~0x0-shorten-uri~ prompts for a uri, shortens it
|
||
|
||
IMSMR the shorten-uri functionality is disabled in the 0x0 service
|
||
as apparently is was abused too much.
|
||
|
||
* Finishing Touches
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Finishing Touches")
|
||
#+END_SRC
|
||
|
||
** Tell the module system that *init.el* is available
|
||
Indicates the ~init.el~ file is provided by and ends here:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(provide 'init)
|
||
;;; init.el ends here
|
||
#+END_SRC
|
||
* Future
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Future")
|
||
#+END_SRC
|
||
|
||
** TODO Decide about general keybindings.
|
||
The *general* package offers enhanced support for *evil* keybindings,
|
||
notably it integrates with *use-package* to define keybindings which
|
||
will load the package if not loaded yet.
|
||
It considerably streamlines managing evil bindings so it should offer
|
||
enough value but I just removed it, so it'll have to wait a bit.
|
||
** NEXT Add support for zola blogposts writing in org-mode
|
||
:LOGBOOK:
|
||
CLOCK: [2024-08-15 Thu 13:47]--[2024-08-16 Fri 00:57] => 11:10
|
||
:END:
|
||
|
||
see [[https://github.com/gicrisf/ox-zola][ox-zola]] for exporting org-mode to zola markdown.
|
||
* Final Words
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Final Words")
|
||
#+END_SRC
|
||
|
||
|
||
This is the end of the configuration file.
|
||
|
||
Add a variable so the file is tangling itself each time it is saved.
|
||
|
||
# Local Variables:
|
||
# eval: (add-hook 'after-save-hook #'org-babel-tangle t t)
|
||
# End:
|