蒸茄盒

  • 两个茄子选软且颜色深,去皮.抹盐再冲掉(为了避免表面变黑),切茄盒
  • 葱姜末,料酒,肉米,全蛋,少许盐鸡精,花椒水三勺,二合一酱油20克,加淀粉适量(不用太多,2~3勺)锁住水分.搅拌制作成馅(口感类似午餐肉)
  • 茄盒加入馅,摆盘后洒三分口盐(盐引出茄子的汁),大火蒸15分钟,洒红绿尖椒末,蒸一分钟
  • 浇上酱油,加葱丝香菜,呲热油(速度要快)

视频: 在家炸茄盒太麻烦?试试茄盒这么做,从里到外软嫩咸香,鲜味十足 视频备份

番茄鱼片汤

  • 1~2大片巴沙鱼略挤水分,切7~10毫米厚片.
  • 鱼片加盐鸡精料酒抓一下,加蛋清少量土豆粉搅拌(上浆后鱼片受热表面糊化,锁住鱼片水分)
  • 4~6个番茄底部十字刀,开水烫一分钟,去皮,切块,块不要太小有口感
  • 葱姜下锅炸出香味,很快(10秒)小火,以免葱变焦难看.
  • 番茄出汁后加水,水滚后加适量盐,味精,胡椒粉(可略多点,有辣味),12克(三勺)白醋,和西红柿综合变成果香醋
  • 加鱼片盖锅焖一分钟,洒香菜和葱丝出锅

视频: 龙利鱼腌制诀窍是什么?需要注意什么?老师傅详细讲解番茄鱼片 视频备份

附录,

  • 我加入了豆腐,和少量冷冻绿色蔬菜,调个色,增加营养.

Use CI to improve the quality of emacs distribution

In Better Emacs package development workflow, I proved that running Emacs compiler in CI can give huge boost to package quality.

The same workflow can apply to emacs distributions. But designing a CI workflow for the distribution is much more challenging.

For example, my .emacs.d uses about 300 packages. In CI pipeline, those packages are automatically downloaded and compiled. If compiling errors/warnings from those third party packages are not ignored, the CI will always fail.

There are also many other engineering issues. I struggled for five hours and finally got satisfying solution. Now anyone can use my solution to set up same CI pipeline in ten minutes.

ci-in-emacs.d.png

Makefile is in "~/.emacs.d", it's still simple,

EMACS ?= emacs
RM = @rm -rf
EMACS_BATCH_OPTS = --batch -Q --debug-init

install: clean
    @$(EMACS) $(EMACS_BATCH_OPTS) -l init.el

compile: install
    @$(EMACS) $(EMACS_BATCH_OPTS) -l init.el -l tests/my-byte-compile.el 2>&1 | grep -Ev "init-(hydra|evil).el:.*Warning: docstring wider than 80 characters|an obsolete" | grep -E "[0-9]: ([Ee]rror|[Ww]arning):" && exit 1 || exit 0

You can run make compile && echo good || echo bad in shell to test the pipeline locally. Please note I use grep -v things-to-ignore to ignore some warnings. The warnings are from anonymous functions created by third packages (hydra, general.el, …).

The final missing piece is "~/.emacs.d/tests/my-byte-compile.el",

