My Emacs Applications Config
My personal Emacs Config, 2021-07-05T11:00:00+02:00Table of Contents
Applications
Calendar Stuff
;; How to use:
;; 1. add `(load "/path/to/next-spec-day")` to your dot emacs file.
;; 2. set `NEXT-SPEC-DEADLINE` and/or `NEXT-SPEC-SCHEDULED` property of a TODO task,like this:
;; * TODO test
;; SCHEDULED: <2013-06-16 Sun> DEADLINE: <2012-12-31 Mon -3d>
;; :PROPERTIES:
;; :NEXT-SPEC-DEADLINE: (= (calendar-extract-day date) (calendar-last-day-of-month (calendar-extract-month date) (calendar-extract-year date)))
;; :NEXT-SPEC-SCHEDULED: (org-float 6 0 3)
;; :END:
;; The value of NEXT-SPEC-DEADLINE will return `non-nil` if `date` is last day of month,and the value of NEXT-SPEC-SCHEDULED will return `non-nil` if `date` is the fathers' day(the third Sunday of June).
;; 3. Then,when you change the TODO state of that tasks,the timestamp will be changed automatically(include lead time of warnings settings).
;; Notes:e
;; 1. Execute `(setq next-spec-day-runningp nil)' after your sexp signal some erros,
;; 2. You can also use some useful sexp from next-spec-day-alist,like:
;; * TODO test
;; SCHEDULED: <2013-03-29 Fri>
;; :PROPERTIES:
;; :NEXT-SPEC-SCHEDULED: last-workday-of-month
;; :END:
;; 3. If you encounter some errors like 'org-insert-time-stamp: Wrong type argument: listp, "<2013-03-29 星期五>"' when change the TODO state,please try a new version of org mode.To use the new version:
;; (1). download the new version of org mode from orgmode.org,then uncompress it.
;; (2). add (add-to-list 'load-path "/path/to/org-*.*.*/lisp") to your .emacs file,make sure it's before any (require 'org).If you are not sure,just insert it to the first line of your dot emacs file.
;; (3). restart your emacs,everything should be fine.
(eval-when-compile (require 'cl))
(defvar next-spec-day-runningp)
(setq next-spec-day-runningp nil)
(defvar next-spec-day-alist
'((last-workday-of-month
.
((or
(and (= (calendar-last-day-of-month m y) d) (/= (calendar-day-of-week date) 0) (/= (calendar-day-of-week date) 6))
(and (< (- (calendar-last-day-of-month m y) d) 3) (string= (calendar-day-name date) "Friday")))))
(last-day-of-month
.
((= (calendar-extract-day date) (calendar-last-day-of-month (calendar-extract-month date) (calendar-extract-year date)))))
(fathers-day
.
((org-float 6 0 3))))
"contain some useful sexp")
(defun next-spec-day ()
(unless next-spec-day-runningp
(setq next-spec-day-runningp t)
(catch 'exit
(dolist (type '("NEXT-SPEC-DEADLINE" "NEXT-SPEC-SCHEDULED"))
(when (stringp (org-entry-get nil type))
(let* ((time (org-entry-get nil (substring type (length "NEXT-SPEC-"))))
(pt (if time (org-parse-time-string time) (decode-time (current-time))))
(func (ignore-errors (read-from-whole-string (org-entry-get nil type)))))
(unless func (message "Sexp is wrong") (throw 'exit nil))
(when (symbolp func)
(setq func (cadr (assoc func next-spec-day-alist))))
(cl-incf (nth 3 pt))
(setf pt (decode-time (apply 'encode-time pt)))
(cl-do ((i 0 (1+ i)))
((or
(> i 1000)
(let* ((d (nth 3 pt))
(m (nth 4 pt))
(y (nth 5 pt))
(date (list m d y))
entry)
(eval func)))
(if (> i 1000)
(message "No satisfied in 1000 days")
(funcall
(if (string= "NEXT-SPEC-DEADLINE" type)
'org-deadline
'org-schedule)
nil
(format-time-string
(if (and
time
(string-match
"[[:digit:]]\\{2\\}:[[:digit:]]\\{2\\}"
time))
(cdr org-time-stamp-formats)
(car org-time-stamp-formats))
(apply 'encode-time pt)))))
(cl-incf (nth 3 pt))
(setf pt (decode-time (apply 'encode-time pt)))))))
(if (or
(org-entry-get nil "NEXT-SPEC-SCHEDULED")
(org-entry-get nil "NEXT-SPEC-DEADLINE"))
(org-entry-put nil "TODO" (car org-todo-heads))))
(setq next-spec-day-runningp nil)))
(add-hook 'org-after-todo-state-change-hook 'next-spec-day)
(unless (fboundp 'read-from-whole-string) (require 'thingatpt))
(unless (fboundp 'calendar-last-day-of-month) (require 'thingatpt))
Denote
Denote aims to be a simple-to-use, focused-in-scope, and effective note-taking tool for Emacs. It is based on the following core design principles:
- Predictability
- File names must follow a consistent and descriptive naming convention (The file-naming scheme). The file name alone should offer a clear indication of what the contents are, without reference to any other metadatum. This convention is not specific to note-taking, as it is pertinent to any form of file that is part of the user’s long-term storage (Renaming files).
- Composability
- Be a good Emacs citizen, by integrating with other packages or built-in functionality instead of re-inventing functions such as for filtering or greping. The author of Denote (Protesilaos, aka “Prot”) writes ordinary notes in plain text (
.txt), switching on demand to an Org file only when its expanded set of functionality is required for the task at hand (Points of entry). - Portability
- Notes are plain text and should remain portable. The way Denote writes file names, the front matter it includes in the note’s header, and the links it establishes must all be adequately usable with standard Unix tools. No need for a database or some specialised software. As Denote develops and this manual is fully fleshed out, there will be concrete examples on how to do the Denote-equivalent on the command-line.
- Flexibility
- Do not assume the user’s preference for a note-taking methodology. Denote is conceptually similar to the Zettelkasten Method, which you can learn more about in this detailed introduction: https://zettelkasten.de/introduction/. Notes are atomic (one file per note) and have a unique identifier. However, Denote does not enforce a particular methodology for knowledge management, such as a restricted vocabulary or mutually exclusive sets of keywords. Denote also does not check if the user writes thematically atomic notes. It is up to the user to apply the requisite rigor and/or creativity in pursuit of their preferred workflow (Writing metanotes).
- Hackability
- Denote’s code base consists of small and reusable functions. They all have documentation strings. The idea is to make it easier for users of varying levels of expertise to understand what is going on and make surgical interventions where necessary (e.g. to tweak some formatting). In this manual, we provide concrete examples on such user-level configurations (Keep a journal or diary).
(require 'denote)
;; Remember to check the doc strings of those variables.
(setq denote-directory (expand-file-name "~/wiki/"))
(setq denote-known-keywords '("emacs" "philosophy" "politics" "economics"))
(setq denote-infer-keywords t)
(setq denote-sort-keywords t)
(setq denote-file-type 'org) ; Org is the default, set others here
(setq denote-prompts '(title keywords))
;; Pick dates, where relevant, with Org's advanced interface:
(setq denote-date-prompt-use-org-read-date t)
;; Read this manual for how to specify `denote-templates'. We do not
;; include an example here to avoid potential confusion.
;; We allow multi-word keywords by default. The author's personal
;; preference is for single-word keywords for a more rigid workflow.
(setq denote-allow-multi-word-keywords 0)
(setq denote-date-format nil) ; read doc string
;; By default, we fontify backlinks in their bespoke buffer.
(setq denote-link-fontify-backlinks t)
;; Also see `denote-link-backlinks-display-buffer-action' which is a bit
;; advanced.
(setq denote-link-backlinks-display-buffer-action
'((display-buffer-reuse-window
display-buffer-in-side-window)
(side . right)
(slot . 99)
(window-width . 0.3)))
;; If you use Markdown or plain text files (Org renders links as buttons
;; right away)
;; (add-hook 'find-file-hook #'denote-link-buttonize-buffer)
;; We use different ways to specify a path for demo purposes.
(setq denote-dired-directories
(list denote-directory
(thread-last denote-directory (expand-file-name "attachments"))
(expand-file-name "~/Documents/books")))
;; Generic (great if you rename files Denote-style in lots of places):
;; (add-hook 'dired-mode-hook #'denote-dired-mode)
;;
;; OR if only want it in `denote-dired-directories':
(add-hook 'dired-mode-hook #'denote-dired-mode-in-directories)
;; Here is a custom, user-level command from one of the examples we
;; showed in this manual. We define it here and add it to a key binding
;; below.
(defun my-denote-journal ()
"Create an entry tagged 'journal', while prompting for a title."
(interactive)
(denote
(denote--title-prompt)
'("journal")))
;; Denote DOES NOT define any key bindings. This is for the user to
;; decide. For example:
(let ((map global-map))
(define-key map (kbd "C-c n j") #'my-denote-journal) ; our custom command
(define-key map (kbd "C-c n n") #'denote)
(define-key map (kbd "C-c n N") #'denote-type)
(define-key map (kbd "C-c n d") #'denote-date)
(define-key map (kbd "C-c n s") #'denote-subdirectory)
(define-key map (kbd "C-c n t") #'denote-template)
;; If you intend to use Denote with a variety of file types, it is
;; easier to bind the link-related commands to the `global-map', as
;; shown here. Otherwise follow the same pattern for `org-mode-map',
;; `markdown-mode-map', and/or `text-mode-map'.
(define-key map (kbd "C-c n i") #'denote-link) ; "insert" mnemonic
(define-key map (kbd "C-c n I") #'denote-link-add-links)
(define-key map (kbd "C-c n l") #'denote-link-find-file) ; "list" links
(define-key map (kbd "C-c n b") #'denote-link-backlinks)
;; Note that `denote-rename-file' can work from any context, not just
;; Dired bufffers. That is why we bind it here to the `global-map'.
(define-key map (kbd "C-c n r") #'denote-rename-file)
(define-key map (kbd "C-c n R") #'denote-rename-file-using-front-matter))
;; Key bindings specifically for Dired.
(let ((map dired-mode-map))
(define-key map (kbd "C-c C-d C-i") #'denote-link-dired-marked-notes)
(define-key map (kbd "C-c C-d C-r") #'denote-dired-rename-marked-files)
(define-key map (kbd "C-c C-d C-R") #'denote-dired-rename-marked-files-using-front-matter))
;; (with-eval-after-load 'org-capture
;; (setq denote-org-capture-specifiers "%l\n%i\n%?")
;; (add-to-list 'org-capture-templates
;; '("n" "New note (with denote.el)" plain
;; (file denote-last-path)
;; #'denote-org-capture
;; :no-save t
;; :immediate-finish nil
;; :kill-buffer t
;; :jump-to-captured t)))
;; Also check the commands `denote-link-after-creating',
;; `denote-link-or-create'. You may want to bind them to keys as well.
(defvar my-denote-chordpro-front-matter
"#!/bin/sh
# title: %s
# date: %s
# tags: %s
# identifier: %s
\n"
"Demo shell script front matter.
It is passed to `format' with arguments TITLE, DATE, KEYWORDS,
ID. Advanced users are advised to consult Info node `(denote)
Change the front matter format'.")
(add-to-list 'denote-file-types
'(ChordPro
:extension ".cho"
:date-function denote-date-org-timestamp
:front-matter denote-org-front-matter
:title-key-regexp "^#\\+title\\s-*:"
:title-value-function identity
:title-value-reverse-function denote-trim-whitespace
:keywords-key-regexp "^#\\+filetags\\s-*:"
:keywords-value-function denote-format-keywords-for-org-front-matter
:keywords-value-reverse-function denote-extract-keywords-from-front-matter
:link denote-org-link-format
:link-in-context-regexp denote-org-link-in-context-regexp)
)
(define-minor-mode denote-mode
"Denote is a simple note-taking
tool for Emacs. It is based on the idea that notes should follow
a predictable and descriptive file-naming scheme. The file name
must offer a clear indication of what the note is about, without
reference to any other metadata. Denote basically streamlines
the creation of such files while providing facilities to link
between them.
Denote's file-naming scheme is not limited to notes. It can be used
for all types of file, including those that are not editable in
Emacs, such as videos. Naming files in a constistent way makes
their filtering and retrieval considerably easier. Denote
provides relevant facilities to rename files, regardless of file
type."
:lighter " Note"
:keymap (let ((map
(make-sparse-keymap)))
(define-key map (kbd "C-l") 'denote-link-or-create)
(define-key map (kbd "C-n") 'denote-link-after-creating)
(define-key map (kbd "<f6>")(lambda () (interactive) (find-file "~/wiki")))
map))
Kagi
(require 'kagi)
;; or use a function, e.g. with the password-store package:
(setq kagi-api-token (lambda () (password-store-get "Kagi/API")))
;; Universal Summarizer settings
(setq kagi-summarizer-engine "cecil")
(setq kagi-summarizer-default-language "DE")
(setq kagi-summarizer-cache t)
Nikola
(require 'org)
(require 'ox-html)
(require 'denote)
;;; Custom configuration for the export.
;;; Add any custom configuration that you would like to 'conf.el'.
(setq nikola-use-pygments t
org-export-with-toc nil
org-export-with-section-numbers nil
org-startup-folded 'showeverything)
;; Load additional configuration from conf.el
(let ((conf (expand-file-name "conf.el" (file-name-directory load-file-name))))
(if (file-exists-p conf)
(load-file conf)))
;;; Macros
;; Load Nikola macros
(setq nikola-macro-templates
(with-current-buffer
(find-file
(expand-file-name "macros.org" (file-name-directory load-file-name)))
(org-macro--collect-macros)))
;;; Code highlighting
(defun org-html-decode-plain-text (text)
"Convert HTML character to plain TEXT. i.e. do the inversion of
`org-html-encode-plain-text`. Possible conversions are set in
`org-html-protect-char-alist'."
(mapc
(lambda (pair)
(setq text (replace-regexp-in-string (cdr pair) (car pair) text t t)))
(reverse org-html-protect-char-alist))
text)
;; Use pygments highlighting for code
(defun pygmentize (lang code)
"Use Pygments to highlight the given code and return the output"
(with-temp-buffer
(insert code)
(let ((lang (or (cdr (assoc lang org-pygments-language-alist)) "text")))
(shell-command-on-region (point-min) (point-max)
(format "pygmentize -f html -l %s" lang)
(buffer-name) t))
(buffer-string)))
(defconst org-pygments-language-alist
'(("asymptote" . "asymptote")
("awk" . "awk")
("c" . "c")
("c++" . "cpp")
("cpp" . "cpp")
("clojure" . "clojure")
("css" . "css")
("d" . "d")
("emacs-lisp" . "scheme")
("F90" . "fortran")
("gnuplot" . "gnuplot")
("groovy" . "groovy")
("haskell" . "haskell")
("java" . "java")
("js" . "js")
("julia" . "julia")
("latex" . "latex")
("lisp" . "lisp")
("makefile" . "makefile")
("matlab" . "matlab")
("mscgen" . "mscgen")
("ocaml" . "ocaml")
("octave" . "octave")
("perl" . "perl")
("picolisp" . "scheme")
("python" . "python")
("r" . "r")
("ruby" . "ruby")
("sass" . "sass")
("scala" . "scala")
("scheme" . "scheme")
("sh" . "sh")
("sql" . "sql")
("sqlite" . "sqlite3")
("tcl" . "tcl"))
"Alist between org-babel languages and Pygments lexers.
lang is downcased before assoc, so use lowercase to describe language available.
See: http://orgmode.org/worg/org-contrib/babel/languages.html and
http://pygments.org/docs/lexers/ for adding new languages to the mapping.")
;; Override the html export function to use pygments
(defun org-html-src-block (src-block contents info)
"Transcode a SRC-BLOCK element from Org to HTML.
CONTENTS holds the contents of the item. INFO is a plist holding
contextual information."
(if (org-export-read-attribute :attr_html src-block :textarea)
(org-html--textarea-block src-block)
(let ((lang (org-element-property :language src-block))
(code (org-element-property :value src-block))
(code-html (org-html-format-code src-block info)))
(if nikola-use-pygments
(progn
(unless lang (setq lang ""))
(pygmentize (downcase lang) (org-html-decode-plain-text code)))
code-html))))
;; Export images with custom link type
(defun org-custom-link-img-url-export (path desc format)
(cond
((eq format 'html)
(format "<img src=\"%s\" alt=\"%s\"/>" path desc))))
(org-add-link-type "img-url" nil 'org-custom-link-img-url-export)
;; Export images with built-in file scheme
(defun org-file-link-img-url-export (path desc format)
(cond
((eq format 'html)
(format "<img src=\"/%s\" alt=\"%s\"/>" path desc))))
(org-add-link-type "file" nil 'org-file-link-img-url-export)
;; Support for magic links (link:// scheme)
(org-link-set-parameters
"link"
:export (lambda (path desc backend)
(cond
((eq 'html backend)
(format "<a href=\"link:%s\">%s</a>"
path (or desc path))))))
;; Export function used by Nikola.
(defun nikola-html-export (infile outfile)
"Export the body only of the input file and write it to
specified location."
(with-current-buffer (find-file infile)
(org-macro-replace-all nikola-macro-templates)
(org-html-export-as-html nil nil t t)
(write-file outfile nil)))
(defun org-html-headline (headline contents info)
"Transcode a HEADLINE element from Org to HTML.
CONTENTS holds the contents of the headline. INFO is a plist
holding contextual information."
(unless (org-element-property :footnote-section-p headline)
(let* ((numberedp (org-export-numbered-headline-p headline info))
(numbers (org-export-get-headline-number headline info))
(level (+ (org-export-get-relative-level headline info)
(1- (plist-get info :html-toplevel-hlevel))))
(todo (and (plist-get info :with-todo-keywords)
(let ((todo (org-element-property :todo-keyword headline)))
(and todo (org-export-data todo info)))))
(todo-type (and todo (org-element-property :todo-type headline)))
(priority (and (plist-get info :with-priority)
(org-element-property :priority headline)))
(text (org-export-data (org-element-property :title headline) info))
(tags (and (plist-get info :with-tags)
(org-export-get-tags headline info)))
(full-text (funcall (plist-get info :html-format-headline-function)
todo todo-type priority text tags info))
(contents (or contents ""))
(id (org-html--reference headline info))
(formatted-text
(if (plist-get info :html-self-link-headlines)
(format "<a href=\"#%s\">%s</a>" id full-text)
full-text)))
(if (org-export-low-level-p headline info)
;; This is a deep sub-tree: export it as a list item.
(let* ((html-type (if numberedp "ol" "ul")))
(concat
(and (org-export-first-sibling-p headline info)
(apply #'format "<%s class=\"org-%s\">\n"
(make-list 2 html-type)))
(org-html-format-list-item
contents (if numberedp 'ordered 'unordered)
nil info nil
(concat (org-html--anchor id nil nil info) formatted-text)) "\n"
(and (org-export-last-sibling-p headline info)
(format "</%s>\n" html-type))))
;; Standard headline. Export it as a section.
(let ((extra-class
(org-element-property :HTML_CONTAINER_CLASS headline))
(headline-class
(org-element-property :HTML_HEADLINE_CLASS headline))
(first-content (car (org-element-contents headline))))
(format "%s<%s id=\"%s\" class=\"%s collapsme\">\n%s</%s>\n"
(format "<h%d class=\"collapsible\" id=\"%s\"%s>%s</h%d>\n"
level
id
(if (not headline-class) ""
(format " class=\"%s\"" headline-class))
(concat
(and numberedp
(format
"<span class=\"section-number-%d\">%s</span> "
level
(concat (mapconcat #'number-to-string numbers ".") ".")))
formatted-text)
level)
(org-html--container headline info)
(format "outline-container-%s" id)
(concat (format "outline-%d" level)
(and extra-class " ")
extra-class)
;; When there is no section, pretend there is an
;; empty one to get the correct <div
;; class="outline-...> which is needed by
;; `org-info.js'.
(if (eq (org-element-type first-content) 'section) contents
(concat (org-html-section first-content "" info) contents))
(org-html--container headline info)))))))
Selected
(require 'selected)
(selected-global-mode)
Whisper
Speech-to-Text interface for Emacs using OpenAI’s whisper speech recognition model. For inference, it uses the C/C++ port whisper.cpp that can run on consumer grade CPU without requiring a high end GPU.
You can capture audio with your input device (microphone) or choose a media file on disk, and have the transcribed text inserted into your Emacs buffer, optionally after translating to English from your local language. This runs offline without having to use non-free cloud services (though quality varies depending on the language).
(add-to-list 'load-path "~/.config/emacs/lisp/whisper")
(require 'whisper)
(setq whisper-language "de")
GPTel
(require 'gptel)
(defun get-mistral-api-key ()
"Retrieve the Mistral API key from the system's secret storage using `secret-tool`.
Assumes the key is stored under the attribute `application=mistral-api`.
Returns the API key as a string, or signals an error if not found."
(let ((result (call-process "secret-tool" nil t nil "lookup" "service" "ai.mistral.vibe")))
(if (zerop result)
(buffer-substring-no-properties (point-min) (point-max))
(error "Failed to retrieve Mistral API key or key not found in secret storage"))))
(setq gptel-model 'mistral-small
gptel-backend
(gptel-make-openai "MistralLeChat" ;Any name you want
:host "api.mistral.ai"
:endpoint "/v1/chat/completions"
:protocol "https"
:key (get-mistral-api-key) ;can be a function that returns the key
:models '("mistral-small")))
GPTel-Agent
(require 'gptel-agent)
(gptel-agent-update)