summaryrefslogtreecommitdiff
path: root/guides/applications
diff options
context:
space:
mode:
Diffstat (limited to 'guides/applications')
-rw-r--r--guides/applications/emacs/AUCTeX.org24
-rw-r--r--guides/applications/emacs/development.org121
-rw-r--r--guides/applications/emacs/elisp-demos.org28
-rw-r--r--guides/applications/emacs/use-package.org48
-rw-r--r--guides/applications/emacs/windows.org25
-rw-r--r--guides/applications/ffmpeg.org22
-rw-r--r--guides/applications/git.org40
-rw-r--r--guides/applications/imagemagick.org81
8 files changed, 389 insertions, 0 deletions
diff --git a/guides/applications/emacs/AUCTeX.org b/guides/applications/emacs/AUCTeX.org
new file mode 100644
index 0000000..0082759
--- /dev/null
+++ b/guides/applications/emacs/AUCTeX.org
@@ -0,0 +1,24 @@
+* Completion for cross-references
+To integrate AUCTeX with reftex and get completion for ~\cite~
+(bibliography) and ~\ref~ (intra-document labels):
+#+begin_src elisp
+;; In init file:
+(put 'TeX-auto-save 'safe-local-variable 'booleanp)
+(put 'TeX-parse-self 'safe-local-variable 'booleanp)
+(add-hook 'LaTeX-mode-hook 'turn-on-reftex)
+(setq reftex-plug-into-AUCTeX t)
+(setq reftex-label-illegal-re "[^-a-zA-Z0-9_+=:;,./]") ; Allow slashes.
+(setq TeX-auto-local ".auctex")
+
+;; Top-level .dir-locals.el:
+((latex-mode
+ . ((TeX-auto-save . t)
+ (TeX-parse-self . t)))
+ (bibtex-mode
+ . ((TeX-auto-save . t)
+ (TeX-parse-self . t))))
+
+;; .dir-locals.el files in subfolders:
+((latex-mode
+ . ((TeX-master . "../path/to/main.tex"))))
+#+end_src
diff --git a/guides/applications/emacs/development.org b/guides/applications/emacs/development.org
new file mode 100644
index 0000000..f1363e1
--- /dev/null
+++ b/guides/applications/emacs/development.org
@@ -0,0 +1,121 @@
+* Build
+** bootstrapping
+Libraries that are part of the "bootstrap set" cannot rely on
+autoloading. Example: =files.el= must ~(eval-when-compile (require
+'pcase))~, even though it only uses autoloaded macros.
+* Tests
+** Running
+Somehow I can never remember how to ask =make= how to run a specific
+set of tests.
+
+tl;dr, running =$testcases_regexp= from =test/$testfile.el=, to test
+changes in =lisp/$sourcefile.el=:
+#+begin_src sh
+make -C test $testfile \
+ TEST_LOAD_EL=yes \
+ TEST_BACKTRACE_LINE_LENGTH=nil \
+ SELECTOR="\"$testcases_regexp\"" \
+ EMACS_EXTRAOPT="-l ../lisp/$sourcefile.el"
+#+end_src
+
+Relevant bits from test/Makefile:
+
+#+begin_src makefile
+## filename.log: run tests from filename.el(c) if .log file needs updating
+## filename: re-run tests from filename.el(c), with no logging
+#+end_src
+
+#+begin_src makefile
+# Whether to run tests from .el files in preference to .elc, we do
+# this by default since it gives nicer stacktraces.
+# If you just want a pass/fail, setting this to no is much faster.
+export TEST_LOAD_EL ?= \
+ $(if $(findstring $(MAKECMDGOALS), all check check-maybe),no,yes)
+#+end_src
+
+#+begin_src makefile
+TEST_RUN_ERT = --batch --eval '(ert-run-tests-batch-and-exit (quote ${SELECTOR_ACTUAL}))' ${WRITE_LOG}
+#+end_src
+
+#+begin_src makefile
+ifdef SELECTOR
+SELECTOR_ACTUAL=$(SELECTOR)
+#+end_src
+
+Selector documentation from ~ert-select-tests~:
+#+begin_quote
+Valid SELECTORs:
+
+nil -- Selects the empty set.
+t -- Selects UNIVERSE.
+:new -- Selects all tests that have not been run yet.
+:failed, :passed -- Select tests according to their most recent result.
+:expected, :unexpected -- Select tests according to their most recent result.
+a string -- A regular expression selecting all tests with matching names.
+a test -- (i.e., an object of the ert-test data-type) Selects that test.
+a symbol -- Selects the test that the symbol names, signals an
+ ‘ert-test-unbound’ error if none.
+(member TESTS...) -- Selects the elements of TESTS, a list of tests
+ or symbols naming tests.
+(eql TEST) -- Selects TEST, a test or a symbol naming a test.
+(and SELECTORS...) -- Selects the tests that match all SELECTORS.
+(or SELECTORS...) -- Selects the tests that match any of the SELECTORS.
+(not SELECTOR) -- Selects all tests that do not match SELECTOR.
+(tag TAG) -- Selects all tests that have TAG on their tags list.
+ A tag is an arbitrary label you can apply when you define a test.
+(satisfies PREDICATE) -- Selects all tests that satisfy PREDICATE.
+ PREDICATE is a function that takes an ert-test object as argument,
+ and returns non-nil if it is selected.
+#+end_quote
+** Writing
+*** Mocking functions
+#+begin_src elisp
+(cl-letf (((symbol-function 'read-something)
+ (lambda (&rest _) "result")))
+ (some-command))
+#+end_src
+
+* Third-party packages
+** forge
+*** build
+Use =config.mk= to set =LOAD_PATH=:
+#+begin_src makefile
+find-lib = $(dir $(shell emacs -Q -batch \
+ -eval "(require 'find-func)" \
+ -eval "(package-initialize)" \
+ -eval "(require '$(1))" \
+ -eval "(princ (find-library-name \"$(1)\"))"))
+
+sources = ~/src/emacs/magit/lisp
+
+elpa = \
+ closql \
+ compat \
+ dash \
+ emacsql \
+ ghub \
+ markdown-mode \
+ transient \
+ treepy \
+ with-editor \
+ yaml
+elpa_dirs = $(foreach d,$(elpa),$(call find-lib,$(d)))
+
+LOAD_PATH = $(addprefix -L ,$(elpa_dirs) $(sources))
+#+end_src
+
+*** load sources
+~emacs -Q -L ./lisp~ does not seem to work: after running
+~(package-initialize)~, =M-x find-library forge= returns the instance
+installed under ~package-user-dir~.
+
+This can be circumvented with ~(setq pacakge-load-list '((forge nil)
+all))~.
+
+Full command line for easy startup:
+
+#+begin_src sh
+emacs -Q -L ./lisp \
+ -eval "(setq pacakge-load-list '((forge nil) all))" \
+ -f package-initialize \
+#+end_src
diff --git a/guides/applications/emacs/elisp-demos.org b/guides/applications/emacs/elisp-demos.org
new file mode 100644
index 0000000..029f5be
--- /dev/null
+++ b/guides/applications/emacs/elisp-demos.org
@@ -0,0 +1,28 @@
+Some terse "context-free" Emacs Lisp snippets that hopefully speak for
+themselves.
+
+* The prefix argument
+#+begin_src elisp
+(defun my/prefix-arg-demo (arg)
+ (interactive (list current-prefix-arg))
+ (insert
+ (let ((num (prefix-numeric-value arg)))
+ (format "%s %d %s\n"
+ arg
+ num
+ (cond ((null arg) "N/A")
+ ((numberp arg) (format "M-%d or C-u %d" arg arg))
+ ((equal arg '-) (format "M-- or C-u -"))
+ ((listp arg)
+ (if (< num 0)
+ (format "(M-- or C-u -) C-u × %d" (log (- num) 4))
+ (format "C-u × %d" (log num 4)))))))))
+#+end_src
+#+begin_example
+nil 1 N/A
+2 2 M-2 or C-u 2
+- -1 M-- or C-u -
+(-16) -16 (M-- or C-u -) C-u × 2
+(64) 64 C-u × 3
+#+end_example
+
diff --git a/guides/applications/emacs/use-package.org b/guides/applications/emacs/use-package.org
new file mode 100644
index 0000000..f6cd027
--- /dev/null
+++ b/guides/applications/emacs/use-package.org
@@ -0,0 +1,48 @@
+* Porting from ~custom-file~
+Some very dumb code to generate ~use-package~ declarations from Custom
+settings. Entry point is ~c->us/port~.
+#+begin_src elisp
+(require 'help-fns)
+(require 'radix-tree)
+
+(defun c->us/get-custom-options ()
+ (seq-map
+ (pcase-lambda (`(theme-value ,option user ,value))
+ (list option value))
+ (get 'user 'theme-settings)))
+
+(defun c->us/get-option-file (option)
+ ;; Load packages first, otherwise symbol-file can return "loaddefs".
+ (pcase-dolist
+ (`(_ . ,files)
+ (radix-tree-prefixes (help-definition-prefixes)
+ (symbol-name option)))
+ (dolist (f files)
+ (load f 'noerror 'nomessage)))
+ (when-let ((file (symbol-file option)))
+ (file-name-base file)))
+
+(defun c->us/write-declaration (lib pairs)
+ (insert (format "(use-package %s\n" lib))
+ (insert " :custom")
+ (message "%s -> %s" lib pairs)
+ (pcase-dolist
+ (`(,option ,value) pairs)
+ (insert (format "\n (%s %s)"
+ option
+ (prin1-to-string value))))
+ (insert ")\n\n"))
+
+(defun c->us/symbols< (symlist1 symlist2)
+ (string< (car symlist1) (car symlist2)))
+
+(defun c->us/port ()
+ (seq-map
+ (pcase-lambda (`(,lib . ,pairs))
+ (c->us/write-declaration lib pairs))
+ (sort (seq-group-by
+ (pcase-lambda (`(,option _))
+ (c->us/get-option-file option))
+ (sort (c->us/get-custom-options) 'c->us/symbols<))
+ 'c->us/symbols<)))
+#+end_src
diff --git a/guides/applications/emacs/windows.org b/guides/applications/emacs/windows.org
new file mode 100644
index 0000000..06cad7e
--- /dev/null
+++ b/guides/applications/emacs/windows.org
@@ -0,0 +1,25 @@
+=nt/INSTALL.w64= provides detailed instructions for compiling Emacs on
+recent Windows versions. Here are some additional steps I had to
+figure out on my own:
+* Mingw-w64
+- Set =$HOME= to =%HOMEPATH%= by changing =db_home= to =windows= in
+ =c:/msys64/etc/fstab=.
+- In addition to =c:\msys64\mingw64\bin=, adding =c:\msys64\usr\bin=
+ to the system =PATH= allows Emacs to access applications installed
+ with =pacman=.
+ - It's not obvious whether the Mingw-w64 =PATH= entries should go
+ last (in which case Emacs will pick the wrong ~find~ command) or
+ first (in which case… /everything else/ will pick the wrong ~find~
+ command?). At least Emacs has a variable to configure
+ (~find-program~).
+* Compiling
+Make sure to specify ~-c core.autocrlf=false~ *when cloning* the Emacs
+repository; ~autogen.sh~ gets confused otherwise.
+* Configuring
+AFAICT one must set the variable ~shell-file-name~ explicitly to
+=c:/msys64/usr/bin/bash.exe=, otherwise Emacs's Windows-specific
+initialization logic uses a built-in shell ersatz (=nt/cmdproxy.c=).
+** TODO =HOME=
+Went with the instructions from [[info:emacs#MS-Windows Registry]]; could
+maybe use [[info:emacs#Windows HOME]] by setting =HOME= to =%HOMEPATH%=,
+to avoid having to spell out my username?
diff --git a/guides/applications/ffmpeg.org b/guides/applications/ffmpeg.org
new file mode 100644
index 0000000..eded0e9
--- /dev/null
+++ b/guides/applications/ffmpeg.org
@@ -0,0 +1,22 @@
+ffmpeg recipes I should stuff into config files or scripts, but
+haven't yet.
+* Extracting parts of videos
+~-c copy -map 0~ ensures all streams (audio & subtitles) are kept.
+The timestamp syntax is described in the "Time duration" section of
+=ffmpeg-utils(1)=; =MM:SS= works fine.
+
+#+begin_src sh
+ffmpeg -i $input -c copy -map 0 \
+ -ss $tstart -to $tend \
+ $output
+#+end_src
+
+* Reduce file size
+** with =-vf=: ~-vf "scale=iw/2:ih/2"~
+** with H.265
+openSUSE Tumbleweed's ffmpeg is not built with H.265 support AFAICT.
+
+1. Install =libx265-devel=
+2. Clone =https://git.ffmpeg.org/ffmpeg.git=
+3. ~./configure --enable-libx265 --enable-gpl~
+4. ~ffmpeg -i $input -vcodec libx265 -crf 28~
diff --git a/guides/applications/git.org b/guides/applications/git.org
new file mode 100644
index 0000000..bf42130
--- /dev/null
+++ b/guides/applications/git.org
@@ -0,0 +1,40 @@
+* ~send-email~ with ~.authinfo.gpg~
+** Step 1: Install ~git-credential-netrc~
+AFAICT not all distributions package this script? As far as those I
+use are concerned:
+
+- On Debian, the =git= package brings
+ =/usr/share/doc/git/contrib/credential/netrc/git-credential-netrc.perl=,
+ but the file is not executable, so it's not as simple as symlinking
+ it from =~/.local/bin=: it must be =cp='ed and =chmod='ed.
+
+- openSUSE does not include anything from =contrib=, I think? So just
+ grab =contrib/credential/netrc/git-credential-netrc.perl= from the [[https://git.kernel.org/pub/scm/git/git.git/][Git
+ repository]], stick it in =~/.local/bin= and =chmod= it.
+
+Now ~git help --all | grep credential-~ should show =credential-netrc=.
+
+** Step 2: Configure ~git send-email~
+In =~/.gitconfig=, assuming a GMail account for =luser@gmail.com=:
+
+#+begin_src conf
+[sendemail]
+ smtpencryption = tls
+ smtpserver = smtp.gmail.com
+ smtpuser = luser
+ smtpserverport = 587
+#+end_src
+
+Most =send-email= guides seem to use the full email address for
+=smtpuser=; I'm putting in just the /local-part/, since that's what is in
+my =~/.authinfo.gpg=.
+
+** Step 3: Configure ~git-credential~
+In =~/.gitconfig=:
+
+#+begin_src conf
+[credential]
+ helper = netrc
+#+end_src
+
+** Step 4: Enjoy 📨
diff --git a/guides/applications/imagemagick.org b/guides/applications/imagemagick.org
new file mode 100644
index 0000000..9d56cbf
--- /dev/null
+++ b/guides/applications/imagemagick.org
@@ -0,0 +1,81 @@
+ImageMagick recipes I should stuff into config files or scripts, but
+haven't yet.
+* Configuration
+** Allow conversion from/to PDF
+- Open ImageMagick's =policy.xml= configuration file.
+- Locate the =<policy>= tag with a =pattern= including "PDF".
+- Comment it out.
+* Comparing images
+~magick compare~ is a thing; ~magick convert~ can also do [[https://stackoverflow.com/a/33673440][neat stuff]]:
+#+begin_src sh
+convert '(' a.png -flatten -grayscale Rec709Luminance ')' \
+ '(' b.png -flatten -grayscale Rec709Luminance ')' \
+ '(' -clone 0-1 -compose darken -composite ')' \
+ -channel RGB -combine diff.png
+#+end_src
+
+* Adding watermarks
+Someday, in the Glorious Future, every thirdparty that insists on
+having a copy of my personal documents will be legally obligated to
+request it from a trusted government service that steganographizes the
+requester's identity in the files they send.
+
+Until then…
+#+begin_src sh
+#!/bin/bash
+
+set -eu
+
+src=$1
+dst=$2
+label=$3
+
+mark ()
+{
+ local marksrc=$1
+ local markdst=$2
+
+ w=$(identify -format '%w\n' "$marksrc" | head -1)
+ h=$(identify -format '%h\n' "$marksrc" | head -1)
+ textw=$((w/3))
+ texth=$((h/8))
+
+ pointsize=$((80*w/2500))
+ offx=$((100*w/2500))
+ offy=$((400*h/3500))
+
+ convert -size ${textw}x${texth} xc:none \
+ -fill '#80808080' -pointsize $pointsize \
+ -annotate 340x340+${offx}+${offy} "$label" \
+ miff:- | \
+ composite -tile - "$marksrc" "$markdst"
+}
+
+if [[ "$src" =~ \.pdf ]]
+then
+ # AFAICT if I feed a multi-page PDF to composite, it outputs only
+ # the first page. Jump through hoops to split and concatenate the
+ # PDF; also, roundtrip through JPG because setting -density (to
+ # preserve a good quality) messes with the size calculations for
+ # the label.
+
+ tmpd=$(mktemp -d)
+ bname=$(basename "$src")
+ qpdf --split-pages "$src" "${tmpd}/$bname"
+
+ shopt -s extglob
+
+ for page in ${tmpd}/${bname/%.pdf/-+([0-9]).pdf}
+ do
+ convert -density 300 -flatten "$page" "${page/.pdf/.jpg}"
+ mark "${page/.pdf/.jpg}" "${page/.pdf/-MARKED.jpg}"
+ convert -density 300 "${page/.pdf/-MARKED.jpg}" "${page/.pdf/-MARKED.pdf}"
+ done
+
+ qpdf --empty --pages ${tmpd}/${bname/%.pdf/-+([0-9])-MARKED.pdf} -- "$dst"
+
+ exit
+fi
+
+mark "$src" "$dst"
+#+end_src