(require 'find-lisp)
(require 'scroll-bar)
(require 'ivy)
(require 'counsel)
(require 'w3m)
(require 'ibuffer)
(require 'org)
(require 'diff-mode)
(require 'cliphist)
(require 'eacl)
(require 'tramp)
(require 'dired)
(require 'shellcop)
(require 'counsel-etags)
(require 'typewriter-mode)
(require 'pomodoro)
(require 'emms)
(require 'emms-playlist-mode)
(require 'gnus)
(require 'gnus-sum)
(require 'gnus-msg)
(require 'gnus-topic)
(require 'magit)
(require 'magit-refs)
(require 'gnus-art)
(require 'git-link)
(require 'ace-window)
(require 'js2-mode)
(require 'yasnippet)
(require 'ediff)
(require 'company)
(require 'evil-nerd-commenter)
(require 'git-timemachine)
(require 'pyim)
(require 'cal-china-x)
(require 'wucuo)
(require 'langtool)
(require 'web-mode)
(require 'bbdb)
(require 'gmail2bbdb)
(require 'org-mime)
(require 'pdf-tools)
(require 'recentf)
(require 'bookmark)
(require 'find-file-in-project)
(require 'flymake)
(require 'elec-pair)
(require 'elpy)
(require 'rjsx-mode)
(require 'simple-httpd)
(require 'vc)
(require 'sdcv)
(require 'wgrep)
(require 'mybigword)
(require 'yaml-mode)
(require 'octave)
(require 'undo-fu)
(require 'wc-mode)
(require 'exec-path-from-shell)
(require 'dictionary)
(require 'company-ispell)
(require 'company-ctags)
(require 'lsp-mode)

(let ((files (find-lisp-find-files-internal
              "."
              (lambda (file dir)
                (and (not (file-directory-p (expand-file-name file dir)))
                     (string-match "\\.el$" file)
                     (not (member file '(".dir-locals.el"
                                         "package-quickstart.el"
                                         "company-statistics-cache.el"
                                         "custom-set-variables.el"
                                         "early-init.el")))))
              (lambda (dir parent)
                (member dir '("lisp"))))))
  (dolist (file files)
    ;; (message "file=%s" file)
    (byte-compile-file file)))

(provide 'my-byte-compile)
;;; my-byte-compile.el ends here

As you can see,

  • I need add lots of require statement to make compiling succeed on Emacs 26, 27, 28
  • Some "*.el" files generated by Emacs and 3rd party packages need be ignored
  • My emacs setup code is only in "lisp" directory

That's it.

You can visit https://github.com/redguardtoo/emacs.d for a real world example.

BTW, you can find init-no-byte-compile.el where there are a few lines setup code the compiler will ignore. It's bad practice but sometimes there is no other way.

"init-no-byte-compile.el" is like,

;; -*- coding: utf-8; lexical-binding: t; -*-

;; blah blah

;; Local Variables:
;; no-byte-compile: t
;; End:

Better Emacs package development workflow

The Emacs Lisp syntax error should be automatically detected by CI (Continuous Integration).

Syntax errors could be a bit difficult to locate. For example, developers might use Emacs 28+ only APIs to develop packages running on Emacs 26.

Here is howto.

  • Step 1, create a file my-byte-compile.el in the directory "tests/",
(require 'find-lisp)

(let ((files (find-lisp-find-files-internal
              "."
              (lambda (file dir)
                (and (not (file-directory-p (expand-file-name file dir)))
                     (string-match "\\.el$" file)
                     (not (string-match "\\.dir-locals\\.el$" file))))
              (lambda (dir parent)
                (not (or (member dir '("." ".." ".git" ".svn" "deps" "tests"))
                         (file-symlink-p (expand-file-name dir parent))))))))
  (dolist (file files)
    (byte-compile-file file)))
  • Step 2, insert below command line into the project's Makefile,
compile:
    emacs --batch -Q -L . -l my-package-main-entry.el -l tests/my-byte-compile.el 2>&1 | grep -E "([Ee]rror|[Ww]arning):" && exit 1 || exit 0

DONE! Now Gitlab/Github could use command line make compile in their CI pipeline.

Screenshot of a tricky bug of evil-matchit detected by this new workflow.

byte-compile-ci.png

BTW, I also tried elint, but it's not as reliable as byte-compile.

Content of my-elint.el,

(require 'elint)

(let ((elint-directory-skip-re "\\(\\.dir-locals\\|ldefs-boot\\|loaddefs\\)\\.el\\'"))
  (elint-directory "."))

Here is link to the Makefile from my real world project.

I use below command lines to compile and test the project locally.

EMACS=~/myemacs/26.3/bin/emacs make compile # compile only
EMACS=~/myemacs/26.3/bin/emacs make test # compile and run unit test 

Configure EMMS (Emacs Multi-Media System) for Multimedia Keyboard

Emacs server need be started first.

I use emacsclient to execute emms commands in the emacs server.

Key bindings setup in ~/.i3/config for i3 window manager,

# music player client (mpc, emms ...)
bindsym XF86Search exec --no-startup-id ~/bin/music-player-client show
bindsym XF86Tools exec --no-startup-id ~/bin/music-player-client random
bindsym XF86AudioStop exec --no-startup-id ~/bin/music-player-client toggle
bindsym XF86AudioPause exec --no-startup-id ~/bin/music-player-client toggle
bindsym XF86AudioNext exec --no-startup-id ~/bin/music-player-client next
bindsym XF86AudioPrev exec --no-startup-id ~/bin/music-player-client prev

Content of ~/bin/music-player-client,

#!/bin/bash

# use mpc&mpd or emacsclient&emms to play music

if [ -z "$1" ]; then
    echo "Usage: music-player-client pre|next|toggle|random|show"
    exit 1
fi

# Please uninstall mpc&mpd if using emms
if command -v mpc &> /dev/null; then
    case $1 in
        prev )
            mpc prev
            ;;
        next )
            mpc next
            ;;
        toggle )
            mpc toggle
            ;;
    esac
elif command -v emacsclient &> /dev/null; then
    case $1 in
        prev )
            emacsclient --eval '(emms-previous)'
            ;;
        next )
            emacsclient --eval '(emms-next)'
            ;;
        toggle )
            emacsclient --eval '(emms-pause)'
            ;;
        random )
            emacsclient --eval '(progn (emms-shuffle) (emms-next))'
            ;;
        show )
            # program like dunst can show the notification
            notify-send "$(emacsclient --eval '(file-name-base (emms-show))')"
            ;;
    esac
fi

I usually run M-x emms-play-directory-tree to play music. My emms setup,

(with-eval-after-load 'emms
  ;; minimum setup is more robust
  (emms-minimalistic)
  (setq emms-player-list '(emms-player-mplayer
                           emms-player-vlc)))

Screen shot of running ~/bin/music-player-client show, emms-notification.png

Integrate delta into git

Delta is a syntax-highlighting pager for git, diff, and grep output.

Set up is as simple as copying its sample setup.

I wrote a shell script my-pager which can use both less and delta as pager,

#!/bin/bash
# @see https://stackoverflow.com/questions/19408649/pipe-input-into-a-script
if [ -x "$HOME/.cargo/bin/delta" ]; then
    cat | $HOME/.cargo/bin/delta "$@"
else
    cat | less -F -X
fi

Here is my extra delta setup in ~/.gitconfig (Delta reads settings from ~/.gitconfig),

[core]
pager = ~/bin/my-pager
[interactive]
diffFilter = ~/bin/my-pager --color-only
[merge]
conflictstyle = diff3
[diff]
colorMoved = default
[delta "default"]
file-decoration-style= blue box
hunk-header-decoration-style = purple ol
[delta]
features = default
navigate = true  # use n and N to move between diff sections

Screenshot, git-delta.png

Emacs 28.1 on Debian Linux

As a happy Emacs 27 user on Debian Linux, I plan to use both Emacs 28.1 and Emacs 27 and gradually migrate from Emacs 27 to Emacs 28.

So I installed Emacs 28.1 without X Window System Support (emacs-nox) and it only runs in daemon mode.

Here are the steps,

  • Compare PKGBUILD of emacs-nox 27 and PKGBUILD of emacs-nativecomp 28 to get the difference of build script (I was more familiar with Arch Linux. You can use Gentoo Linux's website to collect information)
  • The only missing third party package is libgccjit. Run sudo apt install libgccjit-12-dev to install it
  • Download Emacs 28.1 source code, run below command in shell,
cd ~/Downloads/emacs-28.1 && mkdir -p ~/myemacs/28.1 && rm -rf ~/myemacs/28.1/* && ./configure CFLAGS=-no-pie --prefix=~/myemacs/28.1 --without-x --without-sound  --with-modules --with-native-compilation --without-compress-install && make && make install
  • After installation, run "~/myemacs/28.1/bin/emacs" and Emacs will compile the packages automatically (I watched a few youtube videos until the compilation finished)
  • Create "~/.config/systemd/user/emacs.service" with below content ("/home/cb" is my HOME directory),
[Unit]
Description=Emacs text editor
Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/

[Service]
Type=forking
ExecStart=/home/cb/myemacs/28.1/bin/emacs -Q --daemon -l "/home/cb/.emacs.d/init.el" --eval "(progn (require 'server) (server-start))"
ExecStop=emacsclient --eval "(kill-emacs)"
Environment=SSH_AUTH_SOCK=%t/keyring/ssh
Restart=on-failure

[Install]
WantedBy=default.target
  • Run systemctl --user restart emacs.service in shell
  • Done! Run emacsclient to enjoy Emacs 28.1.

2.1. disable "idle loader" in server mode

I use "idle loader" in my Emacs distribution. Obviously it need be disabled in server mode.

Here is the code,

(defvar my-disable-idle-timer (daemonp)
  "If not nil, Function passed to `my-run-with-idle-timer' is run immediately.")

(defun my-run-with-idle-timer (seconds func)
  "After SECONDS, run function FUNC once."
  (cond
   (my-disable-idle-timer
    (funcall func))
   (t
    (run-with-idle-timer seconds nil func))))

2.2. Shell script to install Emacs

You could Use below script "install-emacs.sh" to install Emacs,

#!/bin/sh
[ -z "$EMACS_VERSION" ] && echo "Usage: EMACS_VERSION=25.1 install-emacs.sh or EMACS_VERSION=snapshot install-emacs.sh" && exit 1
[ -z "$EMACS_URL" ] && EMACS_URL="http://mirror.aarnet.edu.au/pub/gnu/emacs/"
# I've assign 12G memory to /tmp as ramdisk
[ -z "$EMACS_TMP" ] && EMACS_TMP="/tmp"

if [ "$EMACS_VERSION" != "snapshot" ]; then
    echo "curl $EMACS_URL/emacs-$EMACS_VERSION.tar.gz"
    curl $EMACS_URL/emacs-$EMACS_VERSION.tar.gz | tar xvz -C $EMACS_TMP
fi

# @see http://wiki.gentoo.org/wiki/Project:Emacs/GNU_Emacs_developer_guide
# @see http://packages.gentoo.org/package/app-editors/emacs for info on Gentoo Linux
# --without-gtk and --without-gtk3 is optional
echo "Installing Emacs ..."
if [ "$EMACS_VERSION" = "snapshot" ]; then
    cd $HOME/projs/emacs && mkdir -p $HOME/myemacs/snapshot && rm -rf $HOME/myemacs/snapshot/* && ./autogen.sh && ./configure CFLAGS=-no-pie --prefix=$HOME/myemacs/snapshot --without-x --without-dbus --without-sound --with-gnutls=no && make && make install
    echo "Emacs snapshot was installed!"
elif [ "$EMACS_VERSION" = "28.1" ]; then
    cd $EMACS_TMP/emacs-$EMACS_VERSION && mkdir -p $HOME/myemacs/$EMACS_VERSION && rm -rf $HOME/myemacs/$EMACS_VERSION/* && ./configure CFLAGS=-no-pie --prefix=$HOME/myemacs/$EMACS_VERSION --without-x --without-sound --with-modules --with-native-compilation --without-compress-install && make && make install
else
    cd $EMACS_TMP/emacs-$EMACS_VERSION && mkdir -p $HOME/myemacs/$EMACS_VERSION && rm -rf $HOME/myemacs/$EMACS_VERSION/* && ./configure CFLAGS=-no-pie --prefix=$HOME/myemacs/$EMACS_VERSION --without-x --without-sound  --with-modules && make && make install
    rm -rf $EMACS_TMP/emacs-$EMACS_VERSION
    echo "Emacs $EMACS_VERSION was installed!"
fi

Start teamviewer on i3wm

See yveslec's analysis.

So the solution is just one liner in shell.

ssh -X 127.0.0.1 "DISPLAY=:0.0 teamviewer"

Here is my shell script "~/bin/sshx-teamviewer.sh" with a bit optimization,

#!/bin/sh
# Run "cat ~/.ssh/id_rsa.pubkey >> ~/.ssh/authorized_keys" to
# avoid inputting ssh login password
ssh -X -C 127.0.0.1 "DISPLAY=:0.0 teamviewer"

蒜苔炒蛋

视频教程见老東北美食: 只要多做一步,蒜苔又嫩又入味

要点,

  • 蒜苔老的头掐掉, 开水焯一下后放入冷水,从中间撕成两半,切成四公分的段.
  • 滚刀切葱段备用
  • 炒蛋核桃块大小, 三分盐
  • 油热后加葱和少许海鲜酱油.炒几下后加入蒜苔加盐花椒油翻炒
  • 加蛋翻炒几下,略勾芡

注: 没有花椒油,所以我在油温较高时和葱段一起加入少许花椒粒,利用油温爆出花椒香味.

suantai-egg.jpg

麻婆豆腐

视频教程见大师的菜 麻婆豆腐.

要点,

  • 豆腐要嫩, 过开水去豆腥味, 加盐提味,一点酱油提色
  • 牛肉末炒酥取出
  • 加豆瓣酱,豆豉,辣椒面等.不能加姜,会压住其他味.炒到发香.加肉末,加高汤
  • 汤最多到豆腐的50%
  • 用炒勺的背面推豆腐保持豆腐完整
  • 三次勾芡(第一次让味进入豆腐,第二次起到拉力作用,第三次彻底粘合,不要吐水出来)
  • 最后用小火甚至微火,多烧一下才入味(要不断推豆腐,否则豆腐沾锅底)
  • 起锅前加入蒜苗, 少许花椒粒烤热后磨成粉撒入

mapotofu.jpg