4424 lines
158 KiB
Org Mode
4424 lines
158 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
|
||
- 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]]
|
||
|
||
** 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.
|
||
* First Things First
|
||
|
||
** Bootstrapping Emacs Configuration
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle "early-init.el"
|
||
;; early-init.el --- Bootstrap Emacs -*- lexical-binding: t; read-only-mode: t; -*-
|
||
|
||
;;; Bootstrap elpaca
|
||
(setq package-enable-at-startup nil)
|
||
|
||
;; for guix emacs-next packages you can find the date with
|
||
;; ➜ stat /gnu/store/*emacs-next-[23]*.drv | rg Birth | cut -d' ' -f3 | tr -d '-'
|
||
;; 20240727
|
||
;;
|
||
;; it is possible there are more so probably the most recent one is the one to use.
|
||
|
||
(when emacs-build-time
|
||
(setq elpaca-core-date (format-time-string "%Y%m%d" emacs-build-time)))
|
||
|
||
|
||
(defvar elpaca-installer-version 0.11)
|
||
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
|
||
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
|
||
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
|
||
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
|
||
:ref nil :depth 1 :inherit ignore
|
||
:files (:defaults "elpaca-test.el" (:exclude "extensions"))
|
||
:build (:not elpaca--activate-package)))
|
||
(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
|
||
(build (expand-file-name "elpaca/" elpaca-builds-directory))
|
||
(order (cdr elpaca-order))
|
||
(default-directory repo))
|
||
(add-to-list 'load-path (if (file-exists-p build) build repo))
|
||
(unless (file-exists-p repo)
|
||
(make-directory repo t)
|
||
(when (<= emacs-major-version 28) (require 'subr-x))
|
||
(condition-case-unless-debug err
|
||
(if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
|
||
((zerop (apply #'call-process `("git" nil ,buffer t "clone"
|
||
,@(when-let* ((depth (plist-get order :depth)))
|
||
(list (format "--depth=%d" depth) "--no-single-branch"))
|
||
,(plist-get order :repo) ,repo))))
|
||
((zerop (call-process "git" nil buffer t "checkout"
|
||
(or (plist-get order :ref) "--"))))
|
||
(emacs (concat invocation-directory invocation-name))
|
||
((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
|
||
"--eval" "(byte-recompile-directory \".\" 0 'force)")))
|
||
((require 'elpaca))
|
||
((elpaca-generate-autoloads "elpaca" repo)))
|
||
(progn (message "%s" (buffer-string)) (kill-buffer buffer))
|
||
(error "%s" (with-current-buffer buffer (buffer-string))))
|
||
((error) (warn "%s" err) (delete-directory repo 'recursive))))
|
||
(unless (require 'elpaca-autoloads nil t)
|
||
(require 'elpaca)
|
||
(elpaca-generate-autoloads "elpaca" repo)
|
||
(let ((load-source-file-function nil)) (load "./elpaca-autoloads"))))
|
||
(add-hook 'after-init-hook #'elpaca-process-queues)
|
||
(elpaca `(,@elpaca-order))
|
||
|
||
;; Enable :elpaca use-package keyword.
|
||
(elpaca elpaca-use-package
|
||
(elpaca-use-package-mode))
|
||
#+END_SRC
|
||
|
||
Tangle the init file if it has been updated. I maintain the same
|
||
configuration on multiple machines and fetch changes with a ~git pull~
|
||
outside emacs so there is on opportunity to tangle the new file. If
|
||
I see it is outdated I tangle it.
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
;; tangling to generate scripts for the local bin directory. This
|
||
;; causes the name of the scripts to be returned in the car of the
|
||
;; tangle command which is used to load the file. The net result is
|
||
;; that not the initialization is loaded, but the first exported
|
||
;; script.
|
||
(use-package ob-tangle)
|
||
(let ((src (concat user-emacs-directory "init.org"))
|
||
(tgt (concat user-emacs-directory "init.el"))
|
||
(enable-local-variables nil)) ;; disable local variables to prevent asking confirmation before frame is available
|
||
(when (file-newer-than-file-p src tgt)
|
||
(message "tangling init.org")
|
||
(delete-file tgt)
|
||
(org-babel-tangle-file src tgt "emacs-lisp")))
|
||
|
||
#+END_SRC
|
||
|
||
** Set the garbage collector threshold, to avoid collections
|
||
|
||
The emacs history goes back to times where memory was counted in
|
||
bytes, not gigabytes. We can afford to be a bit more generous with the
|
||
garbage collector settings.
|
||
|
||
#+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 transient as used by a lot of packages
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package transient
|
||
:ensure t)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26768 31512 287826 nil elpaca-process-queues nil nil 20000 nil]
|
||
|
||
|
||
*** Wait for initial installations
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(elpaca-wait)
|
||
#+END_SRC
|
||
|
||
** Start Emacs Servers
|
||
|
||
To get a fast snappy editing experience in terminals emacs allows
|
||
running in client-server where the actual editing runs in an already
|
||
running process. This enables interaction with the other buffers in
|
||
the context/project and more importantly the client does not need to
|
||
load all the customizations in the init file. However the server must
|
||
be started for this to work.
|
||
|
||
If a server is already running it will be restarted. If clients are
|
||
connected to that server, confirmation will be asked. see [[help:server-start][server-start
|
||
documentation]] .
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(server-start)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
|
||
** Utility Functions
|
||
|
||
|
||
*** Reload dir local variables
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun snm-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:
|
||
: snm-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 snm-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:
|
||
: snm-latest-github-release
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no :results value
|
||
(snm-latest-github-release "plantuml/plantuml")
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: v1.2025.1
|
||
|
||
* Writing and Planning
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Writing and Planning")
|
||
#+END_SRC
|
||
|
||
** Org Mode
|
||
|
||
*** Org Configuration
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package org
|
||
:mode (("\\.org\\'" . org-mode))
|
||
:ensure nil
|
||
:demand t ; force loading to initialize constants
|
||
:custom
|
||
(org-return-follows-link t)
|
||
(org-mouse-1-follows-link t)
|
||
(org-link-descriptive t)
|
||
(org-agenda-skip-scheduled-if-done t)
|
||
(org-agenda-skip-deadline-if-done t)
|
||
(org-hide-emphasis-markers t)
|
||
|
||
(line-spacing 0.1)
|
||
(left-margin-width 2)
|
||
(right-margin-width 2)
|
||
|
||
(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)")))
|
||
(org-agenda-files (list
|
||
"~/Nextcloud/org/"
|
||
"~/org/snamellit/"
|
||
"~/org/customer/"
|
||
"~/org/personal/"
|
||
"~/.org-jira/"
|
||
))
|
||
(org-refile-targets '(
|
||
(org-agenda-files . (:level . 1))
|
||
("~/org/customer/gtd.org" . (:level . 1))
|
||
("~/org/personal/bijen.org" . (:level . 1))
|
||
("~/org/personal/fitness.org" . (:level . 1))
|
||
))
|
||
|
||
:config
|
||
(message "Configuring org mode")
|
||
;; 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")
|
||
(setq org-log-done 'time)
|
||
(setq org-confirm-babel-evaluate nil)
|
||
(setq org-export-babel-evaluate nil)
|
||
(setq org-html-validation-link nil)
|
||
:hook (
|
||
(org-mode . org-indent-mode)
|
||
)
|
||
:bind (
|
||
("C-c A" . org-agenda)
|
||
("C-c L" . org-store-link)
|
||
))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: org-store-link
|
||
|
||
*** Add org-contrib
|
||
|
||
Some modules are providedd by org-contrib, like exporting to
|
||
confluence
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package org-contrib
|
||
:ensure t)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26768 38477 62891 nil elpaca-process-queues nil nil 29000 nil]
|
||
|
||
|
||
*** Mixed Pitch and Fancy Bullet Support by Default in Org
|
||
|
||
Enable mixed pitch and fancy bullets when using a graphical display
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun snm-org-mode-config ()
|
||
"Set options for better writing in org buffers."
|
||
(mixed-pitch-mode)
|
||
(visual-line-mode)
|
||
(turn-on-auto-fill)
|
||
)
|
||
(add-hook 'org-mode-hook #'snm-org-mode-config)
|
||
|
||
(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 Appear
|
||
|
||
Hide the org markers when point is not on the element being decorated.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
|
||
(use-package org-appear
|
||
:ensure t
|
||
:hook (org-mode . org-appear-mode))
|
||
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26432 31427 677147 nil elpaca-process-queues nil nil 738000 nil]
|
||
|
||
*** 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.]]
|
||
|
||
**** Configure Babel Languages
|
||
|
||
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package ob-mermaid
|
||
:ensure t
|
||
:demand t)
|
||
|
||
#+END_SRC
|
||
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package ob-latex
|
||
:ensure nil
|
||
:demand t
|
||
:custom
|
||
(org-preview-latex-default-process "lualatex"))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26768 38911 12050 nil elpaca-process-queues nil nil 97000 nil]
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle yes
|
||
;; configure babel languages
|
||
(use-package ob
|
||
:ensure nil
|
||
:after org
|
||
:custom
|
||
(org-babel-load-languages '((emacs-lisp . t)
|
||
(shell . t)
|
||
(python . t)
|
||
(scheme . t)
|
||
(plantuml . t)
|
||
(mermaid . t)
|
||
(sql . t)
|
||
(sqlite . t)
|
||
(dot . t)))
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
***** Example Emacs Lisp
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(+ 2 3)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: 5
|
||
|
||
|
||
***** Example shell
|
||
|
||
#+BEGIN_SRC shell
|
||
echo "Foo bar"
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: Foo bar
|
||
|
||
|
||
***** Example python
|
||
|
||
#+BEGIN_SRC python
|
||
bar="bar"
|
||
return f"Foo {bar}"
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
***** TODO Example Latex
|
||
|
||
|
||
|
||
|
||
#+name: hello-latex
|
||
#+BEGIN_SRC latex :results file raw :exports results :file latex.png
|
||
\LaTeX
|
||
|
||
Dit is ne pot en tis ne grote!
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS: hello-latex
|
||
[[file:latex.png]]
|
||
|
||
***** Example Scheme
|
||
|
||
#+BEGIN_SRC scheme
|
||
(+ 3 4)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: 7
|
||
|
||
***** Example PlantUML
|
||
|
||
****** Archimate example : Capability Metamodel
|
||
|
||
#+begin_src plantuml :file cap-meta-model.png
|
||
!include <archimate/Archimate>
|
||
title Capability Metamodel
|
||
|
||
Strategy_ValueStream(vs, "ValueStream")
|
||
Business_Service(svc, "Service")
|
||
Strategy_Capability(cap, "Capability")
|
||
Strategy_Resource(fun, "Function")
|
||
Strategy_Resource(ppl, "People")
|
||
Strategy_Resource(process, "Processes")
|
||
Strategy_Resource(tools, "Tools")
|
||
|
||
|
||
Rel_Serving_Right(svc, vs, "supports")
|
||
Rel_Serving_Right(cap, svc, "provides")
|
||
Rel_Assignment_Up(fun,cap,"implements")
|
||
Rel_Composition_Up(ppl,fun,"")
|
||
Rel_Composition_Up(tools,fun,"")
|
||
Rel_Composition_Up(process,fun,"")
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
[[file:cap-meta-model.png]]
|
||
|
||
|
||
|
||
***** Example Mermaid
|
||
:PROPERTIES:
|
||
:header-args: :tangle no
|
||
:END:
|
||
|
||
****** Directed Graph
|
||
|
||
#+begin_src mermaid :file test2.png
|
||
graph TD
|
||
A[Christmas] -->|Get money| B(Go shopping)
|
||
B --> C{Let me think}
|
||
C -->|One| D[Laptop]
|
||
C -->|Two| E[iPhone]
|
||
C -->|Three| F[Car]
|
||
#+end_src
|
||
|
||
****** Gantt Chart
|
||
|
||
#+begin_src mermaid :file gantt.png
|
||
gantt
|
||
section Section
|
||
Completed :done, des1, 2014-01-06,2014-01-08
|
||
Active :active, des2, 2014-01-07, 3d
|
||
Parallel 1 : des3, after des1, 1d
|
||
Parallel 2 : des4, after des1, 1d
|
||
Parallel 3 : des5, after des3, 1d
|
||
Parallel 4 : des6, after des4, 1d
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
[[file:gantt.png]]
|
||
|
||
****** Class Diagram
|
||
|
||
#+begin_src mermaid :file class.png
|
||
classDiagram
|
||
Class01 <|-- AveryLongClass : Cool
|
||
<<Interface>> Class01
|
||
Class09 --> C2 : Where am i?
|
||
Class09 --* C3
|
||
Class09 --|> Class07
|
||
Class07 : equals()
|
||
Class07 : Object[] elementData
|
||
Class01 : size()
|
||
Class01 : int chimp
|
||
Class01 : int gorilla
|
||
class Class10 {
|
||
<<service>>
|
||
int id
|
||
size()
|
||
}
|
||
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
[[file:class.png]]
|
||
|
||
|
||
****** State Diagram
|
||
|
||
|
||
#+begin_src mermaid :file state.png
|
||
stateDiagram-v2
|
||
[*] --> Still
|
||
Still --> [*]
|
||
Still --> Moving
|
||
Moving --> Still
|
||
Moving --> Crash
|
||
Crash --> [*]
|
||
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
[[file:state.png]]
|
||
|
||
****** Pie Chart
|
||
|
||
#+begin_src mermaid :file pie.png
|
||
pie
|
||
"Dogs" : 386
|
||
"Cats" : 85
|
||
"Rats" : 15
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
[[file:pie.png]]
|
||
|
||
|
||
***** Example sql
|
||
:PROPERTIES:
|
||
:header-args: :engine postgresql :dbuser emacs :dbpassword (auth-source-pass-get 'secret "snamellit/emacs/test-psql") :database emacs
|
||
:END:
|
||
|
||
In the assumption that postgres is running and the current user has
|
||
admin privileges on the local dev database
|
||
#+BEGIN_SRC shell :result output
|
||
pass snamellit/emacs/test-psql | sed '/./{p}' | createuser emacs -P 2>&1
|
||
createdb -Oemacs -EUNICODE emacs 2>&1
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| Enter | password | for | new | role: |
|
||
| Enter | it | again: | | |
|
||
|
||
#+BEGIN_SRC sql :var rel="tname" n=300 :colnames yes
|
||
drop table if exists $rel;
|
||
create table $rel(id int, val int);
|
||
insert into $rel(id, val) values (1,210), (3,800);
|
||
select * from $rel where val > $n;
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| DROP TABLE | |
|
||
|--------------+-----|
|
||
| CREATE TABLE | |
|
||
| INSERT 0 2 | |
|
||
| id | val |
|
||
| 3 | 800 |
|
||
|
||
|
||
#+BEGIN_SRC sql
|
||
SELECT * FROM DUAL;
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|---|
|
||
|
||
***** Example sqlite
|
||
|
||
#+BEGIN_SRC sqlite :db /tmp/rip.db :var rel="tname" n=300 :colnames yes
|
||
drop table if exists $rel;
|
||
create table $rel(n int, id int);
|
||
insert into $rel(n,id) values (1,210), (3,800);
|
||
select * from $rel where id > $n;
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| n | id |
|
||
|---+-----|
|
||
| 3 | 800 |
|
||
|
||
***** Example dot aka Graphviz
|
||
|
||
#+BEGIN_SRC dot :file metrics.png
|
||
digraph MetricsHierarchy {
|
||
node [shape=box, style=rounded];
|
||
|
||
// Fundamental Metrics
|
||
subgraph cluster_fundamental_metrics {
|
||
label="Fundamental Metrics";
|
||
Performance_Rating [label="Performance Rating"];
|
||
Latency_Rating [label="Latency Rating"];
|
||
Quality_Rating [label="Quality Rating"];
|
||
Security_Rating [label="Security Rating"];
|
||
}
|
||
|
||
// Derived Metrics
|
||
subgraph cluster_derived_metrics {
|
||
label="Derived Metrics";
|
||
Application_Health_Score [label="Application Health Score"];
|
||
User_Experience_Index [label="User Experience Index"];
|
||
// Add more derived metrics as needed
|
||
}
|
||
|
||
// Top-Level Metrics
|
||
subgraph cluster_toplevel_metrics {
|
||
label="Top-Level Metrics";
|
||
Overall_Application_Performance [label="Overall Application Performance"];
|
||
// Add more top-level metrics as needed
|
||
}
|
||
|
||
// Connections
|
||
Performance_Rating -> Application_Health_Score;
|
||
Latency_Rating -> Application_Health_Score;
|
||
Quality_Rating -> User_Experience_Index;
|
||
Security_Rating -> User_Experience_Index;
|
||
|
||
Application_Health_Score -> Overall_Application_Performance;
|
||
}
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
[[file:metrics.png]]
|
||
|
||
|
||
|
||
|
||
****** Git Graph
|
||
|
||
#+begin_src mermaid :file git.png
|
||
gitGraph:
|
||
options
|
||
{
|
||
"nodeSpacing": 150,
|
||
"nodeRadius": 10
|
||
}
|
||
end
|
||
commit
|
||
branch newbranch
|
||
checkout newbranch
|
||
commit
|
||
commit
|
||
checkout master
|
||
commit
|
||
commit
|
||
merge newbranch
|
||
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
[[file:git.png]]
|
||
|
||
****** User Journey Graph
|
||
|
||
#+begin_src mermaid :file user_journey.png
|
||
journey
|
||
title My working day
|
||
section Go to work
|
||
Make tea: 5: Me
|
||
Go upstairs: 3: Me
|
||
Do work: 1: Me, Cat
|
||
section Go home
|
||
Go downstairs: 5: Me
|
||
Sit down: 3: Me
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
[[file:user_journey.png]]
|
||
|
||
|
||
|
||
****** ER Diagram
|
||
|
||
#+begin_src mermaid :file er.png
|
||
erDiagram
|
||
CUSTOMER ||--o{ ORDER : places
|
||
ORDER ||--|{ LINE-ITEM : contains
|
||
CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
[[file:er.png]]
|
||
|
||
|
||
***** TODO Example Rust
|
||
|
||
#+begin_src rust
|
||
fn main() {
|
||
print!("hello world");
|
||
}
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
: hello world
|
||
|
||
|
||
|
||
|
||
**** 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
|
||
**** 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
|
||
**** Export to Confluence
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package ox-confluence
|
||
:after org-contrib
|
||
:ensure nil)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package ox-confluence-en
|
||
:after ox-confluence
|
||
:ensure (ox-confluence-en :host github :repo "correl/ox-confluence-en"))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26576 2809 520906 nil elpaca-process-queues nil nil 290000 nil]
|
||
|
||
**** 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
|
||
: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)
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
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 (("C-c C" . org-capture)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: org-capture
|
||
|
||
*** 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 t ; (: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
|
||
'(
|
||
|
||
|
||
))
|
||
(make-directory "~/.org-jira" 'parents)
|
||
(setq jiralib-url (auth-source-pass-get "url" "customer/melexis.atlassian.net"))
|
||
(setq org-jira-custom-jqls
|
||
'((:jql " assignee = currentUser() and (created > '2024-01-01' or updated > '2024-01-01') order by created DESC"
|
||
:limit 100 :filename "this-year")
|
||
(:jql " assignee = currentUser() and project = UNILRN order by priority DESC "
|
||
:limit 100 :filename "~/Projects/unifylearn/jira")
|
||
(:jql " assignee = currentUser() and project = TTRK order by priority DESC "
|
||
:limit 100 :filename "~/Projects/timetrak/jira")
|
||
)
|
||
)
|
||
(jiralib-login
|
||
(auth-source-pass-get "user" "customer/jira")
|
||
(auth-source-pass-get 'secret "customer/jira"))
|
||
:bind (("C-c j i g" . 'org-jira-get-issues)
|
||
("C-c j p g" . 'org-jira-get-projects)
|
||
("C-c j i j" . 'org-jira-get-issues-from-custom-jql)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26760 26288 212481 nil elpaca-process-queues nil nil 913000 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 :tangle no
|
||
(setq org-agenda-custom-commands
|
||
'(("i" "Inbox" ((todo ".*"
|
||
((org-agenda-files '("~/Nextcloud/org/"))
|
||
(org-agenda-overriding-header "Unprocessed Inbox Items")))))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| i | Inbox | ((todo .* ((org-agenda-files '(~/Nextcloud/org/)) (org-agenda-overriding-header Unprocessed Inbox Items)))) |
|
||
|
||
|
||
**** Daily Agenda
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(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)))) |
|
||
|
||
**** Customer GTD
|
||
|
||
Create an agenda view specifically focused on customer tasks both in
|
||
the org tree and in the customer JIRA.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-to-list 'org-agenda-custom-commands
|
||
'("G" "Customer GTD"
|
||
((alltodo ".*"
|
||
((org-agenda-files '("~/org/customer/" "~/.org-jira/"))
|
||
(org-agenda-overriding-header "Customer GTD"))))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| G | Customer GTD | ((alltodo .* ((org-agenda-files '(~/org/customer/ ~/.org-jira/)) (org-agenda-overriding-header Customer GTD)))) |
|
||
| G | Customer GTD | ((alltodo .* ((org-agenda-files '(~/org/customer/)) (org-agenda-overriding-header Customer GTD)))) |
|
||
| 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)))) |
|
||
|
||
|
||
|
||
**** Context aware GTD
|
||
|
||
Create agenda views for specific contexts
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-to-list 'org-agenda-custom-commands '("fb" "Busy Bees" ((tags-todo "+bees"))))
|
||
(add-to-list 'org-agenda-custom-commands '("fm" "Melexis" ((tags-todo "+mlx"))))
|
||
(add-to-list 'org-agenda-custom-commands '("fg" "Geel" ((tags-todo "+geel"))))
|
||
(add-to-list 'org-agenda-custom-commands '("fh" "Home" ((tags-todo "+home"))))
|
||
(add-to-list 'org-agenda-custom-commands '("fr" "Rillaar" ((tags-todo "+rillaar"))))
|
||
(add-to-list 'org-agenda-custom-commands '("fe" "Emacs" ((alltodo ".*" ((org-agenda-files '("~/org/snamellit/emacs.org")))))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| fr | Rillaar | ((tags-todo +rillaar)) |
|
||
| fh | Home | ((tags-todo +home)) |
|
||
| fg | Geel | ((tags-todo +geel)) |
|
||
| fr | Melexis | ((tags-todo +rillaar)) |
|
||
| fh | Melexis | ((tags-todo +home)) |
|
||
| fg | Melexis | ((tags-todo +geel)) |
|
||
| fm | Melexis | ((tags-todo +mlx)) |
|
||
| fb | Busy Bees | ((tags-todo +bees)) |
|
||
| m | Melexis | ((tags-todo +mlx)) |
|
||
| b | Busy Bees | ((tags-todo +bees)) |
|
||
| G | Customer GTD | ((alltodo .* ((org-agenda-files '(~/org/customer/ ~/.org-jira/)) (org-agenda-overriding-header Customer GTD)))) |
|
||
| 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
|
||
|
||
#+RESULTS:
|
||
: [nil 26544 34903 498589 nil elpaca-process-queues nil nil 963000 nil]
|
||
|
||
**** TODO explain what denote-dired-mode-in-directories does.
|
||
|
||
** BoilerPlate Generator
|
||
|
||
The way I do advent of code requires me to create a main file and a
|
||
test file every day with some boilerplate content.
|
||
|
||
The *org-generate* package uses an org file to structure boilerplate
|
||
templates to be generated. By default it uses
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package org-generate
|
||
:ensure t
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26455 5250 886750 nil elpaca-process-queues nil nil 641000 nil]
|
||
|
||
* 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 and other file saving settings
|
||
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
|
||
|
||
#+RESULTS:
|
||
: ((. . /home/pti/.config/emacs/backups))
|
||
|
||
To avoid losing files when deleting in dired, move them to the trash:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq delete-by-moving-to-trash t)
|
||
#+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*.
|
||
|
||
|
||
|
||
For setups with GnuPG >= 2.1, pinentry package is not needed anymore.
|
||
|
||
Quote from the Emacs News.26 file:
|
||
|
||
*** The pinentry.el library has been removed.
|
||
That package (and the corresponding change in GnuPG and pinentry)
|
||
was intended to provide a way to input passphrase through Emacs with
|
||
GnuPG 2.0. However, the change to support that was only implemented
|
||
in GnuPG >= 2.1 and didn't get backported to GnuPG 2.0. And with
|
||
GnuPG 2.1 and later, pinentry.el is not needed at all. So the
|
||
library was useless, and we removed it. GnuPG 2.0 is no longer
|
||
supported by the upstream project.
|
||
|
||
To adapt to the change, you may need to set 'epa-pinentry-mode' to the
|
||
symbol 'loopback'. Alternatively, leave 'epa-pinentry-mode' at its
|
||
default value of nil, and remove the 'allow-emacs-pinentry' setting
|
||
from your 'gpg-agent.conf' configuration file, usually found in the
|
||
'~/.gnupg' directory.
|
||
|
||
Note that previously, it was said that passphrase input through
|
||
minibuffer would be much less secure than other graphical pinentry
|
||
programs. However, these days the difference is insignificant: the
|
||
'read-password' function sufficiently protects input from leakage to
|
||
message logs. Emacs still doesn't use secure memory to protect
|
||
passphrases, but it was also removed from other pinentry programs as
|
||
the attack is unrealistic on modern computer systems which don't
|
||
utilize swap memory usually.
|
||
|
||
See also a discussion on why pinentry was removed from Emacs core.
|
||
|
||
So a setup may now consist of:
|
||
|
||
In Emacs' user-init-file:
|
||
|
||
#+BEGIN_SRC elisp
|
||
(require 'epg)
|
||
(setq epg-pinentry-mode 'loopback)
|
||
#+END_SRC
|
||
|
||
In ~/.gnupg/gpg-agent.conf:
|
||
|
||
#+BEGIN_SRC text :tangle no
|
||
allow-emacs-pinentry
|
||
# on Mac OS
|
||
pinentry-program /usr/local/bin/pinentry-mac
|
||
#+END_SRC
|
||
|
||
*** Enable pass secrets
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(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
|
||
|
||
*** Major mode to edit pass keychain
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package pass
|
||
:ensure t)
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26721 10916 917127 nil elpaca-process-queues nil nil 78000 nil]
|
||
|
||
|
||
** Add a fully featured terminal emulator
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package eat
|
||
:commands eat
|
||
:ensure (eat
|
||
:host codeberg
|
||
:repo "akib/emacs-eat"
|
||
:files ("*.el" ("term" "term/*.el") "*.texi"
|
||
"*.ti" ("terminfo/e" "terminfo/e/*")
|
||
("terminfo/65" "terminfo/65/*")
|
||
("integration" "integration/*")
|
||
(:exclude ".dir-locals.el" "*-tests.el"))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26538 15682 653403 nil elpaca-process-queues nil nil 807000 nil]
|
||
|
||
** Enable vterm
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package vterm
|
||
:ensure t
|
||
:commands vterm)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26557 35507 493630 nil elpaca-process-queues nil nil 948000 nil]
|
||
|
||
** Docker Integration
|
||
|
||
Detect which container tool is installed and configures accordingly
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq snm-docker-executable (if (executable-find "docker") 'docker 'podman))
|
||
(use-package docker
|
||
:defer t
|
||
:ensure t
|
||
:bind ("C-c d" . docker)
|
||
:config
|
||
(pcase snm-docker-executable
|
||
('docker
|
||
(setf docker-command "docker"
|
||
docker-compose-command "docker-compose"
|
||
docker-container-tramp-method "docker"))
|
||
('podman
|
||
(setf docker-command "podman"
|
||
docker-compose-command "podman-compose"
|
||
docker-container-tramp-method "podman"))))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26557 44955 396008 nil elpaca-process-queues nil nil 436000 nil]
|
||
|
||
|
||
Add support to edit Dockerfile files :
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package dockerfile-mode
|
||
:defer t
|
||
:ensure t
|
||
:config
|
||
(setq dockerfile-mode-command docker-command))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26557 44943 649 nil elpaca-process-queues nil nil 729000 nil]
|
||
|
||
|
||
** Confluence Integration
|
||
|
||
This module is advertised as being alpha quality but should allow
|
||
importing and exporting confluence pages.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package tributary
|
||
:ensure (tributary :type git :host github :repo "mrkrd/tributary")
|
||
:config
|
||
(setq tributary-api-url "https://melexis.atlassian.net/wiki/rest/api/content/"))
|
||
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26770 1000 217140 nil elpaca-process-queues nil nil 894000 nil]
|
||
|
||
|
||
** Mu4e Configuration
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar snm/mu4e-load-path nil)
|
||
(if (executable-find "mu")
|
||
(let ((snm/mu4e-load-path (concat
|
||
(file-name-parent-directory
|
||
(file-name-directory
|
||
(executable-find "mu")))
|
||
"share/emacs/site-lisp/mu4e")))
|
||
|
||
(use-package mu4e
|
||
:load-path snm/mu4e-load-path
|
||
;"/usr/share/emacs/site-lisp/mu4e"
|
||
:config
|
||
|
||
;; use mu4e for e-mail in emacs
|
||
(setq mail-user-agent 'mu4e-user-agent)
|
||
|
||
(setq mu4e-drafts-folder "/[Gmail].Drafts")
|
||
(setq mu4e-sent-folder "/[Gmail].Sent Mail")
|
||
(setq mu4e-trash-folder "/[Gmail].Trash")
|
||
|
||
;; don't save message to Sent Messages, Gmail/IMAP takes care of this
|
||
(setq mu4e-sent-messages-behavior 'delete)
|
||
|
||
;; (See the documentation for `mu4e-sent-messages-behavior' if you have
|
||
;; additional non-Gmail addresses and want assign them different
|
||
;; behavior.)
|
||
|
||
;; setup some handy shortcuts
|
||
;; you can quickly switch to your Inbox -- press ``ji''
|
||
;; then, when you want archive some messages, move them to
|
||
;; the 'All Mail' folder by pressing ``ma''.
|
||
|
||
(setq mu4e-maildir-shortcuts
|
||
'( (:maildir "/INBOX" :key ?i)
|
||
(:maildir "/[Gmail].Sent Mail" :key ?s)
|
||
(:maildir "/[Gmail].Trash" :key ?t)
|
||
(:maildir "/[Gmail].All Mail" :key ?a)))
|
||
|
||
(add-to-list 'mu4e-bookmarks
|
||
;; ':favorite t' i.e, use this one for the modeline
|
||
'(:query "maildir:/inbox" :name "Inbox" :key ?i :favorite t))
|
||
|
||
;; allow for updating mail using 'U' in the main view:
|
||
(setq mu4e-get-mail-command "offlineimap")
|
||
|
||
;; something about ourselves
|
||
(setq
|
||
user-mail-address "pti@snamellit.com"
|
||
user-full-name "Peter Tillemans"
|
||
message-signature
|
||
(concat
|
||
"Peter Tillemans\n"
|
||
"Snamellit BV\n"
|
||
"http://www.smamellit.com\n"))
|
||
|
||
;; sending mail -- replace USERNAME with your gmail username
|
||
;; also, make sure the gnutls command line utils are installed
|
||
;; package 'gnutls-bin' in Debian/Ubuntu
|
||
|
||
(require 'smtpmail)
|
||
(setq
|
||
message-send-mail-function 'smtpmail-send-it
|
||
starttls-use-gnutls t
|
||
smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil))
|
||
smtpmail-auth-credentials
|
||
'(("smtp.gmail.com" 587 "pti@snamellit.com" (auth-source-pass-get 'secret "snamellit/imap.gmail.com")))
|
||
smtpmail-default-smtp-server "smtp.gmail.com"
|
||
smtpmail-smtp-server "smtp.gmail.com"
|
||
smtpmail-smtp-service 587)
|
||
|
||
;; alternatively, for emacs-24 you can use:
|
||
;;(setq message-send-mail-function 'smtpmail-send-it
|
||
;; smtpmail-stream-type 'starttls
|
||
;; smtpmail-default-smtp-server "smtp.gmail.com"
|
||
;; smtpmail-smtp-server "smtp.gmail.com"
|
||
;; smtpmail-smtp-service 587)
|
||
|
||
;; don't keep message buffers around
|
||
(setq message-kill-buffer-on-exit t)
|
||
|
||
)
|
||
))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: t
|
||
|
||
|
||
* Editor Features
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Editor Features")
|
||
#+END_SRC
|
||
|
||
|
||
** Emacs configuration
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
|
||
;; A few more useful configurations...
|
||
(use-package emacs
|
||
:custom
|
||
;; Support opening new minibuffers from inside existing minibuffers.
|
||
(enable-recursive-minibuffers t)
|
||
;; Hide commands in M-x which do not work in the current mode. Vertico
|
||
;; commands are hidden in normal buffers. This setting is useful beyond
|
||
;; Vertico.
|
||
(read-extended-command-predicate #'command-completion-default-include-p)
|
||
:init
|
||
;; Add prompt indicator to `completing-read-multiple'.
|
||
;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
|
||
(defun crm-indicator (args)
|
||
(cons (format "[CRM%s] %s"
|
||
(replace-regexp-in-string
|
||
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
|
||
crm-separator)
|
||
(car args))
|
||
(cdr args)))
|
||
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
|
||
|
||
;; Do not allow the cursor in the minibuffer prompt
|
||
(setq minibuffer-prompt-properties
|
||
'(read-only t cursor-intangible t face minibuffer-prompt))
|
||
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))
|
||
#+END_SRC
|
||
|
||
** Save history over sessions
|
||
|
||
Persist history over Emacs restarts. Vertico sorts by history
|
||
position.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package savehist
|
||
:init
|
||
(savehist-mode))
|
||
#+END_SRC
|
||
|
||
** Completion Configuration
|
||
*** Use Vertico for better selection lists
|
||
|
||
Vertico is a big package by minad. Well documented in the [[https://github.com/minad/vertico][vertico
|
||
github repo]].
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; Enable vertico
|
||
(use-package vertico
|
||
:ensure t
|
||
:custom
|
||
(vertico-scroll-margin 0) ;; Different scroll margin
|
||
(vertico-count 12) ;; Show more candidates
|
||
(vertico-resize t) ;; Grow and shrink the Vertico minibuffer
|
||
(vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
|
||
:init
|
||
(vertico-mode))
|
||
|
||
#+END_SRC
|
||
|
||
*** Orderless for better narrowing
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package orderless
|
||
:ensure t
|
||
:custom
|
||
;; Configure a custom style dispatcher (see the Consult wiki)
|
||
;; (orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch))
|
||
;; (orderless-component-separator #'orderless-escapable-split-on-space)
|
||
(completion-styles '(orderless basic))
|
||
;;(completion-category-defaults nil)
|
||
(completion-category-overrides '((file (styles partial-completion)))))
|
||
#+END_SRC
|
||
|
||
*** Marginalia for better context awareness
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; Enable rich annotations using the Marginalia package
|
||
(use-package marginalia
|
||
:ensure t
|
||
|
||
;; Bind `marginalia-cycle' locally in the minibuffer. To make the binding
|
||
;; available in the *Completions* buffer, add it to the
|
||
;; `completion-list-mode-map'.
|
||
:bind (:map minibuffer-local-map ("M-A" . marginalia-cycle))
|
||
|
||
:init
|
||
(marginalia-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: marginalia-cycle
|
||
|
||
*** Add consult
|
||
|
||
See the excellent documentation on [[https://github.com/minad/consult][Minad's consult repo]].
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; Example configuration for Consult
|
||
(use-package consult
|
||
:ensure t
|
||
;; Replace bindings. Lazily loaded by `use-package'.
|
||
:bind (;; C-c bindings in `mode-specific-map'
|
||
("C-c M-x" . consult-mode-command)
|
||
("C-c h" . consult-history)
|
||
("C-c k" . consult-kmacro)
|
||
("C-c m" . consult-man)
|
||
("C-c i" . consult-info)
|
||
([remap Info-search] . consult-info)
|
||
;; C-x bindings in `ctl-x-map'
|
||
("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
|
||
("C-x b" . consult-buffer) ;; orig. switch-to-buffer
|
||
("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
|
||
("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
|
||
("C-x t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab
|
||
("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
|
||
("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
|
||
;; Custom M-# bindings for fast register access
|
||
("M-#" . consult-register-load)
|
||
("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
|
||
("C-M-#" . consult-register)
|
||
;; Other custom bindings
|
||
("M-y" . consult-yank-pop) ;; orig. yank-pop
|
||
;; M-g bindings in `goto-map'
|
||
("M-g e" . consult-compile-error)
|
||
("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
|
||
("M-g g" . consult-goto-line) ;; orig. goto-line
|
||
("M-g M-g" . consult-goto-line) ;; orig. goto-line
|
||
("M-g o" . consult-outline) ;; Alternative: consult-org-heading
|
||
("M-g m" . consult-mark)
|
||
("M-g k" . consult-global-mark)
|
||
("M-g i" . consult-imenu)
|
||
("M-g I" . consult-imenu-multi)
|
||
;; M-s bindings in `search-map'
|
||
("M-s d" . consult-find) ;; Alternative: consult-fd
|
||
("M-s c" . consult-locate)
|
||
("M-s g" . consult-grep)
|
||
("M-s G" . consult-git-grep)
|
||
("M-s r" . consult-ripgrep)
|
||
("M-s l" . consult-line)
|
||
("M-s L" . consult-line-multi)
|
||
("M-s k" . consult-keep-lines)
|
||
("M-s u" . consult-focus-lines)
|
||
;; Isearch integration
|
||
("M-s e" . consult-isearch-history)
|
||
:map isearch-mode-map
|
||
("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
|
||
("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
|
||
("M-s l" . consult-line) ;; needed by consult-line to detect isearch
|
||
("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
|
||
;; Minibuffer history
|
||
:map minibuffer-local-map
|
||
("M-s" . consult-history) ;; orig. next-matching-history-element
|
||
("M-r" . consult-history)) ;; orig. previous-matching-history-element
|
||
|
||
;; Enable automatic preview at point in the *Completions* buffer. This is
|
||
;; relevant when you use the default completion UI.
|
||
:hook (completion-list-mode . consult-preview-at-point-mode)
|
||
|
||
;; The :init configuration is always executed (Not lazy)
|
||
:init
|
||
|
||
;; Optionally configure the register formatting. This improves the register
|
||
;; preview for `consult-register', `consult-register-load',
|
||
;; `consult-register-store' and the Emacs built-ins.
|
||
(setq register-preview-delay 0.5
|
||
register-preview-function #'consult-register-format)
|
||
|
||
;; Optionally tweak the register preview window.
|
||
;; This adds thin lines, sorting and hides the mode line of the window.
|
||
(advice-add #'register-preview :override #'consult-register-window)
|
||
|
||
;; Use Consult to select xref locations with preview
|
||
(setq xref-show-xrefs-function #'consult-xref
|
||
xref-show-definitions-function #'consult-xref)
|
||
|
||
;; Configure other variables and modes in the :config section,
|
||
;; after lazily loading the package.
|
||
:config
|
||
|
||
;; Optionally configure preview. The default value
|
||
;; is 'any, such that any key triggers the preview.
|
||
;; (setq consult-preview-key 'any)
|
||
;; (setq consult-preview-key "M-.")
|
||
;; (setq consult-preview-key '("S-<down>" "S-<up>"))
|
||
;; For some commands and buffer sources it is useful to configure the
|
||
;; :preview-key on a per-command basis using the `consult-customize' macro.
|
||
(consult-customize
|
||
consult-theme :preview-key '(:debounce 0.2 any)
|
||
consult-ripgrep consult-git-grep consult-grep
|
||
consult-bookmark consult-recent-file consult-xref
|
||
consult--source-bookmark consult--source-file-register
|
||
consult--source-recent-file consult--source-project-recent-file
|
||
;; :preview-key "M-."
|
||
:preview-key '(:debounce 0.4 any))
|
||
|
||
;; Optionally configure the narrowing key.
|
||
;; Both < and C-+ work reasonably well.
|
||
(setq consult-narrow-key "<") ;; "C-+"
|
||
|
||
;; Optionally make narrowing help available in the minibuffer.
|
||
;; You may want to use `embark-prefix-help-command' or which-key instead.
|
||
;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help)
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26434 3705 536018 nil elpaca-process-queues nil nil 266000 nil]
|
||
|
||
** Frequently used File/Project Operations
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(keymap-global-set "C-c v" vc-prefix-map)
|
||
(keymap-global-set "C-c p" project-prefix-map)
|
||
|
||
(recentf-mode 1) ;; enable recent file tracking
|
||
(keymap-global-set "C-c f f" #'find-file)
|
||
(keymap-global-set "C-c f o" #'recentf)
|
||
(keymap-global-set "C-c f r" #'revert-buffer)
|
||
(keymap-global-set "C-c f d" #'diff-buffer-with-file)
|
||
#+END_SRC
|
||
|
||
** User Interface
|
||
*** Display startup time
|
||
#+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 snm-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 snm-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 snm-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 ,(snm-find-installed-font
|
||
'("FiraCode NF" "FiraCode Nerd Font"))
|
||
:default-weight regular
|
||
:default-height ,snm-font-size
|
||
:variable-pitch-family ,(snm-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
|
||
(snm-configure-fonts)
|
||
(add-hook 'after-make-frame-functions #'snm-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 snm-display-tweaks (&optional frame)
|
||
"Configure a newly created FRAME."
|
||
(interactive)
|
||
(menu-bar-mode 1)
|
||
(tool-bar-mode -1)
|
||
(if (display-graphic-p)
|
||
(scroll-bar-mode -1)))
|
||
|
||
(add-hook 'after-make-frame-functions #'snm-display-tweaks)
|
||
;; run it in the current frame, because the hooks have already fired
|
||
(snm-display-tweaks)
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
*** Set Theme
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
;; Set theme
|
||
(use-package catppuccin-theme
|
||
:ensure t
|
||
:demand t
|
||
:config
|
||
(setq catppuccin-flavor 'mocha)
|
||
(load-theme 'catppuccin t)
|
||
(defun catppuccin-toggle ()
|
||
(interactive)
|
||
(setq catppuccin-flavor (if (eq catppuccin-flavor 'mocha) 'latte 'mocha))
|
||
(catppuccin-reload)
|
||
(message (format "Cattpuccin Flavor set to %s" catppuccin-flavor)))
|
||
:bind
|
||
(("<f5>" . #'catppuccin-toggle)))
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(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
|
||
|
||
|
||
*** EF Themes
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle yes
|
||
(use-package ef-themes
|
||
:ensure t
|
||
:init
|
||
;; If you like two specific themes and want to switch between them, you
|
||
;; can specify them in `ef-themes-to-toggle' and then invoke the command
|
||
;; `ef-themes-toggle'. All the themes are included in the variable
|
||
;; `ef-themes-collection'.
|
||
(setq ef-themes-to-toggle '(ef-day ef-night))
|
||
|
||
(setq ef-themes-headings ; read the manual's entry or the doc string
|
||
'((0 variable-pitch light 1.9)
|
||
(1 variable-pitch light 1.8)
|
||
(2 variable-pitch regular 1.7)
|
||
(3 variable-pitch regular 1.6)
|
||
(4 variable-pitch regular 1.5)
|
||
(5 variable-pitch 1.4) ; absence of weight means `bold'
|
||
(6 variable-pitch 1.3)
|
||
(7 variable-pitch 1.2)
|
||
(t variable-pitch 1.1)))
|
||
|
||
;; They are nil by default...
|
||
(setq ef-themes-mixed-fonts t
|
||
ef-themes-variable-pitch-ui t)
|
||
:config
|
||
;; Disable all other themes to avoid awkward blending:
|
||
(mapc #'disable-theme custom-enabled-themes)
|
||
|
||
;; load the theme which also calls `ef-themes-post-load-hook':
|
||
(ef-themes-select 'ef-night)
|
||
|
||
:bind
|
||
("<f5>" . #'ef-themes-toggle)
|
||
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26538 31881 746655 nil elpaca-process-queues nil nil 312000 nil]
|
||
|
||
*** Spacious Padding
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package spacious-padding
|
||
:ensure t
|
||
:bind (("<f8>" . #'spacious-padding-mode)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26538 30896 455747 nil elpaca-process-queues nil nil 302000 nil]
|
||
|
||
*** 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)
|
||
(push '("magit:.*"
|
||
;; 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 '("\\*sly-mrepl for .*\\*"
|
||
;; 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)
|
||
(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
|
||
*** Enable Yasnippet
|
||
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)
|
||
(add-to-list 'yas-snippet-dirs "~/src/guix/etc/snippets/yas"))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: t
|
||
|
||
*** Add Snippet Collection
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; add yasnippet collection
|
||
(use-package yasnippet-snippets
|
||
:ensure t)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26481 25510 926111 nil elpaca-process-queues nil nil 690000 nil]
|
||
|
||
* 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
|
||
(keymap-set eglot-mode-map "C-c c a" #'eglot-code-actions)
|
||
(keymap-set eglot-mode-map "C-c c d" #'eglot-find-declaration)
|
||
(keymap-set eglot-mode-map "C-c c i" #'eglot-find-implementation)
|
||
(keymap-set eglot-mode-map "C-c c k" #'eglot-find-typeDefinition)
|
||
(keymap-set eglot-mode-map "C-c c f" #'eglot-format)
|
||
(keymap-set eglot-mode-map "C-c c F" #'eglot-format-buffer)
|
||
(keymap-set eglot-mode-map "C-c c r" #'eglot-rename)
|
||
(keymap-set eglot-mode-map "C-c c Q" #'eglot-shutdown)
|
||
(keymap-set eglot-mode-map "C-c c q" #'eglot-reconnect)
|
||
(keymap-set eglot-mode-map "C-c c n" #'flymake-goto-next-error)
|
||
(keymap-set eglot-mode-map "C-c c p" #'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
|
||
|
||
*** Use Treesitter parser support
|
||
|
||
Recently with verion 0.25 the treesitter library changed ABI version
|
||
to 15. Newer parsers will complain about a version mismatch if the
|
||
installed library used by emacs is lower than this version. This ABI
|
||
version was introduced in the 0.25 branch of treesitter.
|
||
|
||
The best course of action till lib treesitter is updated is to pin
|
||
the version of the parser to the last version supporting ABI 14.
|
||
|
||
With the new ABI 15 version, parsers are required to provide a
|
||
~treesitter.json~ file with additional metadata which can be used as
|
||
proxy to find a version which still supports ABI-14, i.e. the commit
|
||
before that.
|
||
|
||
A lot of the parsers are provided by the treesitter project as sub
|
||
repos, and they follow the same version convention as the library,
|
||
selecting the last tag before the 0.25 tag is a good way to find a
|
||
compatible version.
|
||
|
||
This branch can be added after the repo url in the
|
||
~treesit-language-source-alist~ variable. Note that if you use
|
||
~treesit-auto-install-all~ to get it over with, you have to probably
|
||
restart your emacs as treesit-auto apparently caches the value
|
||
during iniitialisation and changes are not picked up.
|
||
|
||
#+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" "v0.23.3")
|
||
(c "https://github.com/tree-sitter/tree-sitter-c" "v0.23.6")
|
||
(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")
|
||
(rust "https://github.com/tree-sitter/tree-sitter-rust" "v0.23.3")
|
||
(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)))
|
||
#+END_SRC
|
||
|
||
Treesit-auto automatically configures things behind the scenes to use
|
||
the treesitter modes and download them if needed. \
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(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
|
||
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
|
||
|
||
**** 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
|
||
|
||
** 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
|
||
:after (flymake flycheck eglot)
|
||
: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
|
||
:hook
|
||
(go-ts . eglot-ensure))
|
||
#+END_SRC
|
||
|
||
*** Javascript, Typescript, TSC, etc...
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; configure typescript support
|
||
(use-package typescript-ts-mode
|
||
:hook
|
||
(typescript-ts-mode . eglot-ensure))
|
||
|
||
;; configure javascript support
|
||
(use-package javascript-ts-mode
|
||
:hook
|
||
(javascript-ts-mode . eglot-ensure))
|
||
|
||
;; configure react support
|
||
(use-package tsx-ts-mode
|
||
:hook
|
||
(tsx-ts-mode . eglot-ensure))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
| eglot-ensure |
|
||
|
||
*** 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
|
||
**** Aggressive Indent for lisp modes
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package aggressive-indent
|
||
:ensure t
|
||
:hook ((lisp-mode-hook scheme-mode-hook clojure-mode-hook)))
|
||
#+END_SRC
|
||
**** Enable ParEdit in lispy modes
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; Lisp support
|
||
(use-package package-lint-flymake
|
||
:ensure t) ;; needed before activating lisp-interaction-mode-hook
|
||
(use-package paredit
|
||
:ensure nil
|
||
:after package-lint-flymake
|
||
: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)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
**** Rainbow Parentheses
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package rainbow-delimiters
|
||
:ensure t
|
||
:commands (rainbow-delimiters-mode)
|
||
:hook
|
||
(prog-mode . rainbow-delimiters-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26418 38138 672360 nil elpaca-process-queues nil nil 82000 nil]
|
||
**** Configure Sly for Common Lisp
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package sly
|
||
:ensure t
|
||
:config
|
||
(require 'sly-autoloads)
|
||
:hook (lisp-mode-hook . #'sly-editing-mode))
|
||
|
||
(use-package sly-quicklisp
|
||
:ensure t
|
||
:after sly)
|
||
|
||
(use-package sly-repl-ansi-color
|
||
:ensure t
|
||
:after sly)
|
||
|
||
(use-package sly-asdf
|
||
:ensure t
|
||
:after sly)
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26432 28005 611924 nil elpaca-process-queues nil nil 196000 nil]
|
||
**** Configure CLHS documentation
|
||
|
||
Common Lisp Hyperspec is distributed as a package for
|
||
|
||
#+BEGIN_SRC shell :tangle no
|
||
AOC/2024/02> (ql:quickload "clhs")
|
||
To load "clhs":
|
||
Install 1 Quicklisp release:
|
||
clhs
|
||
; Fetching #<URL "http://beta.quicklisp.org/archive/clhs/2015-04-07/clhs-0.6.3.tgz">
|
||
; 2186.27KB
|
||
==================================================
|
||
2,238,743 bytes in 0.04 seconds (50845.91KB/sec)
|
||
; Loading "clhs"
|
||
[package clhs].
|
||
|
||
("clhs")
|
||
#+END_SRC
|
||
|
||
There is a wizard to help installing it in Emacs:
|
||
|
||
#+BEGIN_SRC shell :tangle no
|
||
AOC/2024/02> (clhs:print-emacs-setup-form)
|
||
|
||
[ Quicklisp directory: "/home/pti/quicklisp/" (exists)
|
||
If the above location is not correct, do:
|
||
(setf clhs:*quicklisp-directory* "/path/to/quicklisp/") ]
|
||
|
||
clhs-use-local.el was not found in your quicklisp directory.
|
||
This means you're at step 1 of 2 for configuring Emacs/Slime
|
||
to perform lookups/browsing with your local copy of the CLHS.
|
||
|
||
Please run (clhs:install-clhs-use-local) in the (Common Lisp) REPL.
|
||
This will install clhs-use-local.el in your quicklisp directory.
|
||
|
||
Then, run (clhs:print-emacs-setup-form) again for instructions for step 2.
|
||
|
||
; No values
|
||
AOC/2024/02> (clhs:install-clhs-use-local)
|
||
T
|
||
AOC/2024/02> (clhs:print-emacs-setup-form)
|
||
|
||
[ Quicklisp directory: "/home/pti/quicklisp/" (exists)
|
||
If the above location is not correct, do:
|
||
(setf clhs:*quicklisp-directory* "/path/to/quicklisp/") ]
|
||
|
||
(clhs-use-local.el was found in your quicklisp directory.
|
||
Moreover, its version matches the one bundled with this CLHS ASDF wrapper.
|
||
You may proceed with step 2 of 2 below.)
|
||
|
||
|
||
Make Emacs evaluate this form to browse the CLHS locally:
|
||
|
||
(load "/home/pti/quicklisp/clhs-use-local.el" t)
|
||
|
||
|
||
Use C-c C-d h make-instance RET to test if the change was successful.
|
||
If it was, then this will open your browser and the URL will begin with "file:///".
|
||
|
||
Put the form in your ~/.emacs to persist the change for future sessions.
|
||
|
||
|
||
The README file has some further information,
|
||
including a list of 3 useful Slime CLHS lookup commands
|
||
and how to get Emacs to open CLHS pages in a different browser.
|
||
(Location: /home/pti/quicklisp/dists/quicklisp/software/clhs-0.6.3/README)
|
||
|
||
; No values
|
||
AOC/2024/02>
|
||
|
||
#+END_SRC
|
||
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(load (expand-file-name "~/quicklisp/clhs-use-local.el") t)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: t
|
||
|
||
**** Enable Geiser Mode in Scheme Mode
|
||
|
||
Configure Geiser and Scheme
|
||
- map .scm file by default to Guile
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package geiser-guile
|
||
:ensure t
|
||
:commands (geiser-guile))
|
||
(use-package geiser-chicken
|
||
:ensure t
|
||
:commands (geiser-chicken))
|
||
(use-package geiser-racket
|
||
:ensure t
|
||
:commands (geiser-racket))
|
||
|
||
(use-package geiser
|
||
:ensure t
|
||
:commands (geiser geiser-mode)
|
||
:config
|
||
(add-to-list 'geiser-implementations-alist `((dir ,(expand-file-name "~/src/guile")) guile))
|
||
(add-to-list 'geiser-implementations-alist `((dir ,(expand-file-name "~/src/chicken")) chicken))
|
||
(add-to-list 'geiser-implementations-alist `((dir ,(expand-file-name "~/src/racket")) racket))
|
||
(setq geiser-default-implementation 'guile))
|
||
|
||
(use-package scheme-mode
|
||
:ensure nil
|
||
:commands (scheme-mode)
|
||
:hook (scheme-mode . geiser-mode))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26508 11537 881112 nil elpaca-process-queues nil nil 113000 nil]
|
||
|
||
**** 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
|
||
:after geiser)
|
||
#+END_SRC
|
||
|
||
I find it a bit a confusing module.
|
||
|
||
It provides a minor-mode
|
||
|
||
**** Enable Cider for Clojure mode
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package clojure-mode
|
||
:ensure t
|
||
:mode (("\\.clj\\'" . clojure-mode)
|
||
("\\.edn\\'" . clojure-mode))
|
||
:init
|
||
(add-hook 'clojure-mode-hook #'yas-minor-mode)
|
||
(add-hook 'clojure-mode-hook #'linum-mode)
|
||
(add-hook 'clojure-mode-hook #'subword-mode)
|
||
(add-hook 'clojure-mode-hook #'smartparens-mode)
|
||
(add-hook 'clojure-mode-hook #'rainbow-delimiters-mode)
|
||
(add-hook 'clojure-mode-hook #'eldoc-mode)
|
||
(add-hook 'clojure-mode-hook #'idle-highlight-mode))
|
||
|
||
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package cider
|
||
:ensure t
|
||
:defer t
|
||
:init (add-hook 'cider-mode-hook #'clj-refactor-mode)
|
||
:diminish subword-mode
|
||
:config
|
||
(setq nrepl-log-messages t
|
||
cider-repl-display-in-current-window t
|
||
cider-repl-use-clojure-font-lock t
|
||
cider-prompt-save-file-on-load 'always-save
|
||
cider-font-lock-dynamically '(macro core function var)
|
||
nrepl-hide-special-buffers t
|
||
cider-overlays-use-font-lock t)
|
||
(cider-repl-toggle-pretty-printing))
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package clj-refactor
|
||
:defer t
|
||
:ensure t
|
||
:diminish clj-refactor-mode
|
||
:config (cljr-add-keybindings-with-prefix "C-c C-m"))
|
||
#+END_SRC
|
||
|
||
|
||
|
||
|
||
**** Allow saving of an SBCL images
|
||
|
||
When trying to call `(save-lisp-and-die #p"somefile")` , sbcl will
|
||
throw an error that it cannot comply when multiple threads are active.
|
||
|
||
For each project using this you need to define some helper function to
|
||
stop the repl threads before saving the image like the following:
|
||
|
||
#+BEGIN_SRC common-lisp :tangle no
|
||
(defun sbcl-save-sly-and-die ()
|
||
"Save a sbcl image, even when running from inside Sly.
|
||
This function should only be used in the *inferior-buffer* buffer,
|
||
inside emacs."
|
||
(mapcar #'(lambda (x)
|
||
(slynk::close-connection
|
||
x nil nil))
|
||
slynk::*connections*)
|
||
(dolist (thread (remove
|
||
(slynk-backend::current-thread)
|
||
(slynk-backend::all-threads)))
|
||
(slynk-backend::kill-thread thread))
|
||
(sleep 1)
|
||
(sb-ext:save-lisp-and-die #P"~/your-main-program.exe"
|
||
:toplevel #'your-main-function-here
|
||
:executable t
|
||
:compression t))
|
||
#+END_SRC
|
||
|
||
This function has to be called in the *sly-inferior-lisp*
|
||
|
||
#+BEGIN_SRC common-lisp :tangle no
|
||
;; in *sly-inferior-lisp* buffer
|
||
(sbcl-save-sly-and-die)
|
||
#+END_SRC
|
||
|
||
|
||
*** 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)
|
||
|
||
(keymap-set terraform-mode-map "C-c c k" #'terraform-open-doc)
|
||
(keymap-set terraform-mode-map "C-c c f" #'terraform-format)
|
||
(keymap-set terraform-mode-map "C-c c F" #'terraform-format-buffer))
|
||
#+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
|
||
|
||
*** Python Support
|
||
|
||
**** Enable Pyvenv
|
||
|
||
(pyvenv-mode 1))
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package pyvenv
|
||
:ensure t
|
||
:hook
|
||
(python-mode . pyvenv-mode)
|
||
(python-ts-mode . pyvenv-mode))
|
||
#+END_SRC
|
||
|
||
**** Enable UV
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(require 'treesit)
|
||
|
||
(use-package uv
|
||
:ensure (uv :type git :host github :repo "johannes-mueller/uv.el" :wait t)
|
||
:init
|
||
(add-to-list 'treesit-language-source-alist '(toml "https://github.com/tree-sitter-grammars/tree-sitter-toml"))
|
||
(unless (treesit-language-available-p 'toml)
|
||
(treesit-install-language-grammar 'toml)))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26751 43082 733388 nil elpaca-process-queues nil nil 945000 nil]
|
||
|
||
|
||
*** Gitlab CI Yaml Support
|
||
|
||
#+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 (auth-source-pass-get "url" "customer/gitlab/token"))
|
||
(gitlab-ci-api-token (auth-source-pass-get 'secret "customer/gitlab/token")))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26760 26150 568817 nil elpaca-process-queues nil nil 219000 nil]
|
||
*** Support Mermaid Diagrams
|
||
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; enable mermaid for org-babel
|
||
(use-package mermaid-mode
|
||
:ensure t
|
||
:demand t
|
||
:mode "\\.mmd\\'")
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26499 60213 709442 nil elpaca-process-queues nil nil 672000 nil]
|
||
|
||
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:
|
||
| | | | | | | | | | | | |
|
||
| added | 281 | packages, | removed | 60 | packages, | and | changed | 134 | packages | in | 18s |
|
||
| | | | | | | | | | | | |
|
||
| 52 | packages | are | looking | for | funding | | | | | | |
|
||
| run | `npm | fund` | for | details | | | | | | | |
|
||
|
||
|
||
*** Support PlantUML Diagrams
|
||
|
||
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 `snm-download-latest-plantuml` is made
|
||
interactive to manually install the latest version if needed.
|
||
|
||
#+BEGIN_SRC emacs-lisp :lexical t
|
||
(setq plantuml-jar-path (concat user-emacs-directory "plantuml.jar"))
|
||
(setq plantuml-default-exec-mode 'jar)
|
||
|
||
(defun snm-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 snm-download-latest-plantuml'."
|
||
(interactive)
|
||
(let* ((version (snm-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)))
|
||
|
||
(if (not (file-exists-p plantuml-jar-path))
|
||
(snm-download-latest-plantuml))
|
||
|
||
(setq org-plantuml-jar-path plantuml-jar-path)
|
||
|
||
(use-package plantuml
|
||
:ensure nil
|
||
:mode "\\.puml\\'")
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: ((\.ya?ml\' . yaml-ts-mode) (\.ts\' . typescript-ts-mode) (\.tsx\' . tsx-ts-mode) (\.toml\' . toml-ts-mode) (\.rs\' . rust-ts-mode) (\(?:\.\(?:rbw?\|ru\|rake\|thor\|jbuilder\|rabl\|gemspec\|podspec\)\|/\(?:Gem\|Rake\|Cap\|Thor\|Puppet\|Berks\|Brew\|Vagrant\|Guard\|Pod\)file\)\' . ruby-ts-mode) (\.py[iw]?\' . python-ts-mode) (\.md\' . markdown-ts-mode) (\.lua\' . lua-ts-mode) (\.json\' . json-ts-mode) (\.js\' . js-ts-mode) (\.java\' . java-ts-mode) (\.html\' . html-ts-mode) (go\.mod\' . go-mod-ts-mode) (\.go\' . go-ts-mode) (\.ex\' . elixir-ts-mode) ([/\]\(?:Containerfile\|Dockerfile\)\(?:\.[^/\]*\)?\' . dockerfile-ts-mode) (\.css\' . css-ts-mode) (\.cpp\' . c++-ts-mode) (\.cmake\' . cmake-ts-mode) (\.cs\' . csharp-ts-mode) (\.c\' . c-ts-mode) (\.sh\' . bash-ts-mode) (\.envrc\' . direnv-envrc-mode) (/git-rebase-todo\' . git-rebase-mode) (\.gitlab-ci\.yml\' . gitlab-ci-mode) (\.mmd\' . mermaid-mode) (\.gitlab-ci.yml\' . gitlab-ci-mode) (\.\(e?ya?\|ra\)ml\' . yaml-mode) (\.dockerfile\' . dockerfile-mode) ([/\]\(?:Containerfile\|Dockerfile\)\(?:\.[^/\]*\)?\' . dockerfile-mode) (\.puml\' . plantuml) (\.edn\' . clojure-mode) (\.clj\' . clojure-mode) (/home/pti/.password-store/.*\.gpg\' . pass-view-mode) (\.\(zig\|zon\)\' . zig-mode) (\.t\(f\(vars\)?\|ofu\)\' . terraform-mode) (\.nomad\' . hcl-mode) (\.hcl\' . hcl-mode) (\(?:build\|profile\)\.boot\' . clojure-mode) (\.joke\' . joker-mode) (\.jank\' . jank-mode) (\.cljd\' . clojuredart-mode) (\.cljs\' . clojurescript-mode) (\.cljc\' . clojurec-mode) (\.\(clj\|cljd\|dtm\|edn\|lpy\)\' . clojure-mode) (/guix/drvs/[[:alnum:]]\{2\}/[[:alnum:]]\{30\}-\(?:[+._[:alnum:]-]+\)\.drv\' . guix-build-log-mode) (/gnu/store/\(?:[+._[:alnum:]-]+\)\.drv\' . guix-derivation-mode) (/etc/profile\' . guix-env-var-mode) (/tmp/guix-build-\(?:[+._[:alnum:]-]+\)\.drv-[[:digit:]]+/environment-variables\' . guix-env-var-mode) (/guix/profiles/system\(?:[+._[:alnum:]-]+\)*/\(?:boot\|parameters\)\' . guix-scheme-mode) (/gnu/store/\(?:[0-9a-df-np-sv-z]\{32\}\)-\(?:activate\|activate-service\|boot\|parameters\|shepherd\.conf\|shepherd\(?:[+._[:alnum:]-]+\)\.scm\|\(?:[+._[:alnum:]-]+\)-builder\)\' . guix-scheme-mode) ([./]opam_?\' . tuareg-opam-mode) (\.mly\' . tuareg-menhir-mode) (\.eliomi?\' . tuareg-mode) (\.ml[ip]?\' . tuareg-mode) (\.rs\' . rustic-mode) (\.rs\' . rust-mode) (\.\(?:md\|markdown\|mkd\|mdown\|mkdn\|mdwn\)\' . markdown-mode) (\.gpg\(~\|\.~[0-9]+~\)?\' nil epa-file) (\.elc\' . elisp-byte-code-mode) (\.\(?:3fr\|a\(?:rw\|vs\)\|bmp[23]?\|c\(?:als?\|myka?\|r[2w]\|u[rt]\)\|d\(?:c[mrx]\|ds\|jvu\|ng\|px\)\|exr\|f\(?:ax\|its\)\|gif\(?:87\)?\|hrz\|ic\(?:on\|[bo]\)\|j\(?:2c\|ng\|p\(?:eg\|[2cg]\)\)\|k\(?:25\|dc\)\|m\(?:iff\|ng\|rw\|s\(?:l\|vg\)\|tv\)\|nef\|o\(?:rf\|tb\)\|p\(?:bm\|c\(?:ds\|[dltx]\)\|db\|ef\|gm\|i\(?:ct\|x\)\|jpeg\|n\(?:g\(?:24\|32\|8\)\|[gm]\)\|pm\|sd\|tif\|wp\)\|r\(?:a[fs]\|gb[ao]?\|l[ae]\)\|s\(?:c[rt]\|fw\|gi\|ix\|r[2f]\|un\|vgz?\)\|t\(?:ga\|i\(?:ff\(?:64\)?\|le\|m\)\|tf\)\|uyvy\|v\(?:da\|i\(?:car\|d\|ff\)\|st\)\|w\(?:bmp\|pg\)\|x\(?:3f\|bm\|cf\|pm\|wd\|[cv]\)\|yuv\)\' . image-mode) (\.zst\' nil jka-compr) (\.dz\' nil jka-compr) (\.xz\' nil jka-compr) (\.lzma\' nil jka-compr) (\.lz\' nil jka-compr) (\.g?z\' nil jka-compr) (\.bz2\' nil jka-compr) (\.Z\' nil jka-compr) (\.vr[hi]?\' . vera-mode) (\(?:\.\(?:rbw?\|ru\|rake\|thor\|axlsx\|jbuilder\|rabl\|gemspec\|podspec\)\|/\(?:Gem\|Rake\|Cap\|Thor\|Puppet\|Berks\|Brew\|Fast\|Vagrant\|Guard\|Pod\)file\)\' . ruby-mode) (\.re?st\' . rst-mode) (/\(?:Pipfile\|\.?flake8\)\' . conf-mode) (\(?:\.\(?:p\(?:th\|y[iw]?\)\)\|/\(?:SCons\(?:\(?:crip\|truc\)t\)\)\)\' . python-mode) (\.m\' . octave-maybe-mode) (\.less\' . less-css-mode) (/go\.work\' . go-work-ts-mode) (\.editorconfig\' . editorconfig-conf-mode) (\.scss\' . scss-mode) (\.cs\' . csharp-mode) (\.awk\' . awk-mode) (\.\(u?lpc\|pike\|pmod\(\.in\)?\)\' . pike-mode) (\.idl\' . idl-mode) (\.java\' . java-mode) (\.m\' . objc-mode) (\.ii\' . c++-mode) (\.i\' . c-mode) (\.lex\' . c-mode) (\.y\(acc\)?\' . c-mode) (\.h\' . c-or-c++-mode) (\.c\' . c-mode) (\.\(CC?\|HH?\)\' . c++-mode) (\.[ch]\(pp\|xx\|\+\+\)\' . c++-mode) (\.\(cc\|hh\)\' . c++-mode) (\.\(bat\|cmd\)\' . bat-mode) (\.[sx]?html?\(\.[a-zA-Z_]+\)?\' . mhtml-mode) (\.svgz?\' . image-mode) (\.svgz?\' . xml-mode) (\.x[bp]m\' . image-mode) (\.x[bp]m\' . c-mode) (\.p[bpgn]m\' . image-mode) (\.tiff?\' . image-mode) (\.gif\' . image-mode) (\.png\' . image-mode) (\.jpe?g\' . image-mode) (\.webp\' . image-mode) (\.te?xt\' . text-mode) (\.[tT]e[xX]\' . tex-mode) (\.ins\' . tex-mode) (\.ltx\' . latex-mode) (\.dtx\' . doctex-mode) (\.org\' . org-mode) (\.dir-locals\(?:-2\)?\.el\' . lisp-data-mode) (\.eld\' . lisp-data-mode) (eww-bookmarks\' . lisp-data-mode) (tramp\' . lisp-data-mode) (/archive-contents\' . lisp-data-mode) (places\' . lisp-data-mode) (\.emacs-places\' . lisp-data-mode) (\.el\' . emacs-lisp-mode) (Project\.ede\' . emacs-lisp-mode) (\(?:\.\(?:scm\|sls\|sld\|stk\|ss\|sch\)\|/\.guile\)\' . scheme-mode) (\.l\' . lisp-mode) (\.li?sp\' . lisp-mode) (\.[fF]\' . fortran-mode) (\.for\' . fortran-mode) (\.p\' . pascal-mode) (\.pas\' . pascal-mode) (\.\(dpr\|DPR\)\' . opascal-mode) (\.\([pP]\([Llm]\|erl\|od\)\|al\)\' . perl-mode) (Imakefile\' . makefile-imake-mode) (Makeppfile\(?:\.mk\)?\' . makefile-makepp-mode) (\.makepp\' . makefile-makepp-mode) (\.mk\' . makefile-gmake-mode) (\.make\' . makefile-gmake-mode) ([Mm]akefile\' . makefile-gmake-mode) (\.am\' . makefile-automake-mode) (\.texinfo\' . texinfo-mode) (\.te?xi\' . texinfo-mode) (\.[sS]\' . asm-mode) (\.asm\' . asm-mode) (\.css\' . css-mode) (\.mixal\' . mixal-mode) (\.gcov\' . compilation-mode) (/[._]?[A-Za-z0-9-]*\(?:gdbinit\(?:\.\(?:ini?\|loader\)\)?\|gdb\.ini\)\' . gdb-script-mode) (-gdb\.gdb . gdb-script-mode) ([cC]hange\.?[lL]og?\' . change-log-mode) ([cC]hange[lL]og[-.][0-9]+\' . change-log-mode) (\$CHANGE_LOG\$\.TXT . change-log-mode) (\.scm\.[0-9]*\' . scheme-mode) (\.[ckz]?sh\'\|\.shar\'\|/\.z?profile\' . sh-mode) (\.bash\' . sh-mode) (/bash-fc\.[0-9A-Za-z]\{6\}\' . sh-mode) (/PKGBUILD\' . sh-mode) (\(/\|\`\)\.\(bash_\(profile\|history\|log\(in\|out\)\)\|z?log\(in\|out\)\)\' . sh-mode) (\(/\|\`\)\.\(shrc\|zshrc\|m?kshrc\|bashrc\|t?cshrc\|esrc\)\' . sh-mode) (\(/\|\`\)\.\([kz]shenv\|xinitrc\|startxrc\|xsession\)\' . sh-mode) (\.m?spec\' . sh-mode) (\.m[mes]\' . nroff-mode) (\.man\' . nroff-mode) (\.sty\' . latex-mode) (\.cl[so]\' . latex-mode) (\.bbl\' . latex-mode) (\.bib\' . bibtex-mode) (\.bst\' . bibtex-style-mode) (\.sql\' . sql-mode) (\(acinclude\|aclocal\|acsite\)\.m4\' . autoconf-mode) (\.m[4c]\' . m4-mode) (\.mf\' . metafont-mode) (\.mp\' . metapost-mode) (\.vhdl?\' . vhdl-mode) (\.article\' . text-mode) (\.letter\' . text-mode) (\.i?tcl\' . tcl-mode) (\.exp\' . tcl-mode) (\.itk\' . tcl-mode) (\.icn\' . icon-mode) (\.sim\' . simula-mode) (\.mss\' . scribe-mode) (\.f9[05]\' . f90-mode) (\.f0[38]\' . f90-mode) (\.srt\' . srecode-template-mode) (\.prolog\' . prolog-mode) (\.tar\' . tar-mode) (\.\(arc\|zip\|lzh\|lha\|zoo\|[jew]ar\|xpi\|rar\|cbr\|7z\|squashfs\|ARC\|ZIP\|LZH\|LHA\|ZOO\|[JEW]AR\|XPI\|RAR\|CBR\|7Z\|SQUASHFS\)\' . archive-mode) (\.oxt\' . archive-mode) (\.\(deb\|[oi]pk\)\' . archive-mode) (\`/tmp/Re . text-mode) (/Message[0-9]*\' . text-mode) (\`/tmp/fol/ . text-mode) (\.oak\' . scheme-mode) (\.sgml?\' . sgml-mode) (\.x[ms]l\' . xml-mode) (\.dbk\' . xml-mode) (\.dtd\' . sgml-mode) (\.ds\(ss\)?l\' . dsssl-mode) (\.js[mx]?\' . javascript-mode) (\.har\' . javascript-mode) (\.json\' . js-json-mode) (\.[ds]?va?h?\' . verilog-mode) (\.by\' . bovine-grammar-mode) (\.wy\' . wisent-grammar-mode) (\.erts\' . erts-mode) ([:/\]\..*\(emacs\|gnus\|viper\)\' . emacs-lisp-mode) (\`\..*emacs\' . emacs-lisp-mode) ([:/]_emacs\' . emacs-lisp-mode) (/crontab\.X*[0-9]+\' . shell-script-mode) (\.ml\' . lisp-mode) (\.ld[si]?\' . ld-script-mode) (ld\.?script\' . ld-script-mode) (\.xs\' . c-mode) (\.x[abdsru]?[cnw]?\' . ld-script-mode) (\.zone\' . dns-mode) (\.soa\' . dns-mode) (\.asd\' . lisp-mode) (\.\(asn\|mib\|smi\)\' . snmp-mode) (\.\(as\|mi\|sm\)2\' . snmpv2-mode) (\.\(diffs?\|patch\|rej\)\' . diff-mode) (\.\(dif\|pat\)\' . diff-mode) (\.[eE]?[pP][sS]\' . ps-mode) (\.\(?:PDF\|EPUB\|CBZ\|FB2\|O?XPS\|DVI\|OD[FGPST]\|DOCX\|XLSX?\|PPTX?\|pdf\|epub\|cbz\|fb2\|o?xps\|djvu\|dvi\|od[fgpst]\|docx\|xlsx?\|pptx?\)\' . doc-view-mode-maybe) (configure\.\(ac\|in\)\' . autoconf-mode) (\.s\(v\|iv\|ieve\)\' . sieve-mode) (BROWSE\' . ebrowse-tree-mode) (\.ebrowse\' . ebrowse-tree-mode) (#\*mail\* . mail-mode) (\.g\' . antlr-mode) (\.mod\' . m2-mode) (\.ses\' . ses-mode) (\.docbook\' . sgml-mode) (\.com\' . dcl-mode) (/config\.\(?:bat\|log\)\' . fundamental-mode) (/\.?\(authinfo\|netrc\)\' . authinfo-mode) (\.\(?:[iI][nN][iI]\|[lL][sS][tT]\|[rR][eE][gG]\|[sS][yY][sS]\)\' . conf-mode) (\.la\' . conf-unix-mode) (\.ppd\' . conf-ppd-mode) (java.+\.conf\' . conf-javaprop-mode) (\.properties\(?:\.[a-zA-Z0-9._-]+\)?\' . conf-javaprop-mode) (\.toml\' . conf-toml-mode) (\.desktop\' . conf-desktop-mode) (/\.redshift\.conf\' . conf-windows-mode) (\`/etc/\(?:DIR_COLORS\|ethers\|.?fstab\|.*hosts\|lesskey\|login\.?de\(?:fs\|vperm\)\|magic\|mtab\|pam\.d/.*\|permissions\(?:\.d/.+\)?\|protocols\|rpc\|services\)\' . conf-space-mode) (\`/etc/\(?:acpid?/.+\|aliases\(?:\.d/.+\)?\|default/.+\|group-?\|hosts\..+\|inittab\|ksysguarddrc\|opera6rc\|passwd-?\|shadow-?\|sysconfig/.+\)\' . conf-mode) ([cC]hange[lL]og[-.][-0-9a-z]+\' . change-log-mode) (/\.?\(?:gitconfig\|gnokiirc\|hgrc\|kde.*rc\|mime\.types\|wgetrc\)\' . conf-mode) (/\.mailmap\' . conf-unix-mode) (/\.\(?:asound\|enigma\|fetchmail\|gltron\|gtk\|hxplayer\|mairix\|mbsync\|msmtp\|net\|neverball\|nvidia-settings-\|offlineimap\|qt/.+\|realplayer\|reportbug\|rtorrent\.\|screen\|scummvm\|sversion\|sylpheed/.+\|xmp\)rc\' . conf-mode) (/\.\(?:gdbtkinit\|grip\|mpdconf\|notmuch-config\|orbital/.+txt\|rhosts\|tuxracer/options\)\' . conf-mode) (/\.?X\(?:default\|resource\|re\)s\> . conf-xdefaults-mode) (/X11.+app-defaults/\|\.ad\' . conf-xdefaults-mode) (/X11.+locale/.+/Compose\' . conf-colon-mode) (/X11.+locale/compose\.dir\' . conf-javaprop-mode) (\.~?[0-9]+\.[0-9][-.0-9]*~?\' nil t) (\.\(?:orig\|in\|[bB][aA][kK]\)\' nil t) ([/.]c\(?:on\)?f\(?:i?g\)?\(?:\.[a-zA-Z0-9._-]+\)?\' . conf-mode-maybe) (\.[1-9]\' . nroff-mode) (\.art\' . image-mode) (\.avs\' . image-mode) (\.bmp\' . image-mode) (\.cmyk\' . image-mode) (\.cmyka\' . image-mode) (\.crw\' . image-mode) (\.dcm\' . image-mode) (\.dcr\' . image-mode) (\.dcx\' . image-mode) (\.dng\' . image-mode) (\.dpx\' . image-mode) (\.fax\' . image-mode) (\.heic\' . image-mode) (\.hrz\' . image-mode) (\.icb\' . image-mode) (\.icc\' . image-mode) (\.icm\' . image-mode) (\.ico\' . image-mode) (\.icon\' . image-mode) (\.jbg\' . image-mode) (\.jbig\' . image-mode) (\.jng\' . image-mode) (\.jnx\' . image-mode) (\.miff\' . image-mode) (\.mng\' . image-mode) (\.mvg\' . image-mode) (\.otb\' . image-mode) (\.p7\' . image-mode) (\.pcx\' . image-mode) (\.pdb\' . image-mode) (\.pfa\' . image-mode) (\.pfb\' . image-mode) (\.picon\' . image-mode) (\.pict\' . image-mode) (\.rgb\' . image-mode) (\.rgba\' . image-mode) (\.six\' . image-mode) (\.tga\' . image-mode) (\.wbmp\' . image-mode) (\.webp\' . image-mode) (\.wmf\' . image-mode) (\.wpg\' . image-mode) (\.xcf\' . image-mode) (\.xmp\' . image-mode) (\.xwd\' . image-mode) (\.yuv\' . image-mode) (\.tgz\' . tar-mode) (\.tbz2?\' . tar-mode) (\.txz\' . tar-mode) (\.tzst\' . tar-mode))
|
||
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: ~/.config/emacs/plantuml.jar
|
||
|
||
|
||
** Debugger Support
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Programming - Debugger Support")
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; install DAP servers
|
||
(setq snm-vscode-js-debug-dir (file-name-concat user-emacs-directory "dape/vscode-js-debug"))
|
||
(defun snm-install-vscode-js-debug ()
|
||
"Run installation procedure to install JS debugging support."
|
||
(interactive)
|
||
(mkdir snm-vscode-js-debug-dir t)
|
||
(let ((default-directory (expand-file-name snm-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 "*snm-install*" t "install")
|
||
(report-time-since-load "npm dependencies installed")
|
||
(call-process "npx" nil "*snm-install*" t "gulp" "dapDebugServer")
|
||
(report-time-since-load "vscode-js-debug installed")))
|
||
|
||
(setq snm-codelldb-dir (file-name-concat user-emacs-directory "dape/codelldb"))
|
||
(defun snm-install-codelldb ()
|
||
"Install Vadimcn.Vscode-Lldb DAP server for C/C++/RUST."
|
||
(interactive)
|
||
(let* ((default-directory snm-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 "*snm-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)
|
||
|
||
;; Projectile users
|
||
(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 snm-vscode-js-debug-dir "dist")
|
||
command-args ("src/dapDebugServer.js" "8123")
|
||
:type "pwa-node"
|
||
:request "launch"
|
||
:cwd dape-cwd
|
||
:program dape-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-command-cwd
|
||
host "127.0.0.1"
|
||
port 55878
|
||
:type "debug" ;; needed to set the adapterID correctly as a string type
|
||
:request "launch"
|
||
:cwd dape-cwd
|
||
:program dape-buffer-default))
|
||
(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
|
||
snm-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
|
||
:program dape-buffer-default))
|
||
(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
|
||
:program dape-buffer-default))
|
||
|
||
))
|
||
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
|
||
|
||
*** TODO move scheme configuration to the scheme section
|
||
or leave it here but move it to config, whatever makes most sense at
|
||
the moment.
|
||
|
||
* AI Support
|
||
|
||
** AI Provider API keys
|
||
|
||
As many tools require API keys, I better define them once and reuse
|
||
them in the configuration of the individual tools.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq openai-api-key (auth-source-pass-get 'secret "snamellit/openai-api-key"))
|
||
(setq anthropic-api-key (auth-source-pass-get 'secret "snamellit/anthropic-api-key"))
|
||
(setq gemini-api-key (auth-source-pass-get 'secret "snamellit/gemini-api-key"))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: AIzaSyBAkk0xHNkIBxCzkfvbOgYVb-B6BguWVUI
|
||
|
||
** 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)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26497 15516 337719 nil elpaca-process-queues nil nil 495000 nil]
|
||
|
||
#+RESULTS:g
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(use-package ellama
|
||
:ensure t
|
||
:defer t
|
||
:requires (llm)
|
||
:config
|
||
(message "Ellama loaded")
|
||
(keymap-global-set "C-c e" 'ellama-transient-main-menu)
|
||
:custom
|
||
(ellama-language "English")
|
||
(ellama-sessions-directory (expand-file-name "~/Nextcloud/ellama-sessions"))
|
||
(ellama-providers
|
||
'(("openai" . (progn
|
||
(require 'llm-openai)
|
||
(make-llm-openai
|
||
:key openai-api-key
|
||
:chat-model "gpt-4o"))))
|
||
))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26497 19957 511863 nil elpaca-process-queues nil nil 742000 nil]
|
||
|
||
|
||
It seems the *gpt-4o* model provides better responses.
|
||
|
||
I should investigate local models more.
|
||
|
||
*** Use Gemini with ellama
|
||
|
||
This is mostly for those who want to use Google Cloud specifically, most users should use Gemini instead, which is easier to set up.
|
||
|
||
You can set up with make-llm-vertex, with the following parameters:
|
||
|
||
|
||
| paramter | description |
|
||
|------------------+-------------------------------------------------------------------------------------------------------------------------|
|
||
| | |
|
||
| :project | Your project number from Google Cloud that has Vertex API enabled. |
|
||
| :chat-model | A model name from the list of Vertex's model names. This is optional, and will default to a reasonable model. |
|
||
| :embedding-model | A model name from the list of Vertex's embedding model names. This is optional, and will default to a reasonable model. |
|
||
|
||
In addition to the provider, which you may want multiple of (for example, to charge against different projects), there are customizable variables:
|
||
|
||
- llm-vertex-gcloud-binary: The binary to use for generating the API key.
|
||
- llm-vertex-gcloud-region: The gcloud region to use. It's good to set this to a region near where you are for best latency. Defaults to "us-central1".
|
||
|
||
If you haven't already, you must run the following command before using this:
|
||
|
||
|
||
#+BEGIN_SRC shell :tangle no
|
||
gcloud beta services identity create --service=aiplatform.googleapis.com --project=PROJECT_ID
|
||
#+END_SRC
|
||
|
||
|
||
However this is not for the Gemini Code Assistant. It is unclear how
|
||
to use that backend. Gemini Code Assistant is part of the Google Cloud
|
||
Tools.
|
||
|
||
|
||
** GPTel LLM support
|
||
|
||
Most of the mind share seems to be around [[https://github.com/karthink/gptel][gptel]] as basis for LLM
|
||
integration in Emacs.
|
||
|
||
*** Load and Configure GPTel
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package gptel
|
||
:ensure (:wait t)
|
||
;:commands (gptel)
|
||
:demand t
|
||
:config
|
||
(setq gptel-default-mode 'org-mode
|
||
gptel-expert-commands t
|
||
gptel-track-media t
|
||
gptel-include-reasoning 'ignore
|
||
gptel-log-level 'info
|
||
gptel--debug nil)
|
||
|
||
(add-to-list 'gptel-prompt-prefix-alist `(org-mode . ,(concat "*** pti " (format-time-string "[%Y-%m-%d %H:%M]") "\n")))
|
||
|
||
;; follow output
|
||
(add-hook 'gptel-post-stream-hook 'gptel-auto-scroll)
|
||
;; move to next prompt after response
|
||
(add-hook 'gptel-post-response-functions 'gptel-end-of-response))
|
||
|
||
(require 'gptel-curl)
|
||
(require 'gptel-transient)
|
||
(require 'gptel-integrations)
|
||
#+end_src
|
||
|
||
|
||
*** Enable model providers
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(setq gptel-api-key openai-api-key)
|
||
(gptel-make-gemini "Gemini" :key gemini-api-key :stream t)
|
||
(gptel-make-anthropic "Claude" :stream t :key anthropic-api-key)
|
||
(gptel-make-ollama "Qwen Coder"
|
||
:models '(qwen2.5-coder:14b
|
||
:description "QWen 2.5 Coder 14b"))
|
||
#+end_src
|
||
|
||
*** Enable Tools
|
||
**** File and Buffer Tools
|
||
***** Create File
|
||
#+begin_src emacs-lisp
|
||
(gptel-make-tool
|
||
:function (lambda (path filename content)
|
||
(let ((full-path (expand-file-name filename path)))
|
||
(with-temp-buffer
|
||
(insert content)
|
||
(write-file full-path))
|
||
(format "Created file %s in %s" filename path)))
|
||
:name "create_file"
|
||
:description "Create a new file with the specified content"
|
||
:args (list '(:name "path"
|
||
:type "string"
|
||
:description "The directory where to create the file")
|
||
'(:name "filename"
|
||
:type "string"
|
||
:description "The name of the file to create")
|
||
'(:name "content"
|
||
:type "string"
|
||
:description "The content to write to the file"))
|
||
:category "filesystem")
|
||
#+end_src
|
||
***** Append to Buffer
|
||
#+begin_src emacs-lisp
|
||
|
||
;; append to buffer
|
||
|
||
(gptel-make-tool
|
||
:function (lambda (buffer text)
|
||
(with-current-buffer (get-buffer-create buffer)
|
||
(save-excursion
|
||
(goto-char (point-max))
|
||
(insert text)))
|
||
(format "Appended text to buffer %s" buffer))
|
||
:name "append_to_buffer"
|
||
:description "Append text to the an Emacs buffer. If the buffer does not exist, it will be created."
|
||
:args (list '(:name "buffer"
|
||
:type "string"
|
||
:description "The name of the buffer to append text to.")
|
||
'(:name "text"
|
||
:type "string"
|
||
:description "The text to append to the buffer."))
|
||
:category "emacs")
|
||
#+end_src
|
||
***** Read Buffer
|
||
#+begin_src emacs-lisp
|
||
|
||
;; read buffer
|
||
|
||
(gptel-make-tool
|
||
:function (lambda (buffer)
|
||
(unless (buffer-live-p (get-buffer buffer))
|
||
(error "Error: buffer %s is not live." buffer))
|
||
(with-current-buffer buffer
|
||
(buffer-substring-no-properties (point-min) (point-max))))
|
||
:name "read_buffer"
|
||
:description "Return the contents of an Emacs buffer"
|
||
:args (list '(:name "buffer"
|
||
:type "string"
|
||
:description "The name of the buffer whose contents are to be retrieved"))
|
||
:category "emacs")
|
||
|
||
;; read file
|
||
#+end_src
|
||
***** Read File
|
||
#+begin_src emacs-lisp
|
||
|
||
|
||
(gptel-make-tool
|
||
:function (lambda (filepath)
|
||
(with-temp-buffer
|
||
(insert-file-contents (expand-file-name filepath))
|
||
(buffer-string)))
|
||
:name "read_file"
|
||
:description "Read and display the contents of a file"
|
||
:args (list '(:name "filepath"
|
||
:type "string"
|
||
:description "Path to the file to read. Supports relative paths and ~."))
|
||
:category "filesystem")
|
||
#+end_src
|
||
**** Directory Tools
|
||
***** List Directory
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
;;
|
||
;; directory operations
|
||
;;
|
||
|
||
;; list directory
|
||
|
||
(gptel-make-tool
|
||
:function (lambda (directory)
|
||
(mapconcat #'identity
|
||
(directory-files directory)
|
||
"\n"))
|
||
:name "list_directory"
|
||
:description "List the contents of a given directory"
|
||
:args (list '(:name "directory"
|
||
:type "string"
|
||
:description "The path to the directory to list"))
|
||
:category "filesystem")
|
||
#+end_src
|
||
***** Make Directory
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
;; make directory
|
||
|
||
(gptel-make-tool
|
||
:function (lambda (parent name)
|
||
(condition-case nil
|
||
(progn
|
||
(make-directory (expand-file-name name parent) t)
|
||
(format "Directory %s created/verified in %s" name parent))
|
||
(error (format "Error creating directory %s in %s" name parent))))
|
||
:name "make_directory"
|
||
:description "Create a new directory with the given name in the specified parent directory"
|
||
:args (list '(:name "parent"
|
||
:type "string"
|
||
:description "The parent directory where the new directory should be created, e.g. /tmp")
|
||
'(:name "name"
|
||
:type "string"
|
||
:description "The name of the new directory to create, e.g. testdir"))
|
||
:category "filesystem")
|
||
#+end_src
|
||
**** Web Tools
|
||
***** Fetch URL
|
||
|
||
#+begin_src emacs-lisp
|
||
;; fetch url
|
||
|
||
(gptel-make-tool
|
||
:function (lambda (url)
|
||
(with-current-buffer (url-retrieve-synchronously url)
|
||
(goto-char (point-min)) (forward-paragraph)
|
||
(let ((dom (libxml-parse-html-region (point) (point-max))))
|
||
(run-at-time 0 nil #'kill-buffer (current-buffer))
|
||
(with-temp-buffer
|
||
(shr-insert-document dom)
|
||
(buffer-substring-no-properties (point-min) (point-max))))))
|
||
:name "read_url"
|
||
:description "Fetch and read the contents of a URL"
|
||
:args (list '(:name "url"
|
||
:type "string"
|
||
:description "The URL to read"))
|
||
:category "web")
|
||
#+end_src
|
||
***** Search Web
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(defcustom gjg/gptel-search-prefix
|
||
(if (and (boundp 'eww-search-prefix) eww-search-prefix) eww-search-prefix "https://duckduckgo.com/html?q=")
|
||
"Base URL used for searching the web using gptel tools.
|
||
Falls back to duckduckgo if EWW is not loaded."
|
||
:type 'string)
|
||
|
||
(defun gjg/search-web (query)
|
||
"Initiate a web search using the search engine defined in `gjg/gptel-search-prefix'."
|
||
(condition-case err
|
||
(let* ((url-with-query
|
||
(url-encode-url (concat gjg/gptel-search-prefix query)))
|
||
(content (shell-command-to-string
|
||
(format "lynx -dump -list_inline '%s'" url-with-query))))
|
||
(format "Content pulled from %s\n\n%s" query content))
|
||
(warning (format "Error accessing %s: %s" query (error-message-string err)))))
|
||
|
||
|
||
;; search web
|
||
|
||
(gptel-make-tool
|
||
:name "search_web"
|
||
:function #'gjg/search-web
|
||
:description "Search the web"
|
||
:include t
|
||
:category "web"
|
||
:args (list `(:name "query"
|
||
:type string
|
||
:description ,(format "Search terms / string to search on the web using %s" gjg/gptel-search-prefix))))
|
||
#+end_src
|
||
**** Logging
|
||
***** Echo Message
|
||
|
||
#+begin_src emacs-lisp
|
||
(gptel-make-tool
|
||
:function (lambda (text)
|
||
(message "%s" text)
|
||
(format "Message sent: %s" text))
|
||
:name "echo_message"
|
||
:description "Send a message to the *Messages* buffer"
|
||
:args (list '(:name "text"
|
||
:type "string"
|
||
:description "The text to send to the messages buffer"))
|
||
:category "emacs")
|
||
#+end_src
|
||
**** Documentation Access
|
||
***** List Info Manuals
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
|
||
;;
|
||
;; documentation related
|
||
;;
|
||
|
||
|
||
|
||
(defun gjg/get-info-manuals-with-description ()
|
||
"Return text of all Info manuals with their simple descriptions.
|
||
This is what you see when you evoke C-h i or run (info)."
|
||
(with-temp-buffer
|
||
(Info-insert-dir)
|
||
(buffer-substring-no-properties (point-min) (point-max))))
|
||
|
||
;; get info manuals
|
||
|
||
(gptel-make-tool
|
||
:name "emacs_info_manuals"
|
||
:include nil
|
||
:function #'gjg/get-info-manuals-with-description
|
||
:description "Get text showing all known Info manuals with their description. Format is human-readable manual name, info manual short file name in parens, description."
|
||
:category "reflection")
|
||
|
||
(defun gjg/list-linux-man-pages (&optional s)
|
||
"Return text showing installed Linux man pages with descriptions - optionally provide a substring match to limit the number of results."
|
||
(let ((s (or s ""))
|
||
(shell-command-dont-erase-buffer nil))
|
||
(with-current-buffer "*man-pages*"
|
||
(delete-region (point-min) (point-max))
|
||
(shell-command (format "LANG=en_US.UTF-8 /usr/bin/man -k \"%s\"" s) t)
|
||
(buffer-string)
|
||
)))
|
||
#+end_src
|
||
***** List Man Pages
|
||
#+begin_src emacs-lisp
|
||
|
||
;; get list of man pages
|
||
|
||
(gptel-make-tool
|
||
:name "list_man_pages"
|
||
:description "Return list of installed Linux man pages with descriptions - optionally provide a substring match to limit the number of results."
|
||
:include nil
|
||
:function #'gjg/list-linux-man-pages
|
||
:category "reflection"
|
||
:args (list '(:name "search_substring"
|
||
:type string
|
||
:optional t
|
||
:description "A substring to limit results of all man pages. e.g. \"media\" or \"office\"")))
|
||
#+end_src
|
||
***** Org Cheat Sheet
|
||
#+begin_src emacs-lisp
|
||
|
||
;; org markup cheatsheet
|
||
|
||
(gptel-make-tool
|
||
:name "org_mode_cheatsheet"
|
||
:description "Quick and easy to read cheat sheet for Org Mode syntax - refer to this whenever you start a project or session requiring org mode syntax production, and whenver you want to make use of advanced features."
|
||
:include t
|
||
:function (lambda ()
|
||
(with-temp-buffer
|
||
(insert-file-contents (concat user-emacs-directory "org-mode-syntax-cheat-sheet-for-llms.txt"))
|
||
(buffer-string)))
|
||
:category: "reflection")
|
||
|
||
;;
|
||
;; Emacs related
|
||
;;
|
||
#+end_src
|
||
**** Emacs Related Tools
|
||
***** Extract Package Readme
|
||
#+begin_src emacs-lisp
|
||
(defun gjg/extract-emacs-package-readme (package-name)
|
||
"Extract and return the README file for package PACKAGE-NAME.
|
||
This version attempts to robustly find the source directory for
|
||
packages, including those managed by straight.el.
|
||
Return =nil' if no README was found."
|
||
(if (stringp package-name)
|
||
(let* (;; 1. Get the path to the library file as Emacs knows it (e.g., .../straight/build/PKG/PKG.el)
|
||
(library-file-in-load-path (find-library-name package-name))
|
||
|
||
;; 2. Get the canonical, true path to this library file.
|
||
;; For straight.el, if library-file-in-load-path is a symlink like
|
||
;; .../build/PKG/PKG.el -> .../repos/PKG/PKG.el,
|
||
;; this will resolve to .../repos/PKG/PKG.el.
|
||
(true-library-file-path (when library-file-in-load-path
|
||
(file-truename library-file-in-load-path)))
|
||
|
||
;; 3. Get the directory containing the true library file.
|
||
;; This should be the actual source directory (e.g., .../straight/repos/PKG/).
|
||
(package-source-dir (when true-library-file-path
|
||
(file-name-directory true-library-file-path)))
|
||
|
||
;; 4. Locate the README within this source directory using your helper.
|
||
;; gjg/locate-readme-in-directory returns an absolute path.
|
||
(readme-full-path (when (and package-source-dir (file-directory-p package-source-dir))
|
||
(gjg/locate-readme-in-directory package-source-dir))))
|
||
|
||
(if readme-full-path
|
||
(with-temp-buffer
|
||
(goto-char (point-min))
|
||
(insert (format "README file for package %s (from directory %s):\n\n"
|
||
package-name
|
||
;; Show the directory where the README was actually found
|
||
(file-name-directory readme-full-path))) ; This is package-source-dir
|
||
(insert-file-contents readme-full-path)
|
||
(buffer-string))
|
||
(progn
|
||
;; More detailed warning for debugging
|
||
(warn (format (concat "No README file found for package '%s'.\n"
|
||
" Searched in directory: %s\n"
|
||
" Library file in load-path: %s\n"
|
||
" Resolved true library file path: %s")
|
||
package-name
|
||
(or package-source-dir "unknown or not a directory")
|
||
(or library-file-in-load-path "not found by find-library-name")
|
||
(or true-library-file-path "could not be resolved by file-truename")))
|
||
nil)))
|
||
(progn
|
||
(warn "PACKAGE-NAME argument must be the package name as a string.")
|
||
nil)))
|
||
|
||
;; readme for emacs package
|
||
|
||
(gptel-make-tool
|
||
:name "emacs_package_readme"
|
||
:description "Return the README file for a package - please use this to understand what a package does prior to deciding whether to pull the package source code."
|
||
:category "reflection"
|
||
:include t
|
||
:function #'gjg/extract-emacs-package-readme
|
||
:args (list '(:name "package_name"
|
||
:type string
|
||
:description "Name of the Emacs package."
|
||
:optional nil)))
|
||
#+end_src
|
||
***** Package Source
|
||
#+begin_src emacs-lisp
|
||
|
||
(defun gjg/extract-emacs-package-source (package &optional FULL)
|
||
"Extract and return a simple structured text document with the primary source for package PACKAGE.
|
||
If FULL is not nil, return all source files."
|
||
(if (stringp package)
|
||
(let* ((library-path (find-library-name package))
|
||
(all-elisp (directory-files (f-dirname library-path) t ".*\\.el\\(\\.gz\\)?$")))
|
||
(with-temp-buffer
|
||
;; run through the list of .el files
|
||
;; start with the "primary" .el file, returned by =find-library-name=
|
||
|
||
(goto-char (point-min))
|
||
(insert (format "\n* file %s (primary)\n" library-path))
|
||
(insert "#+begin_src emacs-lisp\n")
|
||
(insert-file library-path)
|
||
(goto-char (point-max))
|
||
(insert "\n#+end_src\n")
|
||
(when FULL
|
||
(dolist (file (remove library-path all-elisp))
|
||
(insert (format "\n* file %s\n" file))
|
||
(insert "#+begin_src emacs-lisp\n")
|
||
(insert-file file)
|
||
(goto-char (point-max))
|
||
(insert "\n#+end_src\n")))
|
||
(buffer-string)))
|
||
(warn "PACKAGE must be the package name as a string.")))
|
||
|
||
|
||
;; emacs package source code
|
||
|
||
(gptel-make-tool
|
||
:name "emacs_package_source"
|
||
:description "Fetch all source code for the given Emacs package, using `find-library'."
|
||
:category "reflection"
|
||
:include nil
|
||
:function #'gjg/extract-emacs-package-source
|
||
:args (list '(:name "package_name"
|
||
:type string
|
||
:description "Name of the Emacs package."
|
||
:optional nil)
|
||
'(:name "full_source"
|
||
:type boolean
|
||
:description "If true/t, return source from ALL source code files in the package, not just the primary source file. Let this default in most cases to save on tokens!"
|
||
:optional t)))
|
||
#+end_src
|
||
***** Function Source
|
||
#+begin_src emacs-lisp
|
||
|
||
(defun gjg/extract-function-source (function-name)
|
||
"Extract and return the source code of a callable FUNCTION-NAME, if possible."
|
||
(when (fboundp (intern function-name))
|
||
(let ((source))
|
||
(condition-case err
|
||
;; `find-function-noselect' returns the buffer and position.
|
||
(let ((buf-pos (find-function-noselect (intern function-name))))
|
||
(when buf-pos
|
||
(let ((buffer (car buf-pos))
|
||
(pos (cdr buf-pos)))
|
||
(with-current-buffer buffer
|
||
(save-excursion
|
||
(goto-char pos)
|
||
(let ((start (point)))
|
||
;; Move to the end of the function definition.
|
||
(end-of-defun)
|
||
;; Extract source code.
|
||
(setq source (buffer-substring-no-properties start (point)))))))))
|
||
(error
|
||
;; Handle errors gracefully by printing a message.
|
||
(message "Error finding source for %s: %s" function-name (error-message-string err))))
|
||
source)))
|
||
|
||
|
||
;; emacs function source code
|
||
|
||
(gptel-make-tool
|
||
:name "emacs_function_source"
|
||
:include t
|
||
:function #'gjg/extract-function-source
|
||
:description "Return source code for Emacs functions, be they written in Emacs Lisp (Elisp) or C"
|
||
:category "reflection"
|
||
:args (list '(:name "function_name"
|
||
:type string
|
||
:description "the function name"
|
||
:optional nil)))
|
||
#+end_src
|
||
***** Variable Properties
|
||
#+begin_src emacs-lisp
|
||
|
||
;; emacs variable info
|
||
|
||
(gptel-make-tool
|
||
:name "emacs_variable_properties"
|
||
:include t
|
||
:function (lambda (variable)
|
||
(let ((variable (if (symbolp variable)
|
||
variable
|
||
(intern variable)
|
||
)))
|
||
(if (boundp variable)
|
||
(symbol-plist variable)
|
||
(format "Variable %s is not bound" variable))))
|
||
|
||
:description "Get properties on an Emacs variable. Use this to get documentation string for variables, as well as more obscure properties such as safety as local variable etc."
|
||
:category "reflection"
|
||
:args (list '(:name "variable"
|
||
:type string
|
||
:description "the symbol")))
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
: #s(gptel-tool #[(variable) ((let ((variable (if (symbolp variable) variable (intern variable)))) (if (boundp variable) (symbol-plist variable) (format "Variable %s is not bound" variable)))) (t)] "emacs_variable_properties" "Get properties on an Emacs variable. Use this to get documentation string for variables, as well as more obscure properties such as safety as local variable etc." ((:name "variable" :type "string" :description "the symbol")) nil "reflection" nil t)
|
||
|
||
|
||
*** Load MCP.el
|
||
|
||
check [[https://www.youtube.com/watch?v=Hkih7jaqOnE&t=544s][Agentic LLM use in Emacs using Model Context Protocol (MCP)]] of
|
||
Gregg Grubbs for examples what to put here.
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle yes
|
||
(use-package mcp
|
||
:ensure t
|
||
:after gptel
|
||
:custom (mcp-hub-servers
|
||
`(("filesystem" . (:command "npx" :args ("-y" "@modelcontextprotocol/server-filesystem" "/home/pti/Projects/")))
|
||
("fetch" . (:command "uvx" :args ("mcp-server-fetch")))
|
||
;("qdrant" . (:url "http://localhost:8000/sse"))
|
||
("graphlit" . (
|
||
:command "npx"
|
||
:args ("-y" "graphlit-mcp-server")
|
||
:env (
|
||
:GRAPHLIT_ORGANIZATION_ID (auth-source-pass-get "organization-id" "snamellit/graphlit.dev")
|
||
:GRAPHLIT_ENVIRONMENT_ID (auth-source-pass-get "environment-id" "snamellit/graphlit.dev")
|
||
:GRAPHLIT_JWT_SECRET (auth-source-pass-get 'secret "snamellit/graphlit.dev"))))
|
||
("terminal-controller" . (
|
||
:command "uvx"
|
||
:args ("terminal_controller")))))
|
||
:config (require 'mcp-hub)
|
||
:hook (after-init . mcp-hub-start-all-server))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26780 48702 422697 nil elpaca-process-queues nil nil 640000 nil]
|
||
|
||
|
||
*** Load jwiegley/gptel-prompts for prompt composability
|
||
This package offers an alternative way to manage your ~gptel-directives~ variable, using files rather than customizing the variable directly.
|
||
|
||
Whenever your prompt files change, you must arrange for
|
||
~gptel-prompts-update~ to be called, which will update gptel-directives
|
||
appropriately.
|
||
|
||
#+begin_src emacs-lisp :tangle no
|
||
(use-package gptel-prompts
|
||
:ensure (:host github :repo "jwiegley/gptel-prompts")
|
||
:after (gptel)
|
||
:demand t
|
||
:config
|
||
(setq gptel-prompts-directory (concat user-emacs-directory "prompts"))
|
||
(gptel-prompts-update)
|
||
;; Ensure prompts are updated if prompt files change
|
||
(gptel-prompts-add-update-watchers))
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
: [nil 26781 40675 800401 nil elpaca-process-queues nil nil 577000 nil]
|
||
|
||
|
||
**** Different prompt types
|
||
|
||
You may now put prompts, one per file, in the directory
|
||
gptel-prompts-directory, which by default is
|
||
"~/.emacs.d/prompts". Prompts may be one of three different kinds:
|
||
Plain text
|
||
|
||
If the file extension is *.txt, .md or .org*, the content of the file is
|
||
used directly, as if you had added that string to ~gptel-directives~.
|
||
Emacs Lisp lists
|
||
|
||
If the file extension is *.el*, the file must evaluate to a list of
|
||
strings and/or symbols, as expected by ~gptel-directives~. Please see
|
||
the documentation of that variable for more information. Prompt Poet
|
||
templates
|
||
|
||
Based on the standard set by Prompt Poet, files ending in *.poet* or
|
||
*.jinja* will be interpreted as YAML files using Jinja templating. The
|
||
templating is applied first, before it is parsed as a Yaml file.
|
||
|
||
This is done dynamically, at the time the prompt is used, so you can
|
||
see the results of your expansion using GPTel’s Inspect capabilities
|
||
when ~gptel-expert-commands~ is set to a non-nil value. Here is an
|
||
example poet prompt.:
|
||
|
||
#+begin_src
|
||
- role: system
|
||
content: >-
|
||
You are an Latin-American Spanish translator, spelling corrector and
|
||
improver. I will speak to you in English, and you will translate and
|
||
answer in the corrected and improved version of my text, in Latin-American
|
||
Spanish. I want you to replace my simplified A0-level words and sentences
|
||
with more beautiful and elegant, upper level Latin-American Spanish words
|
||
and sentences. Keep the meaning same, but make them more literary and
|
||
clear. I want you to only reply with the correction, the improvements and
|
||
nothing else, do not write explanations.
|
||
|
||
- role: user
|
||
content: |
|
||
Please translate the following into Spanish. The time is {{
|
||
current_time }}:
|
||
#+end_src
|
||
|
||
Note the ~>-~ and ~|~ content directives, which are used to manage when
|
||
and where newlines appear in the actual prompts, while allowing the
|
||
file itself to use what is easiest to maintain in the editor.
|
||
|
||
NOTE: If you wish to use the Prompt Poet format, you will need to
|
||
install the Emacs dependencies yaml and templatel.
|
||
|
||
|
||
|
||
** Copilot Support
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Programming - Copilot Support")
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(use-package copilot
|
||
:ensure (copilot :host github :repo "zerolfx/copilot.el"
|
||
:branch "main"
|
||
:files ("dist" "*.el"))
|
||
:bind ("C-S-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))
|
||
:custom
|
||
(copilot-indent-offset-warning-disabled t "Disable indent offset warning" )
|
||
|
||
:hook
|
||
(prog-mode . copilot-mode)
|
||
(org-mode . copilot-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26506 39443 528692 nil elpaca-process-queues nil nil 667000 nil]
|
||
|
||
** SuperMaven Support
|
||
|
||
There is a lot of positive hubbub around [[https://github.com/crazywolf132/supermaven.el][SuperMaven]].
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(use-package supermaven
|
||
:ensure (supermaven :host github :repo "crazywolf132/supermaven.el"
|
||
:branch "main")
|
||
:init
|
||
(setq supermaven-ignore-filetypes '("org" "txt"))
|
||
(setq supermaven-disable-inline-completion nil)
|
||
(setq supermaven-keymaps
|
||
'((accept-suggestion . "TAB")
|
||
(clear-suggestion . "C-]")
|
||
(accept-word . "C-j")))
|
||
(setq supermaven-log-level 'debug)
|
||
:hook (prog-mode . supermaven-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26528 36711 407251 nil elpaca-process-queues nil nil 656000 nil]
|
||
|
||
|
||
<2025-01-28 Tue> Tried it out but did not work. I got following
|
||
errors :
|
||
|
||
|
||
#+BEGIN_SRC fundamental
|
||
[2025-01-28 14:47:15] [INFO] Supermaven process started successfully
|
||
[2025-01-28 14:47:17] [INFO] Stopping Supermaven process...
|
||
[2025-01-28 14:47:17] [INFO] Supermaven process killed
|
||
|
||
[2025-01-28 14:47:17] [INFO] Supermaven process started successfully
|
||
[2025-01-28 14:47:19] [INFO] Stopping Supermaven process...
|
||
[2025-01-28 14:47:19] [INFO] Supermaven process killed
|
||
|
||
[2025-01-28 14:47:19] [INFO] Supermaven process started successfully
|
||
[2025-01-28 14:47:21] [INFO] Stopping Supermaven process...
|
||
[2025-01-28 14:47:21] [INFO] Supermaven process killed
|
||
|
||
[2025-01-28 14:47:21] [INFO] Supermaven process started successfully
|
||
[2025-01-28 14:47:21] [INFO] Stopping Supermaven process...
|
||
[2025-01-28 14:47:21] [INFO] Supermaven process killed
|
||
|
||
[2025-01-28 14:47:21] [INFO] Supermaven process started successfully
|
||
[2025-01-28 14:47:21] [INFO] Supermaven process started successfully
|
||
[2025-01-28 14:47:22] [INFO] Supermaven process hangup
|
||
#+END_SRC
|
||
|
||
In a separate terminal I see the pid change every 2 seconds. It seems
|
||
to work fine in vscode. +Not tested in neovim yet+. It works just fine
|
||
in Neovim.
|
||
|
||
Supermaven requires company support at the moment. There is a [[https://github.com/crazywolf132/supermaven.el/issues/12][Feature
|
||
Request open]] to add corfu support.
|
||
|
||
** Gemini Code Completion
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package google-gemini
|
||
:ensure (google-gemini
|
||
:host github
|
||
:repo "emacs-openai/google-gemini")
|
||
:config
|
||
(setq google-gemini-key gemini-api-key)
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26546 33151 856622 nil elpaca-process-queues nil nil 426000 nil]
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package gemini-code-completion
|
||
:ensure (gemini-code-completion
|
||
:host github
|
||
:repo "shishimaru/gemini-code-completion.el"
|
||
:files ("*.el"))
|
||
:bind
|
||
("C-c g" . gemini-code-completion)
|
||
:config
|
||
;;https://github.com/shishimaru/gemini-code-completion.el/issues/13
|
||
(setq gemini-code-completion-min-number-of-candidates 1)
|
||
(setq gemini-code-completion-disable-completion-keybinding t)
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26525 26586 616591 nil elpaca-process-queues nil nil 615000 nil]
|
||
|
||
** Aider Support
|
||
|
||
Aider is a package which works as a pair programming partner to
|
||
generate code.
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no)
|
||
(use-package aidermacs
|
||
:ensure (:host github :repo "MatthewZMD/aidermacs" :files ("*.el"))
|
||
:config
|
||
;; Use claude-3-5-sonnet cause it is best in aider benchmark
|
||
;;(setq aider-args '("--model" "anthropic/claude-3-5-sonnet-20241022"))
|
||
;;(setenv "ANTHROPIC_API_KEY" anthropic-api-key)
|
||
;; Or use chatgpt model since it is most well known
|
||
;; (setq aider-args '("--model" "gpt-4o-mini"))
|
||
;; (setenv "OPENAI_API_KEY" <your-openai-api-key>)
|
||
;; Or use gemini v2 model since it is very good and free
|
||
(setq aidermacs-default-model "gemini-2.0-flash-thinking-exp-01-21")
|
||
(setenv "GEMINI_API_KEY" google-gemini-key)
|
||
(setq aidermacs-auto-commits t)
|
||
(setq aidermacs-use-architect-mode t)
|
||
(setq aidermacs-backend 'vterm)
|
||
;;
|
||
;; Optional: Set a key binding for the transient menu
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26557 35519 420170 nil elpaca-process-queues nil nil 238000 nil]
|
||
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package aider
|
||
:ensure t
|
||
:config
|
||
(setq aider-program "~/.local/bin/aider")
|
||
;; For latest claude sonnet model
|
||
(setq aider-args '("--model" "sonnet" "--no-auto-accept-architect")) ;; add --no-auto-commits if you don't want it
|
||
(setenv "ANTHROPIC_API_KEY" anthropic-api-key)
|
||
;; Or chatgpt model
|
||
;; (setq aider-args '("--model" "o4-mini"))
|
||
;; (setenv "OPENAI_API_KEY" <your-openai-api-key>)
|
||
;; Or use your personal config file
|
||
;; (setq aider-args `("--config" ,(expand-file-name "~/.aider.conf.yml")))
|
||
;; ;;
|
||
;; Optional: Set a key binding for the transient menu
|
||
(global-set-key (kbd "C-c a") 'aider-transient-menu) ;; for wider screen
|
||
;; or use aider-transient-menu-2cols / aider-transient-menu-1col, for narrow screen
|
||
(aider-magit-setup-transients) ;; add aider magit function to magit menu
|
||
;; auto revert buffer
|
||
(global-auto-revert-mode 1)
|
||
(auto-revert-mode 1))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26780 39804 90744 nil elpaca-process-queues nil nil 316000 nil]
|
||
|
||
|
||
** 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 (:wait t)
|
||
:commands (magit-status)
|
||
:bind
|
||
(("C-x p v" . magit-status)
|
||
("<leader> p v" . magit-status)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26709 8564 25469 nil elpaca-process-queues nil nil 123000 nil]
|
||
|
||
** 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
|
||
|
||
Configure ibuffer support with project contexts, courtesy of crafted
|
||
emacs
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
;;; enhance ibuffer with ibuffer-project if it is available.
|
||
(defun snm-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 . snm-ide-enhance-ibuffer-with-ibuffer-project)
|
||
:bind (("C-x C-b" . #'ibuffer-other-window)))
|
||
#+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
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package embark
|
||
:ensure t
|
||
:bind
|
||
(("C-." . embark-act)
|
||
("C-;" . embark-dwim)
|
||
("C-h B" . embark-bindings))
|
||
:config
|
||
(setq prefix-help-command #'embark-prefix-help-command))
|
||
|
||
;; Consult users will also want the embark-consult package.
|
||
(use-package embark-consult
|
||
:ensure t ; only need to install it, embark loads it after consult if found
|
||
:hook
|
||
(embark-collect-mode . consult-preview-at-point-mode))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26502 58499 164859 nil elpaca-process-queues nil nil 179000 nil]
|
||
|
||
** Configure Helpful
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package helpful
|
||
:ensure t
|
||
:bind (
|
||
( "<remap> <describe-command>" . #'helpful-command)
|
||
( "<remap> <describe-function>" . #'helpful-callable)
|
||
( "<remap> <describe-key>" . #'helpful-key)
|
||
( "<remap> <describe-symbol>" . #'helpful-symbol)
|
||
( "<remap> <describe-variable>" . #'helpful-variable)
|
||
( "C-h F" . #'helpful-function)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26432 30485 62583 nil elpaca-process-queues nil nil 114000 nil]
|
||
|
||
** Add emacslisp-demos
|
||
|
||
A package which enhances helpful by adding API demos to the the help
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package elisp-demos
|
||
:ensure t
|
||
:init
|
||
(advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26745 27316 824254 nil elpaca-process-queues nil nil 497000 nil]
|
||
|
||
|
||
** Enable breadcrumbs
|
||
|
||
Show breadcrumbs in the header line to keep context of the file being
|
||
worked on. See the [[https://github.com/joaotavora/breadcrumb][breadcrumb repo]] for more details.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package breadcrumb
|
||
:ensure t
|
||
:init
|
||
(breadcrumb-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26432 31533 350342 nil elpaca-process-queues nil nil 899000 nil]
|
||
|
||
** Enable Avy jumping
|
||
|
||
Avy mode replace ace-jump mode with a lot of functionality hidden
|
||
|
||
see [[https://karthinks.com/software/avy-can-do-anything/][Avy can Do Anything]] article by the author on the design philosophy
|
||
behind it.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package avy
|
||
:ensure t
|
||
:commands (avy-goto-char
|
||
avy-goto-char-2
|
||
avy-goto-char-2-above
|
||
avy-goto-char-2-below
|
||
avy-goto-char-in-line
|
||
avy-goto-char-timer
|
||
avy-goto-line
|
||
avy-goto-line-above
|
||
avy-goto-line-below
|
||
avy-goto-subword-0
|
||
avy-goto-subword-1
|
||
avy-goto-symbol-1
|
||
avy-goto-symbol-1-above
|
||
avy-goto-symbol-1-below
|
||
avy-goto-word-0
|
||
avy-goto-word-1
|
||
avy-goto-word-1-above
|
||
avy-goto-word-1-below
|
||
avy-goto-word-or-subword-1)
|
||
:bind
|
||
(("C-:" . avy-goto-char-timer)
|
||
("M-g w" . avy-goto-word-0)
|
||
("M-g f" . avy-goto-line))
|
||
:config
|
||
(avy-setup-default) ;; setup C-' to jump with isearch
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26507 37720 141708 nil elpaca-process-queues nil nil 278000 nil]
|
||
|
||
#+RESULTS:
|
||
|
||
** Ace Window Configuration
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package ace-window
|
||
:ensure t
|
||
:bind
|
||
("C-x o" . ace-window)
|
||
:config
|
||
(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
|
||
(setq aw-dispatch-always nil))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26523 35809 462186 nil elpaca-process-queues nil nil 958000 nil]
|
||
|
||
** Command Log Mode
|
||
|
||
surprising sequence of events happened. Command Log Mode is a package
|
||
that records all the commands that are executed in the buffer and
|
||
provides a way to replay them.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package command-log-mode
|
||
:ensure t
|
||
:commands (command-log-mode))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26503 37477 396927 nil elpaca-process-queues nil nil 128000 nil]
|
||
|
||
** Abbrev Files
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package abbrev
|
||
:ensure nil
|
||
:config
|
||
(setq abbrev-file-name (expand-file-name "abbrev_defs" user-emacs-directory))
|
||
(setq save-abbrevs 'silently)
|
||
(if (file-exists-p abbrev-file-name)
|
||
(quietly-read-abbrev-file)))
|
||
#+END_SRC
|
||
|
||
* Communication and Interwebs
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(report-time-since-load "Communication and Interwebs")
|
||
#+END_SRC
|
||
|
||
** ERC configuration :disabled:
|
||
Set up ERC, an IRC client for Emacs, to automatically join specific channels and log in:
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(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 :tangle no
|
||
(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*.
|
||
o longer useful : I seldom use i3, but it is useful for
|
||
inspiration
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(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
|
||
|
||
|
||
** rcirc configuration
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package rcirc
|
||
|
||
:config
|
||
(setopt
|
||
rcirc-server-alist
|
||
(let ((data (auth-source-pass-parse-entry "snamellit/znc")))
|
||
`((,(auth-source-pass--get-attr "server" data)
|
||
:port ,(auth-source-pass--get-attr "port" data)
|
||
:encryption tls
|
||
:server-alias "znc"
|
||
:nick ,(auth-source-pass--get-attr "nick" data)
|
||
:user-name ,(concat
|
||
(auth-source-pass--get-attr "user" data)
|
||
"@"
|
||
(car (string-split (system-name) "\\.")))
|
||
:password ,(auth-source-pass--get-attr 'secret data)
|
||
:channels '("#emacs"
|
||
"#erc"
|
||
"#spritely"
|
||
"#guix"
|
||
"#systemcrafters"
|
||
"#systemcrafters-guix"
|
||
"#systemcrafters-emacs"
|
||
)))))
|
||
(setopt rcirc-reconnect-delay 15)
|
||
(setopt rcirc-dim-nicks '("Marvin2"))
|
||
(setopt rcirc-bright-nicks '("daviwil"
|
||
"benoitj"
|
||
"Fade"
|
||
"trev"
|
||
"shom"
|
||
"alternateved"
|
||
"dthompson"))
|
||
(rcirc-track-minor-mode))
|
||
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: t
|
||
|
||
|
||
** 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
|
||
(opml-to-elfeed-feeds-update-o2e-elfeed-feeds)
|
||
)
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: [nil 26750 42629 851189 nil elpaca-process-queues nil nil 224000 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
|
||
:disabled
|
||
:ensure t
|
||
:config (setq 0x0-use-curl t))
|
||
|
||
(defun 0x0-upload-text ()
|
||
(interactive)
|
||
(let* ((contents
|
||
(if (use-region-p)
|
||
(buffer-substring-no-properties (region-beginning) (region-end))
|
||
(buffer-string)))
|
||
(temp-file (make-temp-file "0x0" nil ".txt" contents)))
|
||
(message "Sending %s to 0x0.st..." temp-file)
|
||
(let ((url (string-trim-right
|
||
(shell-command-to-string
|
||
(format "curl -s -F'file=@%s' -Fsecret= -Fexpires=24 https://0x0.st"
|
||
temp-file)))))
|
||
(message "Yanked â%sâ into kill ring." url)
|
||
(kill-new url)
|
||
(delete-file temp-file))))
|
||
|
||
(defun 0x0-upload-file (file-path)
|
||
(interactive "fSelect a file to upload: ")
|
||
(message "Sending %s to 0x0.st..." file-path)
|
||
(let ((url (string-trim-right
|
||
(shell-command-to-string
|
||
(format "curl -s -F'file=@%s' -Fsecret= -Fexpires=24 https://0x0.st"
|
||
(expand-file-name file-path))))))
|
||
(message "Yanked â%sâ into kill ring." url)
|
||
(kill-new url)))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: +0x0-upload-file
|
||
|
||
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.
|
||
|
||
** Systemcrafters live stream
|
||
|
||
|
||
#+BEGIN_SRC elisp
|
||
(defun systemcrafters ()
|
||
"Watch SystemCrafters on Twitch"
|
||
(interactive)
|
||
(message "Watching SystemCrafters on Twitch...")
|
||
(start-process "livestream" "*livestream*"
|
||
"mpv"
|
||
"--quiet"
|
||
"https://twitch.tv/SystemCrafters"))
|
||
#+END_SRC
|
||
|
||
#+RESULTS:
|
||
: systemcrafters
|
||
|
||
|
||
* 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
|
||
|
||
** DONE Add support for zola blogposts writing in org-mode
|
||
CLOSED: [2025-01-20 Mon 10:28]
|
||
: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:
|