Rosa

Table of Contents

1. Index

// / / / / / / // / // // / // / // / / / / / / / // / / / / / / / // / / / / / / / / / / / / / / / / / / // / // / / / / // //

/ / / // // / / / / // / // // // / / / // / / / / / / // //

欲寄彩笺兼尺素,山长水阔知何处

眼泪是爱着这个世界的证明

Rosario S.E. 的解决方案管理系统。

1.1. 我的 Emacs 配置

1.1.1. 起兴

皇天既付中国民,越厥疆土于先王。

Emacs启动所必要的初始配置。

  • init

    用于导出 org 内配置的函数,以及加载导出后的配置

    (defun rosa/export (&optional type)
      (pcase type
        ('emacs "~/.emacs.d/rosa.el")
        ('init "~/.emacs.d/init.el")
        ('cnhl "~/.emacs.d/site-lisp/cnhl/cnhl.el")
        ('cnhl-thulac "~/.emacs.d/site-lisp/cnhl/cnhl-thulac.cpp")
        ('cnhl-fasthan "~/.emacs.d/site-lisp/cnhl/cnhl-fasthan.py")
        ('cndict "~/.emacs.d/site-lisp/cndict/cndict.el")
        (_ "no")))
    
    (load-file "~/.emacs.d/rosa.el")
    
  • straightuse-package

    (defvar bootstrap-version)
    (let ((bootstrap-file
           (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
          (bootstrap-version 5))
      (unless (file-exists-p bootstrap-file)
        (with-current-buffer
            (url-retrieve-synchronously
             "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
             'silent 'inhibit-cookies)
          (goto-char (point-max))
          (eval-print-last-sexp)))
      (load bootstrap-file nil 'nomessage))
    
    (straight-use-package 'use-package)
    
  • 在 macOS 下导入环境变量

    否则许多调用shell程序的包会找不到命令。

    (use-package exec-path-from-shell
      :if (memq window-system '(mac ns))
      :straight t
      :config (exec-path-from-shell-initialize))
    
  • 代理

    有必要时开启。

    (setenv "http_proxy" "http://127.0.0.1:7890")
    (setenv "https_proxy" "http://127.0.0.1:7890")
    ;; (setenv "http_proxy" "http://127.0.0.1:20171")
    ;; (setenv "https_proxy" "http://127.0.0.1:20171")
    ;; (setenv "http_proxy" nil)
    ;; (setenv "https_proxy" nil)
    ;; (setq url-proxy-services
    ;;       '(("http" . "127.0.0.1:7890")
    ;; 	("https" . "127.0.0.1:7890")))
    (defun rosa/clash ()
        (if (get-process "clash")
            (progn (kill-process (get-process "clash"))
                   (make-process
                    :name "clash"
                    :command '("clash")
                    :noquery t))
          (make-process
           :name "clash"
           :command '("clash")
           :noquery t)))
    
    (add-hook 'after-init-hook #'rosa/clash)
    
  • 字符编码格式

    刨了 Centaur 大大的配置,以防万一,省掉麻烦。

    ;; UTF-8 as the default coding system
    (when (fboundp 'set-charset-priority)
      (set-charset-priority 'unicode))
    
    ;; Explicitly set the prefered coding systems to avoid annoying prompt
    ;; from emacs (especially on Microsoft Windows)
    (prefer-coding-system 'utf-8)
    (setq locale-coding-system 'utf-8)
    
    (set-language-environment 'utf-8)
    (set-default-coding-systems 'utf-8)
    (set-buffer-file-coding-system 'utf-8)
    (set-clipboard-coding-system 'utf-8)
    (set-file-name-coding-system 'utf-8)
    (set-keyboard-coding-system 'utf-8)
    (set-terminal-coding-system 'utf-8)
    (set-selection-coding-system 'utf-8)
    (modify-coding-system-alist 'process "*" 'utf-8)
    (setq system-time-locale "C")
    
  • 取消一些函数的禁用

    因为已经知道怎么用了ww

    (put 'set-goal-column 'disabled nil)
    (put 'upcase-region 'disabled nil)
    (put 'downcase-region 'disabled nil)
    (put 'narrow-to-region 'disabled nil)
    

1.1.2. 梳妆

七月七日长生殿,夜半无人私语时。

Emacs 美化。

以下美化仅在 Gui Emacs 下生效。

主题

Modus theme

(use-package modus-themes
  :straight t
  :custom
  (modus-themes-italic-constructs t)
  (modus-themes-bold-constructs t)
  (modus-themes-region '(bg-only no-extend))
  (modus-themes-operandi-color-overrides
   '((bg-main . "#fefcf4")
     (bg-dim . "#faf6ef")
     (bg-alt . "#f7efe5")
     (bg-active . "#e8dfd1")
     (bg-inactive . "#f6ece5")
     (bg-region . "#c6bab1")
     (bg-header . "#ede3e0")
     (bg-tab-bar . "#dcd3d3")
     (bg-tab-active . "#fdf6eb")
     (bg-tab-inactive . "#c8bab8")
     (fg-unfocused . "#55556f")))
  :config
  (modus-themes-load-themes)
  (modus-themes-load-vivendi))
标题栏

设置macOS下标题栏跟随系统颜色(而不是灰了吧唧的):

(push '(ns-transparent-titlebar . t) default-frame-alist)
关掉各种栏

除了分散注意力意外也没什么用

(tool-bar-mode -1)
(scroll-bar-mode -1)
(menu-bar-mode -1)
(display-battery-mode 1)
(display-time-mode 1)
光标

竖线光标,不闪烁

因为我特别喜欢盯着光标闪啊闪啊的玩,所以就把它关了哈哈

(setq-default cursor-type '(bar . 3))
(setq blink-cursor-mode nil)
所有字体

Monaco / Spot Mono / VGA / Unifont / 小赖

Monaco , mac 传统了;

Spot Mono 字形优雅,且字体稍扁,在浏览多行文本时会更舒服;

VGA font 搭配 unifont 也很舒服,就是 awesome-tray 可能会出问题,毕竟是点阵字

或者直接上中英文等宽字体,这样就不用单独设置中英文字体啦。

加 hook 是为了在 emacsclient 启动的情况下也能起作用~

(defun rosa/font-all (&rest _)
  (set-fontset-font t 'emoji '("Noto Color Emoji" . "iso10646-1") nil 'prepend)
  (set-face-attribute 'default nil
                      :font "小賴字體 等寬 SC"
                      :height 120))

(rosa/font-all)

(add-hook 'after-make-frame-functions
          #'rosa/font-all)
中文字体

思源宋体 Bold / Unifont / 霞骛文楷……

大字重的思源宋体具有别样的美感,经过精心设计的衬线体能有效增强阅读时的连贯性, 激发人的阅读与写作能力。

用思源宋体的时候推荐加上 1.24 倍的缩放,不仅可以在大多数时候对齐中英文字体,同 时中英文大小搭配也和谐自然。

Unifont 中文字体可以搭配其它 8x16 英文像素字体,比如 VGA ,也会非常好看—— Unifont 自己的英文字体太细了,有点接受不了……

霞骛文楷超级好看!

为什么不用更纱黑体?当年就是因为换了那么丑的字体,差点把我从 emacs 劝退2333……

(defun rosa/font-cn (&rest _)
  (dolist (charset '(kana han cjk-misc bopomofo gb18030))
    (set-fontset-font "fontset-default"
                      charset
                      (font-spec
                       :family "小賴字體 等寬 SC"
                       :weight 'bold))))

(rosa/font-cn)

(add-hook 'after-make-frame-functions
          #'rosa/font-cn)

;; (setq face-font-rescale-alist '(("Unifont" . 1.3)))
页边距

margin

是我自己写的函数,在 buffer 横向全屏的时候加一点边距,眼睛舒服一些

原先用的 olivetti ,但是后来想用 org-indent-mode , olivetti 处理不了,就 自己手写了一个简单的

(defun rosa/margin ()
  (interactive)
  (if (and (not (memq major-mode
                      (list #'exwm-mode)))
           (window-full-width-p)
           (> (frame-pixel-width) 1000)
           (not (window-minibuffer-p)))
      (progn (set-window-margins (selected-window) 25)
             (set-window-margins (previous-window) 25))
    (set-window-margins (selected-window) 0)))

(add-hook 'window-state-change-hook #'rosa/margin)
mode-line 美化

awesome-tray

最好的美化就是把它给去了哈哈哈哈

awesome-tray 真的好可爱呀,抱走~

(use-package awesome-tray
  :straight (:type git :repo "https://github.com/manateelazycat/awesome-tray")
  :config
  (if (display-graphic-p)
      (setq awesome-tray-mode-line-active-color "pink"
            awesome-tray-mode-line-inactive-color "pink4")
    (setq awesome-tray-mode-line-active-color nil
          awesome-tray-mode-line-inactive-color nil)
    (setq-default mode-line-format nil))
  (setq awesome-tray-info-padding-right 1)
  (awesome-tray-mode 1))
图标

all-the-icons

其实我并不是特别喜欢 all-the-icons 的风格,但是有还是比没有好看呢

(use-package all-the-icons
  :straight t)

(use-package all-the-icons-completion
  :straight t
  :config
  (all-the-icons-completion-mode)
  (add-hook 'marginalia-mode-hook
            #'all-the-icons-completion-marginalia-setup))
彩色编程

color-identifiers

可以给局部变量贴上不同的颜色,让色彩变得丰富一些

可是我 elisp 很少用局部变量啊QAQ

(use-package color-identifiers-mode
  :straight t
  :init (global-color-identifiers-mode 1))
Org 表格对齐

valign 包

valign 通过像素计算的方式实现 org-mode 下的表格对齐,不再需要折腾中英文字体等 宽了,能让字体搭配更加自由美观。

可是我一直都在用中英文等宽字体啊哈哈

所以我把它注释掉了……

;; (use-package valign
;;   :if (display-graphic-p)
;;   :straight t
;;   :config (add-hook 'org-mode-hook
;; 		    #'valign-mode))

1.1.3. 乞巧

年年乞与人间巧,不道人间巧已多。

Emacs 功能性配置。

自动补全

Corfu

Corfu 小可爱,抱抱~

还有我最爱的 terminal 版!

以及 corfu 专用小皮筋 kind-icon ,我觉得比 all-the-icons 好看,可惜现在图 标还太少quq

(use-package corfu
  :straight t
  :custom
  ((corfu-cycle t)
   (corfu-auto t))
  :config
  (global-corfu-mode)
  (unless (display-graphic-p)
    (set-face-foreground 'corfu-default "black")
    (set-face-foreground 'corfu-echo nil)
    (set-face-attribute 'corfu-echo nil :inherit nil)))

(use-package corfu-doc
  :straight t
  :init
  (add-hook 'corfu-mode-hook #'corfu-doc-mode)
  (define-key corfu-map (kbd "M-p") #'corfu-doc-scroll-down)
  (define-key corfu-map (kbd "M-n") #'corfu-doc-scroll-up)
  (define-key corfu-map (kbd "M-d") #'corfu-doc-toggle))

(use-package cape
  :straight t
  :init
  (add-to-list 'completion-at-point-functions #'cape-file)
  (add-to-list 'completion-at-point-functions #'cape-dabbrev)
  (add-to-list 'completion-at-point-functions #'cape-keyword))

(use-package dabbrev
  :bind (("M-/" . #'dabbrev-completion)
         ("C-M-/" . #'dabbrev-expand)))

(use-package popon
  :if (not (display-graphic-p))
  :straight (:type git :repo "https://codeberg.org/akib/emacs-popon.git"))

(use-package corfu-terminal
  :if (not (display-graphic-p))
  :straight (:type git :repo "https://codeberg.org/akib/emacs-corfu-terminal.git")
  :config
  (unless (display-graphic-p)
    (corfu-terminal-mode 1)))

(use-package kind-icon
  :straight t
  :after corfu
  :custom
  (kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly
  :config
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
命令补全

vertico + orderless

也是两个小可爱,抱抱~

(use-package vertico
  :straight t
  :config
  (vertico-mode t)
  (setq vertico-count 20))

(use-package vertico-posframe
  :straight t
  :config
  (vertico-posframe-mode 1))

(use-package orderless
  :straight t
  :custom
  ((completion-styles '(orderless))))
按键提示

which-key

离开这个活不下去,会被 Emacs 的按键绑定们埋起来吃掉qwq

使用 minibuffer 显示提示信息,同时使用推荐配置。

(use-package which-key
  :straight t
  :custom
  (which-key-popup-type 'minibuffer)
  (which-key-show-early-on-C-h t)
  (which-key-idle-delay 1.0)
  (which-key-idle-secondary-delay 0.05)
  :config
  (which-key-mode)
  (setq which-key-frame-max-height 30))

(use-package which-key-posframe
  :straight t
  :config
  (set-face-background 'which-key-posframe-border "pink")
  (which-key-posframe-mode 1))
命令提示

marginalia

marginalia 大概是旁注的意思,这个包配合 ivy 或 vertico 这样的补全包,可以在 M-xC-h f 之类的地方把函数或变量的文档显示在命令旁边,实用又好看!

(use-package marginalia
  :straight t
  :config (marginalia-mode 1))
括号配对

electric-pair 和 puni

electric-pair 自动补全成对符号,中文标点也在支持之列。

唯一比较困扰我的就是中文引号输入问题,第二次输入只会出现后引号。不过这也没办法 啦,我也懒得 hacking

puni 可以按词元(额,编程里应该不叫这个名字吧)进行编辑,其实不是很需要,不 过这些天我敲代码比较多,就还是加上吧。

(use-package elec-pair
  :config (electric-pair-mode t))

(use-package puni
  :straight t
  :init
  ;; The autoloads of Puni are set up so you can enable `puni-mode` or
  ;; `puni-global-mode` before `puni` is actually loaded. Only after you press
  ;; any key that calls Puni commands, it's loaded.
  (puni-global-mode)
  (add-hook 'term-mode-hook #'puni-disable-puni-mode)
  (add-hook 'org-mode-hook #'puni-disable-puni-mode))
高级帮助

helpful

helpful 生成的 help page 比原本的 describe 系列多了好多实用内容,比如源码什么 的,很方便

(use-package helpful
  :straight t
  :bind (("C-h f" . #'helpful-callable)
         ("C-h v" . #'helpful-variable)
         ("C-h k" . #'helpful-key)))
快速选择

expand-region

智能扩展选中区域,按一下扩一次

(use-package expand-region
  :straight t
  :bind ("C-=" . #'er/expand-region))
文件管理

Dirvish

基于 dired ,超厉害的文件管理

(use-package dirvish
  :straight t
  :requires (all-the-icons)
  :custom (dirvish-attributes '(all-the-icons file-size))
  :bind ("C-c d" . #'dirvish))
EAF

EAF

;; (use-package eaf
;;   :if (file-exists-p "~/.emacs.d/site-lisp/emacs-application-framework")
;;   :load-path "~/.emacs.d/site-lisp/emacs-application-framework"
;;   :custom
;;   (eaf-browser-continue-where-left-off t)
;;   (eaf-browser-enable-adblocker t)
;;   (browse-url-browser-function 'eaf-open-browser)
;;   :bind ("C-c l" . #'eaf-open-browser-with-history)
;;   :config
;;   (require 'eaf-airshare)
;;   (require 'eaf-browser)
;;   (require 'eaf-demo)
;;   (require 'eaf-file-browser)
;;   (require 'eaf-file-manager)
;;   (require 'eaf-git)
;;   (require 'eaf-image-viewer)
;;   (require 'eaf-markdown-previewer)
;;   (require 'eaf-mindmap)
;;   (require 'eaf-org-previewer)
;;   (require 'eaf-pdf-viewer)
;;   (require 'eaf-rss-reader)
;;   (require 'eaf-terminal)
;;   (require 'eaf-video-player)
;;   (require 'eaf-vue-demo)
;;   (defalias 'browse-web #'eaf-open-browser)
;;   (eaf-bind-key scroll_up "C-n" eaf-pdf-viewer-keybinding)
;;   (eaf-bind-key scroll_down "C-p" eaf-pdf-viewer-keybinding))
终端

eshell

因为常用,所以设个快捷键~

(use-package eshell
  :straight t
  :bind ("C-c v" . eshell))
模板补全

tempel

原先用的是 yasnippet + yasnippet-sneppets ,但后来发现有些细节用不习惯,而 且本来我也不常用 snippet ,就换成更轻量的 tempel 啦。

(use-package tempel
  :straight t
  :bind (("M-+" . tempel-complete) ;; Alternative tempel-expand
         ("M-*" . tempel-insert))
  :init
  ;; Setup completion at point
  (defun tempel-setup-capf ()
    ;; Add the Tempel Capf to `completion-at-point-functions'.
    ;; `tempel-expand' only triggers on exact matches. Alternatively use
    ;; `tempel-complete' if you want to see all matches, but then you
    ;; should also configure `tempel-trigger-prefix', such that Tempel
    ;; does not trigger too often when you don't expect it. NOTE: We add
    ;; `tempel-expand' *before* the main programming mode Capf, such
    ;; that it will be tried first.
    (setq-local completion-at-point-functions
                (cons #'tempel-expand
                      completion-at-point-functions)))
  (add-hook 'prog-mode-hook 'tempel-setup-capf)
  (add-hook 'text-mode-hook 'tempel-setup-capf))
搜索

Isearch

严格来说这大概算美化……只是加了个显示数字的功能。

但由于这个功能还是挺重要的,所以没有舍弃。

(use-package isearch
  :custom
  (isearch-lazy-count t)
  (lazy-count-prefix-format "%s/%s "))

1.1.4. 罄竹

塞上长城空自许,镜中衰鬓已先斑。

Emacs 写作配置。

输入法

pyim

万能女仆 pyim !几年前我第一次装上她,只觉得,我的天,这也太厉害了吧!

(use-package posframe
  :straight t)

(use-package pyim
  :straight t
  :bind ("M-i" . 'pyim-convert-string-at-point)
  :custom
  (pyim-page-tooltip 'posframe)
  (pyim-page-length 9)
  (pyim-english-input-switch-functions
   '(pyim-probe-dynamic-english))
  (pyim-pinyin-fuzzy-alist nil)
  (pyim-enable-shortcode nil)
  (pyim-page-posframe-border-width 1)
  (default-input-method "pyim")
  :config
  (set-face-background 'pyim-page-border "pink")
  (pyim-default-scheme 'xiaohe-shuangpin)
  ;; (pyim-isearch-mode 1)
  )

(use-package pyim-basedict
  :straight t
  :config (pyim-basedict-enable))

(use-package pyim-tsinghua-dict
  :straight (:type git :repo "https://github.com/redguardtoo/pyim-tsinghua-dict.git")
  :config (pyim-tsinghua-dict-enable))
输入法切换

sis

sis 可以尽量让系统原生输入法在 emacs 中获得比较好的体验,不过 pyim 用习惯后也 没觉得原生输入法有多好,还不能跨平台……

而且 sis 涉及到 emacs 与系统的交互,这样的包新手配置起来往往都不会一帆风顺……

现在我还开着 sis ,主要是为了在来回切屏幕的时候不影响使用,平常打字还是用 pyim.

Mac 上我使用 colemak 和双拼, emacs-plus ,需要安装 macism ;注释掉的是 windows 上的配置,需要安装中文语言和英语语言,以及 im-select

sis-get 如果因为不明原因炸掉了的话,就在终端里手动跑 ism 来看当前输入法的信 息吧。

配置照抄范例,在 windows 下去掉了光标颜色一项。

;; (use-package sis
;;   :config
;;   (sis-ism-lazyman-config "1033" "2052" 'im-select)
;;   (sis-global-respect-mode t)
;;   (sis-global-context-mode t))


;; (use-package sis
;;   :straight t
;;   :config
;;   (sis-ism-lazyman-config
;;    "com.apple.keylayout.Colemak"
;;    "com.apple.inputmethod.SCIM.Shuangpin")
;;   (sis-global-cursor-color-mode t)
;;   (sis-global-respect-mode t)
;;   (sis-global-inline-mode t))'
输入法

rime

现在不太用了,因为经常换系统,还是 pyim 这样的 emacs 原生包比较适合我。

这里注释上的是 mac 上的配置,需要先装好鼠须管。

;; (setq-default default-input-method "rime")
;; ;; (setq rime-librime-root "~/.emacs.d/librime/dist")
;; (setq rime-show-candidate 'posframe)

;; (setq rime-posframe-properties
;;  (list :internal-border-width 5))

;; (setq rime-disable-predicates
;;       '(rime-predicate-after-ascii-char-p
;; 	rime-predicate-hydra-p
;; 	rime-predicate-space-after-cc-p
;; 	rime-predicate-current-uppercase-letter-p))

;; (with-eval-after-load 'rime
;;   (define-key rime-mode-map (kbd "M-j") 'rime-force-enable))

;; (setq rime-librime-root "~/.local/bin/")
自动换行

写时与导出

设置默认长行换行显示( truncate-line ),设置80字符自动换行( auto-fill-mode 以及 fill-column )

Org-mode 导出 txt 时会72字符自动换行,可以选择关闭。

;; (setq org-ascii-text-width 9999)
(setq-default truncate-lines t)
(setq-default fill-column 80)
(add-hook 'org-mode-hook #'auto-fill-mode)
(add-hook 'text-mode-hook #'auto-fill-mode)
Org-mode

Capture, agenda…

主要配置了 capture 模板、 agenda 文件和 babel 文学编程。

决定放弃 org-journal 了,它激发不起我写作的欲望……尽管更多是我自己的原因哈哈。

  1. 行内图片

    (setq org-image-actual-width nil)
    

    这样就可以手动调整图片宽度了

  2. 标题缩进

    (add-hook 'org-mode-hook #'org-indent-mode)
    
  3. Capture 模板

    注:现在已经不用 capture 了,但是配置还是先留在这里吧。

    在 Centaur 配置基础上魔改而来,主要是去掉了计时功能(用不到),同时全面使用 Tag 来对记录进行分类。 Capture 快捷键为 C-x c 。

    同时 capture 文件即为 agenda 文件,便于管理。

    目录设置不具有通用性。

    (setq org-modules nil                 ; Faster loading
          org-directory "~/rosa"
          org-capture-templates
          `(("t" "Todo" entry (file+olp ,(concat org-directory "/todo.org"))
             "** TODO %? %^G\n")
            ("n" "Note" entry (file+olp ,(concat org-directory "/note.org"))
             "** %? %^G\n")
            ("j" "Journal" entry (file+olp+datetree
                                  ,(concat org-directory "/journal.org"))
             "** %? %U %^G\n\n")))
    
    (global-set-key (kbd "C-c c") #'org-capture)
    (global-set-key (kbd "C-c a") #'org-agenda)
    
    (setq org-agenda-files '("~/rosa/todo.org"
                             "~/rosa/note.org"
                             "~/rosa/journal.org"))
    
  4. Todo 项

    依旧照抄 Centaur ,它的设置我很喜欢ww

    (setq org-todo-keywords
          '((sequence "TODO(t)" "DOING(i)" "HANGUP(h)"
                      "|" "DONE(d)" "CANCEL(c)")))
    
  5. Babel 文学编程

    网上四处找的配置——事实上在我看来 babel 基本算是开箱即用——只要不过分追求补全 功能的话。

    实用链接

    (setq org-confirm-babel-evaluate nil
          org-src-fontify-natively t
          org-src-tab-acts-natively t
          org-edit-src-content-indentation 0)
    
中文语法高亮

cnhl

自制中文语法高亮~

在 tty 下暂时会有一些问题。

(use-package epc
  :straight t)
(add-to-list 'load-path "~/.emacs.d/site-lisp/cnhl")
(require 'cnhl)
(setq cnhl-thulac-module-path
      (expand-file-name "~/.emacs.d/thulac-model/models"))
;; (add-hook 'org-mode-hook 'cnhl-mode)
;; (add-hook 'text-mode-hook 'cnhl-mode)
(setq cnhl-after-change-waiting "1")
(cnhl-use-dependency 'hl)

(if (display-graphic-p)
    (progn (set-face-foreground 'cnhl-face-1 "#5F0000")
           (set-face-foreground 'cnhl-face-2 "#184034")
           (set-face-foreground 'cnhl-face-3 "#093060")
           (set-face-foreground 'cnhl-face-4 "#5D3026")
           (set-face-foreground 'cnhl-face-5 "#3F3000")
           (set-face-foreground 'cnhl-face-6 "#541F4F")
           (set-face-foreground 'cnhl-face-7 "gray15"))
  (progn (set-face-foreground 'cnhl-face-1 "cyan")
         (set-face-bold 'cnhl-face-1 t)
         (set-face-foreground 'cnhl-face-2 "white")
         (set-face-bold 'cnhl-face-2 t)
         (set-face-foreground 'cnhl-face-3 "cyan")
         (set-face-bold 'cnhl-face-3 nil)
         (set-face-foreground 'cnhl-face-4 "white")
         (set-face-foreground 'cnhl-face-5 "cyan")
         (set-face-foreground 'cnhl-face-6 "white")
         (set-face-foreground 'cnhl-face-7 "white")))
中文辞典

cndict

(add-to-list 'load-path "~/.emacs.d/site-lisp/cndict/")
(require 'cndict)
My Temp

temp

这里用来放一些还没来得及整理进正文的内容~

(use-package dart-mode
  :straight t)

(use-package vlf
  :straight t)

(use-package emacs-everywhere
  :straight t)

(setq default-frame-alist '((width . 90)
                            (height . 50)
                            (alpha-background . 70)))

(use-package backlight
  :straight t
  :config
  (global-set-key [XF86MonBrightnessDown]
                  #'backlight-dec)
  (global-set-key [XF86MonBrightnessUp]
                  #'backlight-inc))

(use-package alsamixer
  :straight t
  :config
  (global-set-key [XF86AudioMute]
                  #'alsamixer-toggle-mute)
  (global-set-key [XF86AudioLowerVolume]
                  #'alsamixer-down-volume)
  (global-set-key [XF86AudioRaiseVolume]
                  #'alsamixer-up-volume))

(use-package org-latex-impatient
  :straight t
  :defer t
  :hook (org-mode . org-latex-impatient-mode)
  :init
  (setq org-latex-impatient-tex2svg-bin
        ;; location of tex2svg executable
        "~/node_modules/mathjax-node-cli/bin/tex2svg"))

(use-package laas
  :hook (LaTeX-mode . laas-mode)
  :hook (org-mode . laas-mode)
  :config ; do whatever here
  (aas-set-snippets 'laas-mode
    ;; set condition!
    :cond #'texmathp ; expand only while in math
    ";t" nil
    ";tf" "\\therefore"
    ";b" nil
    ";bc" "\\because"
    ;; bind to functions!
    "cases" (lambda () (interactive)
              (yas-expand-snippet "\\begin{cases}$0\\end{cases}"))
    "align" (lambda () (interactive)
              (yas-expand-snippet "\\begin{align}$0\\end{align}"))
    ;; add accent snippets
    :cond #'laas-object-on-left-condition
    "qq" (lambda () (interactive) (laas-wrap-previous-object "sqrt"))))

(use-package aas
  :straight t
  :hook (LaTeX-mode . aas-activate-for-major-mode)
  :hook (org-mode . aas-activate-for-major-mode)
  :hook (emacs-lisp-mode . aas-activate-for-major-mode)
  :config
  (aas-set-snippets 'text-mode
    ;; expand unconditionally
    ";o-" "ō"
    ";i-" "ī"
    ";a-" "ā"
    ";u-" "ū"
    ";e-" "ē"))

(setq org-preview-latex-default-process 'dvipng)
(setq org-latex-pdf-process
      '("xelatex --pdf -interaction nonstopmode -output-directory %o %f"))
(setq org-preview-latex-process-alist
      '((dvipng :programs
         ("latex" "dvipng")
         :description "dvi > png" :message "you need to install the programs: latex and dvipng." :image-input-type "dvi" :image-output-type "png" :image-size-adjust
         (1.0 . 1.0)
         :latex-compiler
         ("latex -interaction nonstopmode -output-directory %o %f")
         :image-converter
         ("dvipng -D %D -T tight -bg Transparent -o %O %f"))
 (dvisvgm :programs
          ("xelatex" "dvisvgm")
          :description "xdv > svg" :message "you need to install the programs: xelatex and dvisvgm." :image-input-type "xdv" :image-output-type "svg" :image-size-adjust
          (1.7 . 1.5)
          :latex-compiler
          ("xelatex --no-pdf -interaction nonstopmode -output-directory %o %f")
          :image-converter
          ("dvisvgm %f -n -b min -c %S -o %O"))))

(setq org-format-latex-options
      '(:foreground
        default
        :background "Transparent"
        :scale 1.5
        :html-foreground "Black"
        :html-background "Transparent"
        :html-scale 1.0
        :matchers
        ("begin" "$1" "$" "$$" "\\(" "\\[")))

(use-package exwm
  :straight t)

(use-package exwm-config
  :config
  (exwm-config-example)
  (ido-mode -1))

(use-package exwm-systemtray
  :config (exwm-systemtray-enable))

(use-package exwm-xim
  :config
  (setenv "GTK_IM_MODULE" "xim")
  (setenv "QT_IM_MODULE" "xim")
  (setenv "XMODIFIERS" "@im=exwm-xim")
  (setenv "CLUTTER_IM_MODULE" "xim")
  (exwm-xim-enable)
  (push ?\C-\\ exwm-input-prefix-keys))   ;; use Ctrl + \ to switch input method

(defun rosa/picom ()
  (if (get-process "picom")
      (progn (kill-process (get-process "picom"))
             (make-process
              :name "picom"
              :command '("picom")
              :noquery t))
    (make-process
     :name "picom"
     :command '("picom")
     :noquery t)))

;; (use-package helm
;;   :straight (:type git :host github :repo "emacs-helm/helm" :depth 1)
;;   :config 
;;   (global-set-key (kbd "M-x") #'helm-M-x)
;;   (global-set-key (kbd "C-x r b") #'helm-filtered-bookmarks)
;;   (global-set-key (kbd "C-x C-f") #'helm-find-files)
;;   (helm-mode -1)
;;   (setq helm-describe-function-function #'helpful-callable
;;         helm-describe-variable-function #'helpul-variable
;;         helm-mode-fuzzy-match t))

(use-package wallpaper
  :straight t
  :custom (wallpaper-static-wallpaper-list
           '("~/Pictures/wallpaper/1.jpg"))
  :config
  (wallpaper-set-wallpaper)
  (rosa/picom))

;; (use-package prog-mode
;;   :config
;;   (global-prettify-symbols-mode)
;;   (add-hook 'emacs-lisp-mode-hook
;;             #'(lambda () (set 'prettify-symbols-alist
;;                               '(("lambda" . λ))))))

;; (add-to-list 'load-path "~/.emacs.d/site-lisp/lsp-bridge")
;; (require 'lsp-bridge)
;; (dolist (hook (list
;; 	       'c-mode-hook
;; 	       'c++-mode-hook
;; 	       'python-mode-hook
;; 	       'ruby-mode-hook
;; 	       'rust-mode-hook
;; 	       'elixir-mode-hook
;; 	       'go-mode-hook
;; 	       'haskell-mode-hook
;; 	       'haskell-literate-mode-hook
;; 	       'dart-mode-hook
;; 	       'scala-mode-hook
;; 	       'typescript-mode-hook
;; 	       'js2-mode-hook
;; 	       'js-mode-hook
;; 	       ))
;;   (add-hook hook (lambda ()
;; 		   (lsp-bridge-enable)
;; 		   )))

1.1.5. 传书

马上相逢无纸笔,凭君传语报平安。

Emacs 通讯工具配置。

电报聊天

telega

因为 emacs-ot 群的大家都好可爱,实在放不下呢。

因为众所周知的原因,telegram 需要过代理,代理配置如下:

(setq telega-proxies (list
                      '(:server "127.0.0.1"
                                :port 7890
                                :enable t
                                :type (:@type "proxyTypeSocks5"))))
邮件

Wanderlust ,通过 sendmail 远程发件

使用 wanderlust 作为 emacs 邮箱客户端, emacs 邮箱一站式解决方案~

根据 LdBeth 前辈的教程配置。

使用 sendmail 方法发件,但通过脚本 ssh 调用远程服务器的 sendmail 程序,从而实 现 ssh 远程发件。

随便看看就好,没多少参考价值。

(setq user-full-name "Rosario S.E."
      user-mail-address "rosa@sdf.org")

(setq wl-draft-send-mail-function 'wl-draft-send-mail-with-sendmail)
(setq sendmail-program "/usr/sbin/sendmail")
(setq message-send-mail-function 'sendmail-send-it)
(setq send-mail-function 'sendmail-send-it)

(setq org-mime-library 'semi)

(setq sendmail-program "~/rosa/cc-sendmail.sh")

(if (boundp 'mail-user-agent)
    (setq mail-user-agent 'wl-user-agent))
(if (fboundp 'define-mail-user-agent)
    (define-mail-user-agent
      'wl-user-agent
      'wl-user-agent-compose
      'wl-draft-send
      'wl-draft-kill
      'mail-send-hook))

1.1.6. 已矣

仆夫悲余马怀兮,蜷局顾而不行

Emacs 配置结束,可以开始了,可以停止了。

(toggle-frame-fullscreen)

1.2. 程序

1.2.1. cnhl

Chinese Highlight ——中文语法高亮与分词工具

欲寄彩笺兼尺素,山长水阔知何处? —— (北宋)晏殊

愿这个小工具能为你的文思增添一抹别样的色彩。

  1. 项目简介

    Cnhl 是 Emacs 的一个中文文本高亮与分词工具,可以在 Emacs 中如同代码高亮一般根据 词性高亮中文文本,亦可以进行按词移动、按词编辑等操作。

  2. 截图

    4.gif

    5.gif

    1.gif

    2.gif

    3.png

  3. 安装方法
    1. 安装 Cnhl

      下载 cnhl.elcnhl-thulac.cppcnhl-fasthan.py 三个文件,将其放在同一 目录下,并将该目录添加至 load-path

      git clone --depth 1 https://github.com/3vau/cnhl.git
      
      (add-to-list 'load-path "~/path/to/cnhl/")
      (require 'cnhl)
      

      M-x cnhl-mode 开始使用 Cnhl。

    2. 选择并安装 nlp 工具

      Cnhl 目前支持 THULAC 和 FastHan 两款 NLP 工具,两种工具各有千秋。你可以全部安装, 也可以任选一种。

      THULAC 只有词法分析功能,且自带的模型较大,解压后大小在 500M 左右;但 Cnhl 通过 Dynamic Module 调用 C++ 版 THULAC ,且 THULAC 自身分析速度快,因此初始化耗时很短, 且使用时十分流畅。

      FastHan 兼具词法分析与依存句法分析功能,可以根据句子结构而非词性进行高亮与跳转, 且词法分析准确率高于 THULAC (在 fastHan 官方给出的测试数据中高出约 5%) ; FastHan 提供 base 和 large 两种模型, base 模型大小在 150M 左右, large 模型则为 250M 。与此同时, Cnhl 还提供基于 FastHan 的句子分析与显示功能,同时准备在此基础 上进一步开发更多实用功能。

      但 FastHan 模型初始化的速度较慢,且句法分析对 CPU 的占用较高,同时 FastHan 本身 以 Python 写成,与 Emacs 交互存在一定的延迟。在语法高亮时,这部分延迟通过异步执 行得以解决,但在按句法进行文本操作时(即句法版的按词操作),这段微小的卡顿甚至是 肉眼可见的。

      内存占用方面,两者相差不大,都会额外占用 200M - 400M 左右。

      设置 cnhl-nlp-selected 变量指定要使用的 NLP 工具:

      • THULAC

        (setq cnhl-nlp-selected "thulac")
        
      • FastHan

        (setq cnhl-nlp-selected "fasthan")
        

        设置 cnhl-fasthan-use-large-model-p 变量决定是否使用 large 模型。默认为不使 用。注意,更大的模型往往意味着更大的计算量,而在 fastHan 官方给出的模型表现结 果中, large 模型的准确率只比 base 模型高出一到两个百分点。

        (setq cnhl-fasthan-use-large-model-p t)
        

      如果同时安装了两种 NLP 工具,则可以使用 cnhl-switch-nlp 函数在两种 NLP 间快速 切换。该函数只推荐在运行时使用。

      1. 安装 THULAC 词法分析工具

        THULAC(THU Lexical Analyzer for Chinese)由清华大学自然语言处理与社会人文计算实 验室研制推出的一套中文词法分析工具包,具有中文分词和词性标注功能。

        孙茂松, 陈新雄, 张开旭, 郭志芃, 刘知远. THULAC:一个高效的中文词法分析工具包. 2016.

        THULAC 提供中文词法分析功能。使用 THULAC 作为后端时, Cnhl 将按照词语的词 性对其进行高亮,并按照不同词性的分词进行按词跳转。

        Cnhl 使用 C++ 版 THULAC 作为后端,使用前需要手动下载 THULAC 模型并告知 Cnhl 模型所在的文件夹。

        可以前往 THULAC 官网 填写信息以下载 THULAC v1.2 算法模型压缩包,解压后得到的 models 文件夹下的即为算法模型。

        (如果不愿填写信息,可以联系我。)

        下载并解压 THULAC 模型后,将下面这段代码中 cnhl-thulac-module-path 的值改为 模型所在目录,将其添加到你的配置文件中。

        ( THULAC C++ 版无法识别“~”,所以务必使用 expand-file-name 。)

        (setq cnhl-thulac-module-path
              (expand-file-name "~/path/to/module/dir"))
        
      2. 安装 FastHan 依存句法分析工具

        “fastHan 是基于 fastNLP 与 pytorch 实现的中文自然语言处理工具,像 spacy 一样调用 方便。”

        Zhichao Geng, Hang Yan, Xipeng Qiu and Xuanjing Huang, fastHan: A BERT-based Multi-Task Toolkit for Chinese NLP, ACL, 2021.

        使用 pip 安装 fastHan:

        pip install fastHan
        

        即可开始使用。

        你也可以前往 fastHan 的 github 主页查看更多关于 fastHan 的信息。

  4. 配置注意
    • Cnhl 语法高亮与按词操作的行为模式是相对分离的。在 fastHan 下,你可以通过 cnhl-use-dependency 函数分别指定高亮与按词操作的模式,例如根据句法进行高亮, 而依照词法结果进行按词操作。该方法既可以在运行时使用,也可以通过 elisp 调用:

      (cnhl-use-dependency 'hl)
      

      将其加入配置文件,以默认使用句法分析进行高亮。

    • 设置 cnhl-after-change-waiting 以自定义输入完毕后多长时间高亮已输入文本。默 认为 0.5s 。该变量使用 Emacs 式的字符串时间表示法。

      若使用 fastHan 进行句法分析,推荐适当增长该间隔,以减少资源开支。

      (setq cnhl-after-change-waiting "1")
      
    • Cnhl 的默认主题适用于暗色背景。若使用亮色主题,可以将以下设置加入配置文件:

      (set-face-foreground 'cnhl-face-1 "#5F0000")
      (set-face-foreground 'cnhl-face-2 "#184034")
      (set-face-foreground 'cnhl-face-3 "#093060")
      (set-face-foreground 'cnhl-face-4 "#5D3026")
      (set-face-foreground 'cnhl-face-5 "#3F3000")
      (set-face-foreground 'cnhl-face-6 "#541F4F")
      (set-face-foreground 'cnhl-face-7 "gray15")
      

      Cnhl 的默认高亮配色皆取自 modus-theme 的各级 org 标题颜色。如果想探索新的 配色方案, Adobe Color 可能对你有所帮助。

    1. 我的配置
      (add-to-list 'load-path "~/.emacs.d/cnhl")
      (require 'cnhl)
      (setq cnhl-thulac-module-path
            (expand-file-name "~/.emacs.d/thulac-model/models"))
      (add-hook 'org-mode-hook 'cnhl-mode)
      (add-hook 'text-mode-hook 'cnhl-mode)
      (setq cnhl-after-change-waiting "1")
      (cnhl-use-dependency 'hl)
      
  5. 使用方法
    • 开启 cnhl-mode , Cnhl 会使用选择的 NLP 和高亮模式自动高亮输入的文本,并且使 用中文按词操作函数替换 emacs 自身的按词操作。
    • 使用 cnhl-hl-buffercnhl-hl-paragraphcnhl-hl-sentence 分别对当前 buffer / 当前段落 / 当前句进行中文语法高亮。该功能不需开启 cnhl-mode
    • 使用 cnhl-switch-nlp 切换当前使用的 NLP 工具;
    • 使用 cnhl-analyze-sentence 函数分析一句话的词性与依存关系,并将分析结果以可 视化的形式输出在当前光标位置。若无参数运行,则取上一次高亮的句子分析。
  6. 使用注意

    任何 NLP 工具,其结果皆不会绝对准确。甚至在某些情况下会有不小的错误率。 Cnhl 标注的结果仅供参考。

    如果你有任何建议,或发现了 Cnhl 的任何问题,都可以联系我

    目前在做的增强功能:在 mode-line 加入光标所在词的词性与依存关系提示;优化fastHan 异步执行方法;增强可视化分析句子的功能;支持 fastHan 自定义模型位置;支持fastHan 模型微调;

  7. 致谢

    感谢Emacs China论坛前辈们的热心帮助:

    感谢大家对 Cnhl 的开发提出的建议和问题:

    感谢 GWQ 同学cnhl C++ 部分代码开发的帮助。

    广告:欢迎大家去体验 GWQ 同学的 Demucs-Gui 项目,对音频特征提取工具 Demucs 进行 了算法优化和图形化,预计将于 2022 年 4 月前发布第一版。其实他的初衷是帮助我们年 级英语配音大赛各班的参赛组消除视频中的人声……

    感谢大家的使用、鼓励与认可!

    本程序使用了 THULAC:一个高效的中文词法分析工具包,谨在此致以感谢:

    孙茂松, 陈新雄, 张开旭, 郭志芃, 刘知远. THULAC:一个高效的中文词法分析工具包. 2016.

  8. GPL-3.0 声明

    This file is not part of GNU Emacs.

    This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

  9. Cnhl Emacs 部分源代码

    以下是 cnhl 的源码,以文学编程的方式较为详细地写出了每个功能的设计思路和实现方式, 可以放心食用~

    1. debug
      (defvar cnhl-install-dir
        (file-name-directory (or load-file-name buffer-file-name)))
      
      (setq cnhl-install-dir (expand-file-name "~/.emacs.d/cnhl/"))
      
      (require 'epc)
      (setq cnhl-fasthan-epc
            (epc:start-epc "python"
                           `(,(expand-file-name "cnhl-fasthan.py"
                                                cnhl-install-dir))))
      (epc:call-sync cnhl-fasthan-epc 'fasthan_init_model '("" ""))
      (epc:call-sync cnhl-fasthan-epc 'fasthan_parsing_string '("我爱北京天安门"))
      
    2. 头部注释

      包含 GPL 声明和英文简介啊什么的,例行公事~

      ;;; cnhl.el --- Make Chinese highlight lexically -*- lexical-binding: t -*-
      
      ;; Copyright (C) 2022 Rosario S.E.
      
      ;; Author: Rosario S.E. <ser3vau@gmail.com>
      ;; URL: https://github.com/3vau/cnhl
      
      ;; This file is not part of GNU Emacs.
      ;;
      ;; This program is free software; you can redistribute it and/or modify
      ;; it under the terms of the GNU General Public License as published by
      ;; the Free Software Foundation, either version 3 of the License, or
      ;; (at your option) any later version.
      
      ;; This program is distributed in the hope that it will be useful,
      ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
      ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      ;; GNU General Public License for more details.
      
      ;; You should have received a copy of the GNU General Public License
      ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
      
      ;;; Commentary:
      ;; A Emacs trick to make Chinese highlight lexically.
      ;;
      ;; It used THULAC (THU Lexical Analyzer for Chinese) by Tsinghua University.
      ;; Maosong Sun, Xinxiong Chen, Kaixu Zhang, Zhipeng Guo, Zhiyuan Liu. THULAC: An Efficient Lexical Analyzer for Chinese. 2016.
      ;;
      ;; For more infomation, read https://github.com/3vau/cnhl/blob/main/README.md
      ;; and https://emacs-china.org/t/topic/18977/38
      ;;
      ;; Thanks to people who helped me:
      ;;  @LdBeth http://ldbeth.sdf.org/
      ;;  @cireu https://citreu.gitlab.io/
      ;;  @twlz0ne https://emacs-china.org/u/twlz0ne
      
      ;;; Code:
      
    3. 设置安装目录

      安装目录用于载入外部文件,如 THULAC 模型和 FastHan Python 文件。

      (defvar cnhl-install-dir
        (file-name-directory (or load-file-name buffer-file-name))
        "Cnhl 的安装目录。请将 cnhl.el, cnhl-thulac.so / cnhl-thulac.dll, cnhl-fasthan.py
      放在该文件夹下。")
      
    4. 设置 NLP

      NLP 是“自然语言处理”的缩写, Cnhl 使用第三方 NLP 工具作为后端来解析中文语句,以 进行中文语法高亮。

      1. 选择 NLP

        设置 cnhl-nlp-selected 变量以选择要使用的 NLP 工具。

        目前 Cnhl 支持的 NLP 工具有: THULAC / FastHan

        cnhl-switch-nlp 用于在使用过程中切换 nlp ,不建议在配置文件中使用。

        (defvar cnhl-nlp-selected "fasthan"
          "指定 Cnhl 使用的 NLP 工具类型。
        若该值为 \"thulac\" ,则使用 THULAC 工具。请完成 THULAC 模型位置的设置。
        若该值为 \"fasthan\" ,则使用 FastHan 工具。请安装 FastHan pip 模组。
        默认使用 FastHan 。 ")
        
        (defun cnhl-switch-nlp (nlp)
          (interactive "s请输入你想切换到的 nlp 工具(THULAC / FastHan):")
          (setq nlp (downcase nlp))
          (pcase nlp
            ("thulac"
             (setq cnhl-nlp-selected "thulac"
                   cnhl-hl-use-dep-p nil
                   cnhl-word-use-dep-p nil)
             (setq cnhl-nlp-reinit 'reload))
            ("fasthan"
             (setq cnhl-nlp-selected "fasthan")
             (setq cnhl-nlp-reinit 'reload))
            (_ (message "抱歉,未能识别你输入的 nlp 名称。"))))
        
      2. 选择高亮与分词模式

        根据 NLP 工具的不同, Cnhl 提供基于词法分析和基于依存句法分析的两种高亮与分词模 式。

        cnhl-hl-use-dep-p 变量指示是否使用依存句法方式进行语法高亮,而 cnhl-word-use-dep-p 变量指示是否使用依存句法方式进行按词操作。

        使用 cnhl-use-dependency 方法设置这两个变量。它在设置前提前检查目前选择的 nlp 工具是否具有依存句法分析的功能——比如 THULAC 就没有。这时候它就会只选择可用的方式。

        (defvar cnhl-hl-use-dep-p nil)
        (defvar cnhl-word-use-dep-p nil)
        
        (defun cnhl-use-dependency (&optional type)
          "设置 Cnhl 是否使用依存句法分析。
        
        若 type 为 \"hl\" ,则使用依存句法方式进行高亮,使用普通分词方式进行按词操作;
        若 type 为 \"word\" ,则使用词法分析方式进行高亮,使用依存句法方式进行按词操作;
        若 type 为空或 nil ,则高亮和按词操作都使用词法分析;
        若 type 为其它非 nil 的值,则高亮和按词操作都使用依存句法分析。
        
        默认情况下, Cnhl 的高亮和按词操作都使用词法分析。"
          (interactive "s请输入你要切换到的行为模式(\"hl\":句法高亮、词法分词; \"word\":词法高亮、句法分词;空则全词法,其它非nil值则全句法):")
          (if (and (equal cnhl-nlp-selected "thulac")
                   type)
              (progn (setq cnhl-hl-use-dep-p nil
                           cnhl-word-use-dep-p nil)
                     (message "当前选择的 NLP 不具备依存句法分析能力,自动使用词法分析。"))
            (progn (if type
                       (pcase type
                         ("hl" (setq cnhl-hl-use-dep-p t
                                    cnhl-word-use-dep-p nil))
                         ("word" (setq cnhl-hl-use-dep-p nil
                                      cnhl-word-use-dep-p t))
                         (_ (setq cnhl-hl-use-dep-p t
                                  cnhl-word-use-dep-p t)))
                     (setq cnhl-hl-use-dep-p nil
                           cnhl-word-use-dep-p nil))
                   t))
          (setq cnhl-nlp-reinit t))
        ;; (cnhl-use-dependency 'hl)
        
      3. 针对选择的 NLP 进行特别设置
        1. THULAC
          1. 编译 THULAC Dynamic Module
            (defun cnhl-thulac-compile-module ()
              (if (file-exists-p (expand-file-name "cnhl-thulac.cpp"
                                                   cnhl-install-dir))
                  (async-shell-command
                   (format "git clone --depth 1 https://github.com/thunlp/THULAC.git %s ; g++ -shared -fPIC -I %s -std=c++11 %s -o %s"
                           (expand-file-name "thulac/"
                                             cnhl-install-dir)
                           (expand-file-name "thulac/include/"
                                             cnhl-install-dir)
                           (expand-file-name "cnhl-thulac.cpp"
                                             cnhl-install-dir)
                           (expand-file-name (if (equal system-type 'windows-nt)
                                                 "cnhl-thulac.dll"
                                               "cnhl-thulac.so")
                                             cnhl-install-dir)))
                (error "cnhl-thulac.cpp 源文件不存在,请重新下载 Cnhl。")))
            
          2. 设置 THULAC 分词模型的路径

            请将 cnhl-thulac-module-path 变量设置为 THULAC 算法模型文件夹的位置。

            可以前往 THULAC 官网 下载 THULAC v1.2 算法模型压缩包,解压后得到的 models 文件 夹即为模型文件夹。

            (defvar cnhl-thulac-module-path
              cnhl-install-dir
              "THULAC 算法模型文件夹的目录。")
            
        2. FastHan
          1. 设置是否使用更大的模型

            是否使用 FastHan 的 large 模型。

            (defvar cnhl-fasthan-use-large-model-p nil
              "是否使用 FastHan 的 large 模型。
            
            Base 模型占用 350M 左右的内存; large 模型占用 450M 左右的内存。
            
            默认使用 base 模型。
            
            若想实时生效,请在设置后手动执行 \"(cnhl-nlp-init t)\"。")
            
      4. 初始化 NLP 并绑定对应的函数

        大多数 NLP 工具都需要初始化,将算法模型读入内存,才可以进行使用。

        该函数将被未经初始化的 NLP 分析函数调用,并在初始化指定 NLP 后通过 advice 的方 式将抽象的 NLP 分析函数映射到对应 NLP 的专用分析函数上。

        (defvar cnhl-nlp-initialized nil
          "指示 Cnhl 是否已经初始化。")
        
        (defvar cnhl-nlp-reinit nil
          "指示是否需要在下次使用 cnhl 函数时重新进行 nlp 初始化设置。
        若该值为 'reload ,则在下次使用时重新加载 nlp 模型并绑定函数;
        若该值为其它非 nil 的值,则在下次使用时只重新绑定函数,不重新加载 nlp 模型。")
        
        (defun cnhl-nlp-init (&optional restart-nlp-p)
          (setq restart-nlp-p (or restart-nlp-p
                                  (null cnhl-nlp-initialized)))
          (when restart-nlp-p
            (when cnhl-fasthan-epc
              (cnhl-nlp-deinit-fasthan))
            (cnhl-nlp-deinit-thulac))
          (advice-remove 'cnhl-nlp-analyse-sentence 'analyse-func)
          (advice-remove 'cnhl-nlp-get-overlay 'overlay-func)
          (pcase cnhl-nlp-selected
            ("thulac"
             (when restart-nlp-p (cnhl-nlp-init-thulac))
             (advice-add 'cnhl-nlp-analyse-sentence
                         :override #'cnhl-thulac-analyse-sentence
                         (list (cons 'name 'analyse-func)))
             (advice-add 'cnhl-nlp-get-overlay
                         :override #'cnhl-get-overlay-thulac
                         (list (cons 'name 'analyse-func))))
            ("fasthan"
             (when restart-nlp-p (cnhl-nlp-init-fasthan))
             (advice-add 'cnhl-nlp-analyse-sentence
                         :override #'cnhl-fasthan-analyze-sentence
                         (list (cons 'name 'analyse-func)))
             (if cnhl-hl-use-dep-p
                 (advice-add 'cnhl-nlp-get-overlay
                             :override #'cnhl-get-overlay-dep
                             (list (cons 'name 'analyse-func)))
               (advice-add 'cnhl-nlp-get-overlay
                           :override #'cnhl-get-overlay-stanford
                           (list (cons 'name 'analyse-func))))))
          (setq cnhl-nlp-initialized t
                cnhl-nlp-reinit nil))
        
        1. 检查是否需要重新初始化的函数

          该函数将被分析函数调用,检查重新初始化的执行状态,并在需要时重新初始化 Cnhl 。

          (defun cnhl-nlp-reinit-check ()
            (when cnhl-nlp-reinit
              (pcase cnhl-nlp-reinit
                ('reload (cnhl-nlp-init t))
                (_ (cnhl-nlp-init)))))
          
        2. THULAC 的初始化与反初始化

          cnhl-nlp-init 函数将初始化 THULAC 工具,载入 THULAC 模型,如果未找到模型则尝试 编译; Dynamic Module 内的 cnhl-thulac-module-deinit 将调用 THULAC 类提供的 deinit() 函数释放模型。

          (defun cnhl-nlp-init-thulac ()
            (condition-case err
                (require 'cnhl-thulac)
              ('file-missing (cnhl-thulac-compile-module)
                             (require 'cnhl-thulac)))
            (cnhl-thulac-module-init cnhl-thulac-module-path))
          
          (defun cnhl-nlp-deinit-thulac ()
            (condition-case err
                (cnhl-thulac-module-deinit)
              (t nil)))
          
        3. FastHan 的初始化与反初始化

          cnhl-nlp-init-fasthan 函数将初始化 FastHan ,使用 epc 新建一个 python 连接并载入 FastHan 模型;

          cnhl-nlp-deinit-fasthan 将关闭 epc 进程。

          (defvar cnhl-fasthan-epc nil)
          
          (defun cnhl-nlp-init-fasthan ()
            (require 'epc)
            (setq cnhl-fasthan-epc
                  (epc:start-epc "python"
                                 `(,(expand-file-name "cnhl-fasthan.py"
                                                      cnhl-install-dir))))
            (epc:call-sync cnhl-fasthan-epc 'fasthan_init_model
                           `(,@(if cnhl-fasthan-use-large-model-p
                                   (list "large" "")
                                 (list "base" "")))))
          
          (defun cnhl-nlp-deinit-fasthan ()
            (epc:stop-epc cnhl-fasthan-epc)
            (setq cnhl-fasthan-epc nil))
          
    5. 高亮主题的定义与相关方法
      1. 定义高亮主题色
        (defgroup cnhl nil
          "Cnhl 高亮颜色。"
          :group 'cnhl)
        (defface cnhl-face-1
          '((t (:foreground "#FFCCCC")))
          "第一种,在 THULAC 中是名词、代词、简称颜色"
          :group 'cnhl)
        (defface cnhl-face-2
          '((t (:foreground "#BFEBE0")))
          "第二种,在 THULAC 中是动词、习语颜色"
          :group 'cnhl)
        (defface cnhl-face-3
          '((t (:foreground "#C6EAFF")))
          "第三种,在 THULAC 中是形容词颜色"
          :group 'cnhl)
        (defface cnhl-face-4
          '((t (:foreground "#F8DEC0")))
          "第四种,在 THULAC 中是方位词、处所词、时间词、数词、量词、数量词颜色"
          :group 'cnhl)
        (defface cnhl-face-5
          '((t (:foreground "#DFDFB0")))
          "第五种,在 THULAC 中是副词、连词、介词颜色"
          :group 'cnhl)
        (defface cnhl-face-6
          '((t (:foreground "#E5CFEF")))
          "第六种,在 THULAC 中是助词、语气助词、前接成分、后接成分颜色"
          :group 'cnhl)
        (defface cnhl-face-7
          '((t (:foreground "gray85")))
          "第七种,在 THULAC 中是语素、标点、叹词、拟声词及其它颜色"
          :group 'cnhl)
        
        ;; dark
        
        ;; (set-face-foreground 'cnhl-face-1 "#5F0000")
        ;; (set-face-foreground 'cnhl-face-2 "#184034")
        ;; (set-face-foreground 'cnhl-face-3 "#093060")
        ;; (set-face-foreground 'cnhl-face-4 "#5D3026")
        ;; (set-face-foreground 'cnhl-face-5 "#3F3000")
        ;; (set-face-foreground 'cnhl-face-6 "#541F4F")
        ;; (set-face-foreground 'cnhl-face-7 "gray15")
        
      2. 建立每个颜色的第一个 overlay

        此后所有高亮所使用的 overlay 皆复制于这里。这是为了避免 Invalid face reference 错误。

        (defvar cnhl-overlay-1 (make-overlay 1 1))
        (defvar cnhl-overlay-2 (make-overlay 1 1))
        (defvar cnhl-overlay-3 (make-overlay 1 1))
        (defvar cnhl-overlay-4 (make-overlay 1 1))
        (defvar cnhl-overlay-5 (make-overlay 1 1))
        (defvar cnhl-overlay-6 (make-overlay 1 1))
        (defvar cnhl-overlay-7 (make-overlay 1 1))
        
        (overlay-put cnhl-overlay-1 'face 'cnhl-face-1)
        (overlay-put cnhl-overlay-2 'face 'cnhl-face-2)
        (overlay-put cnhl-overlay-3 'face 'cnhl-face-3)
        (overlay-put cnhl-overlay-4 'face 'cnhl-face-4)
        (overlay-put cnhl-overlay-5 'face 'cnhl-face-5)
        (overlay-put cnhl-overlay-6 'face 'cnhl-face-6)
        (overlay-put cnhl-overlay-7 'face 'cnhl-face-7)
        
      3. 从词性代号返回对应高亮颜色的 overlay

        建立一个词性代号的首字母与原始 overlay 的 alist 对应关系列表,通过查询该列表来获 取某词性应贴的 overlay 。

        (defun cnhl-nlp-get-overlay (str)
          (cnhl-nlp-init)
          (cnhl-nlp-get-overlay str))
        
        1. THULAC
          (defvar cnhl-overlay-alist-thulac
            (list (cons "n" cnhl-overlay-1)
                  (cons "r" cnhl-overlay-1)
                  (cons "j" cnhl-overlay-1)
                  (cons "u" cnhl-overlay-6)
                  (cons "y" cnhl-overlay-6)
                  (cons "h" cnhl-overlay-6)
                  (cons "k" cnhl-overlay-6)
                  (cons "v" cnhl-overlay-2)
                  (cons "i" cnhl-overlay-2)
                  (cons "a" cnhl-overlay-3)
                  (cons "d" cnhl-overlay-5)
                  (cons "c" cnhl-overlay-5)
                  (cons "p" cnhl-overlay-5)
                  (cons "g" cnhl-overlay-7)
                  (cons "w" cnhl-overlay-7)
                  (cons "x" cnhl-overlay-7)
                  (cons "e" cnhl-overlay-7)
                  (cons "o" cnhl-overlay-7))
            "存储词性标记首字母与 overlay 对应关系的 alist")
          
          (defun cnhl-get-overlay-thulac (str)
            "匹配词性类型对应的face"
            (or (cdr (assoc (string (aref str 0)) cnhl-overlay-alist-thulac))
                cnhl-overlay-4)) ;; 用首字母从 alist 中获取值
          
        2. FastHan
          (defvar cnhl-overlay-alist-stanford
            (list (cons "VA" cnhl-overlay-3)
                  (cons "VC" cnhl-overlay-2)
                  (cons "VE" cnhl-overlay-2)
                  (cons "VV" cnhl-overlay-2)
                  (cons "NR" cnhl-overlay-1)
                  (cons "NT" cnhl-overlay-1)
                  (cons "NN" cnhl-overlay-1)
                  (cons "LC" cnhl-overlay-4)
                  (cons "PN" cnhl-overlay-1)
                  (cons "DT" cnhl-overlay-4)
                  (cons "CD" cnhl-overlay-4)
                  (cons "OD" cnhl-overlay-4)
                  (cons "M" cnhl-overlay-4)
                  (cons "AD" cnhl-overlay-5) 
                  (cons "P" cnhl-overlay-5)
                  (cons "CC" cnhl-overlay-5)
                  (cons "CS" cnhl-overlay-5)
                  (cons "DEC" cnhl-overlay-5)
                  (cons "DEG" cnhl-overlay-5)
                  (cons "DER" cnhl-overlay-5)
                  (cons "DEV" cnhl-overlay-5)
                  (cons "AS" cnhl-overlay-6)
                  (cons "SP" cnhl-overlay-6)
                  (cons "ETC" cnhl-overlay-6)
                  (cons "MSP" cnhl-overlay-6)
                  (cons "IJ" cnhl-overlay-7)
                  (cons "ON" cnhl-overlay-7)
                  (cons "LB" cnhl-overlay-5)
                  (cons "SB" cnhl-overlay-5)
                  (cons "BA" cnhl-overlay-5)
                  (cons "JJ" cnhl-overlay-3)
                  (cons "FW" cnhl-overlay-1)
                  (cons "PU" cnhl-overlay-7)))
          
          (defun cnhl-get-overlay-stanford (str)
            (or (cdr (assoc str cnhl-overlay-alist-stanford))
                cnhl-overlay-7))
          
      4. 从依存关系标识返回对应高亮颜色的 overlay

        依存关系标识为对应标签的后三个字母。见下方为依存句法分析的特别计算部分。

        (defvar cnhl-overlay-alist-dep
          (list (cons "ubj" cnhl-overlay-1)
                (cons "ass" cnhl-overlay-1)
                (cons "obj" cnhl-overlay-3)
                (cons "oot" cnhl-overlay-6)
                (cons "ssm" cnhl-overlay-5)
                (cons "omp" cnhl-overlay-2)
                (cons "onj" cnhl-overlay-4)
                (cons "nct" cnhl-overlay-7)))
        
        (defun cnhl-get-overlay-dep (tag)
          (assoc-default tag cnhl-overlay-alist-dep))
        
      5. 生成供每个字使用的高亮列表
        1. 词法

          cnhl-generate-hl-word 做的事很简单:输入一个标签列表,根据分词的情况输出每个字 的标签的列表。

          (defun cnhl-generate-hl-word ()
            (cl-loop for tag in cnhl-last-prop-list
                     for word in cnhl-last-word-list
                     collect (make-list (length word) tag) into r
                     finally (cl-return (flatten-list r))))
          
        2. 句法

          cnhl-genarate-hl-dep 将检测每个词的依存关系是否是“独立”的,如果是,它的标签的 后三个字母将成为它的颜色标识;如果不是,则将它的长度先存起来,当遇到“独立”标签时, 一并使用它的颜色标识。

          这些数据将被上面抓取 overlay 的函数所使用,进而决定高亮的行为。

          (defun cnhl-generate-hl-dep (&optional giving-result)
            (let ((holding 0)
                  (r))
              (cl-loop for i = 0 then (1+ i)
                       with dep-list = (or (cadr giving-result) cnhl-last-dep-list)
                       and word-list = (or (car giving-result) cnhl-last-word-list)
                       for tag in dep-list
                       if (cnhl-dep-check-independent tag)
                       do (progn
                            (dotimes (_ (+ holding
                                           (length (nth i word-list))))
                              (let ((l (length tag)))
                                (push (substring tag (- l 3) l) r)))
                            (setq holding 0))
                       else do (setq holding
                                     (+ holding
                                        (length (nth i word-list))))
                       finally (when (not (= holding 0))
                                 (dotimes (_ holding)
                                     (push "nct" r))))
              (reverse r)))
          
    6. 文本截取
      1. 设置单句最大长度

        为爱写大长句和使用特殊标点符号的同学设计,旨在降低性能消耗。

        默认为 100 ,句子前后各 50 。

        (defvar cnhl-sentence-max-length 100)
        
      2. 获取光标所在句子的首尾位置

        在词法分析中,每次分析只需要取被标点隔开的语段即可,因为词性是不会被标点所影响的; 但在依存句法分析中,必须取被“。”、“?”等标点分开的整句,才能保证句法的完整。

        因此,根据 cnhl-hl-use-dep-p 的不同,匹配语句分隔标点的正则表达式将有两套不同 的值,分别为 cnhl-punc-regexp-wordcnhl-punc-regexp-dep

        引号有时有分割句子的含义,有时却又没有;为了获得最佳体验,在句法分析时,统一不将 引号算作分隔符。不过总体而言,两套分隔符在表观上的差异其实不算很大。

        (defvar cnhl-punc-regexp-word
          "[,。?;:、‘’“”…—!()~《》「」【】〖〗『』〔〕,.?!():;/\\*#]")
        
        (defvar cnhl-punc-regexp-dep
          "[。?;:…—!()~《》「」【】〖〗『』〔〕.?!():;/\\*#]")
        

        之后定义 cnhl-detect-sentence 函数匹配当前句子。

        该方法返回一个点对列表,第一项是句子开始位置(包括上一句的标点),第二项是句子结 束位置。

        该函数判断 cnhl-hl-use-dep-p 决定使用哪套分隔符。

        (defun cnhl-detect-sentence (&optional beg end)
          (save-excursion
            (unless end
              (unless beg
                (setq beg (point)))
              (setq end beg))
            (let* ((max-len (/ cnhl-sentence-max-length 2))
                   (min-pos (max (- beg max-len) (point-min)))
                   (max-pos (min (+ end max-len) (point-max)))
                   (regexp (if cnhl-hl-use-dep-p
                               cnhl-punc-regexp-dep
                             cnhl-punc-regexp-word))
                   (beg-r (or (progn
                                (goto-char beg)
                                (search-backward-regexp regexp min-pos t))
                              min-pos))
                   (end-r (or (progn
                                (goto-char end)
                                (search-forward-regexp regexp max-pos t))
                              max-pos)))
              (list beg-r end-r))))
        
      3. 预处理字符串

        (已废弃:预处理字符串将导致英文句子粘连成一个单词,按词移动函数无法匹配到其位 置,导致按词移动失效。)

        将待传入 THULAC 分析的字符串进行预处理,去除其中的空格、特定符号等。

        (defvar cnhl-content-regexp
          "[\u2e80-\u9fa5,。?;:、‘’“”…—!()~《》「」【】〖〗『』〔〕,.?!():;/\\*#a-zA-Z0-9]")
        
        (defvar cnhl-not-content-regexp
          "[^\u2e80-\u9fa5,。?;:、‘’“”…—!()~《》「」【】〖〗『』〔〕,.?!():;/\\*#a-zA-Z0-9]")
        
        (defun cnhl-string-pretreatment (beg end)
          (replace-regexp-in-string cnhl-not-content-regexp ""
                                    (buffer-substring-no-properties beg end)))
        
        ;; test: (apply #'cnhl-string-pretreatment (cnhl-detect-sentence 24033))
        
    7. 使用 NLP 分析句子,解析结果并存储

      设计思路:使用 NLP 分析句子,根据分析结果确定每一个字应该使用什么颜色的 overlay ,将这些 overlays 按顺序存在 cnhl-last-prop-list 中。贴 overlay 时,只需将光标 移至上次分析的开头,而后把 overlays 一个字一个字贴上去即可。

      1. 存储分析结果的变量
        (defvar cnhl-last-word-list nil
          "词语列表,存储分词后的所有词汇们。")
        (defvar cnhl-last-prop-list nil
          "词性列表,存储与被分析句的字数相对应数量的词性标记
        使用何种词性标记由 NLP 决定。")
        (defvar cnhl-last-region-list (list 0 0)
          "上次分析的句子的起始与结束位置。")
        (defvar cnhl-last-dep-list nil
          "依存关系列表,存储依存句法分析后的每个词的依存性质。")
        (defvar cnhl-last-targ-list nil
          "存储每个词的依存关系所指向的词在句中的位置。")
        
      2. NLP 分析函数的基础形态

        用于在第一次被调s用时初始化对应的 NLP ,此后该函数将被初始化函数设置为指向该 NLP 所对应的分析函数。

        (defun cnhl-nlp-analyse-sentence (&optional beg end)
          (unless cnhl-nlp-initialized
            (cnhl-nlp-init)
            (cnhl-nlp-analyse-sentence beg end)))
        
      3. THULAC 的分析与解析

        总流程:截取句子 -> 送入分析 -> 解析结果 -> 存储结果。

        因 THULAC C++ 版本只能通过字符串输出结果,所以要对结果进行一些正则处理。

        cnhl-thulac-string-process 函数将解析 THULAC 返回的分析结果为 分词数据词性数据 ,分别用于分词和高亮。

        THULAC 返回值示例: "我rv 北京ns 天安门ns"

        该函数首先依照空格将整个字符串拆为列表,提取词语部分收入分词数据中。对空格和回车 的分析结果将在这里被过滤掉;

        之后判断词性结果的类型数字,根据类型在词性数据列表中插入一定的数字组成一个与文字 数量相对应的词性列表。高亮将根据该列表进行。

        cnhl-thulac-analyse-sentence 函数负责接收 cnhl-thulac-string-process 的结果 并储存起来。

        (defun cnhl-thulac-string-process (str)
          (setq str (string-trim
                     (replace-regexp-in-string
                      "\n" "" str))
                str (replace-regexp-in-string
                     "\s\s_w" "" str))
          (let ((word-prop-lst (split-string str " "))
                (word-lst nil)
                (prop-lst nil))
            (dolist (item word-prop-lst)
              (let* ((pos (string-match "_[a-z]+$" item))
                     (word (substring item 0 pos))
                     (prop (substring item (1+ pos))))
                (push word word-lst) ;; 插入词语
                (push prop prop-lst)))
            (cons (reverse word-lst) (reverse prop-lst))))
        
        (defun cnhl-thulac-analyse-sentence (&optional beg end)
          (cnhl-nlp-reinit-check)
          (unless (and (bound-and-true-p end)
                       (>= beg end))
            (let* ((region (cnhl-detect-sentence beg end))
                   (result (cnhl-thulac-string-process
                            (cnhl-thulac-string
                             (apply #'buffer-substring-no-properties region)))))
              (setq cnhl-last-word-list (car result)
                    cnhl-last-prop-list (cdr result)
                    cnhl-last-region-list region))))
        
        ;; (cnhl-nlp-init)
        ;; (cnhl-nlp-analyse-sentence 25141)
        
      4. FastHan 的分析与解析
        (defun cnhl-fasthan-analyze-sentence (&optional beg-or-sentence end giving-result)
          (cnhl-nlp-reinit-check)
          (cl-loop with sentencep = (and (bound-and-true-p beg-or-sentence)
                                         (stringp beg-or-sentence))
                   with region = (if giving-result
                                     (list beg-or-sentence end)
                                   (unless sentencep
                                     (let ((r (cnhl-detect-sentence beg-or-sentence end)))
                                       (setq beg-or-sentence
                                             (apply #'buffer-substring-no-properties r))
                                       r)))
                   and result = (or giving-result
                                    (car (epc:call-sync
                                          cnhl-fasthan-epc
                                          'fasthan_parsing_string
                                          (list beg-or-sentence))))
                   for r in result
                   unless (string-match-p "\\`[\s\n]+\\'" (car r))
                   collect (replace-regexp-in-string "[\s\n\u3000]" "" (car r)) into word-list
                   and collect (cadr r) into targ-list
                   and collect (caddr r) into dep-list
                   and collect (cadddr r) into prop-list
                   finally do (setq cnhl-last-word-list word-list
                                    cnhl-last-dep-list dep-list
                                    cnhl-last-targ-list targ-list
                                    cnhl-last-prop-list prop-list
                                    cnhl-last-region-list region)
                   finally return (list word-list dep-list targ-list prop-list region)))
        
        ;; cnhl-last-word-list
        ;; cnhl-last-region-list
        ;; (mapcar #'(lambda (i) (insert (concat i " "))) cnhl-last-dep-list)
        
        ;; (string-match-p  "\\`[\s\n]+\\'" "       \n\n  ")
        ;; (cnhl-fasthan-analyze-sentence 39426 39486)
        

        鸟儿将窠巢安在繁花嫩叶当中,高兴起来了,呼朋引伴地卖弄清脆的喉咙,唱出婉转的曲子, 与轻风流水应和着。

    8. fasthan tmp
      鸟儿将窠巢安在繁花嫩叶当中,高兴起来了,呼朋引伴地卖弄清脆的喉咙,唱出婉转的曲子,与轻风流水应和着。
      。确定下一个字没有被分析函数排出去。
      (defvar cnhl-last-dep-list nil)
      (defvar cnhl-last-targ-list nil)
      cnhl-last-word-list
      cnhl-last-prop-list
      (mapcar #'(lambda (i) (insert i " ")) cnhl-last-dep-list)
      (mapcar #'(lambda (i) (insert i " ")) (cnhl-generate-hl-dep))
      (epc:call-sync
       cnhl-fasthan-epc
       'fasthan_parsing_string
       '("。     跳往开头 -> 确定下一个字"))
      
      (cnhl-fasthan-analyze-sentence 2 53)
      
      (defun cnhl-dep-find-root (&optional lst)
        (let ((l (or lst cnhl-last-targ-list)))
          (+ 1 (- (length l) (length (member 0 l))))))
      
      (defun cnhl-dep-children (n &optional lst)
        (cl-loop for i from 1 to (length (or lst cnhl-last-targ-list))
                 for targ in (or lst cnhl-last-targ-list)
                 if (= targ n) collect i into r
                 finally (cl-return r)))
      
      (defun cnhl-dep-build-tree (&optional lst)
        (let ((r))
          (cl-loop for i = 1 then (+ 1 i)
                   for targ in (or lst cnhl-last-targ-list)
                   do (let ((pos (alist-get targ r)))
                          (setf (alist-get targ r)
                                (append (list i) pos))))
          r))
      
      (defvar cnhl-last-dep-tree nil)
      
      (defun cnhl-dep-tree-find-children (elt &optional lst)
        (alist-get elt (or lst cnhl-last-dep-tree)))
      
      (defun cnhl-dep-tree-find-parent (elt &optional lst)
        (cl-loop for i in (or lst cnhl-last-dep-tree)
                 when (member elt (cdr i))
                 do (cl-return (car i))))
      
      (cnhl-dep-find-root)
      (cnhl-dep-children 4)
      (cnhl-dep-tree-find-children 0)
      (cnhl-dep-tree-find-parent 4)
      
      独立:主语;宾语;ROOT;补语;并列词;标点
      跟随:修饰;介词;依赖;把;被;
      
      (window-total-width)
      
      (cnhl-analyze-sentence "鸟儿将窠巢安在繁花嫩叶当中,高兴起来了,
      呼朋引伴地卖弄清脆的喉咙,唱出婉转的曲子,与轻风流水应和着。")
      
      (cl-loop
       for i = 0 then (1+ i)
       for c = (char-to-string
                (char-after (+ 68609 i)))
       until (string-match-p
              "[^\s\n\u3000]" c)
       finally return i)
      
      (cnhl-get-overlay-stanford "JJ")
      
    9. 依存句法分析的特别计算

      对于依存句法分析, Cnhl 的设计是这样的:对于每句话中的所有主语、宾语、补语和并列 词,由于它们往往占据句子中重要的位置,且大量的修饰语以它们为中心,因此它们将作为 “独立”的词语存在;根谓语和标点因其独特性也算入其中。

      在高亮时,这些“独立”词语将根据其性质分配到不同的颜色,而其它修饰语则跟随它后面的 “独立”词语的颜色。这样句子就被分成逻辑显明的几个部分了。

      1. 检测词语是否为“独立”

        由于 stanford 标签集的特性,大部分主语、宾语、补语的标签,其后半部分是相同的,可 以利用这一点来快速归类。

        cnhl-dep-independent-tag-end 内存储了八种三个字母长的后缀, cnhl-dep-generate-end-list 将依据它们生成一张便于程序使用的表,存储在 cnhl-dep-end-list 中。

        (defvar cnhl-dep-independent-tag-end
          (list "ubj" "obj" "oot" "ssm" "omp" "onj" "nct" "ass"))
        (defvar cnhl-dep-end-list nil)
        
        (defun cnhl-dep-generate-end-list ()
          (setq cnhl-dep-end-list
                (list (mapcar #'(lambda (i) (aref i 0))
                              cnhl-dep-independent-tag-end)
                      (mapcar #'(lambda (i) (aref i 1))
                              cnhl-dep-independent-tag-end)
                      (mapcar #'(lambda (i) (aref i 2))
                              cnhl-dep-independent-tag-end))))
        (cnhl-dep-generate-end-list)
        

        cnhl-dep-check-independent 可以利用这张表检查一个标签是否属于“独立”的。例外情 况在这里被单独排除。

        (defun cnhl-dep-check-independent (tag)
          (condition-case nil
              (let ((l (length tag)))
                (when (and (member (aref tag (- l 3)) (car cnhl-dep-end-list))
                           (member (aref tag (- l 2)) (cadr cnhl-dep-end-list))
                           (member (aref tag (- l 1)) (caddr cnhl-dep-end-list))
                           (not (member tag (list "pass" "auxpass" "asp"))))
                  t))
            (t nil)))
        ;; (cnhl-dep-check-independent "asp")
        
    10. 执行高亮

      食用方法:先调用 NLP 分析函数分析,然后调用它即可。

      跳往开头 -> 定下一个字没有被分析函数排出去(不是空格、回车) -> 确定下 一个字上没有其它 overlay -> 从表里抓出一个 overlay 贴上去 -> 下一个

      ;; (save-excursion
      ;;   (profiler-start 'cpu+mem)
      ;;   (goto-char 16056)
      ;;   (dotimes (i 600)
      ;;     (face-at-point)
      
      ;;     (forward-char))
      ;;   (profiler-stop)
      ;;   (profiler-report))
      
      (defun cnhl-hl (&optional giving-result)
        (save-excursion
          (goto-char (car (or (car (last giving-result))
                              cnhl-last-region-list)))
          (let ((lst (if cnhl-hl-use-dep-p
                         (cnhl-generate-hl-dep giving-result)
                       (cnhl-generate-hl-word))))
            (while lst
              (when (string-match-p "[^\s\n\u3000]"
                                    (char-to-string (following-char)))
                (if (let ((f (face-at-point)))
                      (or (null f)
                          (string= (substring (symbol-name f) 0 4)
                                   "cnhl")))
                    (move-overlay
                     (copy-overlay (cnhl-nlp-get-overlay (pop lst)))
                     (point) (1+ (point))
                     (current-buffer))
                  (pop lst)))
              (forward-char)))))
      ;; (cnhl-nlp-analyse-sentence 26763)
      ;; (cnhl-hl)
      
    11. 输入时实时高亮效果的实现

      确定是在 cnhl-mode 下 -> 设置 timer :如果有延时就给去了,按照旧的起始位置重上 / 如果没有就新上一个。

      Timer 的内容:先把自己清空 -> 将从设定的起始位置到当前光标所在位置的区域高亮。

      (defvar cnhl-after-change-timer nil)
      (defvar cnhl-after-change-begin nil)
      (defvar cnhl-after-change-waiting "0.5")
      
      (defun cnhl-hl-after-change (beg end len)
        (when cnhl-mode
          (if cnhl-after-change-timer
              (cancel-timer cnhl-after-change-timer)
            (setq cnhl-after-change-beginning beg))
          (setq cnhl-after-change-timer
                (run-at-time
                 cnhl-after-change-waiting
                 nil
                 #'(lambda ()
                     (setq cnhl-after-change-timer nil
                           cnhl-last-region-list (cnhl-detect-sentence
                                                  cnhl-after-change-beginning
                                                  (point)))
                     (if cnhl-hl-use-dep-p
                         (deferred:$
                          (epc:call-deferred
                           cnhl-fasthan-epc
                           'fasthan_parsing_string
                           (list (apply #'buffer-substring-no-properties
                                        cnhl-last-region-list)))
                          (deferred:nextc it (lambda (x)
                                               (apply #'cnhl-nlp-analyse-sentence
                                                      (append cnhl-last-region-list
                                                              x))))
                          (deferred:nextc it (lambda (x) (cnhl-hl x))))
                       (progn (cnhl-nlp-analyse-sentence)
                              (cnhl-hl))))))))
      
    12. 数个手动高亮一定区域的方法
      1. 高亮全 buffer
        (defun cnhl-hl-buffer ()
          " 一口气高亮整个 buffer 。注意,若使用依存句法分析进行高亮将会较慢。"
          (interactive)
          (cnhl-nlp-analyse-sentence (point-min) (- (point-max) 2))
          (cnhl-hl))
        
      2. 高亮当前段落
        (defun cnhl-hl-paragraph ()
          "高亮光标所在段落。"
          (interactive)
          (save-excursion
            (cnhl-nlp-analyse-sentence
             (progn (backward-paragraph)
                    (search-forward-regexp "[^\s]"))
             (progn (forward-paragraph)
                    (1- (search-backward-regexp "[^\s]")))))
          (cnhl-hl))
        
      3. 高亮当前句
        (defun cnhl-hl-sentence ()
          "高亮光标所在句。"
          (interactive)
          (cnhl-nlp-analyse-sentence)
          (cnhl-hl))
        
    13. 分词
      1. 根据词性分词

        设计思路:先取得光标左右最临近的词语的位置,再根据需求进行跳转、插入删除等操作。

        1. 获取光标周围的词语位置

          返回本词词末、上词词末、上上词词末。

          设计思路:

          先判断光标是否位于上次分析的句子中,如果不在就先分析;

          之后从第一个词开始遍历整个分词列表,不断比对词末位置相对于光标的位置,直到取得光 标词的词末以及光标前一词的词末。

          值得注意的是,为了减少代码的逻辑量,我没对“光标在词中 / 光标在词末”两种情况分 别处理,而是统一按照在词末的方式处理。不过这在使用体验上不会有什么影响——词法分析 实在是太细致了……

          (defvar cnhl-get-word-time nil)
          
          (defun cnhl-get-word-pos-arround ()
            (let ((beg (car cnhl-last-region-list))
                  (end (cadr cnhl-last-region-list))
                  (p-now (point)))
              (if (and (or (>= p-now end)
                           (<= p-now beg))
                       (null cnhl-get-word-time))
                  (progn (cnhl-nlp-analyse-sentence
                          p-now (1+ p-now))
                         (setq cnhl-get-word-time t)
                         (cnhl-get-word-pos-arround))
                (save-excursion
                  (when cnhl-get-word-time
                    (setq cnhl-get-word-time nil))
                  (goto-char beg)
                  (if cnhl-word-use-dep-p
                      (cl-loop for word in cnhl-last-word-list
                               for p = beg then (search-forward word)
                               for tag in cnhl-last-dep-list
                               with prev-tag
                               when (and (cnhl-dep-check-independent tag) ;; indent 0
                                         (not (equal tag prev-tag)))
                               do (setq prev-tag tag) ;; indent 1
                               and collect p into prev-p ;; indent 1
                               and if (> p p-now) ;; indent 1
                               if (< (length prev-p) 3) ;; indent 2
                               do (progn (cnhl-nlp-analyse-sentence ;; indent 3
                                          (1- beg) beg)
                                         (cnhl-get-word-pos-arround))
                               else return (last prev-p 3) ;; indent 2
                               else do '(nil)) ;; indent 1
                    (cl-loop for word in cnhl-last-word-list
                             for p = beg then (search-forward word)
                             collect p into prev-p
                             until (> p p-now)
                             finally return (last prev-p 3)))))))
          
        2. 覆盖原本的按词操作函数

          先用 advice around 模式覆写 forward-word 函数,之后重新加载 emacs 本身的按词操作 函数,简单实现中文按词操作~

          这段代码将被插入 cnhl-mode 代码块内,以按需加载。

          (define-advice forward-word
              (:around (orig-func &optional arg)
                       cnhl-forward-word)
            (if cnhl-mode
                (condition-case err
                    (let ((p (point)))
                      (if (< arg 0)
                          (dotimes (i (- arg))
                            (goto-char (car (cnhl-get-word-pos-arround))))
                        (dotimes (i (or arg 1))
                          (goto-char (caddr (cnhl-get-word-pos-arround)))))
                      t)
                  (t nil))
              (funcall orig-func arg)))
          
          ;; (load "simple.el")
          ;; (load "subr.el")
          
      2. 根据句法分词

        这个模式下,句子的分隔位置和高亮时的行为是相同的。

        
        
    14. 一些基于 NLP 工具的实用功能
      1. Stanford 依存句法标签的中文含义 alist
        (defvar cnhl-dep-meaning-alist
          (list (cons "root" "根谓语")
                (cons "punct" "标点")
                (cons "subj" "主语")
                (cons "nsubj" "主语")
                (cons "nsubjpass" "主语(被动)")
                (cons "top" "主题")
                (cons "npsubj" "主语(被动)")
                (cons "csubj" "主语(子句)")
                (cons "xsubj" "主语(子句)")
                (cons "obj" "宾语")
                (cons "dobj" "宾语(直接)")
                (cons "iobj" "宾语(间接)")
                (cons "range" "宾语(间接,数量词)")
                (cons "pobj" "宾语(介词)")
                (cons "lobj" "时间介词")
                (cons "comp" "补语")
                (cons "ccomp" "补语(子句)")
                (cons "xcomp" "补语(子句)")
                (cons "acomp" "补语(形容词)")
                (cons "tcomp" "补语(时间)")
                (cons "lccomp" "补语(位置)")
                (cons "rcomp" "补语(结果)")
                (cons "asp" "助词")
                (cons "mod" "修饰")
                (cons "pass" "修饰(被动)")
                (cons "tmod" "修饰(时间)")
                (cons "rcmod" "修饰(关系子句)")
                (cons "numod" "修饰(数量)")
                (cons "ornmod" "修饰(序数)")
                (cons "clf" "修饰(类别)")
                (cons "nmod" "修饰(复合名词)")
                (cons "amod" "修饰(形容词)")
                (cons "advmod" "修饰(副词)")
                (cons "vmod" "修饰(动词)")
                (cons "prnmod" "修饰(插入词)")
                (cons "neg" "修饰(不定)")
                (cons "det" "修饰(限定)")
                (cons "nn" "修饰(名词)")
                (cons "nummod" "修饰(数词)")
                (cons "possm" "所属标记")
                (cons "poss" "修饰(所属)")
                (cons "dvpm" "状中标记")
                (cons "dvpmod" "修饰(状中)")
                (cons "assm" "关联标记")
                (cons "assmod" "修饰(关联)")
                (cons "prep" "修饰(介词)")
                (cons "clmod" "修饰(子句)")
                (cons "plmod" "修饰(介词,地点)")
                (cons "csp" "时态标词")
                (cons "partmod" "修饰(分词)")
                (cons "etc" "等")
                (cons "conj" "并列词")
                (cons "cop" "系动") ;; *
                (cons "cc" "并列连接词")
                (cons "attr" "定语")
                (cons "cordmod" "并列联合词")
                (cons "mmod" "情态动词")
                (cons "ba" "把字句标词")
                (cons "tclaus" "时间子句")
                (cons "cpm" "补语化成分")
                (cons "auxpass" "被动词")
                (cons "case" "依赖关系") ;; *
                (cons "relcl" "依赖关系")
                (cons "nfincl" "依赖关系")))
        
      2. Stanford 词法标签的中文含义 alist
        (defvar cnhl-prop-meaning-alist-stanford
          (list (cons "VA" "形容词(谓词性)")
                (cons "VC" "系动词")
                (cons "VE" "动词(存在性)")
                (cons "VV" "动词")
                (cons "NR" "专有名词")
                (cons "NT" "名词(时间)")
                (cons "NN" "名词")
                (cons "LC" "方位词")
                (cons "PN" "代词")
                (cons "DT" "限定词")
                (cons "CD" "基数词")
                (cons "OD" "序数词")
                (cons "M" "度量词")
                (cons "AD" "副词")
                (cons "P" "介词")
                (cons "CC" "并列连接词")
                (cons "CS" "从属连接词")
                (cons "DEC" "的(补语)") ;; *
                (cons "DEG" "的(偏正)")
                (cons "DER" "得(补语)")
                (cons "DEV" "地(偏正)")
                (cons "AS" "助动词") 
                (cons "SP" "助词(句末)") ;; *
                (cons "ETC" "等") ;; *
                (cons "MSP" "助词")
                (cons "IJ" "感叹词")
                (cons "ON" "拟声词")
                (cons "LB" "被") ;; *
                (cons "SB" "被") ;; *
                (cons "BA" "把")
                (cons "JJ" "修饰词")
                (cons "FW" "外来词") ;; *
                (cons "PU" "标点")))
        
      3. 对一句话进行依存句法分析并完整显示结果
        (defun cnhl-analyze-sentence (&optional sentence)
          (interactive)
          (when sentence
            (cnhl-fasthan-analyze-sentence sentence))
          (save-excursion (insert "\n\n\n\n\n\n"))
          (next-line)
          (cl-loop for i from 1 to (length cnhl-last-targ-list)
                   for word in cnhl-last-word-list
                   for targ in cnhl-last-targ-list
                   for dep in cnhl-last-dep-list
                   for prop in cnhl-last-prop-list
                   for i-str = (number-to-string i)
                   for dep-with-meaning = (concat
                                           (assoc-default dep cnhl-dep-meaning-alist)
                                           "(" dep ")")
                   for targ-with-meaning = (concat
                                           (number-to-string targ)
                                           "(" (nth (- targ 1) cnhl-last-word-list) ")")
                   for prop-with-meaning = (concat
                                            (assoc-default prop
                                                           cnhl-prop-meaning-alist-stanford)
                                            "(" prop ")")
                   for lengthes = (mapcar
                                   #'(lambda (i)
                                       (length
                                        (replace-regexp-in-string
                                         "[\u2000-\u206f\u3000-\u9fff\uff00-\uffef]"
                                         "aa" i)))
                                   (list word
                                         targ-with-meaning
                                         dep-with-meaning
                                         prop-with-meaning))
                   for distance = (+ 1 (apply #'max lengthes))
                   for total-distance = distance then (+ distance total-distance)
                   when (> total-distance (window-width))
                   do (progn (forward-line 5)
                             (save-excursion (insert "\n\n\n\n\n\n"))
                             (next-line)
                             (setq total-distance distance))
                   do (save-excursion
                        (goto-char (line-end-position))
                        (insert i-str (make-string (- distance (length i-str)) 32))
                        (next-line)
                        (goto-char (line-end-position))
                        (insert word (make-string (- distance (car lengthes)) 32))
                        (next-line)
                        (goto-char (line-end-position))
                        (insert targ-with-meaning
                                (make-string (- distance (cadr lengthes)) 32))
                        (next-line)
                        (goto-char (line-end-position))
                        (insert dep-with-meaning
                                (make-string (- distance (caddr lengthes)) 32))
                        (next-line)
                        (goto-char (line-end-position))
                        (insert prop-with-meaning
                                (make-string (- distance (cadddr lengthes)) 32)))))
        
    15. 定义 minor mode

      让这个东东有点插件的样子哈哈。

      (defcustom cnhl-lighter
        " Cnhl"
        "Cnhl 的 Mode line 提示符。"
        :type '(choice (const :tag "No lighter" "") string)
        :safe 'stringp)
      
      (defcustom cnhl-mode-hook '()
        "flex mode hook."
        :type 'hook
        :group 'cnhl)
      
      (define-minor-mode cnhl-mode
        "Cnhl mode."
        :init-value nil
        :lighter cnhl-lighter
        (cnhl-nlp-init)
        (add-hook 'after-change-functions 'cnhl-hl-after-change)
        (unless (advice-member-p 'forward-word@cnhl-forward-word
                                 'forward-word)
          (define-advice forward-word
              (:around (orig-func &optional arg)
                   cnhl-forward-word)
            (if cnhl-mode
                (condition-case err
                (let ((p (point)))
                  (if (< arg 0)
                      (dotimes (i (- arg))
                        (goto-char (car (cnhl-get-word-pos-arround))))
                    (dotimes (i (or arg 1))
                      (goto-char (caddr (cnhl-get-word-pos-arround)))))
                  t)
              (t nil))
              (funcall orig-func arg)))
      
          ;; (load "simple.el")
          ;; (load "subr.el")
          )
        (run-hooks 'cnhl-mode-hook))
      
    16. 已矣

      步余马于兰臯兮,驰椒丘且焉止息。

      Cnhl 结束于此。

      (provide 'cnhl)
      
      ;;; cnhl.el ends here
      
  10. Cnhl THULAC Dynamic Module 部分源代码

    为了更好地调用 NLP 后端, Cnhl 采用 Dynamic module 方式调用并返回 NLP 的分析 数据。这部分源码在这里,同样写了较为详细的注解。

    在此向伟大的 GWQ 同学 致以诚挚的敬意,他一个午休帮我 de 掉了 12 个 bug ,今年他 生日的时候我一定要再把他的名字往我的网站上挂俩月~~

    1. 头文件与命名空间

      引用 Dynamic module 和 THULAC 的头文件。

      #include <iostream>
      #include <emacs-module.h>
      #include "thulac/include/thulac.h"
      
      using namespace std;
      
    2. 必要的全局变量

      plugin_is_GPL_compatible GPL 标识~

      t 是 THULAC 类的实例;

      initialized 标识 THULAC 是否已初始化过。

      int plugin_is_GPL_compatible;
      
      THULAC t;
      
      bool initialized = false;
      
    3. 摘抄的轮子:把收到的 Emacs 参数转为字符串

      需要被初始化和分析函数调用,所以直接放在前面~

      static char *
      retrieve_string (emacs_env *env, emacs_value str)
      {
        char *buf = NULL;
        ptrdiff_t size = 0;
      
        env->copy_string_contents (env, str, NULL, &size);
      
        buf = (char *) malloc (size);
        if (buf == NULL) return NULL;
      
        env->copy_string_contents (env, str, buf, &size);
      
        return buf;
      }
      
    4. 初始化 THULAC 类

      cnhl-thulac-init 函数,用于初始化 THULAC 类,将算法模型读入内存。

      如果已加载过,再次调用的话会卸载模型并重新加载。

      static emacs_value
      Fcnhl_thulac_module_init(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) noexcept
      {
        if (initialized)
          {
            t.deinit();
          }
        string module_path = retrieve_string(env, args[0]);
        t.init(module_path.data(), NULL, 0, 0, 0, '_');
        cout << "THULAC initialized!" << endl;
        initialized = true;
        return env->intern(env, "t");
      }
      
    5. 反初始化 THULAC 类
      static emacs_value
      Fcnhl_thulac_module_deinit(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) noexcept
      {
        if (initialized)
          {
            t.deinit();
          }
        initialized = false;
        return env->intern(env, "t");
      }
      
    6. 分析函数

      cnhl-thulac-string ,极度简单,如果 THULAC 实例已经初始化则把参数传入 THULAC ,返回分析结果~

      static emacs_value
      Fcnhl_thulac_string(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) noexcept
      {
        if (initialized != true)
          {
            cout << "THULAC module hasn't initialized!" << endl;
            return env->intern(env, "");
          }
        string s = retrieve_string(env, args[0]);
        THULAC_result r = t.cut(s);
        s = t.toString(r);
        return env->make_string(env, s.data(), s.length());
      }
      
    7. 绑定 Module 函数到 Emacs 函数

      摘抄摘抄~

      static void
      provide (emacs_env *env, const char *feature)
      {
          emacs_value Qfeat = env->intern (env, feature);
          emacs_value Qprovide = env->intern (env, "provide");
          emacs_value args[] = { Qfeat };
      
          env->funcall (env, Qprovide, 1, args);
      }
      
      static void
      bind_function (emacs_env *env, const char *name, emacs_value Sfun)
      {
          emacs_value Qfset = env->intern (env, "fset");
          emacs_value Qsym = env->intern (env, name);
          emacs_value args[] = { Qsym, Sfun };
      
          env->funcall (env, Qfset, 2, args);
      }
      
      int
      emacs_module_init(struct emacs_runtime *ert) noexcept
      {
      
        emacs_env *env = ert->get_environment (ert);
      
      #define DEFUN(lsym, csym, amin, amax, doc, data)			\
        bind_function (env, lsym,						\
                       env->make_function (env, amin, amax, csym, doc, data))
        DEFUN("cnhl-thulac-string", Fcnhl_thulac_string, 1, 1, "Send string to THULAC and return the result.", NULL);
        DEFUN("cnhl-thulac-module-init", Fcnhl_thulac_module_init, 1, 1, "Load THULAC module.", NULL);
        DEFUN("cnhl-thulac-module-deinit", Fcnhl_thulac_module_deinit, 0, 0, "Deinit THULAC module.", NULL);
      
      #undef DEFUN
      
        provide(env, "cnhl-thulac");
        return 0;
      }
      
  11. Cnhl fastHan Python 部分源代码

    Cnhl fastHan 部分使用 fastHan python module 进行词法分析和句法分析,使用 EPC 与 Emacs 通信。

    1. 导入模组

      导入 fastHan 和 EPC 模组。

      from epc.server import EPCServer
      from fastHan import FastHan
      
      server = EPCServer(('localhost', 0))
      
    2. 初始化模型

      全局声明 model 变量,根据设置的 modelType (base 或 large) 及模型位置初始化模 型。

      @server.register_function 是 EPC 注册函数的标识。

      global model
      
      @server.register_function
      def fasthan_init_model(modelType, path):
          global model
          if len(path) == 0:
              model = FastHan(model_type=modelType)
          else:
              model = FastHan(model_type=modelType, url=path)
              pass
          return 't'
      
      
    3. 分析函数

      传入句子,返回 json string.

      @server.register_function
      def fasthan_parsing_string(sentence):
          return model(sentence, 'Parsing')
      
    4. EPC server 部分
      server.print_port()
      server.serve_forever()
      
  12. 结语

          辛丑咏 Emacs

      铸炼琢磨五九年,春秋一去尔一坚。

      力出盘古开寰宇,朗若云神御九天。

      四海芳邻常伴侧,玲珑情虑每增添。

      料得此心君身系,无奈今生爱恨间。

              —— Rosario S.E.

1.2.2. cndict

Chinese Dictionary 中文辞典。

一个非常简单的小工具,希望大家喜欢。

善为文者,富于万篇,贫于一字。——《文心雕龙》

本工具中的中文字典与词典数据来自 https://github.com/mapull/chinese-dictionary , 遵循 MIT 协议使用,谨在此向作者表示衷心的感谢!

感谢 emacs-china 各位朋友的鼓励。感谢 @manateelazycat 前辈、 @LdBeth 前辈在词典 格式上的帮助;感谢 @steve 同学的陪伴与鼓励。

  1. 截图

    6.png

    7.png

    8.png

    14.png

  2. 安装

    下载 cndict.elcndict-char-data.elcndict-word-data.el 并将其所在目 录加入~load-path~ ,在配置文件中加入 (require 'cndict) 即可。

    git clone https://github.com/3vau/cndict ~/.emacs.d/site-lisp/
    
    (add-to-list 'load-path "~/.emacs.d/site-lisp/cndict/")
    (require 'cndict)
    
  3. 使用

    cndict.el 提供两个命令: cndictcndict-minibuffer

    选中你想查询的字或词,或将你想查询的字词复制进 kill-ring ,随后执行上述命令。 cndict 命令将使用一个临时 buffer 显示完整的查询结果,~cndict-minibuffer~ 将适 当缩短查询结果并使用 minibuffer 显示出来。

    cndict-minibuffer 命令输出的最大长度可以通过 cndict-short-length 变量设置。 受限于字符宽度等问题,最终输出长度并不会与该变量的值完全吻合。

    若词典中没有所查询的词,将查询该词的最后一个字;若仍没有结果将提示查询失败。

    cndict 另外还提供一个查询单字拼音的函数 cndict-char-pinyin ,输入单字,返回这 个字所有读音的列表,可供有需要的同学使用。

  4. cndict 源码
    1. 头部注释

      包含 GPL 声明和英文简介啊什么的,例行公事~

      ;;; cndict.el --- Chinese Dictionary -*- lexical-binding: t -*-
      
      ;; Copyright (C) 2022 Rosario S.E.
      
      ;; Author: Rosario S.E. <ser3vau@gmail.com>
      ;; URL: https://github.com/3vau/cndict
      
      ;; This file is not part of GNU Emacs.
      ;;
      ;; This program is free software; you can redistribute it and/or modify
      ;; it under the terms of the GNU General Public License as published by
      ;; the Free Software Foundation, either version 3 of the License, or
      ;; (at your option) any later version.
      
      ;; This program is distributed in the hope that it will be useful,
      ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
      ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      ;; GNU General Public License for more details.
      
      ;; You should have received a copy of the GNU General Public License
      ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
      
      ;;; Commentary:
      ;; cndict - Chinese Dictionary.
      ;;
      ;; It used dictionary data from https://github.com/mapull/chinese-dictionary,
      ;; Thanks to it's author.
      ;;
      ;; Thanks to @manateelazycat (https://emacs-china.org/u/manateelazycat/)
      ;; and @LdBeth (https://ldbeth.sdf.org/) for inspiration.
      ;; Thanks to @steve (https://emacs-china.org/u/steve/summary)
      ;; for his encouragement.
      ;;
      ;; Thanks to all my friends in https://emacs-china.org
      
      ;;; Code:
      
    2. 加载字典、词典和韵字表

      加载字典文件和词典文件。这两个文件提供了 cndict-char-dict-tablecndict-word-dict-table 两个 hash-table 常量。

      cndict-char-dict-table 结构,以“了”字为例:

      #s(hash-table
         data (
               "了" #s(hash-table
                       data (
                             "le" #s(hash-table
                                     data (
                                           "用在动词或形容词后,表示完成 。如:我已经问了老王;人老了,身体差了;头发白了;这双鞋太小了" nil
                                           "表示肯定语气 。如:明天又是星期六了;要过新年了,人们都很高兴" nil
                                           "表示促进或劝止 。如:快躲了;别吵了!闪开了!" nil
                                           "表示感叹语气 。如:好了!别闹了!" nil
                                           "另见liǎo" nil
                                           )
                                     )
                             "liǎo" #s(hash-table
                                       data (
                                             "(象形。从子,无臂。小篆字象婴儿束其两臂形。初生的婴儿,往往束其两臂而裹之。本义:束婴儿两臂)" nil
                                             "手弯曲" (list
                                                       "了,尥也。从子无臂象形。——《说文》。按,犹交也。手之挛曰了,胫之絷曰尥。"
                                                       "凡物二股或一股结纠紾缚不直伸者曰了戾。——段玉裁《说文解字注》"
                                                       )
                                             "又如:了尥(手腿弯曲,引申指二物纠结绞缠不直伸的样子)" nil
                                             "假借为“憭”、“悟”。懂得,明白其意思" (list
                                                                                  "嫌人不了。——《尔雅·释丘》注" "虽神气不变,而心了其故。——《世说新语》"
                                                                                  )
                                             "如:了法(领悟法理);了得事(懂行);了利(清楚,明白);了然(明白,清楚)" nil
                                             "结束,了结" (list
                                                          "小乔初嫁了。——宋·苏轼《念奴娇·赤壁怀古》"
                                                          )
                                             "又如:了还(了却,偿还);了局(结局;结束);了了(了结了);了劣(了账;了结);了休(终止,了结)" nil
                                             "聪敏,颖慧" (list
                                                          "小而聪了,大未必奇。——《后汉书·孔融传》" "了,快也。秦曰了。——《方言二》"
                                                          )
                                             "又如:了慧(聪明);了干(精明干练)" nil
                                             "清楚,明晰 。如:了利(清楚;明白);了辩(对答清楚敏捷)" nil
                                             "明亮,光亮" (list
                                                          "收到一片秋香,清辉了如雪。——清·纳兰性德《琵琶仙》"
                                                          )
                                             "完全,全然——与“无”、“不”连用,用在动词或形容词前面,表示范围,相当于“完全。如:了无恐色;了不相涉;了不可得(到最后也得不到)" nil
                                             "放在动词之后,与“得”或“不”连用,表示可能 。如:办得了;你来得了来不了?" nil
                                             "另见le" nil)
                                       )
                             )
                       )
               )
         )
      

      cndict-word-dict-table 是一个简单的哈希表, key 值是词本身, value 是该词经过 初步排版后的释义。

      (require 'cndict-char-data)
      (require 'cndict-word-data)
      (require 'cndict-tongyun-data)
      
    3. 从字典中获取单字释义

      考虑到后续开发需求以及语料库的详细程度,字典部分采用了更为细致的嵌套哈希表方式保 存,因此需要独立的函数进行逐层解析并排版。

      cndict-char-content-detail 将生成一个完整的、带换行的释义,而 cndict-char-content 将生成一个不带例句的、长度(大致)不超过 cndict-short-length 的释义。

      缩短的释义将保持每个释义至少有 20 字符的长度,仍多出的部分将直接丢弃。

      (defvar cndict-short-length 100)
      
      (defun cndict-char-content-detail (str)
        (let ((table (gethash str cndict-char-dict-table))
              (r (format "* %s \n\n" str)))
          (maphash
           #'(lambda (pinyin expl)
               (setq r
                     (concat
                      r
                      (format
                       "- %s: %s\n\n"
                       pinyin
                       (let ((num 1)
                             (s ""))
                         (maphash
                          #'(lambda (content detail)
                              (setq s (concat
                                       s "\n\n  " (number-to-string num) ". "
                                       content "; "
                                       (when detail
                                         (concat
                                          "\n     "
                                          (string-join detail "; "))))
                                    num (1+ num)))
                          expl)
                         s)))))
           table)
          r))
      
      (defun cndict-char-content (str)
        (let ((table (gethash str cndict-char-dict-table))
              (r (format "* %s " str)))
          (maphash
           #'(lambda (pinyin expl)
               (setq r (concat
                        r
                        (format "%s: %s| "
                                pinyin
                                (let* ((num 0)
                                       (contents (hash-table-keys expl))
                                       (l (max (/ (- cndict-short-length
                                                     8 (length pinyin))
                                                  (length contents))
                                               20)))
                                  (mapconcat
                                   #'(lambda (cont)
                                       (setq num (1+ num))
                                       (concat
                                        (number-to-string num) ". "
                                        (if (< (length cont) l)
                                            cont
                                          (concat (substring cont 0 l)
                                                  "..."))
                                        "; "))
                                   contents ""))))))
           table)
          (if (> (length r) cndict-short-length)
              (concat (substring r 0 (- cndict-short-length 3)) "...")
            r)))
      
    4. 查询函数
      (defun cndict-minibuffer (str)
        "查询选中字词或上一个 kill-ring 记录的字词,通过 minibuffer 输出简短的结果。"
        (interactive (list (or (funcall region-extract-function nil)
                               (current-kill 0 t))))
        (let ((r (or (ignore-errors
                         (string-replace "\n\n  " ""
                                         (gethash str cndict-word-dict-table)))
                     (ignore-errors
                       (cndict-char-content
                        (char-to-string (aref str (1- (length str))))))
                     "未找到该词")))
          (message r)))
      
      (defun cndict (str)
        "查询选中字词或上一个 kill-ring 记录的字词,使用临时 buffer 输出完整的结果。"
        (interactive (list (or (funcall region-extract-function nil)
                               (current-kill 0 t))))
        (let ((r (or (gethash str cndict-word-dict-table)
                     (ignore-errors
                       (cndict-char-content-detail
                        (char-to-string (aref str (1- (length str)))))))))
          (if r
              (progn (with-temp-buffer-window
                         (format "*“%s”的释义*"
                                 (substring
                                  r 2
                                  (progn (string-match " \n\n" r)
                                         (match-beginning 0))))
                         (list (lambda (_ _) (org-mode) (toggle-word-wrap -1) nil))
                         nil
                       (with-current-buffer standard-output
                         (insert r))))
            (message "未找到该词"))))
      
      (provide 'cndict)
      
      ;;; cndict.el ends here
      
    5. 获取单字拼音的函数
      (defun cndict-char-pinyin (str)
        "输入字符,返回其读音的列表"
        (hash-table-keys (gethash str cndict-char-dict-table)))
      
    6. 判断拼音的声调和在中华通韵中的韵部
      (defconst cndict-tune-name-alist
        '((1 . "阴平")
          (2 . "阳平")
          (3 . "上声")
          (4 . "去声")))
      
      (defun cndict-lastn= (n targ str)
        (condition-case err
            (string-match-p targ (char-to-string (elt str (- (length str) n 1))))
          (t nil)))
      
      (defun cnrhy-pinyin-details (pinyin)
        (let ((tune (cond ((string-match-p "[àòèìùǜ]" pinyin) 4)
                          ((string-match-p "[ǎǒěǐǔǚ]" pinyin) 3)
                          ((string-match-p "[áóéíúǘ]" pinyin) 2)
                          ((string-match-p "[āōēīūǖ]" pinyin) 1)
                          (t 0)))
              (tongyun-rhyme))
          (setq pinyin (replace-regexp-in-string "[āáǎàɑ]" "a" pinyin))
          (setq pinyin (replace-regexp-in-string "[ōóǒò]" "o" pinyin))
          (setq pinyin (replace-regexp-in-string "[ēéěè]" "e" pinyin))
          (setq pinyin (replace-regexp-in-string "[īíǐì]" "i" pinyin))
          (setq pinyin (replace-regexp-in-string "[ūúǔù]" "u" pinyin))
          (setq pinyin (replace-regexp-in-string "[ǖǘǚǜü]" "v" pinyin))
          (setq tongyun-rhyme
                (cond ((cndict-lastn= 0 "a" pinyin) 1)
                      ((cndict-lastn= 0 "o" pinyin)
                       (if (cndict-lastn= 1 "a" pinyin)
                           9
                         2))
                      ((cndict-lastn= 0 "e" pinyin) 3)
                      ((cndict-lastn= 0 "i" pinyin)
                       (cond ((cndict-lastn= 1 "a" pinyin) 7)
                             ((cndict-lastn= 1 "[ue]" pinyin) 8)
                             (t 4)))
                      ((cndict-lastn= 0 "u" pinyin)
                       (cond ((cndict-lastn= 1 "[oi]" pinyin) 10)
                             ((cndict-lastn= 1 "[jxqy]" pinyin) 6)
                             (t 5)))
                      ((cndict-lastn= 0 "v" pinyin) 6)
                      ((cndict-lastn= 0 "n" pinyin)
                       (if (cndict-lastn= 1 "a" pinyin)
                           11
                         12))
                      ((cndict-lastn= 0 "[gɡ]" pinyin)
                       (cond ((cndict-lastn= 2 "o" pinyin) 15)
                             ((cndict-lastn= 2 "[ie]" pinyin) 14)
                             (t 13)))
                      ((cndict-lastn= 0 "r" pinyin) 16)
                      (t 17)))
          (cons tune tongyun-rhyme)))
      
    7. 查找同韵字功能的实现
      (define-button-type 'cndict-button
        'action #'cndict-button)
      
      (defun cndict-button (button)
        (cndict (buffer-substring
                 (button-get button 'begin)
                 (button-get button 'end))))
      
      (defun cndict-rhyme-insert-tune (rhyme tune)
        (insert
         (format "*** %s, %s\n\n"
                 (alist-get rhyme cndict-tongyun-name-alist)
                 (alist-get tune cndict-tune-name-alist)))
        (mapcar
         #'(lambda (char)
             (let ((name (char-to-string char)))
               (insert-text-button
                name
                'type 'cndict-button
                'begin (point)
                'end (1+ (point)))
               (insert "  ")))
         (gethash tune
                  (gethash rhyme cndict-tongyun-table)))
        (insert "\n\n"))
      
      (defun cndict-rhyme (str)
        (interactive (list (or (funcall region-extract-function nil)
                               (current-kill 0 t))))
        (let* ((char (char-to-string ;; 只要最后一个字
                      (aref str (1- (length str)))))
               (r (ignore-errors
                    (cndict-char-content char))))
          (if r
              (progn
                (with-temp-buffer-window
                    (format "*“%s”的释义*" char)
                    (list (lambda (_ _)
                            (org-mode)
                            (toggle-word-wrap -1)
                            nil))
                    nil
                  (with-current-buffer standard-output
                    (let ((pys (cndict-char-pinyin char)))
                      (insert "* ")
                      (insert-text-button char
                                          'type 'cndict-button
                                          'begin (point)
                                          'end (1+ (point)))
                      (insert "\n\n")
                      (insert (format "- 读音: %s\n\n"
                                      (mapconcat #'identity pys ", ")))
                      (insert (format "- 释义: %s\n\n" (substring r 3)))
                      (dolist (py pys)
                        (let* ((pinyin-details (cnrhy-pinyin-details py))
                               (tune (car pinyin-details))
                               (rhyme (cdr pinyin-details)))
                          (when (= tune 0) (setq tune 1)) ;; 轻声按照平声处理
                          (insert
                           (format "** %s, %s, %s\n\n"
                                   py
                                   (alist-get rhyme cndict-tongyun-name-alist)
                                   (alist-get tune cndict-tune-name-alist)))
                          (if (memq tune '(2 4)) ;; 这时只需-1, 下面只需+1
                              (progn (cndict-rhyme-insert-tune rhyme tune)
                                     (cndict-rhyme-insert-tune rhyme (1- tune)))
                            (progn (cndict-rhyme-insert-tune rhyme tune)
                                   (cndict-rhyme-insert-tune rhyme (1+ tune))))))))))
            (message "未找到该字"))))
      
    8. 生成字典与词典哈希表的代码

      第一段用于生成字典,第二段用于生成词典。

      只是一个简单的解析而已ww

      如果要使用的话记得改参数。

      (let ((r (make-hash-table :test #'equal)))
        (seq-doseq (char (f-read "~/chinese-dictionary/data/character/char_base_detail.json"))
          (let ((pinyintable (make-hash-table :test #'equal)))
            (seq-doseq (pron (gethash "pronunciations" char))
              (let ((table (make-hash-table :test #'equal)))
                (seq-doseq (expl (gethash "explanations" pron))
                  (let ((meaning)
                        (detail)
                        (modern (gethash "morden" expl))
                        (same (gethash "same" expl))
                        (refer (gethash "refer" expl))
                        (simplified (gethash "simplified" expl))
                        (cont (gethash "content" expl)))
                    (when modern
                      (setq meaning (format "古字,同“%s”; " modern)))
                    (when same
                      (setq meaning (concat meaning (format "同“%s”; " same))))
                    (when simplified
                      (setq meaning (concat meaning
                                            (format "“%s”的繁体; " simplified))))
                    (when refer
                      (setq meaning (concat meaning (format "[“%s”]; " refer))))
                    (when cont
                      (setq meaning (concat meaning
                                            (if (equal (type-of cont) 'vector)
                                                (aref cont 0)
                                              cont))))
                    (puthash meaning (append (gethash "detail" expl) nil) table)))
                (puthash (gethash "pinyin" pron) table pinyintable)))
            (puthash (gethash "char" char) pinyintable r)))
        (f-write (format "(defconst cndict-char-dict-table %S)\n\n(provide 'cndict-char-data)" r)
                 'utf-8 "~/cndict-char-data.el"))
      
      (let ((r (make-hash-table :test #'equal)))
        (seq-doseq (table (f-read "~/chinese-dictionary/data/word/word.json"))
          (let ((s (format "* %s \n\n  %s\n\n  "
                           (gethash "word" table)
                           (gethash "explanation" table)))
                (source (gethash "source" table))
                (similar (gethash "similar" table))
                (opposite (gethash "opposite" table)))
            (when similar
              (setq s (format "%s\n\n  近义: %s; " s similar)))
            (when opposite
              (setq s (format "%s\n\n  反义: %s; " s opposite)))
            (when source
              (setq s (format "%s\n\n  出自%s: “%s”; "
                              s (gethash "book" source) (gethash "text" source))))
            (puthash (gethash "word" table) s r)))
        (f-write (format "(defconst cndict-word-dict-table %S)\n\n(provide 'cndict-word-data)" r)
                 'utf-8 "~/cndict-word-data.el"))
      
  5. GPL-3.0 声明

    This file is not part of GNU Emacs.

    This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

1.2.3. eof

;;; Extra functions operating face attributes

(defun eof-get-face-attribute-values (start end object attr)
  "Get all the face values from START to END which attribute is ATTR,
and return each of them in a format of '(start end object value) in a list."
  (let ((p start)    ;	  |--face1--|--face2--|
        (prev) 	     ;	  fooooooooobaaaaaaaaar
        (result))    ;	  ^         ^
    (while (< p end) ;	  prev ---> p -----> (p)
      (setq prev p)  ;   (check)
      (setq p (next-single-property-change p 'face object end))
      (let ((value (car ; face between current 'prev and 'p
                    (alist-get attr
                               (get-text-property prev 'face))))
            (prev-added (car result)))
        (if (and (equal (car (last prev-added)) value)
                 prev-added)
            (setcar result ; expand bound if same
                    (list (car prev-added)
                          p
                          value))
          (push (list prev p value) result))))
    result))

(defun eof-attibute-values (start end object attr)
  "Get all posible value of face attribute ATTR in rigion between START and END.
Haven't delete duplicates."
  (mapcan #'last
          (eof-get-face-attribute-values
           start end object attr)))

(defun eof-remove-face-attribute (start end object attr)
  "Remove face attribute ATTR from all text from START to END.
Only useful to faces in eof's style (one attibute per key-list)."
  (let ((p start)
        (prev))
    (while (< p end)
      (setq prev p)
      (setq p (next-single-property-change p 'face object end))
      (put-text-property
       prev p 'face
       (assoc-delete-all attr (get-text-property prev 'face))))))

;; End of Extra functions operating face attributes

;;; Basic face operating function.

(defun eof-set-family (start end object &optional family)
  (eof-remove-face-attribute start end object :family)
  (when family
    (add-face-text-property
     start end
     `(:family ,family) nil object)))

(defun eof-set-foreground (start end object &optional color)
  (eof-remove-face-attribute start end object :foreground)
  (when color
    (add-face-text-property
     start end
     `(:foreground ,color) nil object)))

(defun eof-set-background (start end object &optional color)
  (eof-remove-face-attribute start end object :background)
  (when color
    (add-face-text-property
     start end
     `(:background ,color) nil object)))

(defun eof-set-height (start end object &optional height)
  (eof-remove-face-attribute start end object :height)
  (when height
    (add-face-text-property start end
                            `(:height ,height)
                             nil object)))

(defun eof-set-weight (start end object &optional weight)
  (eof-remove-face-attribute start end object :weight)
  (when weight
    (add-face-text-property start end
                            `(:weight ,weight)
                             nil object)))

(defun eof-set-slant (start end object &optional slant-p)
  (eof-remove-face-attribute start end object :slant)
  (when slant-p
    (add-face-text-property start end
                            '(:slant italic)
                             nil object)))

(defun eof-set-underline (start end object &optional color-or-t wave-p)
  (eof-remove-face-attribute start end object :underline)
  (when color-or-t
    (add-face-text-property
     start end
     `(:underline
       (:color ,color-or-t :style ,(when wave-p 'wave)))
      nil object)))

(defun eof-set-overline (start end object &optional color-or-t)
  (eof-remove-face-attribute start end object :overline)
  (when color-or-t
    (add-face-text-property start end
                            `(:overline ,color-or-t)
                            nil object)))

(defun eof-set-strike (start end object &optional color-or-t)
  (eof-remove-face-attribute start end object :strike-through)
  (when color-or-t
    (add-face-text-property start end
                            `(:strike-through ,color-or-t)
                            nil object)))

(defun eof-set-box (start end object &optional vwidth hwidth color style)
  (eof-remove-face-attribute start end object :box)
  (when vwidth
    (add-face-text-property
     start end
     `(:box
       (:line-width (,vwidth . ,hwidth) :color ,color :style ,style))
     nil object)))

;; End of Basic face operating function.

;;; Basic face operating command.

(defvar-local eof-editing-buffer nil
  "Current editing eof file.")

(defun eof-slant (start end)
  (interactive "r")
  (let ((attrs (eof-attibute-values
                start end eof-editing-buffer :slant)))
    (if (or (length> attrs 1)
            (car attrs))
        (eof-set-slant start end eof-editing-buffer)
      (eof-set-slant start end eof-editing-buffer t))))

(defun eof-weight (start end)
  (interactive "r")
  (let ((attrs (eof-attibute-values
                start end eof-editing-buffer :weight)))
    (if (or (length> attrs 1)
            (car attrs))
        (eof-set-weight start end eof-editing-buffer)
      (eof-set-weight start end eof-editing-buffer 'bold))))

(defun eof-weight-select (start end)
  (interactive "r")
  (let ((choice
         (completing-read "Choose a weight: "
                          '("ultra-light"
                            "extra-light"
                            "light"
                            "semi-light"
                            "normal"
                            "semi-bold"
                            "bold"
                            "extra-bold"
                            "ultra-bold"))))
    (eof-set-weight start end eof-editing-buffer
                    (make-symbol choice))))

(defun eof-underline (start end)
  (interactive "r")
  (let ((attrs (eof-attibute-values
                start end eof-editing-buffer :underline)))
    (if (or (length> attrs 1)
            (car attrs))
        (eof-set-underline start end eof-editing-buffer)
      (eof-set-underline start end eof-editing-buffer t))))

(defun eof-underline-select (start end)
  (interactive "r")
  (let ((color (read-color "Choose a color: "))
        (wave-p (y-or-n-p "Use wave line?")))
    (eof-set-underline start end eof-editing-buffer
                       color wave-p)))

(defun eof-family-select (start end)
  (interactive "r")
  (eof-set-family
   start end eof-editing-buffer
   (completing-read "Choose a font: "
                    (mapcar
                     #'(lambda (family)
                         (propertize family 'face
                                     (list (list :family family))))
                     (font-family-list)))))

(defun eof-foreground-select (start end)
  (interactive "r")
  (eof-set-foreground
   start end eof-editing-buffer
   (read-color "Choose a foreground color: ")))

(defun eof-background-select (start end)
  (interactive "r")
  (eof-set-background
   start end eof-editing-buffer
   (read-color "Choose a foreground color: ")))

(defun eof-box (start end)
  (interactive "r")
  (let ((attrs (eof-attibute-values
                start end eof-editing-buffer :box)))
    (if (or (length> attrs 1)
            (car attrs))
        (eof-set-box start end eof-editing-buffer)
      (eof-set-box start end eof-editing-buffer
                   1 1))))

(defun eof-box-select (start end)
  (interactive "r")
  (eof-set-box
   start end eof-editing-buffer
   (read-number "Vertical line width: ")
   (read-number "Horizontal line width: ")
   (read-color "Box color: ")
   (make-symbol
    (completing-read "Choose a box style: "
                     '("released-button"
                       "pressed-button"
                       "flat-button")))))

(defun eof-height (start end)
  (interactive "r")
  (eof-set-height
   start end eof-editing-buffer
   (read-number "Height number: " nil)))

(defun eof-height-select (start end)
  (interactive "r")
  (eof-set-height
   start end eof-editing-buffer
   (completing-read "Choose a height: "
                    (mapcar
                     #'(lambda (height)
                         (propertize (number-to-string height)
                                     'face
                                     (list (list :height height))))
                     (number-sequence 50 720 10)))))

(defun eof-height-increase-decrease-number (start end inc)
  (interactive "r\nnHeight number being increase / decrease: ")
  (mapcar #'(lambda (region-value-list)
              (let ((s (car region-value-list))
                    (e (cadr region-value-list))
                    (value (caddr region-value-list)))
                (eof-set-height s e eof-editing-buffer
                                (+ (or value
                                       (face-attribute 'default :height))
                                   inc))))
          (eof-get-face-attribute-values start end eof-editing-buffer :height)))

(defun eof-height-increase (start end)
  (interactive "r")
  (eof-height-increase-decrease-number start end 10))

(defun eof-height-decrease (start end)
  (interactive "r")
  (eof-height-increase-decrease-number start end -10))

(defun eof-height-resume (start end)
  (interactive "r")
  (eof-set-height start end eof-editing-buffer))

(defun eof-format-clean (start end)
  (interactive "r")
  (remove-text-properties start end '(face) eof-editing-buffer))

;; End of Basic face operating command.

;;; Format painter.

(defvar eof-format-painter-enable-p nil
  "Show if format painter is enabled and not be used now.")

(defvar eof-format-painter-start-format nil
  "The face attibutes (\"format\") of the point
where last time use eof-format-painter.")

(defvar eof-format-painter-mark-point nil
  "The mark point which first be set when format painter is enabled.")

(defvar eof-format-painter-start-object nil
  "When format painter is enabled, and OBJECT in format-panter function is not omitted,
This variable will be set to the OBJECT, and make sure the format painter will only
work in that buffer.")

(defun eof-format-painter-post-command ()
  (if mark-active
      (setq eof-format-painter-mark-point (mark)) ; check if new region
    (when eof-format-painter-mark-point           ; have already been set.
      (put-text-property
       (region-beginning) (region-end)
       'face eof-format-painter-start-format
       eof-format-painter-start-object)
      (remove-hook 'post-command-hook
                   #'eof-format-painter-post-command))))

(defun eof-format-painter (&optional object)
  "Run this command to enable format painter, run again to cancel.

OBJECT is the buffer which apply the painter.

Run this command, mark a region, then deactivate the mark,
format will apply to it.

This function will set a function \"eof-format-painter-post-command\" in
post-command-hook, it will check the region you mark and apply format painter
into the region after you deactivate it."
  (interactive)
  (setq eof-format-painter-start-format ; record format at point.
        (get-text-property (point) 'face object)
        eof-format-painter-mark-point nil
        eof-format-painter-start-object object)
  (add-hook 'post-command-hook
            #'eof-format-painter-post-command))

;; End of Format painter.

;;; Panel dragging.

(defvar-local eof-editing-buffer-frame
  (window-frame (get-buffer-window
                 eof-editing-buffer))
  "Frame of eof-editing-buffer")


(defvar eof-panel " *eof-panel*"
  "Panel of eof.")

(defvar eof-panel-dragging-p nil
  "Non nil if eof-panel is been dragging.")
(defvar eof-panel-drag-start-x nil
  "Panel X position when last drag start.")
(defvar eof-panel-drag-start-y nil
  "Panel Y position when last drag start.")
(defvar eof-panel-drag-start-mouse-x nil
  "Mouse X position when last drag start.")
(defvar eof-panel-drag-start-mouse-y nil
  "Mouse Y position when last drag start.")
(defvar eof-panel-drag-saved-mouse-map nil
  "When dragging eof-panel, eof will remap mouse key to abort dragging globally.
This variable is the a list of mouse map have been changed, use to recover global keymap
after dragging.

Update in each time dragging start.

It's content is list, not cons!")

(defvar eof-panel-drag-mouse-distinct 30
  "The mouse distinct with the panel bottom-right when start dragging panel
from the panel frame, such as the drag button.")

(defvar eof-panel-posn nil
  "Current position of eof panel.")

(defun eof-panel-drag (&rest _)
  "Start / Stop dragging eof panel."
  (interactive)
  (if eof-panel-dragging-p
      (progn (mapc #'(lambda (lst) ; recover mouse keymap.
                       (apply #'global-set-key lst))
                   eof-panel-drag-saved-mouse-map)
             (global-unset-key [mouse-movement]) ; stop track mouse.
             (setq track-mouse nil
                   eof-panel-dragging-p nil
                   eof-panel-posn (posframe-funcall
                                   eof-panel
                                   #'frame-position)))
    (progn
      (when (eq (current-buffer) (get-buffer  ; when focusing posframe, leave posframe
                                  eof-panel)) ; and set mouse pos to the panel around.
        (let ((posframe-posn (frame-position)))
          (set-mouse-pixel-position
           (selected-frame)
           (+ (frame-pixel-width) eof-panel-drag-mouse-distinct)
           (+ (frame-pixel-height) eof-panel-drag-mouse-distinct)))
        (select-frame eof-editing-buffer-frame))
      (let ((mouse-posn (cdr (mouse-pixel-position))) ; record position infomation
            (panel-posn (posframe-funcall             ; in dragging beginning.
                         eof-panel
                         #'frame-position)))
        (setq eof-panel-drag-start-x (car panel-posn)
              eof-panel-drag-start-y (cdr panel-posn)
              eof-panel-drag-start-mouse-x (car mouse-posn)
              eof-panel-drag-start-mouse-y (cdr mouse-posn)
              eof-panel-drag-saved-mouse-map
              (mapcar #'(lambda (key) ; cannot set (current-global-map) directly
                          (list key (global-key-binding key))) ; backup mouse keymap.
                      '([mouse-1] [mouse-2] [mouse-3]))))
      (global-set-key ; user's mouse may enter other frame, so global here.
       [mouse-movement]
       (lambda () (interactive)
         (let* ((mouse-posn (cdr (mouse-pixel-position)))
                (posframe-posn (posframe-funcall
                                eof-panel
                                #'frame-position)))
           (posframe-show eof-panel
                          :position
                          (cons (+ (- (car mouse-posn)
                                      eof-panel-drag-start-mouse-x)
                                   eof-panel-drag-start-x)
                                (+ (- (cdr mouse-posn)
                                      eof-panel-drag-start-mouse-y)
                                   eof-panel-drag-start-y))
                          :border-color eof-panel-border-color
                          :border-width eof-panel-border-width
                          :accept-focus nil))))
      (setq eof-panel-dragging-p t
            track-mouse 'drag-tracking) ; enable track event of [mouse-movement]
      (global-set-key [mouse-1] #'eof-panel-drag) ; map mouse command to abort dragging.
      (global-set-key [mouse-2] #'eof-panel-drag)
      (global-set-key [mouse-3] #'eof-panel-drag))))

;; End of Panel dragging.

;;; Panel button support.

(defface eof-panel-split-face
  '((t (:inherit default)))
  "Face for split bars of eof-panel.")

(defface eof-panel-button-face
  '((t (:inherit 'default)))
  "Basic face for buttons of eof-panel.")

(defface eof-select-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for select button in panel.")

(defface eof-family-button-face
  '((t ((:inherit eof-panel-button-face))))
  "Face for family selection in panel.")

(defface eof-height-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for height button in panel.")

(defface eof-height-select-button-face
  '((t ((:inherit eof-panel-button-face))))
  "Face for height selection in panel.")

(defface eof-height-increase-button-face
  '((t ((:inherit eof-panel-button-face))))
  "Face for height increase button in panel.")

(defface eof-height-decrease-button-face
  '((t ((:inherit eof-panel-button-face))))
  "Face for height decrease button in panel.")

(defface eof-foregrund-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for foreground button in panel.")

(defface eof-background-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for background button in panel.")

(defface eof-weight-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for weight button in panel.")

(defface eof-slant-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for slant button in panel.")

(defface eof-underline-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for underline button in panel.")

(defface eof-drag-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for weight button in panel.")

(defface eof-format-painter-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for format painter button in panel.")

(defface eof-format-clean-button-face
  '((t (:inherit eof-panel-button-face)))
  "Face for format clean button in panel.")

(defface eof-panel-hide-face
  '((t (:inherit eof-panel-button-face)))
  "Face for hide button of panel.")


(define-button-type 'eof-panel-drag-button
  'action #'eof-panel-drag)


(defun eof-button-function (func)
  (select-frame eof-editing-buffer-frame)
  (funcall func (region-beginning) (region-end)))

(define-button-type 'eof-family-select-button
  'action #'(lambda (button)
              (eof-button-function #'eof-family-select)))

(define-button-type 'eof-height-button
  'action #'(lambda (button)
              (eof-button-function #'eof-height)))

(define-button-type 'eof-height-select-button
  'action #'(lambda (button)
              (eof-button-function #'eof-family-select)))

(define-button-type 'eof-height-increase-button
  'action #'(lambda (button)
              (eof-button-function #'eof-height-increase)))

(define-button-type 'eof-height-decrease-button
  'action #'(lambda (button)
              (eof-button-function #'eof-height-decrease)))

(define-button-type 'eof-foreground-button
  'action #'(lambda (button)
              (eof-button-function #'eof-foreground-select)))

(define-button-type 'eof-background-button
  'action #'(lambda (button)
              (eof-button-function #'eof-background-select)))

(define-button-type 'eof-weight-button
  'action #'(lambda (button)
              (eof-button-function #'eof-weight)))

(define-button-type 'eof-weight-select-button
  'action #'(lambda (button)
              (eof-button-function #'eof-weight-select)))

(define-button-type 'eof-slant-button
  'action #'(lambda (button)
              (eof-button-function #'eof-slant)))

(define-button-type 'eof-underline-button
  'action #'(lambda (button)
              (eof-button-function #'eof-underline)))

(define-button-type 'eof-underline-select-button
  'action #'(lambda (button)
              (eof-button-function #'eof-underline-select)))

(define-button-type 'eof-format-painter-button
  'action #'(lambda (button)
              (eof-format-painter eof-editing-buffer)))

(define-button-type 'eof-format-clean-button
  'action #'(lambda (button)
              (eof-button-function #'eof-format-clean)))

(define-button-type 'eof-panel-hide-button
  'action #'(lambda (button)
              (eof-panel-hide)))

(define-button-type 'eof-panel-split-button)


(defvar eof-current-face nil
  "Face at current point.")

(defvar eof-panel-buttons
  '((:weight (lambda ()
             (let ((w (assq :weight eof-current-face)))
               (propertize
                (pcase (cadr w)
                  ('ultra-bold "[UB]")
                  ('extra-bold "[EB]")
                  ('bold "[B]")
                  ('semi-bold "[SB]")
                  ('semi-light "[sl]")
                  ('light "[l]")
                  ('extra-light "[el]")
                  ('ultra-light "[ul]")
                  (_ "[n]"))
                'face (list w '(:inherit eof-panel-button-face)))))
           'eof-weight-button)
    (:weight-select (lambda ()
                    (propertize
                     "⣿"
                     'face 'eof-select-button-face))
                  'eof-weight-select-button)
    (:slant (lambda ()
               (propertize
                "[I]"
                'face (list (assq :slant eof-current-face)
                            '(:inherit eof-slant-button-face))))
             'eof-slant-button)
    (:underline (lambda ()
                  (propertize
                   "[U]"
                   'face (list (assq :underline eof-current-face)
                               '(:inherit eof-underline-button-face))))
                'eof-underline-button)
    (:underline-select (lambda ()
                         (propertize
                          "⣿"
                          'face 'eof-select-button-face))
                       'eof-underline-select-button)
    (:drag (lambda ()
             (propertize
              "[Drag]"
              'face 'eof-drag-button-face))
           'eof-panel-drag-button)
    (:family (lambda ()
               (format "%s %s"
                       (propertize
                        (or (cadr (assq :family eof-current-face))
                            "default")
                        'face 'eof-family-button-face)
                       (propertize
                        "⣿"
                        'face 'eof-select-button-face)))
             'eof-family-select-button)
    (:height (lambda ()
               (propertize
                (number-to-string
                 (or (cadr (assq :height eof-current-face))
                     (face-attribute 'default :height)))
                'face 'eof-he))
             'eof-height-button)
    (:height-select (lambda ()
                      (propertize
                       "⣿"
                       'face '((:inherit eof-panel-button-face))))
                    'eof-height-select-button)
    (:height-increase (lambda ()
                        (propertize
                         "[inc]"
                         'face 'eof-height-increase-button-face))
                      'eof-height-increase-button)
    (:height-decrease (lambda ()
                        (propertize
                         "[dec]"
                         'face 'eof-height-decrease-button-face))
                      'eof-height-decrease-button)
    (:foreground (lambda ()
                   (propertize
                    "[fg]"
                    'face (list (list :foreground
                                      (cadr (assq :foreground
                                                  eof-current-face)))
                                '(:inherit 'eof-backgrund-button-face))))
                 'eof-foreground-button)
    (:background (lambda ()
                   (propertize
                    "[bg]"
                    'face (list (list :background
                                      (cadr (assq :background
                                                  eof-current-face)))
                                '(:inherit 'eof-backgrund-button-face))))
                 'eof-background-button)
    (:format-painter (lambda ()
                       (propertize
                        "[Fp]"
                        'face (list (when eof-format-painter-enable-p
                                      '(:box t))
                                    '(:inherit eof-format-painter-button-face))))
                     'eof-format-painter-button)
    (:format-clean (lambda ()
                     (propertize
                      "[Clean]"
                      'face 'eof-format-clean-button-face))
                   'eof-format-clean-button)
    (:split (lambda ()
              (propertize " | "
                          'face 'eof-panel-split-face))
            'eof-panel-split-button)
    (:hide (lambda ()
             (propertize "[Hide]"
                         'face 'eof-panel-hide-face))
           'eof-panel-hide-button)))

;; End of Panel button support.

;;; Panel build and basic operation.

(defvar eof-panel-basic-format
  '(:split :family :split
           :height " " :height-select :split :height-increase :split :height-decrease
           :split :format-clean :split "\n" :split
           :format-painter :split :weight :weight-select :split :slant :split
           :underline :underline-select :split :background :split :foreground :split
           :drag :split :hide :split))


(defvar eof-panel-current-page 'basic
  "Current panel page.")


(defun eof-panel-generate (&optional panel)
  (mapcar
   #'(lambda (item)
       (if (keywordp item)
           (let ((args (assq item eof-panel-buttons)))
             (list #'insert-text-button
                   (funcall (cadr args))
                   ''type (caddr args)
                   ''eof-button-key (car args)))
         (list #'insert item)))
   (pcase 'eof-panel-current-page
     ('basic eof-panel-basic-format)
     (_ eof-panel-basic-format))))

(defun eof-panel-generate-item (item)
  (let ((args (assq item eof-panel-buttons)))
    (list #'insert-text-button
          (funcall (cadr args))
          ''type (caddr args)
          ''eof-button-key (car args))))

(defun eof-panel-basic ()
  (with-current-buffer (get-buffer-create eof-panel)
    (erase-buffer)
    (mapc #'eval (eof-panel-generate)))
  (setq eof-panel-current-page 'basic))


(defvar eof-panel-border-color "green"
  "Border color of eof-panel.")

(defvar eof-panel-border-width 1
  "Border width of eof-panel.")

(defsubst eof-panel-default-posn ()
  (frame-position eof-editing-buffer-frame))

(defun eof-panel-create-show ()
  "Create eof-panel and show it."
  (eof-panel-basic)
  (require 'posframe)
  (when (posframe-workable-p)
    (posframe-show eof-panel
                   :position (eof-panel-default-posn)
                   :border-color eof-panel-border-color
                   :border-width eof-panel-border-width))
  (setq eof-panel-posn (eof-panel-default-posn)))

(defun eof-panel-show ()
  "Show eof panel."
  (interactive)
  (posframe-show eof-panel
                 :position eof-panel-posn
                 :border-color eof-panel-border-color
                 :border-width eof-panel-border-width))

(defun eof-panel-hide ()
  "Hide eof panel."
  (interactive)
  (posframe-hide eof-panel))

;; End of Panel build and basic operation.

;;; Panel partly rebuild.

(defun eof-panel-find-button (key)
  (with-current-buffer eof-panel
    (let ((p (point-min))
          (max (point-max))
          (prev)
          (result))
      (while (< p max)
        (setq prev p)
        (setq p (next-single-property-change p 'eof-button-key nil max))
        (when (eq key (get-text-property prev 'eof-button-key))
          (setq result (list prev p)
                p (1+ max))))
      result)))

(defun eof-panel-update ()
  (let* ((face-now (get-text-property (1- (point)) 'face))
         (need-update
          (delq nil
                (mapcar
                 #'(lambda (item)
                     (if (xor (memq item face-now)
                              (memq item eof-current-face))
                         (car item)
                       nil))
                 (append face-now eof-current-face)))))
    (setq eof-current-face face-now)
    (with-current-buffer eof-panel
      (mapc
       #'(lambda (key)
           (let ((posn-list (eof-panel-find-button key)))
             (if (eq posn-list nil)
                 (insert (format "%S" key)))
             (apply #'delete-region posn-list)
             (goto-char (car posn-list))
             (eval (eof-panel-generate-item key))))
       need-update))))

;; End of Panel partly rebuild.

;;; Defining mode.

(defvar eof-last-position nil
  "Cursor position in last command.")

(defun eof-post-command ()
  (when (or (not (= (point) eof-last-position)) ; cursor moved
            (ignore-errors ; avoid lambda
              (equal (substring (symbol-name last-command) 0 3) ; run eof commands
                     "eof-")))
    (eof-panel-update)
    (setq eof-last-position (point))))

(defvar eof-mode-map
  (copy-keymap text-mode-map))

(define-derived-mode eof-mode text-mode "EOF"
  "EOF mode - Emacs Office mode."
  :keymap eof-mode-map
  (setq-local eof-editing-buffer (current-buffer)
              eof-editing-buffer-frame (selected-frame))
  (setq eof-last-position (point)
        eof-current-face (get-text-property (1- (point)) 'face))
  (add-hook 'post-command-hook #'eof-post-command nil t)
  (unless buffer-display-table
    (setq buffer-display-table (make-display-table)))
  (aset buffer-display-table ?\^P [?\n ?\n])
  (eof-panel-create-show)
  (run-hooks eof-mode-hook))

;; End of Defining mode.

;;; Paragraph properties.
(defun eof-forward-paragraph (&optional n)
  "Move forward to the next beginning of eof paragraph.

With argument ARG, do it ARG times;
a negative argument ARG = -N means move backward N paragraphs.

In eof,  character means the starting of a new paragraph.
This funtcion fill search  for N times and move to there."
  (interactive "^p")
  (or n (setq n 1))
  (condition-case err
      (search-forward "" nil nil n)
    (search-failed
     (if (< n 0)
         (prog1 (goto-char (point-min))
           (if (eq (following-char) ?\^P)
               nil
             (insert ""))) ; if buffer isn't begin with ^P, insert it
       (prog1 (goto-char (point-max))
         (insert ""))))))

(defun eof-backward-paragraph (&optional n)
  "Move backward to the last beginning of eof paragraph.

With argument ARG, do it ARG times;
a negative argument ARG = -N means move backward N paragraphs.

For more information, see 'eof-forward-paragraph'."
  (interactive "^p")
  (eof-forward-paragraph (- n)))

(defun eof-paragraph-properties (pos)
  "Get eof paragraph properties of eof paragraph at POS.

eof paragraph properties is in the text property 'eof-paragraph-prop at the
starting  character in each paragraph."
  (save-excursion
    (goto-char pos)
    (get-text-property
     (eof-forward-paragraph -1)
     'eof-paragraph-prop)))

(defun eof-set-paragraph-properties (pos properties)
  "Set eof paragraph properties of eof paragraph at POS."
  (save-excursion
    (goto-char pos)
    (setq pos (eof-forward-paragraph -1))
    (put-text-property
     pos (1+ pos)
     'eof-paragraph-prop
     properties)))

(defun eof-get-paragraph-property (pos prop)
  "Get eof paragraph property PROP of eof paragraph at POS."
  (cadr (assq prop
              (eof-paragraph-properties pos))))

(defun eof-put-paragraph-property (pos prop value)
  "Set one property of eof paragraph at POS."
  (let ((properties (eof-paragraph-properties pos)))
    (setf (alist-get prop properties) value)
    (eof-set-paragraph-properties
     pos properties)))

;; End of Paragraph properties.

;;; Text alignment and indentation.

;; I don't want to use 'left-margin' and 'right-margin' text properties here,
;; that dispite the using, refill paragraph is unavoidable, stick up those
;; properties will just being a waste.

(defun eof-paragraph-indent (pos)
  (save-excursion
    (goto-char pos)
    (let* ((para-start (save-excursion
                         (1+ (eof-forward-paragraph -1)))) ;exclude 
           (para-end (1- (eof-forward-paragraph 1)))
           (line-start (line-number-at-pos para-start))
           (line-end (line-number-at-pos para-end))
           (para-props (eof-paragraph-properties pos))
           (indent-left (cadr (assq :indent-left para-props)))
           (indent-right (cadr (assq :indent-right para-props)))
           (special (assq :special para-props))
           (special-type (car special))
           (special-indent (cdr special))

           (align-type (cadr (assq :align para-props))))

      (goto-char para-start)
      (delete-horizontal-space)
      (if special-type
          (save-excursion	    
            (if special-type
                (let ((firstline? (eq special-type :firstline))) ;or hanging

                  (indent-line-to	;indent first line
                   (+ left-margin indent-left (if firstline? indentation 0)))

                  (if (<= (- (line-end-position) para-start)
                          fill-column)	;if too long after indent, refill
                      nil
                    (fill-paragraph)
                    (setq line-end (line-number-at-pos
                                           (eof-forward-paragraph 1))))

                  (and (> line-end line-start) ;if multi lines
                       (goto-line (1+ line-start))
                       (indent-line-to	;indent second line
                        (+ left-margin indent-left
                           (if firstline? 0 indentation)))))))

        (indent-line-to (+ left-margin indent-left))) ;left margin

      (let ((fill-column (or indent-right fill-column))) ;right margin
        (fill-region-as-paragraph			 ;refill
         para-start
         (setq para-end (1- (eof-forward-paragraph 1))))

        (if (memq align-type '(:center :right)) ;center and right align
            (progn			;inspired py 'center-line'
              (goto-char para-end)
              (delete-horizontal-space)
              (save-excursion
                (beginning-of-line)
                (delete-horizontal-space))
              (let ((space (- fill-column left-margin (current-column))))
                (if (> space 0)
                    (indent-line-to (+ left-margin
                                       (round (* space
                                                 (if (eq align-type :center)
                                                     0.5
                                                   1)))))))))))))
;; End of Text alignment and indentation.


(insert
 (eval
  (car
   (read-from-string
    (format "%S"
            (buffer-substring 219 223)))))) ; must not in font-lock-mode

1.2.4. eempeg

;; -*- lexical-binding: t -*-
;; (setq-local lexical-binding t)

(require 'posframe)
(require 'f)

;;; Functions for calling ffmpeg.

(defvar eempeg-process-name "*eempeg-process*"
  "External process name for eempeg.")

(defvar eempeg-stdout-buffer "*eempeg-ffmpeg-stdout*"
  "Stdout buffer for eempeg.")

(defvar eempeg-stderr-buffer "*eempeg-ffmpeg-stderr*"
  "Stderr buffer for eempeg.")

(defun eempeg-call-ffmpeg (args &optional sentinel send-string)
  (with-current-buffer (get-buffer-create
                        eempeg-ffmpeg-stdout-buffer)
    (set-buffer-multibyte nil)
    (set-buffer-file-coding-system 'binary t)
    (erase-buffer))
  (with-current-buffer (get-buffer-create
                        eempeg-ffmpeg-stderr-buffer)
    (erase-buffer))
  (let ((process (make-process
                  :name eempeg-process-name
                  :buffer eempeg-stdout-buffer
                  :command (cons "ffmpeg" args)
                  :connection-type 'pipe
                  :noquery t
                  :stderr eempeg-stderr-buffer
                  :coding 'binary
                  :sentinel sentinel)))
    (if send-string
        (progn (process-send-string
                process send-string)
               (process-send-eof process)))))

;; End of Functions for calling ffmpeg.

;;; Define derived mode.

;; End of Define derived mode.

;;; Defining EEmpeg Ps mode.

(defvar eempeg-ps-data nil
  "Image data storage.
Prefer using formats which emacs supported, See 'image-type-header-regexps'.")

(defvar eempeg-ps-original-data nil
  "Original image data storage.")

(defvar eempeg-ps-undo-history nil
  "Undo history list of editing image in eempeg.

Each undo history saved the whole image data once,
So too much history may eat a lot of memory.")

(defvar eempeg-ps-undo-max-items 10
  "Max items in the undo history.

Each undo history saved the whole image data once,
So too much history may eat a lot of memory.")

(defvar eempeg-ps-image nil
  "Image descriptor for the editing image in 'eempeg-ps-mode'.
Gnerated by 'eempeg-ps-data'

This is the image descriptor which directly using the data of
'eempeg-ps-data' as it's data, and do not show.
It is mainly used to get informations of original image using native elisp
functions, such as 'image-size'.")

(defvar eempeg-ps-preview nil
  "Image descriptor for previewing editing image in 'eempeg-ps-mode'.
Gnerated by 'eempeg-ps-data'

This is the image which directly using to image preview.")

(defvar eempeg-ps-preview-args
  '(:transform-smoothing t :scale 1)
  "Arguments for 'eempeg-ps-preview'.")

(defvar eempeg-ps-preview-slice nil
  "Slice value of previewing image.")

(defvar eempeg-ps-preview-buffer nil
  "Buffer name for previewing image in 'eempeg-ps-mode'.")

(defvar eempeg-ps-buffer nil
  "Buffer name for editing image in 'eempeg-ps-mode'.")

(define-derived-mode eempeg-ps-mode fundamental-mode "EEmpeg-ps"
  "Mode for edit image in eempeg."
  (setq-local
     header-line-format
     "Edit, then save with ‘C-x C-s’ or abort with ‘C-c C-k’"))

;; End of Defining EEmpeg Ps mode.

;;; Entering ps mode.

(defun eempeg-ps-data (data)
  (interactive "^P") ;TODO: support interactively insert image data
  (let ((type (image-type-from-data data)))
    (if (not (image-type-available-p type))
        (progn
          (y-or-n-p "Format not supported. Try transcoding?")
          (eempeg-call-ffmpeg	;format not available, ask for transcoding
           (list "-i" "-"
                 "-frames:v" "1"
                 "-vcodec" "png"
                 "-")
           #'(lambda (process event)
               (and (string= event "finished\n")
                    (eempeg-ps-data
                     (with-current-buffer eempeg-stdout-buffer
                       (buffer-string)))))))
      (setq eempeg-ps-buffer		;create buffers
            (get-buffer-create (concat (current-time-string) ;using time as name
                                       "."
                                       (symbol-name type)))
            eempeg-ps-preview-buffer
            (get-buffer-create (concat (current-time-string)
                                       "."
                                       (symbol-name type)
                                       " Preview")))
      (with-current-buffer eempeg-ps-buffer ;edit buffer init
        (let ((inhibit-read-only t))
          (erase-buffer)))
      (with-current-buffer eempeg-ps-preview-buffer ;preview buffer init
        (let ((inhibit-read-only t))
          (erase-buffer)))
      (delete-other-windows)		;building windows
      (switch-to-buffer eempeg-ps-preview-buffer)
      (select-window (split-window))
      (switch-to-buffer eempeg-ps-buffer))))

(defun eempeg-ps-file (filename)
  (interactive "fTarget file:")
  (eempeg-ps-data (f-read-bytes filename)))

;; End of Entering ps mode.

;; (defun eempeg-image-edit (data)
;;   (switch-to-buffer (get-buffer-create
;; 		     eempeg-image-edit-buffer))
;;   (let ((inhibit-read-only t))
;;     (erase-buffer))

;;   (setq eempeg-image-temp-data-origin data)
;;   (ignore-errors
;;     (eempeg-image-edit-redisplay data))

;;   (insert (mapconcat #'eval
;; 		     eempeg-image-edit-sections ""))
;;   (goto-char (point-min))
;;   (setq eempeg-image-edit-preview-position
;; 	(text-property-search-forward
;; 	 'eempeg-preview))
;;   (eempeg-edit-mode))

;; (defvar eempeg-image-edit-buffer "*eempeg-shot-edit*"
;;   "Buffer of image editing.")

;; (defvar eempeg-image-edit-preview-position nil
;;   "Position of the text which display property is the temp image of eempeg.")

(defvar eempeg-image-edit-resize-timer nil
  "Timer for resize image.")

(defvar eempeg-image-edit-resize-delay 3
  "Delay for process resize with ffmpeg.")

(defun eempeg-image-edit-redisplay (data)
  (setq eempeg-image-temp-data data 	;save new

        eempeg-image-temp-undo-list ;save history
        (if (>= (length eempeg-image-temp-undo-list)
                eempeg-image-edit-undo-length)
            (append (cdr eempeg-image-temp-undo-list)
                    (list eempeg-image-temp-data))
          (append eempeg-image-temp-undo-list
                  (list eempeg-image-temp-data)))

        eempeg-image-temp (cons 'image	;create image
                                 (list :type (image-type
                                              eempeg-image-temp-data nil t)
                                       :data eempeg-image-temp-data))

        eempeg-image-temp-displaying	;create image for display
        (create-image eempeg-image-temp-data nil t
                      :max-height (cons
                                   (round (* (window-body-height) 0.5))
                                   'em))))

;; (put-text-property
;;        (prop-match-beginning eempeg-image-edit-preview-position)
;;        (prop-match-end eempeg-image-edit-preview-position)
;;        'display
;;        (cons eempeg-image-temp-displaying
;; 	     (assoc-delete-all 'image
;; 			       (get-text-property
;; 				(prop-match-beginning
;; 				 eempeg-image-edit-preview-position)
;; 				'display))))

;; (defun eempeg-image-properties-at (position)
;;   "Image properties of the first image after position POSITION."
;;   (cdr (or (assq 'image
;; 		 (get-text-property position
;; 				    'display))
;; 	   (assq 'image
;; 		 (prop-match-value
;; 		  (text-property-search-forward
;; 		   'display 'image
;; 		   #'(lambda (val p-val)
;; 		       (eq val (car p-val)))))))))
;; (defun eempeg-set-image-properties (position properties)
;;   "Set the image properties of the first image after position POSITION."
;;   ())
;; (defun eempeg-put-image-property ())
;; (defun eempeg-get-image-property ())

;;; Ps preview image.

(defun eempeg-ps-preview-build ()
  "Build and set preview image
through 'eempeg-ps-data' and 'eempeg-ps-preview-args'."
  (setq eempeg-ps-preview
        (cons 'image
              (append (list :data eempeg-ps-data)
                      eempeg-ps-preview-args))))

(defun eempeg-ps-preview-insert ()
  "Insert contents of 'eempeg-ps-preview-buffer'."
  (with-current-buffer (get-buffer-create
                        eempeg-ps-preview-buffer)
    (save-excursion
      (let  ((exist (text-property-search-forward
                     'eempeg-preview)))
        (and exist
             (delete-region (goto-char (prop-match-beginning exist))
                            (prop-match-end exist))))
      (insert (propertize " "
                          'display (list eempeg-ps-preview-slice
                                         eempeg-ps-preview)
                          'eempeg-preview t
                          'read-only t)))))

(defun eempeg-ps-preview-redisplay ()
  "Redisplay preview image."
  (with-current-buffer (get-buffer-create
                        eempeg-ps-preview-buffer)
    (let ((prop-match (text-property-search-forward
                       'eempeg-preview)))
      (or prop-match (eempeg-ps-preview-insert))
      (put-text-property
       (prop-match-beginning prop-match)
       (prop-match-end prop-match)
       'display (list eempeg-ps-preview-slice
                      (eempeg-ps-preview-build))))))

;;; Ps preview slice functions.

(defun eempeg-ps-preview-slice (&optional x y width height)
  (let* ((slice (cdr eempeg-ps-preview-slice))
         (width (min (or width (caddr slice))
                     (car size)))
         (height (min (or height (cadddr slice))
                      (cdr size)))
         (x (or (and x
                     (setq width (- (+ width x) (car slice)))
                     x)
                (car slice)))
         (y (or (and y
                     (setq height (- (+ height y) (cdr slice)))
                     y)
                (cdr slice))))
    (setq eempeg-ps-preview-slice
          (list 'slice x y width height))
    (eempeg-ps-preview-redisplay eempeg-ps-preview-slice)))

(defun eempeg-ps-preview-rotate (rotation)
  (org-plist-delete eempeg-ps-preview-args
                    :rotation)
  (plist-put eempeg-ps-preview-args :rotation
             (* 90 (round (/ (float rotation) 90))))
  (eempeg-ps-preview-redisplay
   (eempeg-ps-preview-build)))

(defun eempeg-image-edit-slice (x y width height)
  (let ((inhibit-read-only t))
    (put-text-property
       (prop-match-beginning eempeg-image-edit-preview-position)
       (prop-match-end eempeg-image-edit-preview-position)
       'display
       (cons (list 'slice x y width height)
             (assoc-delete-all 'slice
                               (get-text-property
                                (prop-match-beginning
                                 eempeg-image-edit-preview-position)
                                'display))))
    (setq eempeg-image-edit-resize-timer
            (run-with-timer
             eempeg-image-edit-resize-delay
             nil
             #'(lambda ()
                 (eempeg-ffmpeg-call
                  (list "-i" "-"
                        "-vf" (format "crop=x=%s:y=%s:w=%s:h=%s"
                                      x y width height)
                        "-frames:v" "1"
                        "-f" "image2pipe"
                        "-vcodec" "png"
                        "-")
                  #'eempeg-image-edit-sentinel
                  eempeg-image-temp-data))))))

(defun eempeg-image-edit-change-slice (&optional x y width height)
  (let ((slice (cdr (assoc 'slice
                           (get-text-property
                            (prop-match-beginning
                             eempeg-image-edit-preview-position)
                            'display))))
        (size (image-size eempeg-image-temp t)))
    (let* ((width (min (or width (caddr slice))
                       (car size)))
           (height (min (or height (cadddr slice))
                        (cdr size)))
           (x (or (and x
                       (setq width (- (+ width x) (car slice)))
                       x)
                  (car slice)))
           (y (or (and y
                       (setq height (- (+ height y) (cdr slice)))
                       y)
                  (cdr slice))))
      (eempeg-image-edit-slice x y width height)
      )))

(defun eempeg-image-edit-sentinel (process event)
  (if (string= event "finished\n")
      (eempeg-image-edit-redisplay
       (with-current-buffer
           (get-buffer
            eempeg-ffmpeg-stdout-buffer)
         (buffer-string)))))

(defun eempeg-field-number (str)
  (string-to-number
   (apply #'string
          (delq ?\  (string-to-list str)))))

(defvar eempeg-image-edit-sections
  '((propertize " "
                'display
                (list eempeg-image-temp-displaying
                      (let ((size (image-size eempeg-image-temp t)))
                        (list 'slice 0 0 (car size) (cdr size))))
                'read-only t
                'eempeg-preview t)
    (propertize "\nSlice X: "
                'read-only t)
    (propertize "0"
                'field 'slice-x
                'face 'widget-field
                'eempeg-field-function
                #'(lambda (x)
                    (eempeg-image-edit-slice
                     (eempeg-field-number x))))
    (propertize "           "
                'field 'slice-x
                'face 'widget-field
                'eempeg-field-function
                #'(lambda (x)
                    (eempeg-image-edit-slice
                     (eempeg-field-number x))))
    (propertize "\nSlice Y: "
                'read-only t)
    (propertize "0"
                'field 'slice-y
                'face 'widget-field
                'eempeg-field-function
                #'(lambda (y)
                    (eempeg-image-edit-slice
                     nil (eempeg-field-number y))))
    (propertize "           "
                'field 'slice-y
                'face 'widget-field
                'eempeg-field-function
                #'(lambda (y)
                    (eempeg-image-edit-slice
                     nil (eempeg-field-number y))))
    (propertize "\nSlice Width: "
                'read-only t)
    (propertize (number-to-string
                 (car (image-size eempeg-image-temp t)))
                'field 'slice-width
                'face 'widget-field
                'eempeg-field-function
                #'(lambda (width)
                    (eempeg-image-edit-slice
                     nil nil (eempeg-field-number width))))
    (propertize "           "
                'field 'slice-width
                'face 'widget-field
                'eempeg-field-function
                #'(lambda (width)
                    (eempeg-image-edit-slice
                     nil nil (eempeg-field-number width))))
    (propertize "\nSlice Height: "
                'read-only t)
    (propertize (number-to-string
                 (cdr (image-size eempeg-image-temp t)))
                'field 'slice-height
                'face 'widget-field
                'eempeg-field-function
                #'(lambda (height)
                    (eempeg-image-edit-slice
                     nil nil nil (eempeg-field-number height))))
    (propertize "           "
                'field 'slice-height
                'face 'widget-field
                'eempeg-field-function
                #'(lambda (height)
                    (eempeg-image-edit-slice
                     nil nil nil (eempeg-field-number height)))))
  "Sections for editing image in eempeg.")

(window-old-body-pixel-width)

(window-pixel-height)

(defun eempeg-image-edit (data)
  (switch-to-buffer (get-buffer-create
                     eempeg-image-edit-buffer))
  (let ((inhibit-read-only t))
    (erase-buffer))

  (setq eempeg-image-temp-data-origin data)
  (ignore-errors
    (eempeg-image-edit-redisplay data))

  (insert (mapconcat #'eval
                     eempeg-image-edit-sections ""))
  (goto-char (point-min))
  (setq eempeg-image-edit-preview-position
        (text-property-search-forward
         'eempeg-preview))
  (eempeg-edit-mode))

(insert-text-button "test" 'action #'(lambda (&rest _) (message "haha")))

;; End of Image editing.

;;; Shot delaying.

(defface eempeg-delay-box-content
  '((t (:inherit default)))
  "Face for delay box contents.")

(defvar eempeg-delay 3
  "Delay before taking shot")

(defvar eempeg-delay-box " *eempeg-delay-box*"
  "Box for showing delay time.")

(defvar eempeg-delay-box-args
  '(:position (1 . 1) :border-width 1 :border-color "green")
  "Arguments for eempeg delay box.
See 'posframe-show' for more info.")

(defvar eempeg-shot-delay-timer nil
  "Timer of shot delaying.")

(defun eempeg-delay (device input)
  (with-current-buffer (get-buffer-create
                        eempeg-delay-box)
    (if eempeg-shot-delay-timer		;in delaying
        (let ((current (string-to-number
                        (buffer-string))))
          (if (<= current 1)		;time arrive
              (progn (cancel-timer eempeg-shot-delay-timer)
                     (setq eempeg-shot-delay-timer nil)
                     (posframe-hide eempeg-delay-box)
                     (eempeg-shot-start device input))
            (erase-buffer)		;update
            (insert (number-to-string
                     (1- current)))))
      (if eempeg-delay			;not delaying
          (progn
            (erase-buffer)		;with delay
            (insert (number-to-string eempeg-delay))
            (apply #'posframe-show
                   eempeg-delay-box
                   eempeg-delay-box-args)
            (setq eempeg-shot-delay-timer
                  (run-with-timer
                   1 1 #'eempeg-delay device input)))
        (eempeg-shot-start device input))))) ;without delay

;; End of Shot delaying.

;;; Shot.

(defun eempeg-shot (&optional device input)
  "Take a shot from device."
  (interactive
   (list
    (completing-read "Choose a device:"
                     '(screen
                       camera))))
  (setq device
        (pcase device
          ("camera"
           (pcase (window-system)
             ('ns (setq input "0")
                  "avfoundation")
             ('w32 (setq input "video=\"Integrated Camera\"")
                   "dshow")
             (_ (setq input "/dev/video0")
                "v4l2")))
          (_
           (pcase (window-system)
             ('x (setq input ":0.0")
                 "x11grab")
             ('ns (setq input "1")
                  "avfoundation")
             ('w32 (setq input "video=\"screen-capture-recorder\"")
                   "dshow")
             (_ (setq input "/dev/fb0")
                "fbdev")))))
    (eempeg-delay device input))

(defun eempeg-shot-start (device input)
  (eempeg-ffmpeg-call
   (list "-f" device
         "-i" input
         "-frames:v" "1"
         "-f" "image2pipe"
         "-vcodec" "png"
         "-")
   #'(lambda (process event)
       (and (string= event "finished\n")
            (eempeg-ps-data (with-current-buffer
                                eempeg-stdout-buffer
                              (buffer-string)))))))

;; End of Shot.
(with-current-buffer (get-buffer-create "*foo*")
  (decode-coding-string
   (buffer-string)
   'raw-text))

(make-process
 :name "foo"
 :buffer "*foo*"
 :connection-type 'pipe
 :command (list "img2txt"
                "-f" "utf8"
                "1.png"))
(current-time)

(process-send-string (get-process "foo")
                     (with-current-buffer "1.png"
                       (buffer-string)))
(make-pipe-process
 :name "bar"
 :buffer "*bar*"
 :noquery t)
(process-command (get-process "bar"))
(delete-process (get-process "bar"))

1.2.5. magick

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <emacs-module.h>
#include <MagickWand/MagickWand.h>

int plugin_is_GPL_compatible;

emacs_value t, nil;

/******************/
/* functions list */
/******************/

/* list of all functions. take a quick view here. */

/* functions for simplier interaction with emacs */
char *get_string (emacs_env *env, emacs_value arg);
emacs_value get_variable (emacs_env *env, char *name);

/* defining errors */
emacs_value wand_not_available_error (emacs_env *env, emacs_value user_ptr);
emacs_value wand_no_image_error (emacs_env *env, emacs_value user_ptr);
emacs_value wand_read_image_failed_error (emacs_env *env, emacs_value filename);
emacs_value wand_write_image_failed_error (emacs_env *env, emacs_value filename);
emacs_value wand_convert_image_failed_error (emacs_env *env, emacs_value format);
emacs_value image_operation_failed_error (emacs_env *env, char *operation);
emacs_value side_effective_error (emacs_env *env, char *causes);

/* wands management */
void magick_wand_gc (void *w);
emacs_value Fmagick_new_wand (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
emacs_value Fmagick_destroy_wand (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
emacs_value Fmagick_wand_available_p (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
MagickWand *get_wand (emacs_env *env, emacs_value user_ptr);
emacs_value Fmagick_new_pixel_wand (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
emacs_value Fmagick_clone_wand (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);

/* image read and write */
emacs_value Fmagick_read_image (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
emacs_value Fmagick_write_image (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
emacs_value Fmagick_image_data (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);

/* get image informations */
emacs_value Fmagick_image_size (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);

/* image operations */
emacs_value Fmagick_crop_image (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
emacs_value Fmagick_resize_image(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
emacs_value Fmagick_flip_image(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
emacs_value Fmagick_flop_image(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
emacs_value Fmagick_rotate_image(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);

/*************************************************/
/* functions for simplier interaction with emacs */
/*************************************************/

char *get_string (emacs_env *env, emacs_value arg)
{
  ptrdiff_t size;
  env->copy_string_contents(env, arg, NULL, &size);
  char *buf = (char*) malloc(size * sizeof(char));
  env->copy_string_contents(env, arg, buf, &size);
  return buf;
}

emacs_value
get_variable (emacs_env *env, char *name)
{
  emacs_value Qsym = env->intern (env, name);
  return env->funcall(env, env->intern(env, "symbol-value"), 1,
                      (emacs_value[]) {Qsym});
}

/********************/
/* defining errors. */
/********************/

emacs_value
wand_not_available_error (emacs_env *env, emacs_value user_ptr)
{
  env->non_local_exit_signal(env,
                             env->intern(env, "wand-not-available"),
                             user_ptr);
  return nil;
}

emacs_value
wand_no_image_error (emacs_env *env, emacs_value user_ptr)
{
  env->non_local_exit_signal(env,
                             env->intern(env, "wand-no-image"),
                             user_ptr);
  return nil;
}

emacs_value
wand_read_image_failed_error (emacs_env *env, emacs_value filename)
{
  env->non_local_exit_signal(env,
                             env->intern(env, "read-image-failed"),
                             filename);
  return nil;
}

emacs_value
wand_write_image_failed_error (emacs_env *env, emacs_value filename)
{
  env->non_local_exit_signal(env,
                             env->intern(env, "write-image-failed"),
                             filename);
  return nil;
}

emacs_value
wand_convert_image_failed_error (emacs_env *env, emacs_value format)
{
  env->non_local_exit_signal(env,
                             env->intern(env, "convert-image-failed"),
                             format);
  return nil;
}

emacs_value
image_operation_failed_error (emacs_env *env, char *operation)
{
  env->non_local_exit_signal(env,
                             env->intern(env, "image-operation-failed"),
                             env->make_string(env, (char *) operation,(size_t) strlen(operation)));
  return nil;
}

/********************/
/* wands management */
/********************/

/* create new wand, get exist wand, destroy wand, clone wand, persistance background and foreground */

/* struct for magick wand

   each time wand is created, it will be packed into magick_wand_struct.
   boolean variable 'available' will show if wand haven't destroyed and can be used. */
typedef struct
{
  MagickWand *wand;
  bool available;
} magick_wand_struct;

/* struct for pixel wand, for saving colors

   almost same with magick_wand_struct */
typedef struct
{
  PixelWand *wand;
  bool available;
} pixel_wand_struct;

/* finalizer for emacs gc */
void
magick_wand_gc (void *ptr)
{
  magick_wand_struct *w = ptr;		/* get pointer. treat it as magick_wand_struct */
  void *wand = w->wand;
  if (w->available)		/* if haven't destroyed manually */
    {
      if (IsMagickWand(wand)) /* declare type */
        {
          DestroyMagickWand(wand); /* memory used by w->wand have been free */
        } else {
        if (IsPixelWand(wand))
          {
            DestroyPixelWand(wand);
          } else {
          DestroyDrawingWand(wand);
        }
      }
    }
  free(w);			/* free the struct itself */
  return;
}

emacs_value			/* no arg; return wand user-ptr */
Fmagick_new_wand (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  magick_wand_struct *w = malloc(sizeof(magick_wand_struct)); /* malloc memory for struct */
  w->wand = NewMagickWand();			/* save values into struct */
  w->available = true;
  void (*finalizer) (void *ptr) = &magick_wand_gc; /* prepare finalizer for emacs gc */
  return env->make_user_ptr(env, /* return it */
                            finalizer,
                            w);
}

emacs_value			/* arg0: wand user-ptr; return t */
Fmagick_destroy_wand (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  magick_wand_struct *w = env->get_user_ptr(env, args[0]);
  if (w->available)
    {
      DestroyMagickWand(w->wand); /* memory used by w->wand have been free */
      w->available = false;
    }
  return t;
}

emacs_value			/* arg0: wand user-ptr; return t or nil */
Fmagick_wand_available_p (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  magick_wand_struct *w = env->get_user_ptr(env, args[0]);
  if (w->available)
    return t;
  return nil;
}

MagickWand
*get_wand (emacs_env *env, emacs_value user_ptr)
{
  magick_wand_struct *w = env->get_user_ptr(env, user_ptr);
  if (w->available)
    {
      return w->wand;
    }
  return NULL;
}

emacs_value			/* arg0: color string, arg1: alpha number or t / nil; return PixelWand user-ptr */
Fmagick_new_pixel_wand (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  char *color = get_string(env, args[0]); /* get color */
  double alpha;
  if (env->is_not_nil(env, args[1]))	/* if t, transparent; nil, fully opaque; otherwise use it's float value */
    {
      alpha = env->extract_float(env, args[1]);
    } else {
    if (env->eq(env, args[1], t))
      {
        alpha = 0.0;
      }
    alpha = 1.0;
  }
  PixelWand *wand = NewPixelWand(); /* create and set */
  PixelSetColor(wand, color);
  PixelSetAlpha(wand, alpha);

  pixel_wand_struct *pw = malloc(sizeof(pixel_wand_struct)); /* prepare structure package */
  pw->wand = wand;
  pw->available = true;
  void (*finalizer) (void *ptr) = &magick_wand_gc; /* prepare finalizer for emacs gc */
  return env->make_user_ptr(env, /* return it */
                            finalizer,
                            pw);
}

emacs_value			/* arg0: wand user-ptr to clone; return a clone of arg0 */
Fmagick_clone_wand (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  magick_wand_struct *w = env->get_user_ptr(env, args[0]); /* get wand struct, treat it as magick_wand_struct to declare available */
  if (!w->available)
    {
      return wand_not_available_error(env, args[0]);
    }

  void *w1;

  if (IsMagickWand(w->wand)) /* declare type */
    {
      magick_wand_struct *w2 = malloc(sizeof(magick_wand_struct)); /* malloc memory for struct */
      w2->wand = CloneMagickWand(w->wand);		 /* save values into struct */
      w2->available = true;
      w1 = w2;
    } else {
    w1 = w;
    pixel_wand_struct *w3 = w1;
    if (IsPixelWand(w3->wand))
      {
        pixel_wand_struct *w2 = malloc(sizeof(pixel_wand_struct));
        w2->wand = ClonePixelWand(w3->wand);
        w2->available = true;
        w1 = w2;
      } else {
      return nil;
    }
  }

  void (*finalizer) (void *ptr) = &magick_wand_gc; /* prepare finalizer for emacs gc */
  return env->make_user_ptr(env, /* return it */
                            finalizer,
                            w1);
}

/************************/
/* image read and write */
/************************/

emacs_value			/* arg0: wand user-ptr; arg1: filename string; return arg0 */
Fmagick_read_image (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]); /* get wand */
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }
  char *filename = get_string(env, args[1]); /* get file name */

  MagickBooleanType status =	/* read image */
    MagickReadImage(wand, filename);
  if (status == MagickFalse)
    {
      return wand_read_image_failed_error(env, args[1]);
    }
  return args[0];
}

emacs_value			/* arg0: wand user-ptr; arg1: filename string or nil; return arg0 */
Fmagick_write_image (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]); /* get wand */
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }

  char *filename;
  if (env->is_not_nil(env, args[1])) /* get filename, or NULL to let it decided by magick according to MagickReadImage and MagickSetImageFilename */
    {
      filename = get_string(env, args[1]); 
    } else {
    filename = NULL;
  }

  MagickBooleanType status =	/* read image */
    MagickWriteImage(wand, filename);
  if (status == MagickFalse)
    {
      return wand_write_image_failed_error(env, args[1]);
    }
  return args[0];
}

/* extract image data (blob) from wand, and return it as an unibyte emacs string.
   mainly function to contact with emacs visibly */
emacs_value			/* arg0: wand user-ptr, arg1: image format string */
Fmagick_image_data (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]); /* get wand */
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }

  char *format = get_string(env, args[1]);
  char *format_origin = MagickGetImageFormat(wand); /* get origin format */
  bool need_convert = !strcmp(format, format_origin) == 0;

  if (need_convert)
    {
      MagickBooleanType status =	/* convert format */
        MagickSetImageFormat(wand, format);
      if (status == MagickFalse)
        {
          return wand_convert_image_failed_error(env, args[1]);
        }  
    }
  // MagickResetIterator(wand);	/* ensure the write is from the beginning of the image sequence */ for GIF?

  size_t length;
  unsigned char *blob =
    MagickGetImageBlob(wand, &length); /* get blob and it's length */

  if (blob == NULL)		/* blob will return NULL if there is no image in wand */
    {
      return wand_no_image_error(env, args[0]);
    }
  emacs_value blob_data = env->make_unibyte_string(env, blob, length); /* create data string */
  MagickRelinquishMemory((void *) blob);		       /* FIXME: is argument here right?  */

  if (need_convert)
    {
      MagickBooleanType status = /* convert back */
        MagickSetImageFormat(wand, format_origin);
      if (status == MagickFalse)
        {
          return image_operation_failed_error(env, "MagickSetImageFormat");
        }
    }
  return blob_data;
}

/* set image data (blob) from giving string.
   it is the mainly function of the undo-redo implementation. */
emacs_value			/* arg0: wand user-ptr, arg1: image data; return wand user-ptr */
Fmagick_set_image_data (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]); /* get wand */
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }
  // MagickResetIterator(wand);	/* ensure the write is from the beginning of the image sequence */

  ptrdiff_t size;
  env->copy_string_contents(env, args[1], NULL, &size); /* get string size */
  char *buf = (char*) malloc(size * sizeof(char));
  env->copy_string_contents(env, args[1], buf, &size); /* get string */

  MagickBooleanType status =
    MagickReadImageBlob(wand, buf, size); /* set blob */

  if (status == MagickFalse)
    {
      return image_operation_failed_error(env, "MagickReadImageBlob");
    }
  free(buf);
  return args[0];
}

/*************************/
/* get image infomations */
/*************************/

emacs_value			/* arg0: wand user-ptr; return a cons which car is width and cdr is height */
Fmagick_image_size (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]);
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }
  if (GetImageFromMagickWand(wand) == NULL)
    {
      return wand_no_image_error(env, args[0]);
    }
  return env->make_integer(env, MagickGetImageWidth(wand));
  return env->funcall(env, env->intern(env, "cons"), 2,
                      (emacs_value[]){
                        env->make_integer(env, MagickGetImageWidth(wand)),
                        env->make_integer(env, MagickGetImageHeight(wand))
                      });
}

/********************/
/* image operations */
/********************/

/* crop */
emacs_value			/* arg0: wand user-ptr, arg1: width, arg2: height, arg3: x-offset, arg4: y-offset ; return wand user-ptr if success */
Fmagick_crop_image (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]);
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }
  if (GetImageFromMagickWand(wand) == NULL)
    {
      return wand_no_image_error(env, args[0]);
    }

  int width, height, x, y;
  if (env->is_not_nil(env, args[1]))
    {
      width = env->extract_integer(env, args[1]);
    } else {
    width = MagickGetImageWidth(wand);
  }
  if (env->is_not_nil(env, args[2]))
    {
      height = env->extract_integer(env, args[2]);
    } else {
    height = MagickGetImageHeight(wand);
  }
  if (env->is_not_nil(env, args[3]))
    {
      x = env->extract_integer(env, args[3]);
    } else {
    x = 0;
  }
  if (env->is_not_nil(env, args[4]))
    {
      y = env->extract_integer(env, args[4]);
    } else {
    y = 0;
  }

  MagickBooleanType status =
    MagickCropImage(wand, width, height, x, y);
  if (status == MagickFalse)
    {
      return image_operation_failed_error(env, "MagickCropImage");
    }
  return args[0];
}

/* resize */
emacs_value			/* arg0: wand user-ptr, arg1: target width, arg2: target height; return wand */
Fmagick_resize_image(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]);
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }
  if (GetImageFromMagickWand(wand) == NULL)
    {
      return wand_no_image_error(env, args[0]);
    }

  int width, height;
  float width_origin, height_origin; /* use float here, computing ratio will convinient */
  if (env->is_not_nil(env, args[1]))
    {
      if (env->is_not_nil(env, args[2]))
        {
          width = env->extract_integer(env, args[1]);
          height = env->extract_integer(env, args[2]);
        }
      width = env->extract_integer(env, args[1]);
      width_origin = (float) MagickGetImageWidth(wand);
      height_origin = (float) MagickGetImageHeight(wand);
      height = (int) (width / width_origin * height_origin + 0,5);
    } else {
    if (env->is_not_nil(env, args[2]))
      {
        height = env->extract_integer(env, args[2]);
        width_origin = (float) MagickGetImageWidth(wand);
        height_origin = (float) MagickGetImageHeight(wand);
        width = (int) (height / height_origin * width_origin + 0.5);
      } else {
      return image_operation_failed_error(env, "MagickResizeImage");
    }
  }
  MagickBooleanType status =
    MagickResizeImage(wand,width,height,LanczosFilter);
  if (status == MagickFalse)
    {
      return image_operation_failed_error(env, "MagickResizeImage");
    }
  return args[0];
}

emacs_value			/* arg0: wand user-ptr; return wand */
Fmagick_flip_image(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]);
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }
  if (GetImageFromMagickWand(wand) == NULL)
    {
      return wand_no_image_error(env, args[0]);
    }
  MagickBooleanType status =
    MagickFlipImage(wand);
  if (status == MagickFalse)
    {
      return image_operation_failed_error(env, "MagickFlipImage");
    }
  return args[0];
}

emacs_value			/* arg0: wand user-ptr; return wand */
Fmagick_flop_image(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]);
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }
  if (GetImageFromMagickWand(wand) == NULL)
    {
      return wand_no_image_error(env, args[0]);
    }
  MagickBooleanType status =
    MagickFlopImage(wand);
  if (status == MagickFalse)
    {
      return image_operation_failed_error(env, "MagickFlopImage");
    }
  return args[0];
}

emacs_value			/* arg0: wand user-ptr, arg1: degree float number; return wand */
Fmagick_rotate_image(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
{
  MagickWand *wand = get_wand(env, args[0]); /* get wand */
  if (wand == NULL)
    {
      return wand_not_available_error(env, args[0]);
    }
  if (GetImageFromMagickWand(wand) == NULL)
    {
      return wand_no_image_error(env, args[0]);
    }

  PixelWand *background =			/* get current background wand */
    env->get_user_ptr(env, get_variable(env, "magick-background-pixel-wand"));
  double degree = env->extract_float(env, args[1]); /* degree */

  MagickBooleanType status =
    MagickRotateImage(wand, background, degree); /* rotate */
  if (status == MagickFalse)
    {
      return image_operation_failed_error(env, "MagickRotateImage");
    }
  return args[0];
}

/********/
/* main */
/********/

/* binding functions conveniently. codes from example. */
void
provide (emacs_env *env, const char *feature)
{
    emacs_value Qfeat = env->intern (env, feature);
    emacs_value Qprovide = env->intern (env, "provide");
    emacs_value args[] = { Qfeat };

    env->funcall (env, Qprovide, 1, args);
}

void
bind_function (emacs_env *env, const char *name, emacs_value Sfun)
{
    emacs_value Qfset = env->intern (env, "fset");
    emacs_value Qsym = env->intern (env, name);
    emacs_value args[] = { Qsym, Sfun };

    env->funcall (env, Qfset, 2, args);
}

/* main */
int
emacs_module_init (struct emacs_runtime *runtime)
{
  if (runtime->size < sizeof (*runtime))
    return 1;

  emacs_env *env = runtime->get_environment (runtime);
  if (env->size < sizeof (*env))
    return 2;

  t = env->make_global_ref(env, env->intern(env, "t"));
  nil = env->make_global_ref(env, env->intern(env, "nil"));

  MagickWandGenesis();

#define DEFUN(lsym, csym, amin, amax, doc, data)			\
  bind_function (env, lsym,						\
                 env->make_function (env, amin, amax, csym, doc, data))

  DEFUN("magick-new-wand", Fmagick_new_wand, 0, 0, "Create a new wand. return it as an user-ptr.", NULL);
  DEFUN("magick-destroy-wand", Fmagick_destroy_wand, 1, 1, "Delete wand at ARG1. return t if success.", NULL);
  DEFUN("magick-wand-available-p", Fmagick_wand_available_p, 1, 1, "Check if magick wand number ARG1 is aviailable.", NULL);
  DEFUN("magick-clone-wand", Fmagick_clone_wand, 1, 1, "Make a clone of wand ARG1. return the new wand.", NULL);
  DEFUN("magick-new-pixel-wand", Fmagick_new_pixel_wand, 2, 2, "Create a new PixelWand, with color ARG1 and alpha ARG2. return it as an user-ptr.", NULL);

  DEFUN("magick-read-image", Fmagick_read_image, 2, 2, "Read image into wand at index ARG1, from filename ARG2.", NULL);
  DEFUN("magick-write-image", Fmagick_write_image, 2, 2, "Write image from wand at index ARG1, into filename ARG2.\nARG2 can be 'nil' to let it decided by magick according to MagickReadImage and MagickSetImageFilename.", NULL);
  DEFUN("magick-image-data", Fmagick_image_data, 2, 2, "Get image data from wand which at index ARG1, in format ARG2.\nARG2 should be a string of the format (gif, jpeg, png, etc.)", NULL);

  DEFUN("magick-image-size", Fmagick_image_size, 1, 1, "Get image width from image in wand which at index ARG1.", NULL);

  DEFUN("magick-crop-image", Fmagick_crop_image, 1, 5, "Crop image in wand ARG1, with width ARG2, height ARG3, x-offset ARG4, y-offset ARG5.", NULL);
  DEFUN("magick-resize-image", Fmagick_resize_image, 3, 3, "Resize image in wand ARG1, with target width ARG2, target height ARG3.\n one of width or height can be nil, and resize will depend on ratio.", NULL);
  DEFUN("magick-flip-image", Fmagick_flip_image, 1, 1, "Flip image in wand ARG1, reflecting the pixels around the central X-axis.", NULL);
  DEFUN("magick-flop-image", Fmagick_flop_image, 1, 1, "Flop image in wand ARG1, reflecting the pixels around the central Y-axis.", NULL);
  DEFUN("magick-rotate-image", Fmagick_rotate_image, 2, 2, "Rotate image in wand ARG1, with degree ARG2.\nEmpty triangles left over from rotating the image are filled with current background color.", NULL);

  provide(env, "magick-module");
  return 0;
}
(org-babel-tangle)
(shell-command-to-string
 "cc -fPIC -shared -o /home/rosa/.emacs.d/site-lisp/magick/magick-module.so /home/rosa/.emacs.d/site-lisp/magick/magick-module.c `pkg-config --cflags --libs MagickWand`")
(add-to-list 'load-path "~/.emacs.d/site-lisp/magick/")
(require 'magick-module)
(magick-wand-available-p foo)
(setq foo (magick-new-wand))
(magick-destroy-wand foo)
(magick-read-image foo "/home/rosa/1.png")
(magick-image-data foo "png")
(insert (propertize
         " "
         'display (cons 'image
                        (list :type 'png
                              :data (magick-image-data foo "png")))))
(magick-flip-image foo)
(magick-new-pixel-wand "#000000" 0.5)
;; -*- lexical-binding: t -*-
;; (setq-local lexical-binding t)

(defvar magick-default-background "#ffffff"
  "Default background color for magick mode.")
(defvar magick-default-background-alpha 0.0
  "Default background alpha for magick mode.")

(defvar magick-default-foreground "#000000"
  "Default foreground color for magick mode.")
(defvar magick-default-foreground-alpha 1.0
  "Default foreground alpha for magick mode.")

(defvar magick-background-pixel-wand nil
  "Background PixelWand for magick.")

(defvar magick-foreground-pixel-wand nil
  "Foreground PixelWand for magick.")

(defvar magick-current-wand nil
  "Current wand in 'magick-mode'")

;;; Preview

(defun magick-preview-common (buffer-or-name)
  "Return a function, call it to refresh preview in buffer BUFFER-OR-NAME."
  #'(lambda (func)
      (with-current-buffer (get-buffer buffer-or-name)
        (magick-redisplay
         (funcall func
                  (magick-clone-wand magick-current-wand))))))

;;; Other operations.

(defun magick-set-background (color alpha)
  "Set background color for curren magick buffer."
  (interactive (list (read-color "Choose a color: " t)
                     (float (read-number "Alpha (from 0.0 to 1.0): "))))
  (if (or (< alpha 0.0) (> alpha 1.0))
      (signal 'args-out-of-range 1))
  (setq-local magick-background-pixel-wand
              (magick-new-pixel-wand color alpha)))

(defun magick-set-foreground (color alpha)
  "Set background color for curren magick buffer."
  (interactive (list (read-color "Choose a color: " t)
                     (float (read-number "Alpha (from 0.0 to 1.0): "))))
  (if (or (< alpha 0.0) (> alpha 1.0))
      (signal 'args-out-of-range 1))
  (setq-local magick-foreground-pixel-wand
              (magick-new-pixel-wand color alpha)))

;;; Image operations.

(defun magick-single-operation (function)
  "Run single magick operation function.
Prompt reading input and output file name."
  (magick-write-image
   (funcall function
            (magick-read-image (magick-new-wand)
                               (expand-file-name
                                (read-file-name "Target file: "))))
   (expand-file-name
    (read-file-name "Output file: "))))

(defun magick-convert-format ()
  (magick-single-operation #'identity))

(defun magick-flip ()
  "Flip image, reflecting the pixels around the central X-axis."
  (interactive)
  (if magick-current-wand
      (progn (magick-flip-image magick-current-wand)
             (magick-redisplay))
    (magick-single-operation #'magick-flip-image)))

(defun magick-flop ()
  "Flop image, reflecting the pixels around the central Y-axis."
  (interactive)
  (if magick-current-wand
      (progn (magick-flop-image magick-current-wand)
             (magick-redisplay))
    (magick-single-operation #'magick-flop-image)))

(defun magick-rotate (&optional angle)
  (interactive)
  (if angle nil				;if haven't gave argument
    (if magick-current-wand		;and is in magick-mode
        (let ((preview-func		;starting preview
               #'(lambda (start end len) ;prepare function
                   (funcall
                    (magick-preview-common (current-buffer)) ;get basic preview function
                    #'(lambda (preview-wand)		     ;custom behavior
                        (magick-rotate-image
                         preview-wand
                         (or (ignore-errors ;extract number from minibuffer
                               (string-to-number (buffer-string)))
                             0)))))))
          (add-hook 'after-change-functions preview-func) ;add function to after-change-functions
          (setq angle (read-number "Angle: "))		  ;begin reading
          (remove-hook 'after-change-functions preview-func)) ;finish, remove hook
      (setq angle (read-number "Angle: "))))		      ;not in magick-mode
  (if magick-current-wand				      ;process
      (progn (magick-rotate-image magick-current-wand angle)
             (magick-redisplay))
    (magick-single-operation #'(lambda (wand) ;read file, process, write
                                 (magick-rotate-image wand angle)))))

(defun magick-resize (&optional width height)
  (interactive)
  (if (or width height)			;haven't gave arguments
      nil
    (let* ((rand (random))
           (func #'(lambda (prompt)	;customize read function to allow returning nil
                     (let ((r (read-number prompt rand)))
                       (if (= r rand) nil r)))))
      (if magick-current-wand
          (let ((current 'width)
                (preview-func		;starting preview
                 #'(lambda (start end len) ;prepare function
                     (funcall
                      (magick-preview-common (current-buffer)) ;get basic preview function
                      #'(lambda (preview-wand)		     ;custom behavior
                          (magick-resize-image
                           preview-wand
                           (and (eq current 'width)
                                (ignore-errors ;extract number from minibuffer
                                  (string-to-number (buffer-string))))
                           (and (eq current 'height)
                                (ignore-errors
                                  (string-to-number (buffer-string))))))))))
            (add-hook 'after-change-functions preview-func) ;add hook
            (setq width					    ;begin reading
                  (funcall func "New width (keep blank to calculate from another argument): ")
                  current 'height
                  height
                  (funcall func "New height (keep blank to calculate from another argument): "))
            (remove-hook 'after-change-functions preview-func)))
      (setq width			;not in magick-mode
            (funcall func "New width (keep blank to calculate from another argument): ")
            height
            (funcall func "New height (keep blank to calculate from another argument): "))))
  (if magick-current-wand		;process
      (progn (magick-resize-image magick-current-wand width height)
             (magick-redisplay))
    (magick-single-operation #'(lambda (wand)
                                 (magick-rotate-image wand width height)))))

(defun magick-crop (&optional width height x y)
  (interactive)
  (if (or width height x y)
      nil
    (let* ((rand (random))
           (func #'(lambda (prompt)	;customize read function to allow returning nil
                     (let ((r (read-number prompt rand)))
                       (if (= r rand) nil r)))))
      (if magick-current-wand
          (let ((current 'width)
                (preview-func		;starting preview
                 #'(lambda (start end len) ;prepare function
                     (funcall
                      (magick-preview-common (current-buffer)) ;get basic preview function
                      #'(lambda (preview-wand)		     ;custom behavior
                          (magick-resize-image
                           preview-wand
                           (and (eq current 'width)
                                (ignore-errors ;extract number from minibuffer
                                  (string-to-number (buffer-string))))
                           (and (eq current 'height)
                               (ignore-errors
                                 (string-to-number (buffer-string))))
                           (and (eq current 'x)
                               (ignore-errors
                                 (string-to-number (buffer-string))))
                           (and (eq current 'y)
                               (ignore-errors
                                 (string-to-number (buffer-string))))))))))
            (add-hook 'after-change-functions preview-func) ;add hook
            (setq width (funcall func "Crop width: ")	    ;begin reading
                  current 'height
                  height (funcall func "Crop height: ")
                  current 'x
                  x (funcall func "X offset: ")
                  current 'y
                  y (funcall func "Y offset: "))
            (remove-hook 'after-change-functions preview-func)))
      (setq width (funcall func "Crop width: ")	    ;begin reading
            height (funcall func "Crop height: ")
            x (funcall func "X offset: ")
            y (funcall func "Y offset: "))))
  (if magick-current-wand
      (progn (magick-resize-image magick-current-wand width height)
             (magick-redisplay))
    (magick-single-operation #'(lambda (wand)
                                 (magick-rotate-image wand width height)))))

;;; Magick panel.

(defvar magick-panel-buffer nil
  "Current magick buffer's panel name.")

(defvar magick-panel-for nil
  "Show if current buffer is any magick buffer's panel,
If it is, this variable will store it's name.")

;;; Defining mode.

(define-derived-mode magick-mode fundamental-mode "Magick"
  "Mode for magick."  
  (let ((wand (and (buffer-file-name)	;avoid nil
                   (ignore-errors	;if current file is an image
                     (magick-read-image
                      (magick-new-wand)
                      (buffer-file-name)))))
        (name (buffer-name)))		;use current buffer as mode buffer
    (or wand				;if it's not, find an image
        (let ((name (expand-file-name
                     (read-file-name "Find image: "))))
          (setq wand (magick-read-image
                      (magick-new-wand) name))
          (switch-to-buffer (get-buffer-create name)) ;create new buffer
          (insert " ")))
    (setq-local magick-current-wand wand)
    (magick-redisplay)
    (let ((panel-name (concat name
                              " Panel")))
    (setq-local magick-panel-buffer panel-name)
    (save-selected-window
      (select-window
       (split-window (selected-window)
                     (round (* (window-width) 0.75))
                     t))
      (switch-to-buffer (get-buffer-create panel-name))
      (setq-local magick-panel-for name)
      (let ((inhibit-read-only t))
        (erase-buffer)))))
  (setq-local magick-background-pixel-wand
              (magick-new-pixel-wand magick-default-background
                                     magick-default-background-alpha)
              magick-foreground-pixel-wand
              (magick-new-pixel-wand magick-default-foreground
                                     magick-default-foreground-alpha)))

(defvar magick-viewing-format "png"
  "What image format to use in magick viewing.")

(defvar magick-viewing-smoothing t
  "If transform smoothing will apply to the viewing image.")

(defvar magick-viewing-args nil
  "Other image arguments apply to the viewing image.")

(defun magick-redisplay ()
  (interactive)
  (let ((inhibit-read-only t))
    (set-text-properties
     (point-min) (point-max)
     (list 'read-only t
           'display
           (cons 'image
                 (append (list :data (magick-image-data magick-current-wand
                                                        magick-viewing-format)
                               :type (intern magick-viewing-format)
                               :transform-smoothing magick-viewing-smoothing)
                         magick-viewing-args))))))

;; TODO: undo, panel, preview,

;; (buffer-local-value 'bar
;; 		    (get-buffer "*scratch*"))
;; (setf (buffer-local-value 'bar
;; 			  (get-buffer "*scratch*"))
;;       2)
;; (with-current-buffer (get-buffer "*scratch*")
;;   (setq-local foobar 3))
cc -o test test.c `pkg-config --cflags --libs MagickWand`

1.2.6. rosa

1.2.7. Linux hwdb 改键

这套改键方案在将键盘布局改为 colemak 的基础上额外将右 meta 和右 alt 分别改为左 ctrl 和左 alt ,因为有些时候右 alt 可能会被当作 altgr 死键啊什么的……

同时把 capslock 改成了退格键, colemak 键盘布局下 capslock 的退格是 C-h 的退格, 而不是 DEL 的退格,用 emacs 的时候会出问题,还是硬改过来比较好。

所以我现在的使用习惯大概是:右手大拇指按 ctrl , meta 键左右混按。其实很舒服的。

udevadm hwdb --update ; udevadm trigger

evdev:input:b0003v05ACp024C*
 KEYBOARD_KEY_700e7=leftctrl
 KEYBOARD_KEY_700e6=leftalt
 KEYBOARD_KEY_70039=backspace
 KEYBOARD_KEY_70008=f
 KEYBOARD_KEY_70015=p
 KEYBOARD_KEY_70017=g
 KEYBOARD_KEY_7001c=j
 KEYBOARD_KEY_70018=l
 KEYBOARD_KEY_7000c=u
 KEYBOARD_KEY_70012=y
 KEYBOARD_KEY_70013=semicolon
 KEYBOARD_KEY_70016=r
 KEYBOARD_KEY_70007=s
 KEYBOARD_KEY_70009=t
 KEYBOARD_KEY_7000a=d
 KEYBOARD_KEY_7000d=n
 KEYBOARD_KEY_7000e=e
 KEYBOARD_KEY_7000f=i
 KEYBOARD_KEY_70033=o
 KEYBOARD_KEY_70011=k

1.2.8. FreeBSD

  1. rc.conf

    /etc/rc.conf

    hostname="rosa"
    
    # my keymap
    keymap="rosa.kbd"
    
    # macbook touchpad
    moused_port="/dev/wsp0"
    moused_type="auto"
    moused_enable="YES"
    powerd_enable="YES"
    # Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
    dumpdev="NO"
    
    # powersave
    powerd_flags="-a hiadaptive -b minimum"
    performance_cx_lowest="Cmax"
    economy_cx_lowest="Cmax"
    
    # broadcom BCM43224
    wlans_bwn0="wlan0"
    ifconfig_wlan0="WPA DHCP powersave"
    
    # intel graphic card
    kld_list="i915kms"
    
    # wayland
    seatd_enable="YES"
    
    # fuse for exfat
    fuse_enable="YES"
    
    # disable sendmail
    sendmail_enable="NONE"
    
  2. loader.conf
    # bcm43224 wifi card
    hw.bwn_pci.preferred="1"
    
    if_bwn_pci_load="YES"
    bwn_v4_ucode_load="YES"
    bwn_v4_n_ucode_load="YES"
    bwn_v4_lp_ucode_load="YES"
    
    # fuse
    fuse_load="YES"
    
    # power
    hw.pci.do_power_nodriver="3"
    hw.snd.latency="7"
    hint.p4tcc.0.disabled="1"
    hint.acpi_throttle.0.disabled="1"
    
    # intel video card
    drm.i915.enable_rc6="7"
    drm.i915.semaphores="1"
    drm.i915.intel_iommu_enabled="1"
    
    # apple touchpad
    ums_load="NO"
    # atp_load="YES"
    wsp_load="YES"
    
  3. rosa.kbd

    /usr/shared/vt/keymaps/rosa.kbd

    # $FreeBSD$
    #                                                         alt
    # scan                       cntrl          alt    alt   cntrl lock
    # code  base   shift  cntrl  shift  alt    shift  cntrl  shift state
    # ------------------------------------------------------------------
      000   nop    nop    nop    nop    nop    nop    nop    nop     O
      001   esc    esc    esc    esc    esc    esc    debug  esc     O
      002   '1'    '!'    nop    nop    '1'    '!'    nop    nop     O
      003   '2'    '@'    nul    nul    '2'    '@'    nul    nul     O
      004   '3'    '#'    nop    nop    '3'    '#'    nop    nop     O
      005   '4'    '$'    nop    nop    '4'    '$'    nop    nop     O
      006   '5'    '%'    nop    nop    '5'    '%'    nop    nop     O
      007   '6'    '^'    rs     rs     '6'    '^'    rs     rs      O
      008   '7'    '&'    nop    nop    '7'    '&'    nop    nop     O
      009   '8'    '*'    nop    nop    '8'    '*'    nop    nop     O
      010   '9'    '('    nop    nop    '9'    '('    nop    nop     O
      011   '0'    ')'    nop    nop    '0'    ')'    nop    nop     O
      012   '-'    '_'    us     us     '-'    '_'    us     us      O
      013   '='    '+'    nop    nop    '='    '+'    nop    nop     O
      014   del    del    bs     bs     del    del    bs     bs      O
      015   ht     btab   nop    nop    ht     btab   nop    nop     O
      016   'q'    'Q'    dc1    dc1    'q'    'Q'    dc1    dc1     C
      017   'w'    'W'    etb    etb    'w'    'W'    etb    etb     C
      018   'f'    'F'    ack    ack    'f'    'F'    ack    ack     C
      019   'p'    'P'    dle    dle    'p'    'P'    dle    dle     C
      020   'g'    'G'    bel    bel    'g'    'G'    bel    bel     C
      021   'j'    'J'    nl     nl     'j'    'J'    nl     nl      C
      022   'l'    'L'    ff     ff     'l'    'L'    ff     ff      C
      023   'u'    'U'    nak    nak    'u'    'U'    nak    nak     C
      024   'y'    'Y'    em     em     'y'    'Y'    em     em      C
      025   ';'    ':'    nop    nop    ';'    ':'    nop    nop     O
    
      026   '['    '{'    esc    esc    '['    '{'    esc    esc     O
      027   ']'    '}'    gs     gs     ']'    '}'    gs     gs      O
      028   cr     cr     nl     nl     cr     cr     nl     nl      O
      029   lctrl  lctrl  lctrl  lctrl  lctrl  lctrl  lctrl  lctrl   O
    
      030   'a'    'A'    soh    soh    'a'    'A'    soh    soh     C
      031   'r'    'R'    dc2    dc2    'r'    'R'    dc2    dc2     C
      032   's'    'S'    dc3    dc3    's'    'S'    dc3    dc3     C
      033   't'    'T'    dc4    dc4    't'    'T'    dc4    dc4     C
      034   'd'    'D'    eot    eot    'd'    'D'    eot    eot     C  
      035   'h'    'H'    bs     bs     'h'    'H'    bs     bs      C
      036   'n'    'N'    so     so     'n'    'N'    so     so      C
      037   'e'    'E'    enq    enq    'e'    'E'    enq    enq     C
      038   'i'    'I'    ht     ht     'i'    'I'    ht     ht      C
      039   'o'    'O'    si     si     'o'    'O'    si     si      C  
    
      040   '''    '"'    nop    nop    '''    '"'    nop    nop     O
      041   '`'    '~'    nop    nop    '`'    '~'    nop    nop     O
      042   lshift lshift lshift lshift lshift lshift lshift lshift  O
      043   '\'    '|'    fs     fs     '\'    '|'    fs     fs      O
      044   'z'    'Z'    sub    sub    'z'    'Z'    sub    sub     C
      045   'x'    'X'    can    can    'x'    'X'    can    can     C
      046   'c'    'C'    etx    etx    'c'    'C'    etx    etx     C
      047   'v'    'V'    syn    syn    'v'    'V'    syn    syn     C
      048   'b'    'B'    stx    stx    'b'    'B'    stx    stx     C
      049   'k'    'K'    vt     vt     'k'    'K'    vt     vt      C
      050   'm'    'M'    cr     cr     'm'    'M'    cr     cr      C
      051   ','    '<'    nop    nop    ','    '<'    nop    nop     O
      052   '.'    '>'    nop    nop    '.'    '>'    nop    nop     O
      053   '/'    '?'    us     nop    '/'    '?'    nop    nop     O
      054   rshift rshift rshift rshift rshift rshift rshift rshift  O
      055   '*'    '*'    '*'    '*'    '*'    '*'    '*'    '*'     O
      056   lalt   lalt   lalt   lalt   lalt   lalt   lalt   lalt    O
      057   ' '    ' '    nul    ' '    ' '    ' '    susp   ' '     O
      058   del    del    bs     bs     del    del    bs     bs      O
      059   fkey01 fkey13 fkey25 fkey37 scr01  scr11  scr01  scr11   O
      060   fkey02 fkey14 fkey26 fkey38 scr02  scr12  scr02  scr12   O
      061   fkey03 fkey15 fkey27 fkey39 scr03  scr13  scr03  scr13   O
      062   fkey04 fkey16 fkey28 fkey40 scr04  scr14  scr04  scr14   O
      063   fkey05 fkey17 fkey29 fkey41 scr05  scr15  scr05  scr15   O
      064   fkey06 fkey18 fkey30 fkey42 scr06  scr16  scr06  scr16   O
      065   fkey07 fkey19 fkey31 fkey43 scr07  scr07  scr07  scr07   O
      066   fkey08 fkey20 fkey32 fkey44 scr08  scr08  scr08  scr08   O
      067   fkey09 fkey21 fkey33 fkey45 scr09  scr09  scr09  scr09   O
      068   fkey10 fkey22 fkey34 fkey46 scr10  scr10  scr10  scr10   O
      069   nlock  nlock  nlock  nlock  nlock  nlock  nlock  nlock   O
      070   slock  slock  slock  slock  slock  slock  slock  slock   O
      071   fkey49 '7'    '7'    '7'    '7'    '7'    '7'    '7'     N
      072   fkey50 '8'    '8'    '8'    '8'    '8'    '8'    '8'     N
      073   fkey51 '9'    '9'    '9'    '9'    '9'    '9'    '9'     N
      074   fkey52 '-'    '-'    '-'    '-'    '-'    '-'    '-'     N
      075   fkey53 '4'    '4'    '4'    '4'    '4'    '4'    '4'     N
      076   fkey54 '5'    '5'    '5'    '5'    '5'    '5'    '5'     N
      077   fkey55 '6'    '6'    '6'    '6'    '6'    '6'    '6'     N
      078   fkey56 '+'    '+'    '+'    '+'    '+'    '+'    '+'     N
      079   fkey57 '1'    '1'    '1'    '1'    '1'    '1'    '1'     N
      080   fkey58 '2'    '2'    '2'    '2'    '2'    '2'    '2'     N
      081   fkey59 '3'    '3'    '3'    '3'    '3'    '3'    '3'     N
      082   fkey60 '0'    '0'    '0'    '0'    '0'    '0'    '0'     N
      083   del    '.'    '.'    '.'    '.'    '.'    boot   boot    N
      084   nop    nop    nop    nop    nop    nop    nop    nop     O
      085   nop    nop    nop    nop    nop    nop    nop    nop     O
      086   '`'    '~'    nop    nop    '`'    '~'    nop    nop     O
      087   fkey11 fkey23 fkey35 fkey47 scr11  scr11  scr11  scr11   O
      088   fkey12 fkey24 fkey36 fkey48 scr12  scr12  scr12  scr12   O
      089   cr     cr     nl     nl     cr     cr     nl     nl      O
      090   rctrl  rctrl  rctrl  rctrl  rctrl  rctrl  rctrl  rctrl   O
      091   '/'    '/'    '/'    '/'    '/'    '/'    '/'    '/'     N
      092   nscr   pscr   debug  debug  nop    nop    nop    nop     O
      093   lalt   lalt   lalt   lalt   lalt   lalt   lalt   lalt    O
      094   fkey49 fkey49 fkey49 fkey49 fkey49 fkey49 fkey49 fkey49  O
      095   fkey50 fkey50 fkey50 fkey50 fkey50 fkey50 fkey50 fkey50  O
      096   fkey51 fkey51 fkey51 fkey51 fkey51 fkey51 fkey51 fkey51  O
      097   fkey53 fkey53 fkey53 fkey53 fkey53 fkey53 fkey53 fkey53  O
      098   fkey55 fkey55 fkey55 fkey55 fkey55 fkey55 fkey55 fkey55  O
      099   fkey57 fkey57 fkey57 fkey57 fkey57 fkey57 fkey57 fkey57  O
      100   fkey58 fkey58 fkey58 fkey58 fkey58 fkey58 fkey58 fkey58  O
      101   fkey59 fkey59 fkey59 fkey59 fkey59 fkey59 fkey59 fkey59  O
      102   fkey60 paste  fkey60 fkey60 fkey60 fkey60 fkey60 fkey60  O
      103   fkey61 fkey61 fkey61 fkey61 fkey61 fkey61 boot   fkey61  O
      104   slock  saver  slock  saver  susp   nop    susp   nop     O
      105   fkey62 fkey62 fkey62 fkey62 fkey62 fkey62 fkey62 fkey62  O
    
      106   lctrl  lctrl  lctrl  lctrl  lctrl  lctrl  lctrl  lctrl   O
    
      107   fkey64 fkey64 fkey64 fkey64 fkey64 fkey64 fkey64 fkey64  O
      108   nop    nop    nop    nop    nop    nop    nop    nop     O
    
  4. x11 keymap

    添加至 /usr/local/share/X11/xkb/symbols/us

    xkb_symbols "rosa" {
    
        name[Group1]= "English (Rosa)";
    
        key <LSGT> {  	[     grave,	asciitilde	]	};
        key <AE01> {	[	  1,	exclam 		]	};
        key <AE02> {	[	  2,	at		]	};
        key <AE03> {	[	  3,	numbersign	]	};
        key <AE04> {	[	  4,	dollar		]	};
        key <AE05> {	[	  5,	percent		]	};
        key <AE06> {	[	  6,	asciicircum	]	};
        key <AE07> {	[	  7,	ampersand	]	};
        key <AE08> {	[	  8,	asterisk	]	};
        key <AE09> {	[	  9,	parenleft	]	};
        key <AE10> {	[	  0,	parenright	]	};
        key <AE11> {	[     minus,	underscore	]	};
        key <AE12> {	[     equal,	plus		]	};
    
        key <AD01> {	[	  q,	Q 		]	};
        key <AD02> {	[	  w,	W		]	};
        key <AD03> {	[	  f,	F		]	};
        key <AD04> {	[	  p,	P		]	};
        key <AD05> {	[	  g,	G		]	};
        key <AD06> {	[	  j,	J		]	};
        key <AD07> {	[	  l,	L		]	};
        key <AD08> {	[	  u,	U		]	};
        key <AD09> {	[	  y,	Y		]	};
        key <AD10> {	[ semicolon,	colon		]	};
        key <AD11> {	[ bracketleft,	braceleft	]	};
        key <AD12> {	[ bracketright,	braceright	]	};
    
        key <AC01> {	[	  a,	A 		]	};
        key <AC02> {	[	  r,	R		]	};
        key <AC03> {	[	  s,	S		]	};
        key <AC04> {	[	  t,	T		]	};
        key <AC05> {	[	  d,	D		]	};
        key <AC06> {	[	  h,	H		]	};
        key <AC07> {	[	  n,	N		]	};
        key <AC08> {	[	  e,	E		]	};
        key <AC09> {	[	  i,	I		]	};
        key <AC10> {	[	  o,	O		]	};
        key <AC11> {	[ apostrophe,	quotedbl	]	};
    
        key <AB01> {	[	  z,	Z 		]	};
        key <AB02> {	[	  x,	X		]	};
        key <AB03> {	[	  c,	C		]	};
        key <AB04> {	[	  v,	V		]	};
        key <AB05> {	[	  b,	B		]	};
        key <AB06> {	[	  k,	K		]	};
        key <AB07> {	[	  m,	M		]	};
        key <AB08> {	[     comma,	less		]	};
        key <AB09> {	[    period,	greater		]	};
        key <AB10> {	[     slash,	question	]	};
    
        key <BKSL> {	[ backslash,         bar	]	};
        key <CAPS> {	[ BackSpace,    BackSpace	]	};
    
        key <RWIN> { [ Control_L ] };
    
        modifier_map Control { <RWIN> };
    };
    
  5. sysctl.conf

    /etc/sysctl.conf

    # $FreeBSD$
    #
    #  This file is read when going to multi-user and its contents piped thru
    #  ``sysctl'' to adjust kernel values.  ``man 5 sysctl.conf'' for details.
    #
    
    # Uncomment this to prevent users from seeing information about processes that
    # are being run under another UID.
    #security.bsd.see_other_uids=0
    
    # make apple touchpad work in X and wayland
    kern.evdev.rcpt_mask=3
    
  6. 内核配置文件

    /usr/src/sys/amd64/conf/ROSA

    cpu		HAMMER
    ident		ROSA
    
    #makeoptions	DEBUG=-g		# Build kernel with gdb(1) debug symbols
    #makeoptions	WITH_CTF=1		# Run ctfconvert(1) for DTrace support
    
    options		BWN_GPL_PHY
    
    device		apm
    
    options 	SCHED_ULE		# ULE scheduler
    options 	NUMA			# Non-Uniform Memory Architecture support
    options 	PREEMPTION		# Enable kernel thread preemption
    options 	VIMAGE			# Subsystem virtualization, e.g. VNET
    options 	INET			# InterNETworking
    #options 	INET6			# IPv6 communications protocols
    options 	IPSEC_SUPPORT		# Allow kldload of ipsec and tcpmd5
    options		ROUTE_MPATH		# Multipath routing support
    options 	TCP_OFFLOAD		# TCP offload
    options 	TCP_BLACKBOX		# Enhanced TCP event logging
    options 	TCP_HHOOK		# hhook(9) framework for TCP
    options		TCP_RFC7413		# TCP Fast Open
    options 	SCTP_SUPPORT		# Allow kldload of SCTP
    options		KERN_TLS		# TLS transmit & receive offload
    options 	FFS			# Berkeley Fast Filesystem
    options 	SOFTUPDATES		# Enable FFS soft updates support
    options 	UFS_ACL			# Support for access control lists
    #options 	UFS_DIRHASH		# Improve performance on big directories
    options 	UFS_GJOURNAL		# Enable gjournal-based UFS journaling
    options 	QUOTA			# Enable disk quotas for UFS
    options 	MD_ROOT			# MD is a potential root device
    options 	NFSCL			# Network Filesystem Client
    options 	NFSD			# Network Filesystem Server
    options 	NFSLOCKD		# Network Lock Manager
    options 	NFS_ROOT		# NFS usable as /, requires NFSCL
    options 	MSDOSFS			# MSDOS Filesystem
    #options 	CD9660			# ISO 9660 Filesystem
    options 	PROCFS			# Process filesystem (requires PSEUDOFS)
    options 	PSEUDOFS		# Pseudo-filesystem framework
    options 	TMPFS			# Efficient memory filesystem
    options 	GEOM_RAID		# Soft RAID functionality.
    options 	GEOM_LABEL		# Provides labelization
    options 	EFIRT			# EFI Runtime Services support
    options 	COMPAT_FREEBSD32	# Compatible with i386 binaries
    options 	COMPAT_FREEBSD4		# Compatible with FreeBSD4
    options 	COMPAT_FREEBSD5		# Compatible with FreeBSD5
    options 	COMPAT_FREEBSD6		# Compatible with FreeBSD6
    options 	COMPAT_FREEBSD7		# Compatible with FreeBSD7
    options 	COMPAT_FREEBSD9		# Compatible with FreeBSD9
    options 	COMPAT_FREEBSD10	# Compatible with FreeBSD10
    options 	COMPAT_FREEBSD11	# Compatible with FreeBSD11
    options 	COMPAT_FREEBSD12	# Compatible with FreeBSD12
    options 	SCSI_DELAY=5000		# Delay (in ms) before probing SCSI
    #options 	KTRACE			# ktrace(1) support
    options 	STACK			# stack(9) support
    options 	SYSVSHM			# SYSV-style shared memory
    options 	SYSVMSG			# SYSV-style message queues
    options 	SYSVSEM			# SYSV-style semaphores
    options 	_KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions
    options 	PRINTF_BUFR_SIZE=128	# Prevent printf output being interspersed.
    options 	KBD_INSTALL_CDEV	# install a CDEV entry in /dev
    options 	HWPMC_HOOKS		# Necessary kernel hooks for hwpmc(4)
    options 	AUDIT			# Security event auditing
    options 	CAPABILITY_MODE		# Capsicum capability mode
    options 	CAPABILITIES		# Capsicum capabilities
    options 	MAC			# TrustedBSD MAC Framework
    options 	KDTRACE_FRAME		# Ensure frames are compiled in
    options 	KDTRACE_HOOKS		# Kernel DTrace hooks
    options 	DDB_CTF			# Kernel ELF linker loads CTF data
    options 	INCLUDE_CONFIG_FILE	# Include this file in kernel
    options 	RACCT			# Resource accounting framework
    options 	RACCT_DEFAULT_TO_DISABLED # Set kern.racct.enable=0 by default
    options 	RCTL			# Resource limits
    
    # Debugging support.  Always need this:
    options 	KDB			# Enable kernel debugger support.
    options 	KDB_TRACE		# Print a stack trace for a panic.
    
    # Kernel Sanitizers
    #options 	COVERAGE		# Generic kernel coverage. Used by KCOV
    #options 	KCOV			# Kernel Coverage Sanitizer
    # Warning: KUBSAN can result in a kernel too large for loader to load
    #options 	KUBSAN			# Kernel Undefined Behavior Sanitizer
    #options 	KCSAN			# Kernel Concurrency Sanitizer
    
    # Kernel dump features.
    options 	EKCD			# Support for encrypted kernel dumps
    options 	GZIO			# gzip-compressed kernel and user dumps
    options 	ZSTDIO			# zstd-compressed kernel and user dumps
    options 	DEBUGNET		# debugnet networking
    options 	NETDUMP			# netdump(4) client support
    options 	NETGDB			# netgdb(4) client support
    
    # Make an SMP-capable kernel by default
    options 	SMP			# Symmetric MultiProcessor Kernel
    options 	EARLY_AP_STARTUP
    
    # CPU frequency control
    device		cpufreq
    
    # Bus support.
    device		acpi
    options 	IOMMU
    device		pci
    options 	PCI_HP			# PCI-Express native HotPlug
    options		PCI_IOV			# PCI SR-IOV support
    
    options 	COMPAT_LINUXKPI
    
    # Floppy drives
    #device		fdc
    
    # ATA controllers
    device		ahci			# AHCI-compatible SATA controllers
    device		ata			# Legacy ATA/SATA controllers
    device		mvs			# Marvell 88SX50XX/88SX60XX/88SX70XX/SoC SATA
    device		siis			# SiliconImage SiI3124/SiI3132/SiI3531 SATA
    
    # SCSI Controllers
    device		ahc			# AHA2940 and onboard AIC7xxx devices
    device		ahd			# AHA39320/29320 and onboard AIC79xx devices
    device		esp			# AMD Am53C974 (Tekram DC-390(T))
    device		hptiop			# Highpoint RocketRaid 3xxx series
    device		isp			# Qlogic family
    #device		ispfw			# Firmware for QLogic HBAs- normally a module
    device		mpt			# LSI-Logic MPT-Fusion
    device		mps			# LSI-Logic MPT-Fusion 2
    device		mpr			# LSI-Logic MPT-Fusion 3
    device		sym			# NCR/Symbios Logic
    device		isci			# Intel C600 SAS controller
    device		ocs_fc			# Emulex FC adapters
    device		pvscsi			# VMware PVSCSI
    
    # ATA/SCSI peripherals
    device		scbus			# SCSI bus (required for ATA/SCSI)
    device		ch			# SCSI media changers
    device		da			# Direct Access (disks)
    device		sa			# Sequential Access (tape etc)
    device		cd			# CD
    device		pass			# Passthrough device (direct ATA/SCSI access)
    device		ses			# Enclosure Services (SES and SAF-TE)
    #device		ctl			# CAM Target Layer
    
    # RAID controllers interfaced to the SCSI subsystem
    #device		amr			# AMI MegaRAID
    #device		arcmsr			# Areca SATA II RAID
    #device		ciss			# Compaq Smart RAID 5*
    #device		iir			# Intel Integrated RAID
    #device		ips			# IBM (Adaptec) ServeRAID
    #device		mly			# Mylex AcceleRAID/eXtremeRAID
    #device		twa			# 3ware 9000 series PATA/SATA RAID
    #device		smartpqi		# Microsemi smartpqi driver
    #device		tws			# LSI 3ware 9750 SATA+SAS 6Gb/s RAID controller
    
    # RAID controllers
    #device		aac			# Adaptec FSA RAID
    #device		aacp			# SCSI passthrough for aac (requires CAM)
    #device		aacraid			# Adaptec by PMC RAID
    #device		ida			# Compaq Smart RAID
    #device		mfi			# LSI MegaRAID SAS
    #device		mlx			# Mylex DAC960 family
    #device		mrsas			# LSI/Avago MegaRAID SAS/SATA, 6Gb/s and 12Gb/s
    #device		pmspcv			# PMC-Sierra SAS/SATA Controller driver
    #XXX pointer/int warnings
    #device		pst			# Promise Supertrak SX6000
    #device		twe			# 3ware ATA RAID
    
    # NVM Express (NVMe) support
    device		nvme			# base NVMe driver
    device		nvd			# expose NVMe namespaces as disks, depends on nvme
    
    # Intel Volume Management Device (VMD) support
    device		vmd			# base VMD device
    device		vmd_bus			# bus for VMD children
    
    # atkbdc0 controls both the keyboard and the PS/2 mouse
    device		atkbdc			# AT keyboard controller
    device		atkbd			# AT keyboard
    device		psm			# PS/2 mouse
    
    device		kbdmux			# keyboard multiplexer
    
    device		vga			# VGA video card driver
    options 	VESA			# Add support for VESA BIOS Extensions (VBE)
    
    device		splash			# Splash screen and screen saver support
    
    # syscons is the default console driver, resembling an SCO console
    device		sc
    options 	SC_PIXEL_MODE		# add support for the raster text mode
    
    # vt is the new video console driver
    device		vt
    device		vt_vga
    device		vt_efifb
    device		vt_vbefb
    
    device		agp			# support several AGP chipsets
    
    # PCCARD (PCMCIA) support
    # PCMCIA and cardbus bridge support
    device		cbb			# cardbus (yenta) bridge
    device		pccard			# PC Card (16-bit) bus
    device		cardbus			# CardBus (32-bit) bus
    
    # Serial (COM) ports
    device		uart			# Generic UART driver
    
    # Parallel port
    device		ppc
    device		ppbus			# Parallel port bus (required)
    device		lpt			# Printer
    device		ppi			# Parallel port interface device
    #device		vpo			# Requires scbus and da
    
    device		puc			# Multi I/O cards and multi-channel UARTs
    
    # PCI/PCI-X/PCIe Ethernet NICs that use iflib infrastructure
    device		iflib
    #device		em			# Intel PRO/1000 Gigabit Ethernet Family
    #device		ix			# Intel PRO/10GbE PCIE PF Ethernet
    #device		ixv			# Intel PRO/10GbE PCIE VF Ethernet
    #device		ixl			# Intel 700 Series Physical Function
    #device		iavf			# Intel Adaptive Virtual Function
    #device		ice			# Intel 800 Series Physical Function
    #device		vmx			# VMware VMXNET3 Ethernet
    #device		axp			# AMD EPYC integrated NIC
    
    # PCI Ethernet NICs.
    #device		bxe			# Broadcom NetXtreme II BCM5771X/BCM578XX 10GbE
    #device		le			# AMD Am7900 LANCE and Am79C9xx PCnet
    #device		ti			# Alteon Networks Tigon I/II gigabit Ethernet
    
    # Nvidia/Mellanox Connect-X 4 and later, Ethernet only
    # mlx5ib requires ibcore infra and is not included by default
    #device		mlx5			# Base driver
    #device		mlxfw			# Firmware update
    #device		mlx5en			# Ethernet driver
    
    # PCI Ethernet NICs that use the common MII bus controller code.
    # NOTE: Be sure to keep the 'device miibus' line in order to use these NICs!
    device		miibus			# MII bus support
    #device		ae			# Attansic/Atheros L2 FastEthernet
    #device		age			# Attansic/Atheros L1 Gigabit Ethernet
    #device		alc			# Atheros AR8131/AR8132 Ethernet
    #device		ale			# Atheros AR8121/AR8113/AR8114 Ethernet
    #device		bce			# Broadcom BCM5706/BCM5708 Gigabit Ethernet
    #device		bfe			# Broadcom BCM440x 10/100 Ethernet
    #device		bge			# Broadcom BCM570xx Gigabit Ethernet
    #device		cas			# Sun Cassini/Cassini+ and NS DP83065 Saturn
    #device		dc			# DEC/Intel 21143 and various workalikes
    #device		et			# Agere ET1310 10/100/Gigabit Ethernet
    #device		fxp			# Intel EtherExpress PRO/100B (82557, 82558)
    #device		gem			# Sun GEM/Sun ERI/Apple GMAC
    #device		jme			# JMicron JMC250 Gigabit/JMC260 Fast Ethernet
    #device		lge			# Level 1 LXT1001 gigabit Ethernet
    #device		msk			# Marvell/SysKonnect Yukon II Gigabit Ethernet
    #device		nfe			# nVidia nForce MCP on-board Ethernet
    #device		nge			# NatSemi DP83820 gigabit Ethernet
    #device		re			# RealTek 8139C+/8169/8169S/8110S
    #device		rl			# RealTek 8129/8139
    #device		sge			# Silicon Integrated Systems SiS190/191
    #device		sis			# Silicon Integrated Systems SiS 900/SiS 7016
    #device		sk			# SysKonnect SK-984x & SK-982x gigabit Ethernet
    #device		ste			# Sundance ST201 (D-Link DFE-550TX)
    #device		stge			# Sundance/Tamarack TC9021 gigabit Ethernet
    #device		vge			# VIA VT612x gigabit Ethernet
    #device		vr			# VIA Rhine, Rhine II
    #device		xl			# 3Com 3c90x (``Boomerang'', ``Cyclone'')
    
    # Wireless NIC cards
    device		wlan			# 802.11 support
    options 	IEEE80211_DEBUG		# enable debug msgs
    options 	IEEE80211_SUPPORT_MESH	# enable 802.11s draft support
    device		wlan_wep		# 802.11 WEP support
    device		wlan_ccmp		# 802.11 CCMP support
    device		wlan_tkip		# 802.11 TKIP support
    device		wlan_amrr		# AMRR transmit rate control algorithm
    #device		an			# Aironet 4500/4800 802.11 wireless NICs.
    #device		ath			# Atheros NICs
    #device		ath_pci			# Atheros pci/cardbus glue
    #device		ath_hal			# pci/cardbus chip support
    #options 	AH_AR5416_INTERRUPT_MITIGATION # AR5416 interrupt mitigation
    #options 	ATH_ENABLE_11N		# Enable 802.11n support for AR5416 and later
    #device		ath_rate_sample		# SampleRate tx rate control for ath
    #device		bwi			# Broadcom BCM430x/BCM431x wireless NICs.
    device		bwn			# Broadcom BCM43xx wireless NICs.
    #device		ipw			# Intel 2100 wireless NICs.
    #device		iwi			# Intel 2200BG/2225BG/2915ABG wireless NICs.
    #device		iwn			# Intel 4965/1000/5000/6000 wireless NICs.
    #device		malo			# Marvell Libertas wireless NICs.
    #device		mwl			# Marvell 88W8363 802.11n wireless NICs.
    #device		ral			# Ralink Technology RT2500 wireless NICs.
    #device		wpi			# Intel 3945ABG wireless NICs.
    
    # Pseudo devices.
    device		crypto			# core crypto support
    device		aesni			# AES-NI OpenCrypto module
    device		loop			# Network loopback
    device		padlock_rng		# VIA Padlock RNG
    device		rdrand_rng		# Intel Bull Mountain RNG
    device		ether			# Ethernet support
    device		vlan			# 802.1Q VLAN support
    device		tuntap			# Packet tunnel.
    device		md			# Memory "disks"
    device		gif			# IPv6 and IPv4 tunneling
    device		firmware		# firmware assist module
    device		xz			# lzma decompression
    
    # The `bpf' device enables the Berkeley Packet Filter.
    # Be aware of the administrative consequences of enabling this!
    # Note that 'bpf' is required for DHCP.
    device		bpf			# Berkeley packet filter
    
    # USB support
    options 	USB_DEBUG		# enable debug msgs
    device		uhci			# UHCI PCI->USB interface
    device		ohci			# OHCI PCI->USB interface
    device		ehci			# EHCI PCI->USB interface (USB 2.0)
    device		xhci			# XHCI PCI->USB interface (USB 3.0)
    device		usb			# USB Bus (required)
    device		ukbd			# Keyboard
    device		umass			# Disks/Mass storage - Requires scbus and da
    
    # Sound support
    device		sound			# Generic sound driver (required)
    device		snd_cmi			# CMedia CMI8338/CMI8738
    device		snd_csa			# Crystal Semiconductor CS461x/428x
    device		snd_emu10kx		# Creative SoundBlaster Live! and Audigy
    device		snd_es137x		# Ensoniq AudioPCI ES137x
    device		snd_hda			# Intel High Definition Audio
    device		snd_ich			# Intel, NVidia and other ICH AC'97 Audio
    device		snd_via8233		# VIA VT8233x Audio
    
    # MMC/SD
    device		mmc			# MMC/SD bus
    device		mmcsd			# MMC/SD memory card
    device		sdhci			# Generic PCI SD Host Controller
    device		rtsx			# Realtek SD card reader
    
    # VirtIO support
    device		virtio			# Generic VirtIO bus (required)
    #device		virtio_pci		# VirtIO PCI device
    #device		vtnet			# VirtIO Ethernet device
    #device		virtio_blk		# VirtIO Block device
    #device		virtio_scsi		# VirtIO SCSI device
    #device		virtio_balloon		# VirtIO Memory Balloon device
    
    # HyperV drivers and enhancement support
    #device		hyperv			# HyperV drivers 
    
    # Xen HVM Guest Optimizations
    # NOTE: XENHVM depends on xenpci.  They must be added or removed together.
    #options 	XENHVM			# Xen HVM kernel infrastructure
    #device		xenpci			# Xen HVM Hypervisor services driver
    
    # Netmap provides direct access to TX/RX rings on supported NICs
    device		netmap			# netmap(4) support
    
    # evdev interface
    options 	EVDEV_SUPPORT		# evdev support in legacy drivers
    device		evdev			# input event device support
    device		uinput			# install /dev/uinput cdev
    
    # HID support
    options 	HID_DEBUG		# enable debug msgs
    device		hid			# Generic HID support
    options 	IICHID_SAMPLING		# Workaround missing GPIO INTR support
    
    
  7. 编译 emacs (pgtk) 所需的包和编译选项
    texinfo gmp alsa-lib dbus giflib gnutls fontconfig freetype2 harfbuzz jansson
    lcms2 m17n-lib mailutils libotf png tiff cairo librsvg2 libxml2 gtk3 gconf2
    pkgconf gettext-tools meson desktop-file-utils automake gtk3 libgsf
    
    ./configure --with-pgtk --without-gpm --without-selinux --without-libsystemd
    

1.3. 博文

1.3.1. Mac 入门体验 <2021-10-20 Wed>

(夜半无人私语时之胡扯,待修改……)

  1. 概述

    虽然我很不喜欢苹果,但它质量过硬还是真的——无论硬件还是软件。

  2. 按键绑定

    众所周知,macOS的按键绑定很个性,当然也非常好用。

    macOS把ctrl和meta键的功能拆开混合了,其中复制粘贴、撤销重做被视作系统功能扔进了 meta的管辖范畴,而ctrl则完全负责文本操作——这极大地降低了使用者罹患腱鞘炎的概率。

    同时macOS还对ctrl文本操作进行了扩展,使它部分兼容emacs的按键绑定,如C-a、C-e、 C-f等等。

    这种优秀的设计甚至让我有那么一瞬间产生了动摇——最终我没有选择像在Linux上一样直接 把ctrl和meta调换,而是选择改变习惯,让右手的功能键承担工作。这几乎完全保留了 macOS系统的按键绑定,让我有了尝试新事物的空间,配合emacs-mac也能做到和emacs无缝 衔接,十分舒适。

  3. 续航

    虽然macOS值得称道的地方很多,但是唯一一个让我能够下定决心使用macOS的原因只有一 个——惊人的续航能力。请看下图:

    屏幕快照 2021-10-17 下午11.08.28.png

    这真的震惊到我了——我来来回回配置了个把月的arch Linux,续航竟然还不如开箱即用的 macOS?!而且macOS的续s航非但没有随着系统版本更新而越来越短,反而越来越长了?!

    好吧,我认了,人家大牌就是不一样。

    而且我在Linux上使用的自由软件,在macOS上照样能够正常工作。

    真香。

  4. 外观

    这真的没啥可说的,macOS的体验是出了名的美观。不过我发现了其中一些不一样的地方。 直觉告诉我,macOS的桌面实现很简单——没错,就是简单。相比Windows和plasma,它简单很 多。但是macOS把这份简单做得很优秀——流畅的动画和合理的布局使得它简单而不简陋。这 是我很喜欢它的一个地方。

    ——也正是这份简单让我能在续航9小时的情况下还能用上完整的桌面环境,哈哈哈哈

    Mojave的动态壁纸做的也很不错,相比Windows上的Wallpaper Engine,这种轻量、简约、 不影响专注性的动态壁纸更对我胃口。

  5. 包管理

    这大概是我唯一想要吐槽的一点了——我想过homebrew可能是apt的样子,可能是pacman的样 子,但我没想到,真正的homebrew竟然是portage(Gentoo包管理系统)的样子!

    分层级的软件包分类、tap添加第三方软件源、默认编译安装——这不就是个没有make.conf的 portage嘛!!

    好么,最终还是跳不出编译这个大坑……

    除了长相,homebrew用起来远远不如Linux的包管理器顺手。下面是我遇到的几个问题,当 然它们中有很大一部分可能和我用的Mojave不是最新的系统有关。

    1. 如果要用homebrew安装的包替换系统自带的包(例如git和emacs),就需要单独设置 PATH环境变量,否则启动的还是旧版的软件;
    2. 要指定make时所用的CPU核心数,需要设置 HOMEBREWMAKEJOBS 变量,如我在安装 emacs-mac时使用的就是:

      HOMEBREWMAKEJOBS=4 brew install emacs-mac –with-modules

    幸亏有玩Gentoo的经验打底,要不然……🤣

    先写这些吧,有新的经验再补充。

  6. 安装fcpx

    终于成功把fcpx装好啦!不知道传说中超级厉害的剪辑软件到底是什么样子的~

    破解过程虽然不复杂,但还是有许多坑在的,记录一下:

    1. 打开信任所有来源的应用,Sierra以上的版本就不支持在GUI下直接开启了,需要使用 命令:
    sudo spctl --master-disable
    

    才能打开;

    1. Mac系统在启动时会校验软件,破解软件没有签名,是通不过校验的,需要用命令:
    sudo xattr -d com.apple.quarantine /Applications/Final\ Cut\ Pro.app
    

    才可以正常使用。

1.3.2. 静态博客搭建引 <2021-11-14 Sun>

(天坑待填中……)

我第一次萌生出“搞一个自己的网站”这样的想法是20年的十月份,那时我对网站完全是一个 一窍不通的新手,走了很多的弯路。尽管如此,我还是只用了半个月就建成了第一个自己的 网站。

所以这真的没什么难的。而且还不用花钱。所以为什么不试试呢?

  1. 想先试试?快速入门⬇

    如果你是一个经验主义者,或者你秉持实践出真知的观念,或是不确定自己能不能行,想先 试试再说,再或者只是单纯讨厌看理论,就想先玩……

    那为什么不在一切开始之前,先花个两三分钟建一个自己的网站出来呢?

    1. 一、注册GitHub

      前往 Github 注册一个账号,点击右上角的 Sign Up 即可。

      GitHub作为作为全球最大的代码平台,我想应该没有人不知道吧?🤷‍♀️

      如果打不开,请上工具,或者多等一会儿,多刷新几下,再不行就换个时间(实测半夜 GitHub速度最快😂)

    2. 二、创建仓库

      截屏2021-10-27 下午11.04.41.png

      如图,进入新建仓库页面。

      截屏2021-10-27 下午11.08.51.png

      在上图的 Repository name 中填写 <你的用户名>.github.io ,我因为已经有一个同 名仓库,所以出现了一些不该出现的提示……

      剩下的选项不用改,默认配置即可。

    3. 三、新建一个html文件

      截屏2021-10-27 下午11.32.56.png

      找到这里,点击 Create new file ,进入编辑界面,将文件名填写为 index.html

      文件内容模版如下,双击代码框进入复制模式:

        <!DOCTYPE html> <html> <head> <title>网站标题(显示在标签栏里那个)</title>
      </head> <body> <h1>大字标题,随便写点啥</h1> <p>小字段落,随便写点啥</p>
      </body> </html>
      

      修改后保存即可。

    4. 四、设置部署

      进入设置中的Pages项:截屏2021-10-27 下午11.33.28.png

      如此修改即可。

    5. 五、你站成了

      稍等一分钟左右(GitHub需要反应一会儿),然后访问 https://<你的用户名 >.github.io ,就能看到你的网页了。

      虽然它现在看上去很简陋,但美化它非常简单。你可以试着到我网站的仓库,把仓库 themes 文件夹下的 style.css 复制到你的仓库里(不用建立文件夹,和 index.html 同级),然后在你 index.html</head> 前加入:

      <link rel="stylesheet" href="style.css" type="text/css">
      

      再刷新下试试?

  2. 网站的概念——这个输入一串链接就能带我穿越的东西到底是啥?

    网站到底是个什么东西?为什么我点一个神秘链接,或者输入一串神秘字符,就能访问一个 新世界?

    其实访问网页的本质就是打开一个文件。和你双击打开桌面上的一个文件没什么区别——只不 过这个文件在别人的电脑(服务器)上。

    1. 先试试

      在理论之前,你依然可以先试试!在你的电脑桌面上新建一个和上面一样的 index.html 文件(记得改对后缀名!),然后双击它,看看你浏览器顶端的地址栏,它在显示什么?

      在我的 Mac 上,它是 file:///Users/rosa/Desktop/index.html 。是不是很像?

      1. 为什么

        平常我们访问一个网页,网页的地址栏会显示一串这样的东西:

        https://ser3vau.club/journal.html
        

        这一串网址的用处,就是告诉你的浏览器,你要打开的文件在哪。

        其中 https:// 代表访问协议(学习技术知识的基操就是忽略那些你看不懂的东西), ser3vau.club 是我网站的域名,它指向一个IP地址。

        IP地址就是我的服务器的位置,长得像 114.114.114.114 这样,域名就是它的一个好记的 外号。

        “/”后面的那一串代表文件在我服务器里的位置,类似于你的文件在你C盘里的位置,比如 https://ser3vau.club/images/wechat.jpg 就代表我服务器根目录下(类似于你C盘里) 的 images 文件夹下的 wechat.jpg 文件。访问这个网址,就相当于访问了我服务器里 image下的wechat.png这张图片。你的浏览器会显示这张图片,就像电脑上的图片查看器一 样。

        所以访问网站的过程其实就是一个打开别人电脑里的文件的过程。

        那搭建网站的方法也就简单明了了:无非就是找一台服务器,把你的文件扔上去,再给你的 服务器取个名,就完活了。

        为什么不能用自己的电脑搭网站,必须要一个服务器呢?因为无良运营商把你的IP“阉割”啦, 外面的人访问不到!如果你是三大运营商的宽带,可以试试打电话让他们把你的IP“对外开 放”;或者你可以尝试内网穿透~^_^

  3. 网站的基本结构——我都需要些什么东西?

    综上,可以知道,一个网站的基本元素有:

    • 内容,文章啊、图片啊什么的;
    • 服务器,存放你的内容,提供访问服务;
    • 域名,

      (等待填坑)

1.3.3. FreeBSD

  1. FreeBSD 安装博通( broadcom )网卡驱动 <2022-06-01 Wed>

    当我度过那被 Windows 和 macOS 笼罩的童年,抱着那本陪我度过了大半辈子的 Macbook Air ,小心翼翼地踏入那异彩纷呈的广阔世界起,我才发现,原来我的 BCM43224 网卡是一 个渣得不能再渣的 pua 大师,动不动就罢工不理我让我手足无措,又在我几乎绝望的时候 给我一丝转机,折磨得我死去活来。现在,我把绝大多数操作系统装了一遍,终于可以把那 个小贱人拿捏在手中啦哈哈哈哈!

    除去 Linux-libre 这种琅苑仙葩, Linux 系统上的博通网卡其实很好对付——用过的同学大 概都对b43 、 brcm[fs]mac 、 broadcom-wl 等一系列驱动(被迫)耳熟能详。有闭源驱动 的魔法加持,就算困难很大也是有希望的罢(悲)。相比之下, FreeBSD 上的博通网卡才 是真正的小恶魔,上网查找半天自家网卡的型号,好不容易发现一个叫做“ bhnd ”的内核 模块对它有支持,结果一查 man page ,发现 bhnd 竟然只是个“ interconnect bus ”,距 离能够联网,还有十万八千里……

    那话说回来,到底怎么才能在 FreeBSD 上使用博通网卡呢?

    FreeBSD 自身的博通网卡驱动一共有两种: bwnbwi ,其中 bwi 是用于较旧型 号的, bwn 是用于较新型号的。两者的支持范围有所重叠,而 bwn 对硬件的支持要更 好。

    那么,怎么知道自己应该使用哪种驱动呢?根据 bwi manual 的说明,如果你用的是 BCM4301BCM4303BCM4306 rev 2 以及其它差不多老的网卡,由于固件的原因, 只能使用 bwi 驱动。要配置好 bwi 驱动,首先,在 /boot/loader.conf 中添加:

    if_bwi_load="YES"
    

    来让 bwi 在开机时自动启动。

    然后去 ports 里安装一下 bwi 的固件(无论 bwi 还是 bwn 的固件, pkg 仓库里都是 没有的,只能去 ports 装):

    # cd /usr/ports/net/bwi-firmware-kmod/
    # make install clean
    

    什么,你问我没网怎么装软件?用手机线共享网络呀!~

    # ifconfig <你的设备> up #运行 ifconfig 查看设备。如果没有其它联网设备,那 lo0 以外的另一个就是
    # dhclient <你的设备>
    

    如果只是这样,内核扫到的设备名字叫 bwi0 ,不会被识别为网卡,所以还要在 /etc/rc.conf 中来一句:

    wlans_bwi0="wlan0"
    

    重启就可以看见可爱的 wlan0 在 ifconfig 里朝你招手啦!

    bwi 支持的设备很少,真要说本事还是 bwn 更强。要知道 bwn 是不是支持你的 型号,光看 manual 可是不行,要进这里:

    https://www.landonf.org/code/freebsd/Broadcom_WiFi_Improvements.20180122.html

    在这位前辈列出的表格中,找到 bwn 那一栏,看看你的型号有没有被打上绿勾。如果有, 就可以快乐地准备啦!

    安装 bwn 固件:

    # cd /usr/ports/net/bwn-firmware-kmod/
    # make install clean
    

    配置 /boot/loader,conf :

    if_bwn_load="YES"
    

    配置 /etc/rc.conf

    wlans_bwn0="wlan0"
    

    不过,你有没有注意到,有的网卡的小绿勾,它的右上角有些奇怪的符号,仔细看一看下面 的注释,还会发现什么 PHY ,什么 b43 GPL code 之类的……如果你的设备有,那就有 点小麻烦了。

    (没错,就是作者的网卡, BCM43224 !大规模存在于 12 、 13 年左右的 Macbook 中……)

    这意味着, bwn 并没有自己实现对这些网卡的支持,但是它可以借用 Linux 系统下的 b43brcmsmac 驱动程序,从而让自己也支持这些网卡。不过,出于版权原因, bwn 不好默认开启这些支持(毕竟对有的同学来说,一不小心用了 GPL 的代码可是会很 头疼的),所以我们只能手动开启它,然后重新编译 bwn 模块,以及整个系统内核。

    具体该怎么做呢?首先参考 Handbook 建立自己的内核配置文件,在自己的配置文件中加入 options BWN_GPL_PHY ,之后按照教程使用自己的配置编译安装新内核即可。

    如果嫌麻烦,下面的命令可以帮到你。必须先安装好内核源码(参考这里)。

    # cd /usr/src/ # 把这里换成你的内核源码安装目录。
    # cd ./sys/amd64/conf/ # 如果不是 X86 或 amd 架构的 cpu ,把 amd64 换成你的 cpu 架构。
    # cp ./GENERIC ./MYKERNEL
    # echo "options BWN_GPL_PHY" >> ./MYKERNEL
    # cd ../../../
    # make buildkernel KERNCONF=MYKERNEL
    # make installkernel KERNCONF=MYKERNEL
    

    小心哦,编译内核可能会很慢(最长可能需要一小时,对于一台十年前的电脑来说)。所以 我们为什么不在 make 命令后面加上 -jn (你是几核 CPU 就把 n 换成数字几), 来并行编译加快速度,或者直接研读一下内核配置文件,删掉那些没用的模块呢?

    有了新内核了,先别着急重启,还没完,在 /boot/loader.conf 加入这些配置:

    hw.bwn_pci.preferred="1"
    
    if_bwn_pci_load="YES"
    bwn_v4_ucode_load="YES"
    bwn_v4_n_ucode_load="YES"
    bwn_v4_lp_ucode_load="YES"
    

    再重启,不出意外的话, wlan0 已经出来咯~之后按照 Handbook 中的教程配置网络即 可。

    当然,以上都只是乐观情况。如果你的电脑很新,那么它的博通网卡大概率是不被支持的…… 不过,除了 FreeBSD 自身的驱动,我们还可以(抱着侥幸的心态)试一试使用 NDISimulator 移植 Windows 驱动 。但是,如果有条件,而且想要在开源操作系统中愉快 玩耍的话,还是买一张免驱网卡吧。

    所谓奢而堕者贫,而力而俭者富。我相信我们折腾的这一切都有意义,并将为我们的生命增 添一份光泽。

    感谢 Landon Fuller 前辈为 FreeBSD 平台上的博通网卡驱动作出的贡献,感谢 FreeBSD基 金会的 Broadcom Wi-Fi Modernization 计划。关于 bwn 中N-PHY 支持的详细信息,请见 邮件列表(我会告诉你他少打了一个冒号嘛哈哈哈哈)。

1.4. 学校相关

1.4.1. 作文

  1. 高二自我介绍 <2021-09-05 Sun>

    我叫Rosario S.E.,虽然我很希望自己今年只有三岁,但迫于现实的压力,我还是不得不承 认我今年已经十七岁的事实。这个事实曾一度让我十分苦恼,但我用漫长的时间发现:虽然 我现在对自己的年龄感到很糟糕,但到了明年,我就会对自己一年前的年龄感到很满意。自 那以后,我就释然了,并且学习了老子的“无为”思想,以自然的心态面对时光的飞逝——直到 我心目中老子那慈祥的面孔被高考的铁拳击得粉碎。不过这是后话了。

    我今年三,哦不十七岁,性别男。悄悄告诉你一个秘密,我曾经一度想当个女孩。我从很小 的时候就喜欢二次元——在小学时代,我几乎把全中国能找到的有名有姓的动漫都看了个遍。 动漫里的女孩们简直就是美好的代名词,当时的我为了追求美好,便也萌生出了想当女孩的 念头。当然,这个略显天真的念头在很早以前就一点点地不见了——那是因为我接触到了一些 更伟大、更深刻的理念,它们使我更客观、更全面地看待世界的一切。那些深刻却又总伴在 我们身边的哲思,给我一百万字也写不完呢。所以我喜欢哲学,虽然它艰深晦涩,但它能给 我带来那些我人生中最宝贵的成长。正是这些成长让我的生命得以称之为生命,生活得以称 之为生活。

    我性别男,爱好女。不过很遗憾我至今仍然没有像某些同龄人那样找到过“女朋友”。我曾有 过喜欢的女生。尽管未能“成功”,但那段经历让我知道,一个人如果真的拥有了热爱,他是 会不一样的。所以我一直在寻找能让我永远热爱下去的事物。所以我未来想当一个作家、学 者或公务员——它们都能给我以热爱的力量。

    我热爱学习。我的成绩很一般,但我自诩自己是个热爱学习的人,活像个打肿脸充胖子的。

    我最喜欢的学科是语文。语文中我尤其喜欢古诗词,我觉得古诗词是我所学习的一切知识中 最能给予我最全面的、最震撼的感动的一项。古诗词里有遍地英雄下夕烟的革命豪情,也有 浓睡不消残酒的细腻情思;有出师未捷身先死的宏大历史的慨叹,也有闲来垂钓碧溪上的个 人命运的细咏;有瑞脑销金兽,也有鸡鸣桑树颠;有城春草木深,也有东风夜放花千树。更 不要说落月摇情满江树背后勃勃律动的春江,请君为我倾耳听背后踉踉醉歌的仙人,七月七 日长生殿背后含恨终生的夫妻……多么的美好!多么的震撼!我深深地热爱它们!

    除读诗外,在情不自禁的时候,我偶尔也会摹仿出一些拙作。不过在此刻意地展示未免有失 于“企者不立”的毛病,所以在此处呢,就暂且略过。

    我其次喜欢的是历史。要说历史的好处,那可太多啦,和语文一样,说一天都说不完。我再 次喜欢的是政治。为什么政治排在历史后面呢?因为历史主要学的是过去的事,政治则主要 学的是现在的事。如果在学习历史中接触到了一些烦心事的话,我大可以溜到历史长河的另 一头躲得清静,但如果这种事发生在政治上的话,我就只能硬着头皮继续顶上着了。所以我 很希望自己有朝一日能够真正学好政治,成为一个真正坚强的、快乐的、愿意敞开怀去拥抱 这个世界的人,从而更好地为社会发展贡献力量。

    我热爱学习,是个文科生。你一定发现了我最喜欢的三门学科都是文科。没错,我高中的三 门选考科目是历史、政治、地理——全部都是文科。再悄悄告诉你一个秘密,其实我原本是个 理科生。小升初的时候,我语文考了97分,英语考了98分,数学考得比它们都高,差一点就 满分了。我同学的家长都夸我是个全才,尽管我觉得他们的语气酸得掉牙。初中的时候,我 依然是大半个理科生,两门理科我都选齐了。

    我相当喜欢理科。理科给我以探索这个热闹而又安静、复杂而又单纯、神秘而又热情的世界 的力量。但我最终还是选择了文科。这绝对不是因为我数学不好。我们生活在这个世界上, 但对我们而言,是这个世界、这个自然对我们更重要呢,还是这个社会、这些人对我们更重 要呢?是社会和人对我们更重要。想必某些环保主义者要跳出来揭我的不是了:没有我们赖 以生存的世界,哪里有我们人呢?那我势必会问问他:那如果世界上没有人了,这个世界再 美好,有意义吗?所以我一直坚信人是我们生活的一切,那么我的奋斗、我的探索就都应该 围绕人来进行——不学习则已,如果学习,那我一定要学习最本质的知识;不探索则已,如果 探索,那我一定要探索最本质的道理。这是我的骄傲。

    我热爱学习,同时爱好广泛。我是个善变的人,我的爱好就像这个撒丫子狂奔的时代一样, 三天两头变出一个新花样。

    先说说我的第一个爱好吧。上学以来,我的第一个爱好是书法,毛笔书法。但准确来说书法 不能算作我的爱好,因为我至多喜欢了它两年,再之后我学习书法就不是完全出于我的个人 意志了。我在书法上取得了很多“成就”,包括三四枚特等奖的奖牌、一堆奖状和几个奖杯。 现在它们都在我家长的屋里收藏着。不过我对此很有些无动于衷,那些金牌上镀的黄铜总是 散发着一股我不喜欢的味道。我不喜欢比赛,什么比赛我都不喜欢。因为我从那些当年和我 一样只有十一二岁的参赛生的稚气面孔上,很少看到从真正的热爱或理想中焕发出的光芒—— 作为一个追求理想的人,这是我所不能接受的。而毛笔字呢,作为一门沟通古今的技艺,我 如今也偶尔会提起笔来划拉两篇;但作为一门“特长”,它早已从我的生活中远去

    我的第二个爱好是——做up主。是的,这是一个相当新潮的爱好——至少在2015年的时候还是这 样。那段做up主的时光是我永生难忘的回忆。我当年有很多朋友,我现在还能叫出他们中绝 大多数人的网名。他们来自五湖四海——浙江、黑龙江、四川、河南……他们也处于不同的年龄: 有的在备战中考,在自己的卷子上画上江河湖海,向我谈天说地;有的在上高二,在虚拟世 界里给我炫耀自己的编程本领;有的已经是大学生了,一边在宿舍吃着午饭一边邀请我到他 的虚拟世界里度过愉快的周末……

    后来我家长不让我做up主了,直接没收了我的电脑和手机,说影响我的学习,并直接把我送 进了住宿学校。我再次看到他们已经是两年后了。那个和我谈天说地的朋友没有开成他的公 司,而是进了一家工厂,见了我半天也没说出什么;那个高二的学长天天泡在一间实验室里, 婉拒了和我一起玩一局游戏的邀请;而那个大学的朋友,他的账号已经注销了。

    所以我从来不会把自己的意志强加于任何人,不轻易做不利于他人的事。己所不欲,勿施于 人。另外,千万不要在网络上和我聊天,我不会记住你。

    让我们回到快乐的时光里来。请允许我自豪地向你介绍我的第三个爱好——计算机。关于我一 个文科生为什么会酷爱计算机的问题请先放到一边,我已经忍不住向你展示我在计算机方面 惊人的成果了:

    我在暑假中在我2012年的Macbook Air上成功安装了最难安装的linux发行版,也可以说是世 界上第二难以安装的现代个人操作系统——Gentoo Linux。安装过程耗时七天,中途没有一次 错误和失败,成功手动编译安装了整个电脑操作系统从文件系统、硬件驱动、内核模块到桌 面环境、浏览器等所有日常软件的每一个部分,输入代码和命令上百行,并根据本机具体情 况做了优化。有诗为证:

      莫疑繁此为何妨,夜觅阑珊守尺方。

      代码行行映人肃,个中点点入心长。

      肠肥恶见三牲貌,腹馁唯知一口张。

      当敬人间才与苦,方成诸夏景还长。

                  (七阳)

    我热爱计算机,一是因为它务实。工欲善其事,必先利其器,我的计算机知识可以让我用很 简单、舒适的方式完成许多任务,譬如我写这篇文章,我的编辑器护眼、清晰、美观、令人 专注,可以让我连写两千余字而丝毫不累;不过,一个更重要的原因是,我的计算机知识可 以使我自由——我使用自由免费的软件,不用受到那些付费软件的压迫、受到那些广告的恶心。 我自己配置过的系统可以在这台2012年的、连windows都几乎无法运行的老电脑焕发青春, 这是因为我完全自己安装、配置我电脑上的一切组件,我不用被迫在我的电脑上安装那些微 软公司强加给我的东西,比如我用不到的功能和广告服务。

    所以我热爱自由。我不喜欢任何形式的压迫、欺凌,哪怕是在我的电脑和手机上都是如此。 我愿意为了反抗压迫、争取自由而付出我所能付出的一切努力。正是这股动力推动我学习了 如此多艰深的计算机知识,也正是它给我一种使命,让我为消除全社会的压迫与剥削贡献力 量。

    我其实还有第四个爱好,那是我最不起眼的爱好——写小说,网络小说。大部分人都不知道我 还有这么个爱好。但事实上,我从上初中就开始写,到现在已经快五年了,从未中断过。我 为什么没有自豪地把这个爱好宣扬出来呢?那是因为,我从初一写到现在,初中时不让用手 机电脑,我光是活页纸就写满了手指厚的一摞,却是连一篇勉强称得上“作品”的都没有。每 次,我仔细思考我的理想,然后幻想一个故事,动笔,发现故事中根本性的不合理之处,于 是重新来过。就这样日复一日,五年过去了,我没有做出任何成就,没有书法上的奖牌奖杯, 也没有计算机上的实用便利,确实是什么都没有。

    但我还是坚持着,因为写作是我的梦想。

    小学,我的语文老师就和我家长说:“这孩子长大了适合当一个作家”——我一直记得她的话。

    或许将来有一天,我会成为一个律师,成为一个体制内的公务员,或者一个普普通通的白领。 但永远我不会忘记我今天的梦想。我想我这样做是对的。

    所以我是一个相信希望的人,是一个永远怀着一颗天真、执拗的心,且不愿放弃它的人。

    这就是我,Rosario S.E.,17岁,性别男,爱好女,热爱学习,爱好广泛。希望大家多多关 照。

  2. 有知识没文化 <2021-12-05 Sun>

    人托生于地球这样一颗行星上,托生于这样一个社会中,实在是莫大的不幸:一颗颗伟大而 纯真的心灵就这样一点点地被这个贫瘠的、冰冷的、残酷的世界所扭曲、打碎,不啻为一种 极度的无奈与悲哀。可尽管我们的生活是如此不幸,我们那注定为后代的幸福而倾尽一生的 命运是如此的不可撼动,我们依然没有理由放弃对伟大与幸福的追逐——这是我们生而为人的 权利。所以,当我们决定为获得知识而牺牲文化素养时,仔细想一想:真的应该这么做吗?

    知识不仅是知识,在这个贫瘠而冰冷的社会里,知识还代表着金钱、名利,代表着物质的富 足。相比之下,从外在来看,文化似乎要单纯的多,也无足轻重得多:作为一个人的道德修 养、言谈风尚、人生智慧的体现,它的“变现”是间接的、低效的、“撞大运”的。这样看来, “文化”似乎真的没有什么修习的必要。但文化真的只有这么“单薄”吗?

    文化是一座冰山,在你看不到的地方,它的内涵与作用无比庞大。相比于通过掠夺物质世界 而使你富有,它更喜欢在精神世界为你创造出不朽的财富。举一个最简单,也最根本的例子: 什么是幸福?你幸福吗?你怎样才能幸福?没有融合文化内涵的、单纯的知识是无法解答这 些问题的——更无法让你变得幸福。但文化可以做到:在天愿作比翼鸟说爱情是幸福,常回家 看看说亲情是幸福,为人民服务说奉献是幸福……当我们“有文化”后,我们就能知道什么是属 于我的幸福——幸福难道不比世间一切财富和名誉都要美好吗?

    可惜的是,当今,很多人没有看到这一点,舍弃了文化素养,一心追求知识带来的“飞黄腾 达”。其实这本无可厚非——那些没有看到的人,他们已经为我们这个世界奉献了太多太多, 我们没有理由去要求他们做到更多。但是,对于文化的追求不应该是一种刻意的、强制的要 求,而应该是一种自觉的追求,因为唯有文化才能让我们得到真正的爱与幸福,而让人意识 到这一点的关键在于教育。

    我们的学校教育总是在“吹毛求疵”——占据学生百分之九十以上精力的理科自不必提,连那剩 不到百分之十比重的文科,其学习中也充斥着死记硬背不求甚解精神和“Shared Bike in Amsterdam”这样令人生厌的文章——这是高中,小学初中的问题只会更严重;而我们这一代人 的家长大多生长于文革之末、改革开放之初,不懂如何科学地教育子女。于是,我们多数人 价值观的形成只能靠“散养”,甚至让互联网和游戏起了决定性作用,那些真正无价的文化却 被埋没,被当作过眼云烟,被徒增厌恶,被无情丢弃。

    要从根本上改变这种状况,我们的教育需要时刻弘扬正确的价值观,告诉学生什么是真善美,什么是幸福,鼓励学生去追求它们,而不是淡化这些重点,让学生对着一坨酸腐的方块字抓耳挠腮,那是对文化的精神灭绝;而作为一切的中心,我们能做的唯有正确清晰地认识这一切,用坚毅、希望和长远的思考斧正我们前行的路,用力追求生活,在这个过程中爱上文化,努力成为一个既有知识、又有文化的人。须莫及,落花时。

1.4.2. 作业

  1. 研学论文读后任务 <2021-10-09 Sat>
    1. 篇目一
      1. 1500年以后500年的世界历史整体上呈现出什么趋势?发生了哪些重大事件?

        1500年以后的世界整体上呈现出世界政治从以欧洲为中心转移到两极格局,再转移到世界多极化的趋势;呈现出从欧美的强权政治转移到“协和万邦”的公正合理的政治的趋势;呈现出从殖民主义到民族独立的趋势

      2. 两次世界大战对世界格局和国际秩序产生了什么影响?结合所学举例说明

        第一次世界大战加速了欧洲的衰落,两极格局萌芽,中国开始修正不平等条约、开始走向进步;第二次世界大战导致欧洲进一步衰落,欧洲失去优势地位,美苏成为超级大国,两极格局形成;中国彻底改变过去的面貌,走向进步

      3. 世界“百年未有这大变局”体现在哪些方面?

        百年未有这大变局体现在:“一超多强”国际格局发生变化,“多强”力量上升,“一超”力量下降,发展中国家力量蓬勃发展,中国与世界命运相连,传统大国和新兴大国矛盾加剧,世界危机加剧,世界格局的不确定性加剧。

      4. 世界近现代史有哪些重要的发展线索(或历史主题)可供研究?
        • 一战前资本主义的发展和欧洲的发展
        • 一战对世界各个国家和资本主义的影响
        • 一战后国际秩序对世界各个国家的短期影响和长远影响
        • 一战对二战产生的影响
        • 二战的影响
        • 二战对世界各个国家产生的影响
        • 一战和二战对国际秩序的影响
        • 一战和二战及战后国际秩序对人的观念的影响
        • 两极格局和苏联解体的影响
        • 新时代经济发展对国际格局的影响
        • 发展中国家兴起对国际格局的影响
        • 意识形态多元化对国际形式的影响
        • 中国在过去多年中对世界发展起到的作用

        等等

    2. 篇目二
      1. 一篇学术论文由哪几部分构成?
        • 标题
        • 作者
        • 摘要
        • 正文
        • 引用
      2. 该论文参考了哪些类型的史料?各举一例
        论文
        p130.1
        艺术品
        p131.1
        档案
        p131.2
        书籍
        p134.2
      3. 相较与分析大轰炸本身,这篇文章研究的重点是什么?

        这篇文章的研究重点在于大轰炸对当代社会产生的影响和影响发生的原因

      4. 关于大轰炸有哪些叙事主体?他们各自做出了什么评价?如何看待?
        大轰炸幸存者
        可怕悲惨的遭遇
        执行轰炸者持不满的一方
        轰炸行为在战略上不适当
        执行轰炸者持肯定的一方
        轰炸行动有很大战略意义
        德国纳粹政府
        “野蛮”、“大屠杀”,批判盟军
        东德政府
        是西方帝国主义的战争准备
        苏占时期和两德统一后
        纳粹是始作俑者,主张和解
        历史书写者
        理性叙事或悲情表达
      5. 基于不同价值认知,当代人回忆大轰炸的目的有何不同?如何看待?
        报复
        索取道歉和赔偿
        战斗
        对抗帝国主义
        平衡
        把德国的二战罪责相对化
        教训
        客观评价大轰炸
      6. 承载历史记忆的形式有哪些?有何特点?如何认识其在传播和研究历史过程中的价值?
        符号化
        把和大轰炸相关的事物作为德国二战记忆的重要象征
        仪式化
        主张把纪念大轰炸作为一种仪式,如游行
        博物馆化
        把有关大轰炸的记忆收集起来,保存并使人们记忆
        纪念日化
        把大轰炸发生日确定为固定的纪念日
      7. 基于以上,如何在历史研究中尽可能接近“真实的历史”?

        应当科学分析历史中“人”的心态和行为,才能从发展的轨迹中还原历史的真实面貌

      8. 从这篇文章中还收获了什么?

        历史的构成是复杂多元的,历史不是单调的“发生过的事”,时代和人民是历史的创造者、塑造者、改变者,对任何历史的分析都离不开这段历史发生以后的一切广大人民。

  2. 明德人文选修感想 <2021-11-07 Sun>
    1. 诗歌

      诗歌是我最喜爱的传统文化。在明德人文选修课的第一节课上,老师用他富有感情的讲述进一步丰富了我对诗歌的认识。 诗歌是对生命体验的表达,是对美的感发。它拥有首屈一指的声韵美,并且和意蕴美相融合,表达出美好的诗意。 诗歌阐发作者的所思所想,而作者的情感蕴含其中。我们品鉴诗歌,要在诗中追求与诗人的情感共鸣,从而领悟诗歌真正的含义与美。 研究诗歌,要关注作者的背景信息,脱离作者的创作背景是难以得出诗歌内容的真正含义的。 诗歌在流传过程中可能会受到时代的影响,而被后人所修改。在对诗歌的研究中应多查阅史料,注意到这些修改,从而不致出现错漏。多查阅古籍资料、积累认识是诗歌研究的必由之路。

    2. 历史

      历史是我最喜爱的学科,三节明德人文选修课分别从中国史研究、世界史研究和考古研究的层面为我们讲解了历史研究的许多事项,拓宽了我对历史研究的认识。 中国史方面,我们研究历史,其首要目的就是为现代社会发展所服务,从历史发展的脉络中找出对我们社会发展有益的东西。中国上下五千年,拥有丰富的历史,爬梳史料、把握历史线索是研究历史的重要方法。 考古研究方面,考古是“用间接的方法去发现无法观察到的人类行为”,它零散、残缺,但时空有序,可复原,且形象、直观,可分类、提取和记录。考古可以帮助我们发掘历史的真相,印证文献记载的内容,考证许多文献记载中缺失的内容,如社会风俗等。 世界史方面,我们要从世界普遍联系的角度研究世界史,世界历史在时间、空间上都是相互联系的,要全面地发现问题、思考问题。

  3. 2021寒假艺术作业 <2022-01-20 Thu>

    (水分水分……)

    1. 版画

      这周,美术老师为我们带来了精彩的版画知识教学。版画不仅在外国由来已久,更是中华民族的传统技艺之一,分为木版画、金属版画、石版画、丝版画等多种,色彩丰富,趣味十足。课程中,老师用他详细而敬业的教学将版画在中国与外国的历史娓娓道来,并在此过程中为我们展示了版画所独有的丰富内涵和艺术价值,向我们充分普及了版画艺术,陶冶了我们的情操,增长了我们的见识,让我们劳逸结合,快乐地学习别样的知识,从而能够德智体美劳全面发展

    2. 交响乐

      附中的交响乐团一直是我校引以为豪的学生组织之一,我们班就有很多交响乐团的同学,他 们才艺丰富、才思敏捷、风姿卓绝,是班级的骄傲。在两个月前的交响乐活动中,我看到他 们代表班级、代表学校上台,抱起自己的乐器,奏出动人的旋律,内心的激动与豪情久久不 能平静。每当我想起那美好的乐音,他们的面容就会在我心中浮现,聊慰期末复习时单调乏 味的生活。时隔许久,假期再次回想他们,便又是一番味道,美好的时光在记忆里酿成美酒, 充实了我的生活。

    3. 舞蹈

      假期降至,舞团的同学们为我们带来的精彩舞蹈表演可谓是一份再真诚不过的礼物。我们坐在电脑桌前,安静认真地看到,舞团的同学们在台上,用优美的身姿划过一道道艺术的弧线,恍若天仙般飞舞,不似人间。这胜景不禁让我想起敦煌壁画上的“飞天”,又想起主席的诗:“神女应无恙,当惊世界殊”。可见,艺术在它美的本质上是相通的。舞蹈落幕,我由内心深处生发出一声怅惘的慨叹,艺术对人的洗礼是多么的深刻、多么的回味无穷啊!只有艺术才能赋予生命深刻的内涵。

    4. 合唱

      所谓子闻《韶》,三月不知肉味,合唱团同学的表演正给我一种三月不知肉味的感觉。美妙动人的歌声响起,我就仿佛被带入了艺术的天地:那是心灵在最原始的美好韵律中的悸动,它令我沉醉在那无边的飘渺里,随着歌声而沉浮。古往今来,曼妙的歌声是很多的,可惜它们大多都未能传承下来,但合唱团同学们的表演,仿佛将中华上下五千年里理想的歌声都带到了我的面前般。一饱耳福。想到这样才华横溢的同学平日里就在我的身边,我不禁觉得,艺术果然是从生活中来,到生活中去。

  4. 微写作给菜园起名 <2022-05-06 Fri>

    我希望给这个菜园起名为“加餐园”。“弃捐勿复道,努力加餐饭”,“加餐”这个始于汉朝的民 间俗语,以朴实却诚恳的殷殷叮嘱,寄托了希望对方保重身体、健康生活的朴素愿望。以 “加餐”作为菜园名,可以生动地表达出学校对学子们健康生活、健康成长的希冀,也传达了 学校希望学生能自立自强、菜园可以物尽其用的实干精神。

  5. 呐喊封面图起名 <2022-06-16 Thu>

    鲁迅曾在《呐喊》的自序中,与《新青年》的创办者谈论过这样一个例子:假如我们被关在 了一方严丝合缝的铁屋子里,屋子里的人们大都昏昏沉沉地睡着,丝毫没有意识到窒息的死 亡将要到来。作为少数清醒着的人,我们应该怎么做。

    内忧外患、积贫积弱,那时的中国,正如那铁屋子一般,外寇将要灭亡我们,而我们的国民 却还昏昏沉沉地睡着。

    而鲁迅先生,选择倾尽他毕生的心血,以锋锐的笔墨,在这铁屋中发出最有力的呐喊,使国 民在呐喊中觉醒,使民族在呐喊中迎来希望。这就是“呐喊”的含义。

1.4.3. 笔记

  1. 政治
    1. 矛盾及以前
      1. 总结
        1. 总结 物质与意识
          • 物质决定意识 , 意识是 对物质世界的反映 , 要 尊重客观规律 , 坚持 一切从实际出发,实事求是
          • 人能够 能动地认识世界 , 认识具有 目的性、自觉选择性和能动创造性 , 人能够 能动地改造世界通过实践把观念的东西变成现实的东西 ,创造出 符合人的目的的客观事物正确 的意识 促进 事物发展, 错误 的意识 阻碍 事物发展
        2. 总结 规律
          • 规律 是事物运动过程中 固有的本质的、必然的、稳定的联系
          • 规律是 客观的,具有普遍性的
          • 我们要 尊重规律,按客观规律办事
          • 尊重规律是 正确发挥主观能动性的前提条件
          • 我们要把 发挥主观能动性尊重客观规律 结合起来
        3. 总结 联系
          普遍性
          联系是普遍的, 联系是事物之间以及事物内部诸要素之间的 相互依赖、相互影响、相互制约和相互作用世界是一个普遍联系的有机整体
          客观性
          联系是客观的,联系是事物本身所 固有的,不以人的意志为转移 ,要 从事物固有的联系中把握事物,切忌主观随意性
          多样性
          联系是多样的,要善于分析和把握事物存在和发展的各种条件,要 一切以时间、地点和条件为转移
        4. 总结 整体与部分
          总起
          整体与部分是相互区别、相互联系的
          整体
          整体居于主导地位,整体统率着部分,整体的功能、状态及其变化会影响部分;应当树立全局观念,掌握系统优化的方法,用综合的思维方法认识事物;要立足整体,优化组合,选择最佳方案,实现整体的最优目标,达到整体功能大于部分功能之和的理想效果。
          部分
          部分在事物的存在和发展中处于被支配的地位,部分的功能及其变化会影响整体的功能,必须重视部分的作用,用局部的发展推动整体的发展。
        5. 总结 发展
          前进性
          • 事物发展的前途是光明的
          • 任何事物都要经历 肯定、否定、再到否定之否定 的辩证发展过程
          • 辩证否定的实质是扬弃
          • 要对未来充满信心,热情支持和悉心保护新事物,促使其成长、壮大
          曲折性
          • 事物发展的方向是前进的、上升的,道路是曲折的、迂回的
          • 要做好充分的思想准备,不断克服前进道路上的困难,勇敢接受挫折与考验
        6. 总结 量变与质变
          量变
          量变是质变的必要准备,要脚踏实地,埋头苦干,重视量的积累,为实现事物的质变创造条件;
          质变
          质变是量变的必然结果,要果断地抓住时机,促成质变
        7. 矛盾
          1. 概念

            事物内部的对立统一关系

          2. 同一性和斗争性
            • 同一性不能脱离斗争性而存在,斗争性寓于同一性之中
            • 矛盾双方的对立统一推动事物的发展
            • 要用对立统一的观点看问题
          3. 普遍性与特殊性
            1. 普遍性

              承认矛盾、直面矛盾、分析矛盾、解决矛盾

            2. 特殊性
              1. 不同的事物
              2. 同一事物在发展的不同过程阶段
              3. 同一矛盾的两个不同方面
            3. 普特关系
              • 普遍性和特殊性相互联结
                • 普遍性寓于特殊性
                • 特殊性离不开普遍性
              • 事物都是普遍性与特殊性的对立统一
            4. 方法论
              马 + 中
              • 把马克思主义基本原理同中国具体实际相结合
          4. 主次矛盾和矛盾的主次方面
            1. 主次矛盾
              主要矛盾
              对事物发展起决定作用的矛盾
              (no term)
              相互依赖、相互影响,在一定条件下相互转化
            2. 矛盾的主次方面

              相互依赖、相互排斥,在一定条件下相互转化

            3. 方法论
              • 坚持两点论和重点论的统一,抓住主流
              • 把我主要矛盾,统筹兼顾次要矛盾
          5. 具体问题具体分析

            在矛盾普遍性原理的指导下,具体分析矛盾的特殊性

            “对症下药”、“一把钥匙开一把锁”

            • 是正确认识事物的基础
            • 是正确解决矛盾的关键
      2. 笔记
        1. 哲学的起源
          • 哲学是一门追求智慧的学问
          • 哲学产生于人类的 实践活动
          • 哲学起源于人们在生活实践中对宇宙、人生的 追问和思考
        2. 哲学是什么
          哲学是系统化理论化的世界观
          (no term)
          哲学是 关于世界观的学问 人们对整个世界的 总的看法和根本观点 就是世界观
          (no term)
          世界观 决定 方法论
          (no term)
          方法论 体现 世界观
        3. 哲学与科学的关系
          哲学是对自然、社会和思维知识的概括和总结
          (no term)
          哲学对具体领域的知识进行 概括和升华 ,抽象出 最一般的本质最普遍的规律
          (no term)
          具体科学的进步 推动 哲学的发展 哲学 不是“科学之科学”
          (no term)
          哲学为具体科学研究 提供世界观和方法论的指导
        4. 哲学的基本问题
          思维和存在 何者是本源 的问题 (第一性问题)
          • 思维 => 唯心
          • 存在 => 唯物
          思维能否 正确认识存在 的问题(同一性问题)
          • 同一 => 可知论
          • 不同一 => 不可知论
          1. 为何思维和存在的关系问题是哲学的基本问题
            • 是人们在生活和实践活动中遇到的和 无法回避的基本问题
            • 贯穿哲学的始终,是 一切哲学不能回避、必须回答 的问题
            • 决定着各种哲学的 基本性质和方向 ,决定着它们对其他哲学问题的回答
        5. 唯物主义
          • 物质是本源的
          • 物质决定意识
          1. 三种基本形态
            古代朴素唯物主义
            • 把物质混同于物质的 具体形态 (水、活火、原子和虚空……)
            近代形而上学唯物主义
            • 自然科学的物质结构 直接混同于物质概念(原子、机械……)
            • 具有机械性、形而上学性、历史观上的唯心主义等特点
            辩证唯物主义和历史唯物主义
            • 客观实在 是物质的唯一特征
        6. 唯心主义
          • 把认识的某一片段片面地、夸大地发展,就会导致唯心主义
          • 唯物主义与唯心主义、辩证法和形而上学的对立和斗争长期存在,但归根到底只是 唯物主义和唯心主义两种
          • 人的主观精神 (如人的目的、意志、感觉、经验、心灵等)理解为世界的本原
          • 客观精神 (上帝、神、理念、绝对精神……)看作世界的主宰和本原
        7. 马克思主义哲学
          理论来源
          • 黑格尔的辩证法费尔巴哈的唯物主义
          历史使命
          • 实现无产阶级和全人类的解放
          创立标志
          • 《关于费尔巴哈的提纲》《德意志意识形态》
          意义
          • 实现 唯物主义与辩证法的有机结合
          • 实现唯物辩证的 自然观和历史观的统一
          • 全部理论来自 实践
        8. 物质与意识
          1. 自然界的物质性
            • 自然界在 本质上是物质的
              • 物质是 不依赖于人类的意识 ,并 能为人的意识所反映 的客观实在
              • 物质的唯一特性是 客观实在性
          2. 人类社会的物质性
            社会与物质
            • 人类社会是 物质世界长期发展的产物 ,本质上是一个 客观的物质体系
            社会的物质基础
            • 构成社会物质生活的基本要素是 地理环境、人口因素和生产方式
          3. 意识:是物质世界长期发展的产物
            意识是什么
            • 人的意识不仅是 自然界长期发展的产物 ,而且是 社会发展的产物
            物质和意识的关系
            • 人脑是意识活动的物质器官 ,是产生意识的 生理基础 。意识是 人脑的机能
            意识的内容
            • 意识是 对物质世界的反映 ,客观存在 通过生活和实践 的环节进入人脑
            主观与客观
            • 意识的 内容是客观的,形式是主观的 。意识是 客观存在的主观映像
        9. 运动与规律
          1. 运动的规律性
            运动与物质
            • 物质是运动的物质, 运动是物质固有的根本属性和存在方式
            运动的条件
            • 运动是物质的运动
            规律是什么
            • 规律是 事物运动过程中固有的本质的、必然的、稳定的联系
            规律的性质
            • 规律是 客观的,不以人的意志为转移不能被改变、改造
            • 规律具有 普遍性
          2. 正确发挥主观能动性
            能动地认识世界
            • 意识活动具有 目的性、自觉选择性、能动创造性(主动创造性)
            能动地改造世界
            • 通过 实践 把观念的东西变成现实的东西,创造出符合人的目的的客观事物
            条件
            • 尊重客观规律 是正确发挥主观能动性的前提条件
            做法
            • 坚持一切 从实际出发,实事求是把发挥主观能动性和尊重客观规律结合起来 ,把高度的革命热情同严谨踏实的科学态度结合起来
        10. 联系
          联系是什么
          • 联系是事物之间以及事物内部诸要素之间的 相互依赖、相互影响、相互制约和相互作用
          普遍性
          • 世界是一个普遍联系的有机整体
          客观性
          • 联系是事物本身所 固有的,不以人的意志为转移 ,要 从事物固有的联系中把握事物,切忌主观随意性
          多样性
          • 人们容易看到那些直接的、表面的和眼前的联系,而往往忽视那些间接的、本质的和长远的联系, 忽视事物之间相互联系的中间环节

          要善于分析和把握事物存在和发展的各种条件,要 一切以时间、地点和条件为转移

        11. 整体与部分

          用联系的观点看问题——整体与部分

          整体与部分的关系
          • 整体与部分是相互区别、相互联系的
          整体的地位
          • 整体居于 主导 地位,整体 统率 着部分,
          整体对部分
          • 整体的 功能、状态及其变化 会影响部分;
          如何为整体服务
          如何思考
          • 树立 全局观念
          • 掌握 系统优化 的方法
          • 综合的思维方法 认识事物
          如何做事
          • 立足整体
          • 优化组合
          • 选择最佳方案

          以实现整体的最优目标,达到 整体功能大于部分功能之和 的理想效果

          部分的地位
          • 部分在事物的存在和发展中处于被支配的地位
          部分的作用
          • 部分的功能及其变化会影响整体的功能,必须重视部分的作用,用局部的发展推动整体的发展。
        12. 发展
          发展的存在
          • 世界处于永恒发展中
          • 自然界人类 和每个人的 认识 都在发展
          发展的原因
          • 事物发展的原因分为内因和外因, 内因起决定性作用
          发展的实质
          • 发展的实质是事物的前进和上升,是新事物的产生和旧事物的灭亡
          新事物
          • 新事物是 符合客观规律、具有强大生命力和远大发展前途的事物
          新事物的判定
          • 判定一个事物究竟是新事物还是旧事物的根本标准,是 是否同事物发展的必然趋势相符合
          前进性
          • 事物发展的前途是光明的
          • 任何事物都要经历 肯定、否定、再到否定之否定 的辩证发展过程
          辩证的发展观
          实质
          扬弃
          方法论
          要对未来充满信心,热情 支持 和悉心 保护 新事物,促使其成长、壮大
          发展的曲折性
          • 事物发展的方向是前进的、上升的,*道路是曲折的、迂回的*
          • 要做好充分的 思想准备 ,不断 克服前进道路上的困难 ,勇敢 接受挫折与考验
        13. 量变与质变

          事物发展的形式——量变与质变

          量变是什么
          • 量变是事物 数量的增减或场所的变更 ,是一种渐进的、不显著的变化
          量变的意义
          • 量变是质变的 必要准备
          方法论
          • 脚踏实地,埋头苦干,重视量的积累,为实现事物的质变创造条件
          质变是什么
          • 质变是事物 根本性质的变化
          质变的意义
          • 质变是量变的 必然结果
          方法论
          • 要果断地 抓住时机,促成质变
        14. 矛盾
          1. 矛盾就是对立统一,要求我们用一分为二的观点(全面的观念)看问题。
          2. 矛盾的基本属性是同一性和斗争性。
          3. 矛盾的普遍性和特殊性:

            矛盾具有 普遍性 ,即事事有矛盾,时时有矛盾。要敢于 承认 矛盾,勇于 直面 矛盾,善于 分析 矛盾,积极寻求 解决 矛盾的具体方法。

            矛盾具有 特殊性 。要求我们 具体问题具体分析

            普遍性寓于特殊性之中,并通过特殊性表现出来。

          4. 两点论和重点论

            主要矛盾和次要矛盾、矛盾的主要方面和次要方面辩证关系的原理要求我们,要坚持 两点论和重点论相统一 的方法。

            坚持两点论,就是既要看到主要矛盾,又要看到次要矛盾;既要看到矛盾的主要方面,又要看到矛盾的次要方面。

            坚持重点论,就是要着重把握主要矛盾,着重把握矛盾的主要方面,要抓住主流。

            辩证法的两点论时有重点的两点论;重点论是看到两点中的重点。

    2. 辨证思维
      1. 特征
        1. 整体性

          辨证思维用 全面的观点看问题 ,将认识对象的各个要素、各种联系的丰富性和多样 性在头脑中再现出来,并从整体角度思考如何解决问题。

          整体性的辨证思维能科学地处理“此”和“彼”的关系,事物之间既相对独立,又相互联系

          辨证思维能科学地处理整体与部分之间的关系

          整体有层次之分,辨证思维把多层次、多视角的认识综合起来,整体性地思考问题

        2. 动态性

          辨证思维 用变化发展的观点看问题 ,用矛盾运动观点看问题,不仅要考察事物的现 状和历史,而且要想到事物的未来

          动态性的辨证思维能科学地把握事物的发生、发展过程

          辨证思维在整体性与独立性、动态性与静态性的对立统一中把握事物

          辨证思维用实践的观点看问题,是同唯心主义诡辩论的根本区别

      2. 分析与综合
        1. 分析

          分析就是把认识对象 分解为各个部分、各个要素、各个层次 ,或者把认识对象的复杂发展过 程分解为 若干阶段 分别加以认识

          表现:定性分析、定量分析、功能分析、因果分析

          社会生活中,人们需要对社会的 基本矛盾、主要矛盾 进行科学的分析

        2. 综合

          把认识对象的各个部分、各个要素、各个层次,按照其固有的联系 联结和统一 起来进行 考察

        3. 分析与综合的辨证关系

          分析是综合的基础,综合是分析的先导;分析为综合做准备,综合的结果指导人们继续对事 物进行新的分析

          方向相反,却相辅相成

          客观基础

          客观事物的矛盾多样性和矛盾统一性的相互依赖

          是客观事物的矛盾在思维中的反映

          方法
          是矛盾分析法的体现
      3. 质量互变

        以统一性的观念正确把握事物发展过程中的量变与质变、渐进性与飞跃性、连续性与间断性

        • 连续性:事物只是在量上发生了变化,事物还是自身,是事物发展中的渐进性过程
        • 间断性:事物在质上发生了变化,从旧质到新质的飞跃,是 连续性的间断 ,即表现为 出现了新事物
        1. 适度原则

          事物质与量的统一体现在“度”中——一事物保持自身质的定性的量的限度

  2. 历史
    1. 秦及以前
      1. 旧石器时代
        1. 特征
          打制石器
          旧石器时代指以打制方法制作石器的时代
          渔猎采集
          从事渔猎和采集
          群居生活
          过着群居生活
          元谋人、北京人已经学会用火
          母系氏族社会
          社会落后,人只知其母,不知其父,只能以母系记录世系
        2. 代表
          元谋人
          距今170万年
          北京人
          距今70-20万年
      2. 新石器时代
        1. 特征
          1. 磨制石器

            新石器时代指以磨制方法制作石器的时代

          2. 陶器

            当时人们已经大量使用陶器

          3. 原始农业、畜牧业

            当时人们开始从事原始农业,饲养家畜,生活逐渐稳定

          4. 分布广泛,多元一体

            目前,中国已发现的新石器时代文化遗存有一万多处,且分布广泛,奠定了多元一体的发展基础

          5. 父系氏族社会
          6. 贫富分化、阶级分化、私有制

            新石器时代形成的父系氏族社会,社会贫富分化与不平等开始出现,氏族间的联系趋于紧密,形成较大的部落甚至部落联盟

      3. 炎黄部落联盟
        地域
        黄河中上游
        遗址
        五帝后期的龙山文化时代,“万邦”时代
        (no term)
        禅让制
        (no term)
        具备国家的初始形态

      4. 时间
        约2070-1600BC
        建立者
        中央
        世袭制,设有主管行政、军事、司法、宗教的机构与职官
        地方
        聚族而居,在夏部族生活的地区直接统治,对其他地方间接统治
        灭亡
        1600BC 商汤灭夏

      5. 时间
        1600-1046BC
        政治
        • 商王是最高统治者
        • 下设尹及各类事务官
        • 内外服制
        文化
        • 甲骨文:占卜记录,成熟的文字
        灭亡
        1046BC,武王伐纣,牧野之战
      6. 西周
        1. 兴替:1046-771BC
          1046BC 周武王
          武王伐纣,牧野之战,西周建立
          841BC 周厉王
          国人暴动,共和行政
          771BC 周幽王
          犬戎攻破镐京,杀死幽王,西周灭亡
        2. 政治
          1. 分封制:”封建亲戚,以蕃屏周“
            目的
            实行有效统治,拱卫王室
            作用
            加强了周天子对地方的政治统治
            对象
            宗室、功臣、姻亲、先代贵族
            地区
            王畿之外
            诸侯义务
            • 听命天子
            • 定期朝见
            • 守卫土地
            • 随从作战
            • 交纳贡赋
            诸侯权利
            • 世袭领有封土
            • 封土内再分封
            • 行政高度自主(设置官员、建立军队、征收赋税)
            等级结构
            天子-诸侯-卿大夫-士 天下-国-采邑-禄田
          2. 宗法制:嫡长子继承制
            作用
            解决了统治阶级内部在权利和财产分配方面的冲突和矛盾
      7. 商和西周的经济
        1. 前中期
          1. 黄河中游:仰韶文化
            • 时间:7000-5000BC
            • 彩陶
          2. 黄河下游:大汶口文化
            • 时间:6500-4500BC
        2. 晚期
          1. 黄河流域:龙山文化
            • 时间:5000-4000BC
            • 黑陶(蛋壳陶)
          2. 辽河流域:红山文化
            • 时间:6000-5000BC
            • 玉器
            • 祭坛和神庙
          3. 长江下游:良渚文化
            • 时间:5000-4000BC
            • 玉器
            • 祭坛和神庙
      8. DONE 春秋战国——诸侯纷争与变法运动
        1. 时间
          春秋
          770BC - 475BC
          战国
          475BC - 221BC (东周256BC灭亡)

          截屏2021-10-21 下午11.11.36.png

        2. 政治变革和民族认同
          1. 诸侯争霸
            春秋五霸
            齐桓公、晋文公、楚庄王、宋襄公、秦穆公 (一说后二位为吴王阖闾、越王勾践)

            截屏2021-10-21 下午11.18.43.png

            1. 意义
              • 区域性的局部统一,加快了统一中国的步伐
              • 促进民族交融
              • 促进社会变革
          2. 卿室崛起

            公室大权旁落,政权执于大夫家族

            • 三家分晋
            • 田氏代齐
          3. 战国
            战国七雄
            齐、楚、燕、韩、赵、魏、秦
            各国称王
            各国国君自称为王,反映了君主集权体制的形成

            截屏2021-10-21 下午11.30.35.png

          4. 变法
            1. 春秋战国时期的社会变革
               
              经济 土地国有(井田制) 土地私有
              政治格局 诸侯分封 争霸、兼并、县制
              行政权力 宗法世袭 专制官僚
              社会阶层 世袭 流动
              文化学术 贵族 民间(百家争鸣)
              非绝对君权 君主权利加强,君主专制
              分封制 郡县制,中央集权
              宗法血缘关系、分封制 官僚制

              以上原因导致各国的变法运动。

            2. 各国变法
              目的

              建立君主专制、中央集权、官僚制度

              官僚制度(取代贵族制度)  
                将相分立,文武分职
                官员选拔和任免
                俸禄供养官吏
                考核与监察
              行政区划(取代分封制度)  
                建立县制
                中央集权
              以法治国(取代以礼治国)  

              截屏2021-10-21 下午11.43.24.png

          5. 商鞅变法

            政治和社会措施

            措施 目的及作用
            废世卿世禄制,军功授爵 打破世袭贵族的特权,发展官僚制度
            废分封,立郡县 中央集权
            编制户口、“什伍连坐” 加强社会控制,便利征收赋役
            “燔诗书而明法令” 加强思想控制

            经济措施

            措施 目的及作用
            “开阡陌封疆”“废井田”“民得买卖” 以法律形式废除井田制,确立土地 私有制
            “重农抑商”、奖励耕织 发展农耕,富国强兵
            拆大家庭为小家庭 移风易俗,加强社会控制,增加税收
            统一度量衡 便税收和交换,加强集权制度
            1. 意义
              • 顺应历史潮流
              • 是战国时期持续时间最长,涉及面最广,改革最彻底的变法
              • 使秦国国富兵强,为秦统一中国奠定基础
          6. 经济
            • 铁农具、牛耕
            • 水利工程
              • 都江堰
              • 郑国渠
              • 芍陂(que去 bei阴平)
            • 手工业分工细密
            • 货币流通广泛
            • 中心城市
          7. 各国变法
            目的
            • 富国强兵
            手段
            • 变法
            作用
            • 逐步建立君主专制的政治制度
      9. 百家争鸣
        学派 人物 主张   意义
        儒家        
          孔子     有教无类,打破贵族垄断教育,推动私学发展
            核心观念:仁    
            关爱他人,建立和谐的人际关系    
            顺应民心、爱惜民力、为政以德    
            恢复礼乐制度    
                 
          孟子      
            性善论    
            “仁政”    
            民本思想    
            重土地农时    
            轻刑薄赋    
                 
          荀子      
            性恶论    
            隆礼重法    
        道家        
          老子 将天地万物的本源归结为抽象的“道”   中国古代朴素的唯物论和辩证法
            顺其自然、无为而治    
                 
          庄子      
            逍遥自由    
            消极无为的人生观、社会观    
        阴阳家        
          邹衍      
            五行   中国古代对自然界朴素的科学认识
            相生相胜    
        墨家        
          墨子      
            节俭   代表下层平民利益
            兼爱、非攻    
            尚贤    
        法家        
          韩非子      
            以法为工具管理国家    
            控制臣民    
                 
            限制贵族特权    
            维护封建制度    
            适应历史趋势    
                 
            对人民暴力镇压    
            崇尚阴谋权术    
            文化专制主义    
      10. 秦统一多民族封建国家的建立
        1. 秦统一的条件
          客观
          • 人们厌恶战乱带来的灾难,渴望统一
          • 各地经济发展要求结束分裂
          主观
          • 秦国地理位置优越,物质基础雄厚
          • 秦王励精图治,广纳贤才,吏治清明
          • 商鞅变法后遵奉法家,奖励耕战,国力强盛
        2. 秦统一的过程
          • 灭六国
          • 征服南方越族地区,加强对云贵一带西南夷的控制
          • 击退匈奴进攻
        3. 秦巩固中央集权的措施
          皇帝制度(君主专制制度)
          • 皇权不可转移
          • 最高权力不可分割
            行政 军事 监察
          皇帝      
          中央 丞相 太尉 御史大夫
          郡守 郡尉 监御史
          县令 县尉 县丞
          三老 游徼 啬夫
          里正 亭长  
          郡县制
          • 在地方彻底废除分封制,实行郡县两级制;
          • 主要官员由中央任免考核;
          (no term)
          统一车轨,修建道路网;
          (no term)
          统一货币、文字、度量衡;
          (no term)
          创立文书制度;
          (no term)
          律令国家。
        4. 秦朝疆域
          朝鲜
          西
          临洮、羌中
          北回归线以南
          河套、阴山、辽东
        5. 秦朝暴政
          • 赋税徭役繁重(大兴土木)
          • 严刑酷法
          • 焚书坑儒
            • 使阶级矛盾和统治阶层内部矛盾尖锐化,农民起义爆发
    2. 中华文明的发展历程 <2022-06-07 Tue>
      1. 多元一体和华夏认同
        多元

        中华文明多元起源

        在中华大地上有多个起源

        黄河流域、长江流域、珠江流域、辽河流域、北方草原、天山南北、青藏高原、四川盆 地……都是孕育中华文明的摇篮

        一体
        中原华夏族率先成为核心,向四周辐射,推动多元一体中华文化的形成
        华夏认同
        春秋战国,内迁的戎狄蛮夷融入华夏族,形成文化认同
      2. 百家争鸣和思想文化源头
        儒家思想
        春秋战国社会大变革,礼崩乐坏,孔子阐述西周礼乐文明,儒家文化思想核心形成
        百家争鸣
        战国时期,百家争鸣局面出现,成为后世思想文化发展的源头,成为中华文化的奠基时期
      3. 正统思想和文化主流
        • 汉武帝接受董仲舒的建议,尊崇儒术

          确立儒学的正统地位,儒家思想成为中华传统文化的主流。

      4. 三教交融和儒学创新
        1. 儒、道、佛交汇融通
          • 佛教传入、道教兴起,呈现儒、道、佛交汇融通的景象
          • 魏晋玄学盛行(以道家思想为主),唐朝佛学繁荣

            传统儒学受到挑战,促进儒学创新与发展

          • 隋唐时期,中华传统文化辉煌灿烂
          1. 魏晋玄学:风靡一时的主流思潮
            背景

            社会政治上,源于汉末“清议”,由于朝廷高压,世人的谈论由具体社会问题转为空玄的人性

            思想文化上,源于对《老子》《庄子》《周易》的注疏

            主要思想
            “无”和“有”的关系
            “贵无”“崇有”
            “名教”与“自然”的关系
            “越名教而任自然”
        2. 理学的形成与影响
          背景
          宋代,吸收佛、道思想阐释儒学的新学派理学形成
          代表人物
          朱熹,“存天理,灭人欲”,“格物致知”
          影响

          丰富了中华文化的理论思维

          封建礼教束缚人们的精神世界

          其它
          宋元时期,科技、史学、文学、艺术高度繁荣
      5. 明清思想发展和制约
        发展

        明清之际,提倡个性自由的思想出现

        黄宗羲、顾炎武、王夫之

        批判理学,抨击封建专制,倡导经世致用

        制约
        康雍乾时期,君主专制,文字狱,制约中华文化发展
      6. 近代的新思想

        学习西方,救亡图存,科学与民主

        1. 鸦片战争爆发后,向西方学习以救亡图存成为中华文化潮流
        2. 20 世纪初期,新文化运动抨击封建思想,科学与民主成为中华文化的追求目标
        3. 五四运动以后,马克思主义在中国广泛传播,与中国实际相结合
    3. 中华优秀传统文化的内涵 <2023-06-08 Thu>
      1. 以人为本和民本思想
        以人为本

        周公“敬天保民”(与鬼神相对言)

        孔子“仁”“仁者爱人”

        民本思想

        以人为本在政治伦理上的体现

        管子提出君主治国要顺应民意

        孔子要求体察民情、反对苛政

        孟子“仁政”学说,“民贵君轻”

        历代思想家继承先秦民本思想,一定程度上转化为政治实践

      2. 天人合一

        朴素的唯物观

        摒弃天命的绝对权威

        将天、地、人视为一个整体,人应尊重自然规律、顺应自然,建立人与自然和谐发展的关系

        • 商朝人遇事必祭天地、祖先
        • 老子提出“道”,“人法地,地法天,天法道,道法自然”

          凸显人的存在,追求天人合一

        • 荀子“天行有常,不为尧存,不为桀亡”“制天命而用之”
      3. 家国情怀

        中华文化提倡爱国,追求家国情怀

        以天下为己任

        • 孟子“天下之本在国,国之本在家,家之本在身”
        • 张载“为天地立心,为生民立命……”
        • 范仲淹“先天下之忧而忧,后天下之乐而乐”
        • 顾炎武“天下兴亡,匹夫有责”
      4. 崇德尚贤,天下为公
        • 西周“明德”“敬德”
        • 孔子“为政以德”“道之以德,齐之以礼”“见贤思齐”
        • 墨子“尚贤”
        • 孟子“尊贤使能”
        • 《礼记》“大道之行也,天下为公,选贤与能,讲信修睦”
        • 德才兼备、以德为先的用人制度
      5. 自强不息,厚德载物
        • 周易“天行健,君子以自强不息;地势坤,君子以厚德载物”
        • 孟子“富贵不能淫……”
        • 屈原“路曼曼其修远兮,吾将上下而求索”
      6. 和而不同
        • 西周末年太史伯:“和实生物,同则不继”
        • “和为贵”“君子和而不同”
    4. 中华优秀传统文化的特点和价值
      • 本土性,多样性,包容性,凝聚性,连续性
      • 民族发展的思想源泉和精神动力
      • 维护民族统一,推动发展进步
    5. 中华文化的世界意义
      1. 中华文化在交流中发展
        1. 佛教
          传入
          两汉
          兴盛
          魏晋
          本土化
          隋唐,禅宗
          融合
          宋明理学
          影响

          宗教信仰、哲学观念、逻辑思维、语言词汇、礼仪习俗

          文学艺术:诗词、书法、绘画

          建筑艺术:石窟

          消极影响:因果轮回、消极避世

        2. 明清西方科学技术传入
          • 明末,利玛窦,徐光启等
          • 17 世纪,清朝汤若望、南怀仁主持钦天监工作
        3. 新文化运动

          1915 年

          陈独秀、李大钊、鲁迅、蔡元培等人

          主张吸收民主与科学的思想

      2. 中华文化对世界的影响
        1. 中华文化制度对周边国家的影响
          1. 汉字

            公园前 4 - 3 世纪,汉字已传入朝鲜半岛、日本列岛和东南亚地区。后来各国在汉字的 基础上创造了本国文字——朝鲜谚文、日本假名、越南喃字等

          2. 儒家思想和佛教

            3 - 5 世纪日本等国的各级学校把儒学经典作为教科书,儒学成为官学。

          3. 社会制度

            古代朝鲜、日本和越南的社会制度大多来自唐朝。

            朝鲜政治制度基本模仿中国

            7 世纪,日本大化改新,学习唐朝中央集权、土地制度、赋税制度

            越南的教育体制主要移植于中国,科举制度和中国基本一样

            律令制度

          4. 各种文化习俗

            历法、建筑、绘画、音乐、饮食、服饰、节日等

  3. 英语
    1. 不定式
      • 充当主语

        To lose your heart means failure

        It took me only five minutes to finish the job

      • 充当宾语

        固定搭配: refuse hope bother afford offer attempt intend… + todo

        I wonder when to start the meeting

      • 充当表语

        What she wants to do most now is (to) travel abroad

        表按计划要做的事情

      • 充当定语

        I need a pen to write with <- 不能省略

        I need some paper to write on

        She has four children to take care of

        Charles Lindbergh is the first man to fly the Atlantic alone

        He is the second one to make a speech 数量词之后

      • 作后置定语

        Are you going to attend the meeting to be beld next month

        The question being discussed now is very important

      • 充当宾语补足语
        省略 to

        see hear watch notice feel…

        当被动时不能被省略

      • 充当状语

        表目的

        Tim sat near the fire to get warm

      • 作结果状语

        enough to, sufficient to, only to, never to

        Would you be so kind as to step this way, please?

      • 作原因状语

        放在表示心理感觉的形容词后

        I am happy to see you

    2. 重要短语
      • do harm to sb. 伤害某人
      • desire sb. to do sth. 想要某人做某事
      • desire that … (should) do sth. 渴望…(应该)做
      • The / A reason why … is that … …的原因是…
      • That / This is because … 那是因为…
      • bring about 引起
      • invest 投入(时间、精力);投资
      • in conclusion 总之、最后
      • be beneficial to / be of (great/much) benefit to …
      • for the benefit of
      • to one's benefit
      • aprove of
      • request sb. to do sth.
      • make a request that… (should) do…
      • not until 直到…才…
  4. 数学
    1. 函数的单调性
      1. 用函数的单调性的定义证明函数 \(f(x)=\frac{x+2}{x-1}\) 在区间 \((1,+\infty)\) 上是 单调递减函数

        证明:

        1. 取值:任取 \(x_1,x_2\in (1,+\infty)\) ,且 \(1
        2. 作差变形:

          \(f(x_2)-f(x_1)=(1+\frac{3}{x_2-1})-(1+\frac{3}{x_1-1})=\frac{3[(x_1-1)-(x_2-1)]}{(x_1-1)(x_2-1)}=\frac{3(x_1-x_2)}{(x_1-1)(x_2-1)}\)

          \(\because 1

          \(\therefore x_1-x_2<0,x_1-1>0,x_2-1>0\)

          \(\therefore \frac{3(x_1-x_2)}{(x_1-1)(x_2-1)}<0\)

          \(\therefore f(x_2)

          \(\therefore f(x)\) 在 \((1,+\infty)\) 上单调递减

      2. 如果函数 \(f(x)=ax^2+2x-3\) 在区间 \((-\infty,4)\) 上单调递增,求实数 \(a\) 的取值范 围

        解: \(f'(x)=2ax+2\)

        \(\therefore \begin{cases} 2ax+2>0 \\ x<4 \end{cases}\)

        当 \(x<0\) 时,

        \begin{align} 2ax+2&>0 \\ 2ax&>-2 \\ ax&>-1 \\ a&<-\frac{1}{x} \end{align}

        即 \(a<0\) ;

        当 \(0 \begin{align} ax&>-1 \\ a&>- \frac{1}{x} \end{align}

        即 \(a>- \frac{1}{4}\)

        当 \(a=0\) 时, \(2ax+2=2>0\) ,成立

        \(\therefore - \frac{1}{4}

      3. 已知 \(f(x)= \begin{cases} (2a-1)x+1,x<1 \\ \log_{a}x,x\ge 1 \end{cases}\) ,是 \((-\infty,+\infty)\) 上的减函数,求 \(a\) 的取值范围

        由题意: \((2a-1)x+1\) 是减函数,且 \(\log_{a}x\) 是减函数,且当 \(x=1\) 时, \(2a\ge \log_{a}x\)

        \(\because \log_{a}x\) 过点 \((1,0)\)

        \(\therefore \begin{cases} 2a-1<0 \\ 00 \end{cases}\)

        解得 \(0

      4. 设 \(f(x)\) 是定义在 \((0,+\infty)\) 上的增函数, \(f(2)=1\) ,且 \(f(xy)=f(x)+f(y)\) ,求满足 \(f(x)+f(x-3)\le 2\) 的 \(x\) 的取值范围

        \begin{align} f(x)+f(x-3)&\le 2 \\ f(x^2-3x)&\le 2 \end{align}

        \(\because f(2)=1,2=1+1\)

        \(\therefore 2=f(2)+f(2)=f(4)\)

        \(\because f(x)\) 是 \((0,+ \infty)\) 上的增函数

        \(\therefore x^2-3x&\le 4\)

        \begin{align} x^2-3x-4&\le 0 \\ (x-4)(x+1)&\le 0 \\ -1 \(\therefore -1
      5. 设函数 \(f(x)=xe^{kx}(k\not =0)\)
        • 若 \(k=1\) ,求函数 \(f(x)\) 的单调区间

          由题意:

          \begin{align} f'(x)&=e^{kx}+xe^{kx}\times k \\ &=e^{kx}(kx+1) \\ &=e^x(x+1) \end{align}

          \(\because e^x>0, \begin{cases} x+1> 0,x\in (-1,\infty) \\ x+1<0,x\in (-\infty,-1) \end{cases}\)

          \(\therefore\) 当 \(x\in (-1,\infty)\) 时, \(f(x)\) 单调递增;当 \(x\in (-\infty,-1)\) 时, \(f(x)\) 单调递减。

          \(f(x)\) 、 \(f'(x)\) 随 \(x\) 的变化如下表…

        • 若 \(f(x)\) 在区间 \((-1,1)\) 内单调递增,求 \(k\) 的取值范围

          由题意: \(e^{kx}(kx+1)>0,x\in (-1,1)\)

          \(\because e^{kx}>0\)

          \(\therefore kx+1>0,x\in (-1,1)\)

          解得 \(\begin{cases} k<1,&x\in (-1,0) \\ k>-1,&x\in \left[0,1\right) \end{cases}\)

      6. 下列中在 \((0,+\infty)\) 上是增函数的是: 2
        1. \(y=-x^2\)
        2. \(y=x^2-2\)
        3. \(y=\left(\frac{1}{2}\right)^x\)
        4. \(y=log_2\frac{1}{x}\)
      7. \(y=ax^2+2(a-2)x+5\) 在 \((4,+\infty)\) 上是减函数, \(a\) 范围? \(a \le 0\)

        要单调递减二项式系数 \(a\) 必小于零,而 \(a=0\) 时 \(2(a-2) = -4 < 0\) ,可以取得。

    2. 3.4 函数的单调性
      1. \(f(x) = |x - 2|x\) 的单调减区间是: \([0,2]\)
      2. \(a \in R\) ,已知函数 \(y = f(x)\) 是定义在 \([-4,4]\) 上的减函数,且 \(f(a + 1) > f(2a)\) , \(a\) 取值范围: \((1, 4]\)

        \begin{align} f(a+1) &> f(2a) \\ a+1 &< 2a \\ a &> 1 \end{align}
      3. 函数 \(f(x) = x^2 - 2(k-1)x - 8\) 在 \([5,20]\) 上不单调,则 \(k\) 取值范围: $$

        \begin{align} -\frac{b}{2a} &= \frac{2(k-1)}{2} \\ &= k - 1 \end{align}

        可得:

        \begin{align} 5 &< k-1 < 20 \\ 6 &< k < 21 \end{align}
      4. 已知 \(f(x) = \begin{cases} x^2 + x, &x \ge 0 \\ x - 2, &x < 0 \end{cases}\) ,则 \(f(x^2 - x + 1) < 12\) 解集:

        \(x^2 - x + 1 = 0 \\ \Delta = -3 \\ \therefore x^2 - x + 1 > 0 \\ \therefore f(x^2 - x + 1) &< 12 \\ x^2 + x &< 12 \\ (x + 4)(x - 3) &< 0 \\ x \in (-4, 3)\)

    3. 奇偶性和周期性
      • \(f(-x) = -f(x)\) ,则 \(f(x)\) 为奇函数

        关于原点对称

      • \(f(-x) = f(x)\) ,则 \(f(x)\) 为偶函数

        关于 \(y\) 轴对称

      • 对称轴 + 对称中心 \re 2 ,则为周期函数
      • 奇×奇=偶;偶×偶=偶;奇×偶=奇
      • 任意定义域为全体实数的函数都可以被表示为一个奇函数与一个偶函数的和

        \(F(x) = f(x) + g(x) \\ F(-x) = -f(x) + g(x) \\ g(x) = \frac{F(x) + F(-x)}{2} \\ f(x) = \frac{F(x) - F(-x)}{2}\)

      1. example
        1. \(f(x) = \frac{x}{(2x+1)(x-a)}\) 为奇函数, \(a\) ?

          \(f(-x) = \frac{-x}{(-2x+1)(-x-a)} = -\frac{x}{(2x+1)(x-a)}\)

          \((2x+1)(x-a) = (-2x+1)(-x-a) \\ 2x^2 - 2ax + x -a = 2x^2 + 2ax - x - a \\ 4ax = 2x \\ a = \frac{1}{2}\)

        2. 定义域为 \((-1, 1)\) 上的奇函数 \(f(x)\) 是减函数,且 \(f(1-a) + f(1-a^2) < 0\) , \(a\) ?

          \(f(1-a) < f(1-a^2) \\ a^2 < a, 1-a < 1, 1-a^2 < 1\)

        3. \(f(x)\) 是 \(R\) 上的奇函数,且对任意实数有 \(f(x+1) = f(1-x)\)
          • 证明 \(f(x)\) 是周期为 4 的周期函数

            \(f(x+1) = f(1-x) \\ f(x-1) = -f(x+1)\)

            设 \(t = x + 1 \\ f(t) = -f(t + 2) \\ \therefore T = 2 \times 2 = 4\)

          • 若 \(f(x) = \sqrt{x} (0< x \leq 1)\) ,求 \(f(x), x \in [-5, -4]\)

            \(\because f(x) \text{为奇函数} \\ \therefore f(x) = -\sqrt{x}, x \in [-1, 0] \\ \because T = 4 \\ \therefore f(x-4) = f(x) \\ \therefore f(x) = -\sqrt{x}, x \in [-5, -4]\)

        4. 非奇非偶的函数是: \(y = x^2 - x\)

          \(y = 2^{|x|} \\ y = x^2 - x \\ y =2x \\ y = x^3\)

        5. D
        6. \(f(x)\) 为 \(R\) 上的奇函数, \(x \req 0\) 时, \(f(x) = 2^x + 2x + b\) ( \(b\) 为常 数),则 \(f(-1) = -3\)

          \(f(1) = 2 + 2 + b \\ = b + 4 \\ \because f(x) \text{为奇函数} \\ \therefore f(0) = 0 \\ 1 + b = 0 \\ b = -1 \\ \therefore b + 4 = 3 \\ f(1) = 3 \\ f(-1) = -f(1) = -3\)

        7. \(f(x)\) 周期为 5 , \(f(x) = x + \log_{4}x, 0

          \(54 = 5 \times 10 + 4 \\ f(4) = 4 + \log_{4}4 = 4 + 1 = 5 \\ \therefore f(54) = f(4) = 5\)

        8. \(f(x)\) 是定义域为 \(R\) 的奇函数,且满足 \(f(1) = 2, f(x+2) = -f(x)\) ,则 \(f(1) + f(2) + ... + f(99) = 0\)

          由题意: \(T = 4, f(-1) = -2, f(0) = f(2) = 0, f(1) = 2, f(3) = -2, f(4)=0 \\ \therefore f(1) + f(2) + f(3) + f(4) = 0 \\ \because 99 = 4 * 24 + 3 \\ \therefore f(1) + f(2) + ... + f(99) = f(1) + f(2) + f(3) = 0 \\\)

    4. 二次函数
      1. \(f(x)\) 满足 \(f(0) = 1, f(x+1) - f(x) = 2x\)
        • \(f(x)\) 解析式

          设 \(f(x) = ax^2 + bx + c \\ \because f(0) = 1 \\ \therefore f(1) - f(0) = 0 \\ \therefore f(1) = 1\)

    5. 周末练习 13
      1. \(y = 2^x + \frac{9}{2^x} - 1\) 最小值为: 2

        \(2^x + \frac{9}{2^x} \leq 2\sqrt{2^x \times \frac{9}{2^x}} \\ \leq 3 \\ \therefore 2^x + \frac{9}{2^x} - 1 \leq 2\)

      2. \(y=\sqrt{x}\) 在 \(x=1\) 处的瞬时变化率为: \(\frac{1}{2}\)

        \(y = x^{\frac{1}{2}} \\ y' = \frac{1}{2}x^{-\frac{1}{2}} = \frac{1}{2\sqrt{x}}\)

        \(\therefore y'=\frac{1}{2},x=1\)

      3. \((1+x)^4\) 展开式中 \(x^2\) 系数为: 6

        1 4 6 4 1

      4. \(a = \log_{2.1}0.6, b = 2.1^{0.6}, c = \log_{0.5}0.6\) , a b c 大小关系: \(b>c>a\)

        \(2.1^{0.6}\) 是唯一一个大于 0 的,而 a 比 c 底数大且都大于 0 ,所以 a 比 c 更 陡,在 0.6 上也就更小

      5. \(f(x) = \begin{cases} -1,&x < 0 \\ 1, &x \geq 0 \end{cases}\) ,则不等式 \(xf(x-1) \leq 1\) 的解集为: \([-1,1]\)

        \(\leq -1 \times \leq -1\) 比 1 大, \(\geq 1 \times \geq 1\) 也比 1 大。

      6. 10 张钞票里混有 4 张假钞,任取 2 张,在 1 张是假钞的条件下,两张都是假钞的概 率: \(\frac{1}{3}\)

        第二张有 \(\frac{3}{9} = \frac{1}{3}\) 概率是假钞

      7. \(60 + 82 - 96 = 46\)
      8. \({a}\)
    6. 二次函数
      1. 关于 \(x\) 的一元二次方程 \(x^2 + 2mx + 2m +1 = 0\)

        • 若方程有两个实根分别在 \((-1,0), (1,2)\) 内, $m = $

        \(\begin{cases} \Delta = 4m^2 - 4(2m+1) \geq 0 \\ f(-1) > 0 = 1 - 2m + 2m + 1 > 0 \\ f(0) < 0 = 2m + 1 < 0 \\ f(1) < 0 = 1 + 2m + 2m + 1 < 0 \\ f(2) > 0 = 4 + 6m + 1 > 0 \end{cases} \therefore -\frac{5}{6} < m < -\frac{1}{2}\)

        • 若两实根都在 (0, 1) 内,求 m 取值范围:

        $\begin{cases}

        \end{cases}$

      2. \(f(x) = ax^2 + bx + c, (a > 0)\) , \(f(x) - x = 0\) 两根 \(x_1, x_2\) 满足 \(0 < x_1 < x_2 < \frac{1}{a}\)
        • 当 \(x \in (0, x_1)\) 时,证明 \(x < f(x) < x_1\)

          \(ax^2 + (b - 1)x + c\)

          设 \(f(x) - x = a(x-x_1)(x-x_2) > 0\) ( \(a, x, x_1, x_2\) 都大于 0 )

          \(\therefore f(x) > x\) ;

          \(f(x) - x_1 = a(x-x_1)(x-x_2) - x_1 \\\)

    7. 导数
      1. 检测练习
        1. \(y = x^2 + 2x + 1\) 在 \(x = 1\) 上的导数:

          \(y' = x + 2, x = 1, y' = 3\)

        2. \(f(x) = 1 + \frac{1}{x}\) 图像在 \((\frac{1}{2}, f(\frac{1}{2}))\) 处的切线斜率 为:

          \(f'(x) = (x^{-1})' = \frac{1}{x^2} \\ x = \frac{1}{2}, f'(x) = 4\)

        3. B
        4. \(f(x) = e^x\ln{x}, f'(x) = e^x\ln{x} + \frac{e^x}{x} = e^x(\ln{x} + \frac{1}{x})\)
        5. \(y = sinx + 2cosx\) 在 \((\pi, -2)\) 上的切线方程:

          \(y' = cosx - 2sinx, x = \pi, y = 0 - 2 = -2, y' = -1\)

          \(-\pi + b = -2, b = \pi + 2, \therefore y = -x + \pi + 2\)

        6. 函数在 \(x = 5\) 处的切线方程是 \(y = -x + 8\) , \(f(5) + f'(5) =\)

          \(y = -5 + 8 = 3, f(5) = 3, f'(5) = -1, result = 2\)

        7. \(f(x) = \ln{x} - ax + b\) 在 \(x = 1\) 处的切线方程为 \(x + y - 3 = 0\), a? b?

          \(f'(x) = \frac{1}{x} - a \\ y = -x + 3 \\ \therefore \frac{1}{1} - a = -1, a = 2, y = 2 \\ \therefore \ln{1} - 2 + b = 2, b = 4\)

        8. P 在 \(y = x^3 - x + \frac{2}{3}\) 上移动,设点 P 处切线的倾斜角为 \(\alpha\), 求 \(\alpha\) 取值范围

          \(y' = 3x^2 - 1 \\ \frac{4ac-b^2}{4a} = \frac{-12}{12} = -1 \\ \therefore y' \in [-1, +\infty) \\ \therefore \alpha \in [0, \frac{\pi}{2}) \cup [\frac{3}{4\pi}, \pi)\)

    8. 导数与函数单调性
      1. 检测练习
        1. \(y = \frac{1}{2}x^2 - \ln{x}\) 的单调递减区间为: \((0, 1)\)

          \(y' = x - \frac{1}{x}, x \in (0, +\infty) \\ x \in (0, 1), x < \frac{1}{x}, y' < 0\)

        2. 若函数 \(f(x) = x^2 + 2x +a\ln{x}\) 在 \((0, 1)\) 上单调递减, \(a \in (-\infty, -4]\)

          \(f'(x) = 2x + 2 + \frac{a}{x}, x > 0 \\ \therefore 2x + 2 + \frac{a}{x} < 0, x \in (0, 1) \\ 2x^2 + 2x + a < 0 \\ \Delta = 4 - 8a \\ x = \frac{-1 \pm \sqrt{-2a + 1}}{2}\)

          a 不能是正数, \(\therefore a \in (-\infty, -4]\)

        3. \(f(x) = 2x^2 - \ln{x}\) 在 \((2m, m+1)\) 单调递增, \(m \in (\frac{1}{4}, 1)\)

          \(f'(x) = 4x - \frac{1}{x}, x \in (0, +\infty) \\ 4x - \frac{1}{x} > 0 \\ 4x^2 - 1 > 0 \\ x^2 > \frac{1}{4} \\ x \in (\frac{1}{2}, +\infty) \\ \therefore \frac{1}{2} < 2m < m+1 m \in (\frac{1}{4}, 1)\)

        4. \(f(x) = \sin x - x\) 则不等式 \(f(x+1) + f(2-2x) > 0\) 解集为: \(C. (3, +\infty)\)
        5. \(f(x) = e^x + \cos x\),则使得 \(f(2^x) \leq f(4^{x-1})\) 成立的 x 取值范围是: \([2, +\infty]\)
        6. \(f(x) = \ln x + \frac{1}{2}x^2 - bx\) 存在单调递减区间,则实数 b 取值范围:

          \(f'(x) = \frac{1}{x} + x - b < 0 \\ x^2 - bx + 1 < 0 \\ \therefore \Delta > 0 \\ b^2 - 4 > 0 \\ b > 2\)

        7. \(f(x) = e^x - ax -1\) 单调性?

          由题意:定义域 \(x \in R\)

          \(f'(x) = e^x - a\)

          当 \(f'(x) = 0\) 时, \(e^x - a = 0\)

          \(x = \ln a\)

          \(f(x), f'(x)\) 的变化关系如下表:

            \((-\infty, \ln a)\) \((\ln a, +\infty)\)
          f'(x) - +
          f(x) \(\downarrow\) \(\uparrow\)

          \(\therefore f(x)\) 在 \((-\infty, \ln a)\) 上单调递增,在 \((\ln a, +\infty)\) 上 单调递减

        8. \(f(x) = \frac{1}{3}x^3 + (k-1)x^3 + (k^2-2k-3)x\) 在 \((0, 2)\) 内不单调,求 k 范围

          \(f'(x) = x^2 + (2k-2)x + k^2 - 2k - 3 \\ \therefore \Delta > 0 \\ 4k^2 - 8k + 4 - 4k^2 + 8k + 12 > 0 \\ 16 > 0, k \in R \\ \therefore 0 < -\frac{b}{2a} < 2 \\ 0 < -\frac{2k-2}{2} < 2 \\ -1 < k < 1 \\ 0 < 1 - k < 2\)

    9. 导数与极值最值
      1. 检测练习
        1. \(f(x) = x^3 - 12x, x \in [-3, 3]\), 则 \(f(x)\) 最大值为: -9

          \(f'(x) = 2x^2 - 12 \\ f'(x) = 0, x = 6\)

          \(\therefore\) 最大值在 3 上

          \(f(3) = 27 - 36 = -9\)

        2. 2B
        3. \(f(x) = \frac{1}{3}x^3 - ax^2 + x - 5\) 无极值点, a 取值范围: \(a \in (-1, 1)\)

          \(f'(x) = x^2 - 2ax + 1\) 由题意: \(\Delta < 0 \\ 4a^2 - 4 < 0 \\ a^2 < 1 \\ -1 < a < 1\)

        4. \(f(x) = x - \cos x\) 在 \([0, \pi]\) 上最大值为: \(\pi + 1\)

          \(f'(x) = 1 + \sin x\)

          由题意: \(1 + \sin x = 0, x \in [0, \pi]\)

          \(\therefore f'(x)\) 在 \([0, \pi]\) 上大于 0

          \(\therefore x\) 在 \([0, \pi]\) 上的极大值为 \(f(\pi) = \pi + 1\)

        5. \(f(x) = \ln x + \frac{2}{x}\) 极小值为: \(\ln 2 + 1\)

          \(f'(x) = \frac{1}{x} -\frac{2}{x^2} \\ f'(x) = 0, \frac{1}{x} - \frac{2}{x^2} = 0 \\ x - 2 = 0 \\ x = 2\)

          当 \(x = 2\) 时, \(f(x) = \ln 2 + 1\)

        6. \(f(x) = e^2(-x^2 - x + 5)\) 在 \((a, a+2)\) 上有极大值, a 取值范围: \((-1, 1)\)

          \(f'(x) = e^x(-x^2 - x + 5) + e^x(-2x - 1) \\ = e^x(-x^2 - 3x + 4) \\ = -e^x(x + 4)(x - 1) \\ f'(x) = 0, x = -4, 1\)

          由题意: \(a < 1 < a + 2 \\ a \in (-1, 1)\)

        7. \(f(x) = e^x(x-k)\) 在 \([0, 1]\) 上的最小值:

          \(f'(x) = e^x(x-k) + e^x \\ = e^x(x - k - 1)\)

          \(\because e^x\) 是增函数, \(x - k - 1\) 是增函数

          \(\therefore f'(x)\) 是增函数

          \(f_{min}(x) = f(0) = -k\)

        8. \(f(x) = \ln x + \frac{1}{2}x^2 - ax + \frac{3}{2}, (a \in R)\) 恰有两个极值点 \(x_1, x_2, (x_1 < x_2)\), 求 a 取值范围

          \(f'(x) = \frac{1}{x} + x - a\)

          当 \(f'(x) = 0\) 时, \(x - a + \frac{1}{x} = 0, x \in (0, +\infty) \\ x^2 - ax + 1 = 0\)

          由题意: \(\Delta > 0 \\ a^2 - 4 > 0 \\ a \in (-\infty, -2) \cup (2, +\infty)\)

          \(\because f'(x)\) 二项式系数为 1 > 0

          \(\therefore f'(x)\) 是凹函数

          \(\therefore\) 当 \(a \in (-\infty, -2)\) 时, \(f'(x) > 0, f(x)\) 单调递增,舍

          \(\therefore a \in (2, +\infty)\)

    10. 导数与不等式
      1. 检测练习
        1. B
        2. \(\triangle ABC\) 中, \(\frac{\pi}{4}

          \(f'(x) = e^x\cos x - e^x \sin x \\ = e^x(\cos x - \sin x)\)

          在 \((\frac{\pi}{4}, \frac{\pi}{2})\) 上, \(\sin x > \cos x\)

          \(\therefore \cos x - \sin x < 0\)

          \(e^x(\cos x - \sin x)\) 是减函数

          \(\therefore f(A) > f(B) > f(C)\)

        3. \(\alpha , \beta \in R\), 设 \(p: \alpha > \beta\), \(q: \alpha + \sin \alpha \cos \beta > \beta + \sin \beta \cdot \cos \alpha\), 则 p 是 q 的【既不充分也不必要】条件

          \(q: \alpha - \beta + \sin \alpha \cos \beta - \sin \beta \cos \alpha > 0 \\ \alpha - \beta + \sin (\alpha - \beta) > 0\)

        4. \(x > 0, x > \ln (1+x)\)

          \([\ln (1+x)]' = \frac{1}{1+x} \\ \frac{1}{1+x} > 1 \\ 1 + x < 1 \\ x < 0\)

        5. 证明 \(\ln x \leq \frac{x^2 + x - 2}{3}\) 恒成立

          \((\ln x)' = \frac{1}{x}\)

          \((\frac{x^2+x-2}{3})' = \frac{x+1}{3} < 0\)

  5. 语文
    1. 高考作文
      1. 分类
        事件类
        写事件的意义
        现象类

        与事件类相比,涉及人多,持续时间久,范围广

        自发地,而非被动地这样做

        适合写原因

        概念类

        以一个概念为中心展开,写内涵

        《佛系青年》既是概念,又是现象

        寓言类

        找寓意,寓意往往指向社会生活

        北京没出过

        话题类

        写内涵

        《从来如此便对吗》

        以题目的解释为基础

        关系类

        写关系

        《每一颗都有自己的功用》——整个系统 <=> 每一颗——个人和大众的关系

        这几年常出?

        记叙类
        呈现要素
      2. 构架
        1. 第一段:呼应题目,提出观点和论题
        2. 以讲道理的方式阐述观点
        3. 举例论证

          举例要求:能当代不古代,能国内不国外,能现象不事件。

        4. 举例论证
        5. 收束全文
      3. 注意
        • 认知要准确、有价值、表达清晰
        • 不一定要写“怎么办”
        • 尽量举例
        • 例子不一定要指名道姓,提倡简洁
        • 例子不一定越多越好
        • 语言不一定越优美越好

1.4.4. 诗社稿

  1. 社联会议记录 <2021-09-30 Thu>
    1. 活动信息

      小型活动>=3人,简述活动意义(>=50字) 大型活动>=10人,>=100字,>=6张 sau34th@163.com 超大型活动>=60,>=200,>=6张 线上 互动 >=5人,50分 非互动>=500字,50分(推送、文章) 9.1-11.24一结

    2. 公共资料

      sauforever@163.com saushelian

    3. 申请

      大型活动以上、推送、校外、涉外、教室

    4. 教室申请

      提前一周 周日发问卷,周二20:00截止 高中楼1th、2th阶梯,图书馆1、2th阶梯,高中楼7th会议、报告厅

    5. 各项问题

      活动报告从公邮下模板! 毕业生不参与社团事务 春秋注册 分数达到800分可续注册 假期活动不计分 指导老师学期至少参加2次 不许有离京活动 流量活动无法统计人数可估报, 宣传海报不可随意张贴

    6. 文化节
      • 时间:10.15中午
      • 位置:图书馆南侧、中心花园
      • 摊位问卷、表演问卷 海报、设计摊位
      • 参加+100分
  2. 中秋稿 <2021-09-15 Wed>

    常言道“今夜月明人尽望,不知秋思落谁家”。中秋,无论是空中皎皎的月,还是去日无多的夏,都是那么的惹人遐思。如此美好的日子,如此灿烂的岁月,不如就在此刻,办起我们本学期的第一集吧? 以京城之大,以月之皎皎,凡是中秋在的地方,我们都可以去;以佳节之美,以文华之景,凡是遐思所至的,我们都可以说——你心中的中秋是什么?你心中的文化是什么?“三人行,必有我师焉”,我们期待着你心中充满诗意的答案。 远方很近,诗亦不远。热爱文化的我们聚在一起,良辰美景偕行,三言两语倾心,诗意就在其中了。诗社的意义就在于此,我们相聚的意义也在于此。唯望大家不要拘谨,说不定一段美好的邂逅就在这里呢? “月极明于中秋”,月华一定会把一切美好带到我们身边。 诸君,中秋快乐!

    1. 社联会议 <2021-09-30 Thu>
  3. 文化节 <2021-11-30 Tue>

1.4.5. 学生会稿

  1. 高维麒 lilic 访谈 <2021-08-24 Tue>

    李锂老师给我们班上的第一节课我至今难忘。 那是期中前的一节复习课。他站在台上,一边提问,一边把知识点写上黑板;同学们在台下,一边回答着一个个问题,一边在笔记本上奋笔疾书。 老师写字速度让我们望尘莫及,我纵使出千般本事,也只能眼巴巴看着老师一骑绝尘,徒呼奈何。待到下课,黑板已是花白一片——那是老师丰功伟绩的证明。同学们指着它调侃:“锂具有漂白性”。 虽然粉笔一动连篇累牍,但他绝不是照本宣科的刻板老师。遇到重点难点易错点,他幽默的特点就成了把同学们拽离苦海的回春妙手。老师幽默一句,同学们会心一笑,知识点便牢记于心;走神的同学听到笑声,忙前后左右追问老师说了什么,然后聚精会神听老师讲后面的内容。那次实验课,课前老师强调要点:“这个试管里的层析液是老师用各种有毒物质精心配置而成,”同学们大笑,“所以千万不要把它拿起来,就在试管架上操作。”讲渗透作用时,他问:“自由扩散不消耗细胞自身的能量,但它要不要能量?”一边说,一边让手中的粉笔头一截一截摔到地上,“快说,我就爱掰粉笔(这是老师的微信昵称),再掰就没了。” 李锂老师的口头禅很快成了我们班的流行语,比如:“有不会的就要‘掏出手机查一查’”——真可惜我们班规定不能带手机进教室;他还抓住每个班的特点“因材施教”——到了别的班就换一套口头禅。 他上课从不拖堂,总能在短短40分钟里把所有内容清晰简洁地讲完,不会有“PPT发群里大家回去自己抄”这样的事——他的PPT里只有用来阐释内容的图片。

  2. 高维麒改组纪检部 <2021-10-10 Sun>

    新学期新气象,从高一一路陪伴大家的年级学生会也迎来了改组。高二年级学生会现将设立检查部,由高二22班刘晗瑞任部长,下设纪检分部和卫生分部,分别取代原有的纪检部和卫生部的职责。

    同学们,你们一定遇到过那些横亘在一层电梯口,”拒人以电梯之外”的同学;一定遇到过眼操时四处逡巡,监督同学好好休息的“检查官”;当然还有合唱比赛时,穿梭在场地中组织引导大家的工作人员……他们,正是纪检分部。 总有同学觉得他们不近人情。但是不要忘记,我们的年级如此庞大,让它秩序井然,纪检分部功不可没。或许有同学愿意加入他们,为守护我们的年级贡献自己的力量? 而今,同学们都迈入新班级,纪检分部在人手安排上遇到了困难。纪检分部现面向全年级招募干事,一同为维护年级秩序、组织年级活动做出贡献。如果你愿意加入我们,请于10月3日前往逸夫楼604教室,我们期盼着你的到来! 注:更多通知,请关注各班班群

  3. 冰雪进校园 <2023-03-01 Wed>

    三月,春寒正浓,大家期待已久的“冰雪进校园”活动终于举行。我们用冬奥的热烈拼搏,冲 走“冷藏”一假期的恹恹,冲开一个活力满满的新学期。

    1. 趣味活动

      “冰雪进校园”共有八个活动,可以随意选择;小组参与的方式,更是给看似普通的活动平添 了一颗有趣的灵魂。队友间的一个动作、一个眼神都可以碰撞出奇妙的火花,让同学们在实 践中真正意识到友谊与团结竟然如此美妙。

      小记者:下面我们来到了冰车活动场,同学们好像玩的很开心的样子。

      同学甲:快快快!你快爬呀!

      同学乙:我已经在快了啊啊啊!

      小记者:让我们来采访一下他们~

      同学甲:下一个来来来,我想到个好主意,咱们一块推他一把!

      同学丙:你要干什么?!

      小记者:同学你好~请问你对这个活动的感受是什么样的呢?

      同学甲:三,二,一,走你!

      同学丙:(嗖——)哇啊啊啊!!!

      小记者:同学你好?

      同学甲、乙、丁:他过去了!耶!!!

      小记者:……看来同学们暂时无法交流的样子。

      老师:嘿!那边那组!犯规!

      小记者:咳咳,让我们先去寻找下一个采访目标……

      纵使没有真正的冰雪,拼搏的紧张热烈依旧不减;纵使没有荣誉的奖牌,运动的魅力依旧能 吸引所有人。

      火炬传递,同学们盯着队友手里的小球,一颗心和跑向自己的队友一起慢慢提上嗓子眼;旱 地冰壶,为让己方的冰壶停入正中,大家或殚精竭虑、或挥斥方遒,筹谋着如何抢占高地、 打跑对手。快乐就是这样悄无声息地诞生、成长的。

      小记者:现在我们来到了雪垒球赛场,同学们戴着头盔,用沙包激烈地战斗着。啊,这里有 一位同学正在津津乐道地观战中,让我们采访一下他。

      小记者:同学你好!你能用一句诗形容一下这个你所喜爱的项目吗?

      同学戊:诶呀让我想想……有了!一包两包三四包,五六七八九十包……砸得对面满头包!

      小记者:……咳咳,同学真是文采斐然,谢谢谢谢……诶,这一轮比赛快结束了,让我们采访一 下刚下场的同学们吧!

      小记者:同学你好!看你们打得很热烈,这一局战况如何?

      同学己:我们把对面打得落花流水!我们已经连战三局了,感觉良好,正在准备参加下一局, 我还能再战五百年!

      小记者:诶?可以重复参加的呀!我也想来……

      运动的乐趣并非运动员的专利。我们在课堂畔迈步欢笑,一如古希腊的健儿在古奥林匹克的 夯土大道上赤身奔跑,宏伟与平凡就这样相互成就、相与为一。

    2. 开奖时刻

      小记者:请问本次冰雪进校园中,你认为最紧张刺激的是哪个项目?

      同学庚:那当然是“抽卡环节”啦!今儿我们班可谓“包公脸”,直到倒数第二个奖才抽到我 们班的一个组。刚开始我们还一直期待着中、中、中,后来气氛就慢慢地变得微妙了起来, 也不知道是期待着中还是期待着不中了哈哈哈哈

      为了鼓励大家积极参与活动,老师给每个小组下发了地图和任务单,在任务单上集齐六个项 目的贴纸就可以参与最终的抽奖——精美的冬奥纪念币将承载我们满是欢乐的回忆。

      ——尽管老师未免有杞人忧天之嫌,毕竟没有哪组同学能够拒绝活动的诱惑,但亮晶晶的纪念 币谁不爱呢?

      走在校门口的大道上,举起手中的纪念币,原本是身后的太阳,窜到那金色的纹路中跳跃着, 那美好的一刻定将刻入我们心中。

      而没抽到奖的同学也没关系,当你看着同学举着纪念币发朋友圈,自己化身乳酸菌培养皿时, 这次活动也以另一种方式进入你的心中啦!~

    3. 结语

      作为高二下半学期的第一场活动,“冰雪进校园”为这丰富美好的一年奏响了序曲。这热烈的 序曲犹如冲锋号般,呼唤我们振奋起来,如奥运健儿般,奔向明天。

      小记者:如果用一句话形容这次冰雪进校园,你会怎样说呢?

      小记者:逸夫楼六层的板报上摘有一句诗,我觉得很适合:

      “追风赶月莫停留,平芜尽处是春山。”

      大家觉得呢?

1.4.6. 研学论文:从 GNU Emacs 的历史看开源软件应当如何发展 <2022-05-07 Sat>

  1. 摘要

    开源已成为全球科技进步至关重要的创新渠道。近年来,我国开源生态呈现出蓬勃发展的态 势,但由于我国开源生态成熟较晚,多种问题也随之出现。 GNU Emacs 是自由软件运动的 模范项目,具有悠久的历史积淀。了解 GNU Emacs 的发展历史、学习其在发展过程中解决 问题的经验,对我国开源软件发展具有借鉴作用。本文梳理了 GNU Emacs 从创始至今所经 历的挑战与进步,并据此对我国开源软件发展提出建设性的意见。

    关键词
    GNU , Emacs ,开源软件,自由软件运动
  2. 引言

    GNU Emacs 是一款高度可扩展、可自定义的文本与代码编辑器,是 Emacs 编辑器家族中最 主要的一支。 GNU Emacs 及 Emacs 编辑器家族的历史可以追溯到由理查德・斯托曼 ( Richard Stallman )和盖伊・史提尔( Guy Steele )于 1975 年共同开发 的最初的 Emacs ,而在 1984 年,Richard Stallman 重写了 Emacs 的全部代码并使用 GNU 通用公共许可证发布,称之为GNU Emacs。目前 GNU Emacs 由 GNU 计划持续开发与维 护,采用 GNU 通用公共许可证第三版( GPL v3 )发布,至今仍处于活跃开发中。

    GNU Emacs 以其优秀的项目管理模式、高度团结的开发群体和高素质且活跃的社区而能够活 跃开发数十年,至今仍拥有强大的活力,是自由软件事业、乃至所有开源软件的的模范之一。 在发展过程中, GNU Emacs 也曾遇到众多危机与挑战,如 1984 年与 Gosling 之间的商业 与开源的对抗、 Lucid Emacs (后称 XEmacs )的分裂、乃至于今天 VSCode 等更加现代 化的编辑器对 GNU Emacs 的冲击,等等。 GNU Emacs 以正确的方法应对了大部分挑战,在 时代的大潮中矗立不倒,为开源软件的诸多共性问题提供了宝贵的解决方案。研究 GNU Emacs 的发展历程,可以在一定程度上了解 Emacs 能够永葆活力的原因,学习GNU Emacs 解决问题的经验,对开源软件的健康发展具有指导作用。

    我国开源生态起步较晚,生态规模小,开发者多以程序员个人为主,个人开发者经验不足的 问题十分显著,在开源软件逐渐发展的过程中往往会犯下诸多问题,如开源协议选择不恰当、 程序定位把握不稳、开发模式不成熟、社区治理方式不合理等。倘若处理不慎,这些问题轻 则会使开发者劳动权益遭到侵害、开源事业参与积极性受到打击,重则会对整个中国开源生 态造成不利影响,不利于我国数字技术“开源”发展的国家战略规划。 GNU Emacs 作为一款 历史悠久且成功的开源软件,学习其在发展路途中的先进经验,对我国开源软件开发者如何 做大、做好开源项目,以及如何让开源项目可持续发展具有重要的参考价值。

  3. 一、 Emacs 的诞生

    Emacs 编辑器的前身是 "Text Editor and Corrector" 编辑器,简称 TECO 编辑器。最早 的 TECO 编辑器发布于 1962 年,由美国麻省理工学院( MIT )的 Dan Murphy 编写而成, 最初被用于编辑早期计算机用以记录数据的打孔纸带 [注1]。早期的 TECO 本体功能十分有限,甚至无法实时看到自己所编辑的内容。但 TECO 编辑器拥有精巧而先进 的设计:它的所有操作都是通过解读传入其控制区域的字符进行的。控制区域的每个字符都 有特定的含义,就像执行一次某个特定函数一样。因此 TECO 也被视为一种用于文本操作的 编程语言,人们可以按照一定顺序排列 TECO 的控制字符,从而实现规模化的操作。这种特 殊的编辑方式被称作“宏”( macro )[注1]。

    1976 年, Guy Steele 联合多人开发了一系列 TECO 的宏,用于使 TECO 实现实时可视化 编辑功能。他们为这些宏起名为 TECMAC 与 TMACS [注2][注3]。同年,曾参与 TECO 软件 开发的Richard Stallman 联合 Guy Steele 整合了 TECMAC 和 TMACS 以及其余的大量 TECO 宏,在此基础上使用 TECO 语言和 PDP-10 汇编语言开发了一套更加先进的宏,并称 其为 EMACS( Editor MACroS )[注2][注3][注4]。

  4. 二、Emacs 的创新

    EMACS 在 TECO 的基础上进行了大量的创新。 TECO 语言本质上只是一串字符,十分不利于 阅读,被 Richard Stallman 形容为“能有多丑陋就有多丑陋”[注5]。为了便于开发者扩展 EMACS 的功能, Richard Stallman 以一种在当时普遍被用于人工智能研究的先进语言—— Lisp 为蓝本,在 EMACS 中实现了一种以“ DWIM ”( Do What I Mean ,执行我想说的话) 为原则的、简单易用的Lisp 变体,被称为“ Emacs Lisp ”,而 EMACS 则作为 Emacs lisp 的解释器执行 Emacs lisp 代码。 Emacs lisp 至今仍为 Lisp 语言的优秀实现之一[注 6][注7]。

    Emacs Lisp 不像 TECO那样,仅仅是一门“ Editing language ”,它最初便以成为一门“功 能完备的编程语言( Programming language )”为目标进行开发,且要拥有“强大的抽象能 力”[注7]。得益于此, Emacs lisp 拥有诸多在当时十分先进语言特性,如变量的 Lexical binding 词法作用域以及 Garbage collection 垃圾回收。有 Emacs lisp 作为支 撑, EMACS 实现了众多在当时十分先进的功能,例如逐词、逐段的光标移动和智能缩进等 [注:同6][注:同7]。

    但是, Emacs lisp 赋予 EMACS 最强大的能力,以及 EMACS 最大的魅力所在,仍然是它无 与伦比的扩展功能。无论是简单易懂的设计理念,还是便利的解释型执行环境,无不在鼓励 人们自己动手、按照自己的需要拓展 EMACS 的功能。因此,大量 EMACS 的扩展代码涌现出 来, EMACS 社区迅速壮大。在这样百花齐放的情况下, Richard Stallman 明智地没有彻 底将 EMACS 交予社区管理,他仍旧把握着 EMACS 的发布权,所有扩展都必须发回给他,并 在经过其审核才能被合并入 EMACS 中。大家都不约而同地遵守着这个不成文的规定。这样 的做法有效地避免了社区过于自由的混乱,保持了社区力量的集中,维护了 EMACS 长远的 活力[注:同5][注:同6]。

  5. 三、 Multics 、 Gosling 、 GNU

    EMACS 有诸多优秀的特性,但也有一些相当严重的缺陷。一个最重要的缺陷是它所使用的编 程语言——或者说,它的“实现方式”。 EMACS 是使用 TECO 语言和 PDP-10 汇编实现的, TECO 作为“ Editing language ”,并不适合用于编写程序;而在那个处理器建构还没有一 家独大的年代,使用 PDP-10 汇编使 EMACS 无法在其它很多电脑上运行,严重限制了 EMACS 的发展。然而 Richard Stallman 在当时似乎没有认识到这一点,因为 EMACS 在 MIT 人工智能实验室已经足够流行了[注5]。不过 EMACS 本身足够优秀,每当有一个新 兴平台出现,总会有程序员将其“移植”到新的平台。这在当时是非常普遍的现象。最早的 EMACS 移植版是 Dan Weinreb 在 Lisp Machine 上实现的 EINE ( EINE Is Not Emacs 的 递归式缩写,但它实际上就是 Emacs 的一种,这种命名方式是黑客文化的一部分),后来 逐渐演变成为 ZMACS,在 Lisp Machine 上广泛使用,可以在 MIT 开发的 CADR Lisp Machine 上找到[注8]。

    第二个相对成功的 Emacs 移植版是 Bernie Greenberg 的 Multics Emacs 。 Multics 是 一个主要服务于多人同时使用的分时操作系统,在 1970 年到 1985 年由 GE 公司销售,吸 引了相当一部分用户的使用[注9]。由于 EMACS 无法在 Multics 机器上运行,Bernie Greenberg 等人使用 Multics MacLisp 编写了一个适用于 Multics 机器的 Emacs。由于 Multics 逐渐式微,而 Multics 独有的 MacLisp 编译器和解释器无法移植到其它系统上, Multics Emacs 并没有进一步的发展。但它所采用的众多先进设计理念对后续的Emacs 产生 了诸多影响。[注10]

    以上这些 Emacs 都依赖于平台独有的编译与运行环境,发展潜力不足,在随后的几年中逐 渐陷入停滞。而第一个使用 C 语言编写的、运行在类 Unix 系统上的 Emacs 于 1981 年由 “ Java 之父” James Gosling 编写而成,被称为“ Gosling Emacs ”。 Gosling Emacs 本 身乏善可陈,但这不代表 Gosling Emacs 没有显著的优点,而是因为它被后来的GNU Emacs 完全超越了[注5][注3]。在 Gosling Emacs 前,所有 Emacs 都不遵循着“你必须 发回所有的改进”的传统。 Gosling Emacs 最初也遵循着这样的传统,但 Gosling 在后来 改变了态度,将 Gosling Emacs 作为一款商业软件销售给了 UniPress 公司,不再开源。 1983 年UniPress 公司将 Gosling Emacs 的 Unix 系统版本以 395 美元销售, VMS 系统 版本以 2500 美元价格销售[注11]。商业产品剽窃社区的成果会对社区造成严重的打击,因 此 Gosling 的行为使 Richard Stallman 十分愤怒。他决定同样使用 C 语言开发一款完全 超越 Gosling Emacs 的 Emacs。为了避免与Gosling Emacs 产生著作权上的纠纷, Richard Stallman 参照着 Gosling Emacs 的特性列表,以完全使用自己的代码为目标实现 了 Gosling Emacs 的全部特性,并且加入了诸多新的特性与优化[注5]。 1985 年, GNU Emacs 13 问世,是为 GNU Emacs 第一个公开发布的版本。此后 37 年, GNU Emacs 一直蓬勃发展、生生不息。[注5][注3]

    为了保护 GNU Emacs 不再像 Gosling Emacs 那样受到商业的侵害, Richard Stallman 为 GNU Emacs 编写了一部许可证,以保障 GNU Emacs 可以以开源自由的方式永远发展下去。 这部许可证是后来 GNU 通用公共许可证( GPL )的前身, GNU Emacs 也成为了自由软件 运动的“创始成员”,也是最先使用后来的 GPL 许可证的软件,可谓自由软件运动的元老与 典范。[注12]

    GNU Emacs 最初仍在 Gosling 的授权下使用了部分 Gosling Emacs 的代码[注13: Christopher Kelty; Mario Biagioli; Peter Jaszi; Martha Woodmansee (2015), "Making and Unmaking Intellectual Property", ISBN 9780226172491.],而就在初次发布的四个 月后, Richard Stallman 就将 Gosling 的代码从 GNU Emacs 中全部移除,彻底消灭了著 作权上的纠纷[注5]。

    GNU Emacs 第一个发布版本号是 13 ,这不代表在该版本之前还有 12 个大版本。最初, GNU Emacs 使用三位版本号,因此 GNU Emacs 13 其实应为 1.13.0 ,但 Richard Stallman 后来认为以 GNU Emacs 的发展模式将用不到第一位版本号,便将第一位版本号去 掉,由 1.13 变为 13 ,看上去就像跳过了 12 个大版本,其实不然[注13]。

  6. 四: GNU Emacs 与其分支

    在打败 Gosling Emacs 后, GNU Emacs 在 Richard Stallman 的领导下稳定发展着。这时 Richard Stallman 的工作重心在其 GNU 系统及其组件上,因此 GNU Emacs 的开发并不算 十分活跃[注5]。恰好此时,计算机世界迎来了数个重要的转折。在新时代的冲击下, GNU Emacs 分裂出了多个分支,呈现出机遇与危机并存的局面。

    1. Lucid Emacs

      第一个关键性的转折是图形界面开始兴起,逐渐取代古老的字符界面—— 1983-1984 年苹果 公司的 Apple Lisa 和 Macintosh , 1984 年的 X Window System ,以及后来的 Windows [注14],都显著反映出了这一趋势。而此时的 GNU Emacs 核心开发团队相对保守,大多数 有能力参与开发的用户也还停留在字符界面,并未紧跟时代的潮流,支持尚为青涩的图形界 面。为了能在图形界面下使用 Emacs , Alan M. Carroll 于 1987年基于 GNU Emacs 18.49 创建了一个分支,开始为 Emacs 开发图形界面补丁,取名为“ Epoch ”。[注3][注 15]。 Epoch 只在 1989 年到 1990 年发布了四个版本,并未增加更多特性,最后其补丁被 其继任者 Lucid Emacs 和 GNU Emacs 完全合并[注3]。

      1991 年, Lucid 公司开发了一款名为 Energize 的 C++ 与 Lisp 开发环境,亟须一款好 的编辑器作为其重要组件。 Emacs 当然是它的不二之选。但是 Lucid 公司需要他们的编辑 器拥有良好的图形化支持,而当时的 GNU Emacs 18 并不符合。正好此时, GNU Emacs 19 正在开发中,并计划和 Epoch 联手,合并部分 Epoch 的图形化补丁以增强 GNU Emacs的图 形化能力。于是, Lucid 公司决定和 FSF ( Free Software Fundation ,自由软件基金 会,负责 GNU 工程下的项目的开发与经营)合作,共同开发 GNU Emacs 19 ,并为其加入 更多增强功能。但是,在图形化部分的工作完成后,由于程序中的某些其它问题尚未解决, GNU Emacs 19 的发布延期了。 Lucid 公司由于自身项目的需要,无法容忍项目的继续延期, 便决定在当时 GNU Emacs 19 开发分支的基础上作出部分修改,于 1992 年四月提前发布, 命名为Lucid Emacs 19 ,是为 Lucid Emacs 和其后的 XEmacs的开端。[注15][注2]。

      Lucid Emacs 注重于提升 Emacs 的图形化性能时为 Emacs 加入了许多体验性上的改善,并 数次合并了 GNU Emacs 和 Epoch 的新代码以增强其功能。 Lucid Emacs 从 1992 年到 1994 年一共发行了十个小版本,版本 19.10 是其最后向公众发布的版本[注15]。

    2. MULE

      另一个关键性的发展是多语言支持。二十世纪九十年代,世界经济迅速发展,计算机产业也 日趋成熟,个人计算机走进千家万户。 Emacs 随着时代的潮流走向世界,而此时,众多非 英语国家的 Emacs 用户也开始希望能用Emacs 编辑自己的母语。最先做到这一点的是 1989 年的 Nihongo Emacs 补丁,基于 GNU Emacs 18.53 开发而来——顾名思义,它为 Emacs 加 入了日文支持。之后, Nihongo Emacs 继续发展,于 1992 年三月改名为 MULE( Multilingual Enhancements to Emacs , Emacs 的多语言增强),在 GNU Emacs 的基础上增加了较为完整的多字节编码支持,使 Emacs 支持多种语言,如中文、日文、韩 文、希伯来文等,并加入了多种语言的输入法[注3]。

      在计算机世界中,中日韩文字支持曾是一个长期的难题。过去,计算机长期使用 ASCII 编 码。 ASCII 编码是一种古老的计算机编码标准,仅为英语设计。这种编码下的每个字符所 占的字节数较短,且每个字符占据固定长度的字节。因此,它能够容纳的字符数量较少,无 法容纳数以万计的中日韩表意文字。为了容纳更多字符,人们研发了诸多编码方式,如中文 的 GB2312 (国标 2312 )、日文的 ShiftJIS ,以及目前国际上最为通用的 Unicode 编 码,等等。为了支持大量的字符,这些编码格式下的一个字符往往占据多个字节,或者采取 可变长度编码[注14]。而 MULE 就是通过使 Emacs 支持多字节字符以及不同的编码格 式,从而让 Emacs 支持多种语言的[注3]。

      从 1992 年到 1995 年, MULE 持续为新发布的 GNU Emacs 提供补丁支持。而 GNU Emacs 在 1997 年于版本 20 时开始计划合并 MULE 补丁, XEmacs 也在 1998 年二月发布的第 20.4 版中合并了 MULE 。 MULE 的开发使 Emacs 拥有了完整的多语言支持,是相对于同一 时期的编辑器的巨大进步。它拓宽了 Emacs 的社区,为 Emacs 的持续发展注入了强大的活 力[注2][注3]。

    3. XEmacs

      就在 Lucid 公司为 Energize 和 Lucid Emacs 忙碌时, Sun 公司也开始准备打造一款开 发环境(名为“ SPARCWorks ”),并同样选择将 Emacs 作为其编辑器。于是 Sun 公司加入 了 Lucid Emacs 的开发团队,与 Lucid 公司一同开发 Lucid Emacs 。此时 Lucid Emacs 的开发依然主要由商业力量支撑[注2]。

      1994 年, Lucid 公司因经营不善而破产, Sun 公司主导 Lucid Emacs 的开发,并将其改 名为 XEmacs 。此后一段时间, Sun 公司也逐渐放弃了 XEmacs 的开发,并在 1997 年彻 底退出。 XEmacs 进入以社区为主导的开发阶段,并形成了以多个主要开发者为核心的开发 体系[注2]。

      XEmacs 致力于完善 Emacs 的用户体验,实现那些“大多数人认为更重要”的功能,如图形界 面、多平台支持等等[注16]。相比原始的 GNU Emacs , XEmacs 的一个主要优势在于其开 放性[注15]。为原始 GNU Emacs 贡献代码需要相对严格的审核并且签署 GNU 工程协议, 以及一系列相对繁琐的操作,而 XEmacs 在这些地方则更加友好[注16][注17]。

      XEmacs 不是一个独立于 GNU Emacs 的软件,它从始至终都是 GNU Emacs 的一个分支——不 仅在著作关系上是如此,在行为模式上也是如此。 XEmacs 努力使自己的代码能够并入 GNU Emacs 之中[注15],同时还与 GNU Emacs 团队联合开发了许多十分重要的项目,如 Gnus 和 Dired [注18]。

      XEmacs 兴起后, GNU Emacs 主分支也逐渐改变了其保守的态度,加快了开发速度,合并了 部分 XEmacs 代码,并开始着手提高用户体验。这些变化清楚地反映在了 GNU Emacs 19 和 GNU Emacs 20 中,诸如 frame 、 face 和图形界面下的颜色支持等功能也明显反映出其受 到的 XEmacs 的积极影响[注15][注19]。

      进入新世纪后, XEmacs 的原有开发骨干因种种原因大量退出 XEmacs 的开发,或者加入 GNU Emacs 主分支的开发中;同时 XEmacs 陷入了主版本与开发版本长期分裂的局面,开发 逐渐陷入停滞;而 GNU Emacs 22 则对图形化功能、用户体验以及多平台支持作出了巨大的 改善,基本上完全超越了 XEmacs 。 XEmacs 的开发现已完全停止,其最后一个维护版本为 2009 年一月二十号发布的 XEmacs 21.4.22 [注3][注18][注19]。

      XEmacs 的衰落有诸多原因。一方面, XEmacs 本身是 GNU Emacs 的一个分支,大部分代码 的版权为自由软件基金会所有;另一方面, GNU Emacs 稳定的项目管理方式使其更具长久 的生命力;而且,历经过如此多的挑战后, GNU Emacs 已经趋于成熟,不再需要 XEmacs这 样的分支项目的刺激就能保持旺盛的生命力;同时,进入新时代后,传统的 Emacs 受到了 更严峻的、结构性的挑战,需要集合整个社区的力量进行应对。分裂过后,统一占据了新的 潮流[注18]。

  7. 五:迈入新时代

    进入二十一世纪,计算机行业飞速发展。随着家用计算机性能的提升,字符界面逐渐成为过 去,基于浏览器渲染的 Web 应用兴起,孕育了一批如 Visual Studio Code 的优秀应用, 对古老的 Emacs 产生了巨大的冲击。尽管 Emacs 目前仍在活跃开发,但一方面, Emacs 身上背负着沉重的历史包袱:对新时代的人而言十分陌生的快捷键、因为古老而显得“怪异” 的 Lisp 语言、无法抛弃的字符界面、缓慢而卡顿的单线程……由于历史过于长久,每个改动 都有牵一发而动全身的风险;古老的社区也拖慢了 Emacs 前进的步伐:老用户不愿改变现 有的习惯,新用户因为过于陌生而不愿加入;同时, Emacs 作为开源项目,以及 GNU 工程 的一部分,没有任何商业和资金支持,完全依靠社区成员志愿开发,人力物力都受到限制。

    但与此同时, Emacs也拥有诸多无可替代的优势:长时间的积淀使 Emacs 拥有丰富而强大 的扩展;从早期时代走过来的它拥有强大的兼容性,能够在条件受限的情况下很好地工作; 最重要的是,它设计优秀、易于扩展,使用者可以在尝试扩展 Emacs的过程中获得强大的正 向反馈,拥有让优秀的开发者留下、让使用者变成开发者的魅力。因此, Emacs 的用户虽 然不算太多,但绝大多数的用户都同时是 Emacs 的开发者、贡献者,从而为 Emacs 提供了 一个高素质的社区,为 Emacs 奠定了坚实的用户基础、提供了持久的活力。如何正确发挥 这份优势,扬长避短、取长补短,是 Emacs 在新时代发展的关键。

    希望 Emacs 能够在新时代披荆斩棘、不断前进,克服一切困难,勇立时代潮头。

  8. 六、结论: Emacs 给我们的经验
    1. (一) 优秀的设计,切合大众需要

      只有设计优秀的软件才能长久地发展。 Emacs 具有优秀的设计,它不是一个单纯的编辑器, 而是一个具有无限扩展能力的 Emacs Lisp 解释器,这种设计使 Emacs 自由而灵活,为其 带来了独特的优势。

      开源软件也应当如此。相对大型商业软件,大多数开源软件本身体量较小,更需要切中大众 需要的优秀设计,才能吸引到更多人使用与贡献,从而使项目保持长久的活力。

    2. (二) 选择合适的开源许可证

      开源软件的源代码向全社会公开,如果不采取适当的法律保障的话,其软件成果极易遭到他 人的剽窃。开源许可证就是开源世界普遍采用的法律保障方式。

      不同的开源许可证“严格程度”不同。宽松的如 MIT 许可证,他人可以随意使用,包括使用 其代码牟取商业利益,这很可能不是很多开源软件开发者想要的。而《 GNU 通用公共许可 证》(简称 GPL )则可以保证开源软件不被他人用来牟取商业利益。 EMACS 诞生之初没有 采取类似 GPL 的许可,从而使得集合社区代码的 Gosling Emacs 被买给 UniPress 公司, 对软件发展造成打击。而后 GNU Emacs 采用 GPL 发布,便没有再经历过类似的事件,所有 Emacs 分支都和 GNU Emacs 团结在一起,虽有竞争,但所有竞争都是在为软件本身的发展 贡献力量。

      GPL 第二版和 GPL 第三版之间也有一些微妙的差别,如果想更彻底地避免非自由的侵害, 建议采用 GPL 第三版。无论最终采用什么许可,仔细分析、谨慎选择对于开源软件来说都 至关重要。

    3. (三) 易于改进,具有包容性,找准定位

      相比闭源软件,开源软件最大的优势就在于其旺盛的生命力。要将这份优势发挥到最大,使 软件易于改进、富有包容性是必须的。

      Emacs 拥有简单易懂的扩展语言 Emacs Lisp ,且内置了灵活的解释型环境;同时 Emacs具 有十分优秀的文档,软件中的大多数函数、变量和功能都有详细的文档,且有专门的命令 ( describe-function, describe-variable, etc. )搜索并查阅文档。同时 Emacs 还拥 有完整的扩展包管理系统和数个严格程度不一的扩展包仓库( elpa gnu, elpa nongnu, melpa, etc. ),甚至还有数个社区维护的工具,用以从代码托管网站(如 GitHub )安装 没有进入扩展包仓库的插件。这些都为 Emacs 社区成员提供了便利的参与途经,能有效地 吸引有能力的开发者为软件的发展贡献力量。开源软件开发者应向 Emacs 学习,做好代码 文档和代码注释,以积极的态度对待贡献者,积极审核他人的合并请求,吸引更多高水平开 发者为开源项目添砖加瓦。

      对于大型开源项目,应当抓准项目的自身定位,学习 GNU Emacs 主分支的谨慎态度,有选 择性地增加与合并新代码,不能来者不拒,让非主要功能的代码占据过大比重,从而影响核 心功能的运行效率。同时,开发者也不能忽略这些请求与合并,它们反映了社区对软件的期 望,具有潜在的前瞻性,应当对它们加以正确的引导,使其成为社区的一部分,并且进行仔 细的思考,明确项目进一步的发展方向,是否应该采取变革,否则易于导致社区的分裂,

    4. (四) 营造高水平的优秀社区

      好的社区是开源软件发展的源头活水,而良好的社区环境需要软件开发者用心营造。

      定位不同的开源项目,所能形成的社区也是多样的,但优秀的社区总是有其共性的特点。例 如:社区氛围应当和谐,对于态度恶劣的社区成员应当以合适的方法加以管理;普通用户应 对开发者报以适当的尊重;社区应适当分层,将问题汇报、一般讨论、开发者讨论分开——如 Emacs 便拥有多个专用的邮件列表,各司其职——,这样能有效提高开发者的开发效率,同时 维持社区的干净和谐;以及,应当多吸引高素质人才加入社区当中,对社区起到引领作用。 这些都需要开发者付出相当的努力,妥善经营。

      具体的管理方式可以参考 Emacs 的先进经验,例如其社区定立的规则、邮件列表的维护方 式、管理员的管理方法等,根据自身项目的特性与需要酌情取用。

    5. (五) 与时俱进,兼顾传统

      计算机行业发展迅速,技术迭代快,每过一段时间就会有新的变革出现。开源项目必须紧跟 时代潮流,融入新环境、接纳新技术,才能持续发展下去。

      但是,在接纳新技术后,也不能完全抛弃旧的技术,抛弃旧的人才与劳动成果。要保留原有 技术中优秀的部分,同时做好向下兼容,充分发挥原有社区的力量,这也是包容性的一部分。

    6. (六) 做好开发团队的内部管理

      软件能否发展,其根本动力还是来自开发者,没有开发者、开发者失去开发意愿,或是部分 开发者肆意妄为,都会对开源项目造成巨大打击。 GNU Emacs 就拥有优秀的开发团队内部管 理体系,其开发团队接受自由软件基金会的指导,新开发者的加入、项目负责人的更替等都 处在合理的规划下,从而能够保持稳定开发;相比之下,XEmacs 的开发团队内部缺乏组织, 老开发者陆续离开、新开发者难以上手,最终导致项目失去维护。

      开发者应向 GNU Emacs 学习,做好开发团队的内部管理,保障开源项目拥有稳定而强大的 开发力量。

  9. 参考文献

    1:Murphy, Dan (October-December 2009), "The Beginnings of TECO", IEEE Annals of the History of Computing, 31(4):110-115. doi:10.1109/mahc.2009.127. S2CID 18805607

    2:"A history of Emacs", XEmacs Internals Manual, 2016-12-11, Retrieved 2007-08-22, http://www.xemacs.org/Documentation/21.5/html/internals_3.html

    3:"Emacs Timeline", Jamie Zawinski<jwz@jwz.org>, https://www.jwz.org/doc/emacs-timeline.html

    4:"Where does the name 'Emacs' come from?", The GNU Emacs FAQ, https://www.gnu.org/software/emacs/manual/html_mono/efaq.html#Origin-of-the-term-Emacs

    5:Richard Stallman, "My Lisp Experiences and the Development of GNU Emacs", speech at the International Lisp Conference, 28 Oct 2002

    6:Stallman, Richard (March 26, 1981). "EMACS: The Extensible, Customizable, Self-documenting, Display Editor" (Technical report). MIT AI Lab. AI Memo 519a.

    7:Stefan Monnier, Michael Speber: "Evolution of emacs lisp", Proceedings of the ACM on Programming Languages 4 (HOPL), 1-55, 2020

    8: Retrocomputing - MIT CADR Lisp Machines, http://www.unlambda.com/cadr/

    9: Multics History, https://www.multicians.org/history.html

    10: Bernard S. Greenberg, "Multics Emacs: The History, Design and Implementation", https://web.archieve.org/web/20000819071104/http://www.multicians.org:80/mepap.html

    11:"Unix Spoken Here / and MS-DOS, and VMS too!", BYTE (advertisement). December 1983, p.334. https://archieve.org/details/byte-magazine-1983-12/1983_12_BYTE_08-12_Easy_Software?view=theater#page/n335/mode/2up/search/unipress+emacs

    12: GNU Emacs Copying Permission Notice, https://github.com/larsbrinkoff/emacs-16.56/blob/master/etc/COPYING

    13: GNU Emacs NEWS 1.17, 26-Mar-1986, https://git.savannah.gnu.org/cgit/emacs.git/tree/etc/NEWS.1-17

    14: Wikipedia. https://wikipedia.org

    15: "The Lemacs/FSFmacs Schism", Jamie Zawinski, 11-Feb-2000, https://jwz.org/doc/lemacs.html

    16: "Information for Maintainers of GNU Software", Free Software Foundation, https://www.gnu.org/prep/maintain/maintain.html

    17: "XEmacs vs. GNU Emacs", XEmacs Website, https://www.xemacs.org/About/XEmacsVsGNUemacs.html

    18: "XEmacs is Dead. Long Live XEmacs!", Steve Yegge, https://web.archieve.org/web/20080501053355/http://steve-yegge.blogspot.com/2008/04/xemacs-is-dead-long-live-xemacs.html

    19: The GNU Emacs FAQ, https://www.gnu.org/software/emacs/manual/html_mono/efaq.html

  10. 答辩

    从 GNU Emacs 的历史看开源软件应当如何发展

    芦毅 李卓凝

    指导老师:朱峰朱老师

    1. 选题缘由

      5800 16000

      • Emacs 自身优秀,历史最长, GNU 开端,活跃开发,意义丰富;

        GNU Emacs 是一款高度可扩展、可自定义的文本与代码编辑器,是 Emacs 编辑器家族中 最主要的一支。 GNU Emacs 及 Emacs 编辑器家族的历史可以追溯到由理查德・斯托曼 ( Richard Stallman )和盖伊・史提尔( Guy Steele )于 1975 年共同开发的最初的 Emacs ,而在 1984 年,Richard Stallman 重写了 Emacs 的全部代码并使用 GNU 通用 公共许可证发布,称之为GNU Emacs。目前 GNU Emacs 由 GNU 计划持续开发与维护,采 用 GNU 通用公共许可证第三版( GPL v3 )发布,至今仍处于活跃开发中。

        GNU Emacs 以其优秀的项目管理模式、高度团结的开发群体和高素质且活跃的社区而能够 活跃开发数十年,至今仍拥有强大的活力,是自由软件事业、乃至所有开源软件的的模范 之一。

        “神之编辑器”;

        9.png

        https://www.gnu.org/software/emacs/

      • Emacs 插件开发者、了解深; https://github.com/3vau/cnhl https://github.com/3vau/cndict
      • 热爱 Emacs ,富有美与尊重的软件
      • 软件,材料多;
      • 无竞争、翻译、好写;

        知网搜不出来 emacs

        国外有诸多研究,国内没有

        Orgmode / 统计学论文

        当事人、可信度

        白给

    2. 主要内容
      1. Emacs 历史
        • GNU 以前
          • 诞生: TECO -> TECMAC/TMACS -> EMACS

            sciteco

          • 平台移植: Multics / Zmacs (Lisp Machine)
          • 商业竞争: Gosling

            (RMS 威武)

          • GNU Emacs 诞生
        • GNU Emacs 的挑战与分支
          • 图形: Epoch -> Lucid Emacs
          • 多语言: Nihongo -> MULE (Multilingual) -> Lucid Emacs
          • Lucid Emacs -> XEmacs

            XEmacs 后期陷入分裂, 09 年停止开发;

          • GNU Emacs 变得开放进步,合并 XEmacs 的优化
        • 现代化编辑器的挑战:多线程与完全图形界面
      2. Emacs 优点
        • 软件协议
        • 优秀、创意的设计,效率
        • 高度扩展性
        • 开发水平过硬
        • 优秀社区环境、高水平社区
        • 与时俱进、兼顾传统
      3. 核心史料

        邮件列表、博客、 Wikipedia 、 EmacsWiki 、 gnu.org 、论文等等

    3. 缺点与不足
      • 时间安排与工作协调,选题

        答辩前一周才确定选题

      • 英语能力
      • 技术能力不足,材料收集不够到位

        新闻组、邮件列表

        实践较为缺乏(?)

      • 缺乏创新,社区的老生常谈,搬运工

        班门弄斧啊QAQ

    4. 总结

      感谢老师与同学,感谢可爱的队友,我会继续努力把它写完233……

      感谢 Emacs China 社区的各位前辈

      欢迎使用伟大的 Emacs !

      欢迎使用 Emacs 中文语法高亮插件 cnhl 和 Emacs 中文辞典 cndict !

      https://github.com/3vau/cnhl https://github.com/3vau/cndict

      辛丑咏emacs

      铸炼琢磨五九年,春秋一去尔一坚。

      力出盘古开寰宇,朗若云神御九天。

      四海芳邻常伴侧,玲珑情虑每增添。

      料得此心君身系,无奈今生爱恨间。

1.5. 旧诗

1.5.1. 初八忆初中

  课罢校园后,悠然山水前。

  透书窥五柳,自在会七贤。

  卧任碧空浸,仰观重嶂连。

  良辰终有尽,永乐待何年。

  

          (中华新韵)

1.5.2. 初十怅徘徊

  桃李谢红粉,海棠旋碧流。

  落春明岁起,去日再难留!

  醉恐故物尽,醒忧清梦休。

  何年拨锦瑟,笑叹少痴求。

  

          (中华新韵)

1.5.3. 悼袁老

  朝闻袁老去,激泪霎千行。

  民痛失慈父,国哀折栋梁!

  千秋食伟业,万顷登流芳。

  国士安归去,吾人定自强!

1.5.4. 浣溪沙·十二夜雨晴

  楼外豪泼摧帘窗,楼中长啸正激扬。慰他悲语落潇湘。

  畅饮舒风云卷尽,醉闻润水柳枝芳。赠侬如此好天光!

  

          (中华新韵)

1.5.5. 十三登水长城

  其一

  盘驱绕万丈,长驶尽千山。

  雁叫猿鸣里,惊观长镇繁。

  明朝卒戍苦,今日客游欢。

  蛮野自巍莽,文明更慨然。

  

  其二

  五月金泽映水芳,

  万年青嶂没湖茫。

  凡来尽道清波好,

  千载谁怜栋木亡。

  

  其三

  连云极目栈,壮士远怀雄。

  俯叹比西子,仰息回日龙。

  有怜嘉木燕,不厌细石虫。

  愿此无垠景,随侬上九重。

1.5.6. 十三见栗怀史

  锋锐青芒手易伤,

  甜香黄栗腹难扛。

  谁得撷取金珠入?

  朴智更应胜饿肠。

1.5.7. 十五咏花

  秋冬复春夏,暮雨换朝阳。

  冷雨洗尘色,炎光焕玉妆。

  能禁枯漠旱,不畏绝巅霜。

  朗笑石沙卷,清歌冰雪扬。

  有坚开秽臭,无怨隐丛芳。

  生迹悦心目,遗英哺果粮。

  浮能承咏笔,残可拭啼行。

  问我生何愿,安成君寸肠?

1.5.8. 二十咏八宝粥

  入水粮八色,开锅气一香。

  刚皮销烈火,软糯献融汤。

  细嗅难分味,抿尝有各章。

  堪铭万心志,炼此英琼浆。

  

           (七阳)

1.5.9. 初四自嘲思

  昔每矜博广,今方醒鲁愚。

  惶惶觅安苟,畏畏与川徂。

  寒院知行语,三更殆罔徒。

  终明世无愿,天命各须沽。

  

           (七虞)

1.5.10. 初五乐雅集

  聚欢槐雪社,满笑淮香楼。

  三尽山河味,一浮天地舟。

  乐观花客醉,慢捧画云悠。

  兴会无前里,峥嵘且待稠。

  

          (十一尤)

1.5.11. 初十记思半首

  长风托木叶,逝者卷枯荣。

  零落绝兰茝,迷离难自雄。

  茫茫无谓蚁,寂寂久孤鸿。

  淋透三秋雨,未脱一妄庸。

  问君何处去,偃蹇岂能雍?

  世若崩峦滚,何方是我从?

  嗟卿何日作,碌碌岂能容?

  世若奔轳转,何年是此终?

  百寻不得解,郁郁悱心胸。

  恨把辞章遣,聊将天理穷。

  明明河汉火,历历夏殷松。

  此意难分明,饶托寸管中。

1.5.12. 初六感集社

  近冠犹懂习翰墨,寸管悬悬总二更。

  一捧沁脾天作韵,不离辗转绕梁声。

  未潜书海至终老,敢请文心许此生。

  幸与同窗多济路,长歌一奋不孤征。

               (八庚)

1.5.13. 廿九记安装linux

  莫疑繁此为何妨,夜觅阑珊守尺方。

  代码行行映人肃,个中点点入心长。

  肠肥恶见三牲貌,腹馁唯知一口张。

  当敬人间才与苦,方成诸夏景还长。

  (七阳)

1.5.14. 初三感夜

  夜望灯家胜莽林,少读凿壁复难吟。

  广厦一览悟甘苦,课本百读明旧新。

  当祭千秋累白骨,终成九载化仁心。

  长歌何幸得生此,窃禄无酬愧不禁。

1.6. 计划

1.6.1. 学习

  1. DONE 周末的作业 <2022-05-22 Sun>
    1. DONE 超几何分布
    2. DONE 超几何分布与二项分布
    3. DONE 剩下的地理题
  2. DONE 周一的作业
    1. DONE 语文练习册和文言七项
  3. DONE 业余党校感想
  4. TODO 数学导数与不等式 <2022-06-18 Sat>

1.6.2. Emacs 社区

  1. CANCEL 了解 odt, 让 eof 读写 odt 文档 <2022-05-22 Sun>
  2. CANCEL 用 artist-mode 重写 eof ,先实现文字对齐和缩进 <2022-05-23 Mon>
  3. TODO 完成 magick

1.6.3. 消遣

  1. DONE 美化 Emacs <2022-05-22 Sun>
    1. DONE margin 的按需处理

      这样就可以扔掉 olivetti 用 org-indent-mode 了

    2. CANCEL 再次尝试 org-modern

      好丑,还不如不加

  2. DONE 整理 emacs 配置 <2022-05-22 Sun>
    1. DONE 学习前辈的配置,找些好用的包
    2. DONE 整理配置
  3. 毛玉线圈

1.7. 随笔

1.7.1. 《万历十五年》读书笔记 <2021-08-27 Fri>

《万历十五年》读书笔记

随读随写,内容随心。

我曾特意了解过明朝的历史,对其中很多部分也知其一二。当我最初看到万历清算张居正、和朝臣闹别扭时,我很难以理解,后来我才发现,我这是被那史书感染了,没把万历当人……也不怪我,大明二百余年,历朝文官,也很少有把皇帝当人的。

我觉得这可能是我们这个国家体量庞大所必然带来的问题。礼治、伦常,这一系列中华文明的特质,都应该有国家体量超出政体束缚的原因在。

欧洲小国君主专制就从来不用礼法,他们的礼和我们的礼完全是两个性质的东西——不过他们的专制也不长久。这不全是没有礼治的问题。

我们的礼治有一点特长,那就是稳定性奇高。万历四十年不上朝+朝廷严重缺官+朝争打出狗脑子,这换历史上哪个文明过来都是分分钟亡国,我们中国不一样,顶多是贪腐多了点、土地兼并快了点,中底层该怎么转还怎么转,跟没事一样。

不过,至少16世纪后,它的缺点已经完全盖过了它的优点,光是想想就让人心累。这不是政治作业,就不展开了,反正都家喻户晓。

我们要从这荒诞的历史里提取出规律来,让我们看向未来的眼睛时刻保持清明;提取出知耻而后勇的精神来,让我们不惧一切地前进下去。

我读过儒林外史,写的很有意思,对迂腐儒生的讽刺十分到位。我觉得正德以后大明这一帮文官都跟那里面的人差不多滑稽。

当然,用现代人的思维方式来评判古人,用本朝的剑斩前朝的官是没啥出息的。

有时候真理反而是最简单的。欸,这个人不丁忧,他破坏礼制;这个人他考试出了个什么什么题,他狼子野心;这个人亲戚和谁结婚了,那个谁和谁有个什么关系,所以这个人和那个谁不清不楚……你们说的有道理,于是能使国家强大的人被你们赶下台了,能使国家富足的人被你们赶下台了。所有能动你们饭碗子的人都被赶下台了,剩下个只会耷拉着个袖子的皇帝被你们供着演过家家,国家完了。

所以理想的政治组织理念应该是,谁在哪里做事利于人民就让他到哪里做事,怎么做事利于人民就怎么做事。那些鸡毛蒜皮,有关碍,但真的没那么重要。至于这帮在自己巴掌大的天下里自我陶醉的文人,就更不值得同情了。

人在道德和法理的高地上兜兜转转,最后回到了最原始的本性。从哲学的角度说,这可以有好几种富有哲理的解释,但从现实的角度说,生活在这个世界上的是人,不是法理,不是道德。文明的目的是让人的生活更幸福,而不是让法理和道德——或者其他的一切东西——得到彰显。无数文明和国家在这上面栽过跟头。这应该是一扇窥探未来社会和文明前行方向的窗。

我喜欢文学,因为它允许我思考这些关于未来的、有趣的可能;但我可能不擅长学习文学,因为我喜欢思考文明,不喜欢思考人。我可以面对大明抒发连篇累牍的感慨,但面对囿于臣僚的万历、满心倔强的正德、大起大落的正统,还有很多很多人,面对他们,我却是一句话都说不出。

文官的心口不一在大明是个很滑稽的现象——准确说大明的文官本身就挺神奇的。他们和大明富有特色的皇帝一样让人恨也不是爱也不是。你说他们有骨气,他们能和魏忠贤干出那等鬼事;你说他们没骨气,他们能在太和殿用拳头砸死佞臣,能指挥着九万老弱妇孺战胜蒙古铁骑。大明这个朝代本身就有一种令人怎么也难掩唏嘘的魅力。

这种持续数百年的“道德滑坡”,应该是一个很明显的警告:他们所生活的这个国家出了问题,作为国家的统治阶级,他们的思想和理念出了问题。

迈向近代的路崎岖而漫长,它并不像许多人认为的那样,和陈腐的古中国毫无关系。相反,我看到,在另一种意义上,它和在近代的浪潮中挣扎的古老中国有着剪不断、理还乱的纠葛。或许从中国的角度出发,我们能探索到文明进步的另一面。这将是一个很有价值的课题。

1.7.2. 论语6-10章读书笔记 <2021-08-27 Fri>

《论语》读书笔记

随读随记

6.1 我们汉语中的含蓄表达总是很有意思,像这个“南面”,与其类似的用方位词的,比如我们常见的左右迁什么的。这种含蓄或隐晦在各类官职相关的事情上见的比较多,像太常卿的太常、鸿胪寺的鸿胪,哦当然还有那个寺——这些东西使我们的古老文化散发出一种真正的、富有底蕴的诗意美。这种美可能是地球上独一份的了。我正是因此而对我们的文化深深着迷而无法自拔。

6.2 这番话的意思在我们生活中倒很常见,大概是在说无为而治和应付差事的区别?千年过去,这种简单的道理早已行之天下——但圣人和众生的区别就在这里了。

6.4 道理我懂,但子华作为使节,冉有给他五秉之粟,让他十分体面地出发是不是有其考量呢?那孔子拒绝是不是又有更深一些的考量呢?这大概就是我们的论语译注上没有作者个人看法的原因吧。和孔子的主张背道而驰了呢。总有一些事是以圣人的天才也无法预见的。

6.5 这句话有没有成为过某些士绅做不好的事时的遮羞布呢?

6.6 我一直很羡慕但又学不来的就是这种古朴自然的修辞

6.8 这句话可以说是春秋时普遍的行政治理模式的惊鸿一瞥了,进而能印证当时的社会发展情况和部分社会组织结构。

6.11 我身边很多人避贫穷(或者低收入)如避世界末日,为此不惜一切代价。他们有很充足有力的理由,我没法反驳,我也不知道该怎么办。孔子大义凛然的话并没有给我什么帮助。当然,我们也不能强求老人家太多,现代人的事还是得靠现代人解决——这几年我们的社会好像慢慢探索出了一条解决之道,但可惜那种理念还不够成熟、不够普及,也没有被系统地总结,但已经足够好了。我们拥有光明的未来。

6.12 反正我的理念是遇到自己想解决的问题,无论有多难都会先尝试去做,最后遇到难处往往都能因为沉没成本太高而硬着头皮干下去,最后干成……有的时候人可以故意骗自己,让自己在某件事上缺乏远见——这不是坏事,而且非常刺激,哈哈哈哈。

6.13 不知道孔子看到千年后拿着自己的话搔首弄姿的酸书生会是什么感想……孔子恐怕也预见不到自己一手创立的儒家思想在壮大到一定程度后会发展成一个什么模式——社会学家的任务大概就是研究这个。多么令人欣然神往啊。

6.17 这是体现孔子对自己学说的自信吗?

6.21 和一个人讨论学问,应该考虑他对这方面的了解程度,不要和人讨论对方完全听不懂且没用的知识。但把人划分阶级就没意思了。

6.26 “欺之以方”和“罔以非其道”的区别,大概是后者涉及了道德,而前者没有吧。

6.30 看来至今仍有很多人以为孔子的主张是让每个人都成圣人。

7.8 这是现代化教育所欠缺的,也是时代的无奈之处。所以我更喜欢被需求驱动着学习,这样学得快乐,也记得住——可惜高考考的不都是我愿意学的。人类缺乏远见,就要付出代价。

7.9 7.10 尊重他人,也尊重自己,这是幸福的前提——大概也是仁的一部分吧。

7.11 看孔子教育子路就是一大型吃瓜现场啊!这绝对是论语里最有趣的部分之一。

7.12 当今社会,财富还真是可以求得的——这大概是孔子的某些主张难以使很多现代人认同的原因之一吧。

7.15 上古时代做官对人的吸引好像没那么大,从洗耳恭听到伯夷叔齐,很多人都相当不愿做官,这一定是有其历史原因在的。

7.17 孔子五十岁才能读懂《易》中最深刻的道理吗?它的道理是什么呢?适不适合现代社会呢?我要不要也去了解学习呢?

7.19 这才是一个人面对时光流逝和衰老的正确、理想态度,是我们这一代为此而焦虑的人应当学习的

7.20 孔子为什么不把自己考证的上古时期的历史像司马迁一样写出来呢?他没有预见到这些历史和文化会在未来断绝吗?不过孔子也并不是专门考证历史的史官,留下一部春秋已经是无与伦比的丰功伟绩了。

7.26 所以我们为人要活在当下,坦坦荡荡地生活。

8.2 这几个“无礼”对现代人也有十分重要的借鉴意义。从这之中也可以看出孔子所主张的礼并不是中古时期的礼法,而是更偏向于现代意义的礼——当然,也不是家长夸孩子懂礼貌的那个礼。

8.9 论语中最喜闻乐见的经典篇章。如果按照“民可以乐成,不可以虑始”的意思来还勉强说得过去。不深究了,没法究。

8.13 此天下无道是不是有统治者无道的意思?

8.17 我们的学习很多时候貌似只符合了前半句。

9.24 现在很多人不是这样吗?包括我也是。所以说到的事情就去做,不愿意也可以坦坦荡荡地表达出来——生活不易,我们无妨直率些。

1.7.3. 军训午上书 <2021-09-23 Thu>

尊敬的领导、老师们,你们好。 9月23日中午,食堂一层有人因不明原因,当着年级所有同学的面撒泼闹事,冲着食堂工作人员破口大骂,其语言极为粗鄙恶心,态度极度恶劣,严重破坏学校食堂正常秩序,对食堂同学的正常生活和健康成长造成巨大恶劣影响。十四班王冬毅同学为维护食堂正常秩序,维护同学正常学习生活,制止造成巨大恶劣影响的不文明行为,勇敢地上前劝阻此人,停止其不文明行为,还给同学们一个正常的食堂秩序,却被此人恶意构陷。此人试图以碰瓷的手段将王冬毅同学牵涉入他的撒泼闹事中,从而扩大自己的恶劣影响,给校方施加压力,以达成自己不可告人的目的,为此不惜破坏学校正常秩序,恶意利用学校同学,陷害无辜同学,对学校同学们,尤其是王冬毅同学的正直良好的世界观的树立造成无可挽回的负面影响。希望校方严肃、合理处置此事,还给同学们一个清朗文明的学习生活环境,维护同学们的正常学习和健康成长。

1.7.4. 瞎写关于我和电脑 <2021-09-26 Sun>

随笔瞎写——电脑与我的学习

很小的时候,我就知道,“学习”这个词的含义从来不是唯一的——尽管我身边的所有人——家长、老师——都在拼命地试图给我灌输这样的思想。

我知道孔子学而时习之的时候神州大地上还没有数学物理;我知道每年共青团组织的青年大学习里没有解二次方程;我还知道,那些构成我人生的知识,几乎没有哪些是从听讲和写作业里得来的——不论是文是理。

在我们这么一个不算发达——至少离共产主义差得还远——的社会里,任何高度社会化的东西都会必然地背离人的类本质,逐渐变成一场灾难——换句话说,或多或少,它必定是反人类的——就像现在我们按部就班的教育一样

所以我知道,要想学到真正对我的人生有用的知识,采取那风靡全国的“正确学习态度”肯定是行不通的。在我这里,“学习”的含义必须被改变。

于是我就开始探索什么是真正的、属于人类的学习。是学习计算机——我平常其实更愿意称呼这个过程为玩电脑——帮我抓住了它的尾巴。

我四岁半接触电脑。那时是纯粹的玩,使用电脑的技巧就从玩里学到了。当初我的电脑里只有qq、浏览器和一款单机游戏,其中浏览器专门用于上4399——00后的童年回忆。我爸妈都从来不用电脑,给我计算机“启蒙”的叔叔只带了我很短的时间,电脑的基础使用就被我在一次次玩和探索中学会了。这算不算学习呢?

后来上了小学一年级,发生了一件相当糟糕的事情——老师让我们画小报。我敢打赌,小报绝对是小学时代最非人哉的“高大上教育面子工程”之一。画画太菜的我就抱上了电脑的佛脚——老师要求两个星期交,我就学了两个星期的word,然后我六年的信息课就再也没听过,顺带在班级展览墙上拿到了一席之地。这算不算学习呢?

再后来啊,小学二年级,我们学校带我们出游。家里刚好有个数码相机,我就拿着相机一路狂拍。拍完回来,我发现,欸有些同学的表情好**的有意思!于是我就花了一个周末学了学当时刚出没多久的美图秀秀,把自己p的表情包扔进我建的同学群。那是我最初的p图。这算不算学习呢?

小学三年级,我们又换新花样啦!这回老师把我们分组,让我们搜集语文课文的背景信息,课前轮流上台展示。听着前面同学捧着个手写稿干巴巴地念,我在下面直打哈欠。一想到我自己也要抄那么长的材料,我就一个头两个大。于是我就花了几天课余时间把PowerPoint的每个功能都玩了个遍,在省去写字的功夫之余成为了全班最靓的仔。现在我做PPT还是在用那些知识。这算不算学习呢?

小学五年级,那是2015年,我人生里最重要的一年。互联网黄金十年已逾一半,新媒体开始飞入寻常百姓家。看到各大视频up主们在各种视频网站上风生水起的样子,我心里酸得要命:我为什么不也去试试呢?于是我去百度了一下“怎么做视频”,又花了一星期的时间找资源并破解了Premiere CC 2015视频剪辑软件——那是我当年干过的最难的事,我几乎就以为自己要放弃了。用了半个钟头学会怎么把视频剪成段再拼起来——就像摆弄纸条一样——后,我零零碎碎地做了半年多视频,没有任何成就;但现在,我成了年级宣传部里不可或缺的干员——一切的一切,也不过是那一周加半个钟头。这算不算学习呢?

你当然可以说这些都是在玩,当然不算是学习。那让我们快进到去年十月。

那是个阳光明媚的下午,我去食堂吃午饭。我看到有一个程序员给自己搭了个个人博客,没有依赖任何平台。我心里又泛起酸水来:我也想要一个!然后我就这么去做了。我发现网站的服务器竟然用的不是windows系统,这个系统没有鼠标和桌面,需要靠一行行命令来操控!然后我就去学这个系统,我用了一个月学会了;我又发现网站是代码写成的,我就去学相关的编程语言和服务框架,我用就半个月学会了;十一月,我的个人网站搭起来了,同时我用我新学会的软件做了高效的个人管理,把假期作业安排得明明白白,并且用我新学会的系统技术修好了我家两太快报废的2012年的老电脑,让它们焕发新生;这还没算上我学完还没派上其他用场的两门编程语言。这算不算学习呢?

所以我深刻地明白,为什么孔子说“知之者不如好之者,好之者不如乐之者”,为什么马克思说自由自觉的劳动是人的类本质,为什么毛主席说人的认识由实践中发生——我认为,这样的学习才是真正的学习,是有益于人的生命与生活的,本质的学习。

我还会继续在这条路上探索下去。

1.7.5. 爱与坚守 <2021-11-28 Sun>

这周的语文课讲了苏武传和屈原列传,苏武和屈原为国守节、为国尽忠的坚守令人震撼。这样的坚守肯定不是这些伟人所与生俱来的本领,想做到它,必然需要某些东西等作为支撑。就这一方面,我愿在此说出我浅薄的想法。

要探究坚守,我们必须从它的源流出发。坚守由何而来?我认为是爱。屈原深爱他的祖国楚,因而能为之抱存薪火,至献出生命;苏武深爱他的祖国汉,因而能为之茹毛啮雪,十九年如一。尽管白发苍苍、形容枯槁,但他们心头火热的爱让我看到了他们可爱的心。

道理我们都明白,但到底是一种什么样的爱,才能铸就如此铭流千古的坚守呢?

我认为这种爱一定是自然的、真诚的,像爱美丽的花朵一样,不允许有半点的扭曲和造作;这种爱一定是热忱的,像爱恋人一样,永不忘却,永不背弃。这样的爱一定会长久地盘桓在我们心头,赋予我们的生活以崭新的意义。我认为,这样的一种爱是真正的坚守所必要的,甚至是主要的——爱是如此神奇,能让人拥有创造奇迹的能力。如果没有爱的话,我们为何而坚守呢?

说了这么多,我们该如何做到这种爱呢?我觉得我们可以先从爱自己出发,推而广之——因为爱自己最是真实可感,不易被天下那些纷繁的道理所迷惑,从而失去自己的本心。而对非自己可感的事物的爱,譬如说对祖国——就极易被各式各样的道理所迷惑,失去那份本真。

爱自己,首先要做到的是自尊、自信。这自尊、自信不是那种被滥用所污染的、空洞的自尊自信。你看,事实上,是我们的生命赠予我们以无尽的美好。窗外的风景、身边的伙伴、酣畅的游戏,乃至无边的遐思、专注的努力、远大的前程,一切的一切,都是生命赠予我们的珍贵礼物。生命是什么?生命就是我。时常把手按在心口,听听自己的心跳,忘掉别的一切,这样的生命不值得我们去爱吗?

当我们的心中常萦绕心跳的旋律时,一切都会变得明朗而真诚:路边的草木好像多了一丝灵气,身边的人好像多了一份灵魂,赠予我们美好生活的祖国也会真正进入我们心底。那,我们会愿意为这份爱而坚守吗?

1.7.6. 悲剧是怎样发生的 <2022-03-05 Sat>

这周语文课讲孔雀东南飞,著名的爱情悲剧。老师课上提出了一个问题:如果我们有机会穿 越回去交涉一番,我们该如何制止这幕悲剧的发生?

大家的答案五花八门,从劝府吏的,到劝太守的,一应俱全。的确,这起悲剧中,他们所有 人都脱不开责任。他们如果泉下有知,听到后人的评价,不知会作何感想。

课上便有同学说,这起悲剧是时代的局限所造成的,不过直接导致它发生的是故事角色们的 性格缺陷。但是这些性格缺陷本身也是时代所造成的——如果焦仲卿也有一次穿越的机会,来 附中听上这么一堂课,估计乐府双璧的“双”字就要去掉了——所以归根结底还是时代的问题。

所谓唇亡齿寒,我们看到仲卿兰芝为时代所困,自然也会想到,自己也终会有为时代所困的 地方,而这其实并不鲜见。为此,我曾经伤心过,又自以为找到了正确的应对方法,结果却 发现原来是我太天真,然后再为此迷茫——啊,“迷茫”这个词已经被妖魔化了,还是用“发呆 走神”更好一些,因为我没有如屈原那般,把自己的发呆走神写成传世巨著,所以它当然只 能是发呆走神了。这也是我为什么如此敬爱屈原的作品,因为我能够(至少部分)感同身受。

不过幸好,我还是有一点比得过屈原的——我还没选择“从彭咸”。屈原与仲卿兰芝的故事,固 然是悲剧;而若是简简单单从了彭咸,那也是悲剧,但更悲哀。

《孔雀东南飞》中充斥着悲剧,但诗中仍有一句话能安慰我——文末那句:“多谢后世人,戒 之慎勿忘”。

——读到这句话,我忽然平静下来了。

也没什么别的感觉,千般念头一瞬间泛起来,又在眨眼间碎掉。什么东西从里面飞走了,我 没抓住。回头一看,留下的还是一池无奈的平静。

悲剧就是这样自然而然地发生的。我们该怎么办呢?

我想起了那句“把握住这份荒谬”。那我便不去解答它了。

1.7.7. 《项脊轩志》读后感 <2022-03-26 Sat>

读完《项脊轩志》,我产生了两个思考,一个是关于人的发展,一个是关于人的目标。

一个人怎样才能做到全面发展?一个关键的要素是,他的生活必须是向上走的,在他的信念 中是向上走的。这会加强他的这种信念,而这种信念将同时支撑他全面发展,而不是只在某 些领域做出一些自我安慰般的成就。

那如何拥有这种信念呢?很不幸,除了仰叹“时也命也”外,好像也没有别的什么办法——处在 这样一个欣欣向荣的时代里,我们是幸运的,有能力在全面发展的道路上多走那么一点点; 而归有光明显就没那么幸运。

不过,尽管我们已经知道这条道路注定长而坎坷,长到我们注定穷尽一生也难能看到希望, 但我一直拥有这么一个信念:哪怕我在这条路上摔再多次,哪怕我离这条路的终点越来越远, 当我有能力再次迈开我的步伐,我也一定会向那个方向走去。这不是一个多么值得肯定的想 法,这只是一个幼稚的执念,一个拿回我本应拥有的东西的执念。或许这已经偏离我的初衷 了?

八次落第后的归有光,会是怎么想的呢?

另一个问题,我们一生都在追求些什么?仔细列举一下所有人们所毕生追求的,就会发现, 它们都不可避免地和“永恒”这个词搭上了边——或者,不说永恒,至少是长久,远超出自己这 一生的长久。归有光想要复兴家族也好,我们学生想要通过高考“力争上游”也好,这些看似 和“长久”搭不上边的,其实在我们的潜意识里都和它有隐秘的联系——仔细一思考就会发现相 当明显。即便是马克思,他在奋笔挥下《共产党宣言》时,肯定也是希望人类不断、不断地 进步的。

但我们也都明确地知道,世上唯一不变之事就是变化本身。所以我们不过是在给自己找一个 虚幻的希望。但谁不这么做,他就会掉入虚无主义的无底洞中去。

这时我们该怎么办呢?一个好的选择是,在人生的旅途中不断地全面发展下去。这样,我们 将为自己打造一个变幻着的永恒,它将形影不离地陪伴在我们身边,直到我们离开这个世界, 留下人类前进的一步。

1.7.8. 清明随笔 <2022-04-05 Tue>

原先我们家还祭祖,大概还是在16、17年的时候吧,后来慢慢就不祭了。现在我妈看见路边 烧纸的人甚至不太适应。不过这也无妨,毕竟不是每个人都把人生当作一种体验。

日子就这样过着,冬去春来,一年又一年。昨晚梦见小时候打红警,慢悠悠地打,排兵布阵 都仿佛在搞艺术。蛮不情愿地醒来,想一想,自己现在好像也没失去这样打一盘红警的能力。 由此可见,我这颗赤诚的心是没有变的。只不过这颗心已经跟了我多年,除了偶有罕见的分 别,它总是在默默帮衬着我,毫无变化。可能正是因为毫无变化,所以它现在有些惘然,看 着清明的春光,它承载着我如此多的记忆,格外地不知所措。

清明的记忆,有远的,也有近的。最近的当然就是写诗。去年清明我写了些什么没有?其实 它就在我手机里,可我就是懒得去翻。我的第一部手机是三星的 Galaxy 系列,它有个功能, 在每个节日自动新建一块额外的主屏幕,上面写有祝福语,和过去每一年的今天我收到的短 信和通话。15年刚拿到手机——那是清明之后不久,春光灿烂——我觉得这个功能挺没用的,转 眼就去鼓捣新注册的微信去了。去年最后一次打开那部手机——记得好像是植树节,离清明还 有一段距离,翻开那一页,意外地看到了17年的短信记录。虽然是没什么营养的商业广告, 看完却也挺不是滋味的。

说到商业,2016年的三四月,万网给我的邮箱发过一波云服务器的广告——那时侯我正在四处 想办法搭 Minecraft 服务器,也是难为我一小五的可怜孩子——被我的新浪邮箱拦进商讯信 息里了。去年春天——清明假期左右,我在玩一个古老的邮箱客户端的时候,偶然发现了“商 讯信息”这个分类——在网页版新浪邮箱里它一直是折叠的——,好奇地打开,发现了那封阿里 万网的广告,看到那封信中颇有时代感的CSS——可以认为是一种排版——,再登录去年冬天新 买的阿里云域名的控制台看一眼,两相对比,不胜唏嘘。

这些,可能是我们这一代人独有的记忆吧。

1.7.9. 漫说 Emacs

  1. 从一个文科生的角度说说为什么要用 Emacs

    Emacs 是 GNU 开发的一款编辑器,今年它已经四十五岁了,它的年龄可能已经超过了在看 这篇文章的大多数人。“芳与泽其杂糅兮,惟昭质其尤未亏”,这四十五年间, Emacs 见证 过数不尽的辉煌与坎坷,而它优秀的设计与品格在四十五年的洗炼中愈发昭灼灿烂。

    在今天的中国, Emacs 作为一款以“代码编辑器”为出身的软件,并不为大众所熟知。每位 了解它的人都为它的埋没而悲哀。我们的世界本就松散而零落,人们听到一个陌生的词汇便 把它当作刀山火海一样避开,为此不惜错过无数可能的美好;更何况本就如鸷鸟般不群的 Emacs 呢!

    但是,作为掌握知识的人,我们的身上天然肩负着彰显美好的使命。如果你愿意,请听我讲 一讲 Emacs ,这个传奇的软件,是以怎样的精神感染着一代又一代人。

    如果你从未接触过自由软件运动的人,你可能不了解: Emacs 是一面精神旗帜。每当讲起 Emacs ,我们总会不可避免地提到自由。媒体们总是以浅薄的语气“谈论”这个词,但无论他 们如何丑化、异化它, Emacs 所彰显的自由和它们尽皆不同:它不是虚伪的、资本主义式 的,而是马克思式的自由,夭夭如也、充满希望。

    在道德上, Emacs 是自由软件的模范:它将自己视作全社会共有的财富。它的一切,那些 世界上最天才的黑客们付出心血所著的程序,全部无偿开放给所有人使用,程序的源代码也 无私交予大家学习、分享。这种有类于俯首甘为孺子牛的精神,正是自由软件运动所提倡的。 仅这一点,便值得许多人将 Emacs 引为知己、形影相伴。

    “地势坤,君子以厚德载物”, Emacs 的世界触手可及、充满可能。探索 Emacs 时的那种悸 动与惊喜,没有尝过的人是永远不知其味的。那就像孩子的手抚上一张干净的纸,明明一无 所有,恍惚间却又是整个世界。雪白的纸、雪白的 Emacs ,你和它都来自那已经模糊的过 去,那个脸上还没烙上时光的年代。

    Emacs 是(目前)世界上一切写作软件和编辑器的超集。它能提供那些

    宛如孩子抚上一张空白的纸

    他们在生命的实践中学会了爱与自爱

1.7.10. Linux 王国传 <2022-05-03 Tue>

很久很久以前,在一个距离人类世界说远不远、说近不近的地方,有一个 Linux 王国。其 实叫它 Linux 王国并不很准确,“ Linux ”只是王国的组织形式而已。而“ Linux ”甚至也 只是这个组织形式的简称——它的全称叫“ GNU/Linux ”。

相传,当年 Linux 王国伟大的开国君主—— Linus Torvalds 在争战天下、开疆拓土时,正 是一群自称 GNU 的人,作为她最信赖的伙伴和最有力的臂膀,效死沙场、甘当马前,与 Linus Torvalds 共同打下了 Linux 王国的万里江山。

可为什么如今大家提及 Linux 王国时,往往都忽略了 GNU 的名字呢?这当然不是狡兔死、 走狗烹的戏码,只不过是大家嫌这个名字太长,懒得念了而已。这就可以看出来,生活在 Linux 王国的,究竟是一群什么样的小懒虫了。[注1]

Linux 王国的大家淳朴善良,从来没有什么坏心眼。她们的懒也不是那种坏坏的懒,而是一 种脱离低级趣味的懒。

举个例子吧,有一天, Linux 王国要选举新的“ Init ”——大概类似于首相或总理一类的职 位吧。于是老 init sysvinit 和新候选人 systemd 之间举行了一场比赛,比比谁能让大家 过的更懒一些。 systemd 大手一挥,自己把许多事情包办了,于是大家就开心地让 systemd 当Linux王国的新 init 。而老 init sysvinit 呢,也没有彻底退休——有些领主不 太喜欢systemd,觉得她管得实在太多了,于是就单独聘请 sysvinit 治理自己的领地。[注 2]

Systemd 同学的工作呢,说忙不太忙,说闲也不算闲。她最重要的工作是每天早晨叫王国的 大家起床。

清晨,太阳公公刚刚升起,就会伸出它暖洋洋的触手,把几个负责打理这片大地的小可爱们 戳起来。这些被戳起来的家伙们是 Linux 王国“内核”——大概类似于宫廷或国务院一类的地 方吧——的成员,她们从自己小小的居所中一拥而出,迅速地把整个王国简单打扫一遍——因为 现在大街上还没有站满了人,所以打扫起来还是比较方便的。准备工作完毕后,她们就会 找到 Systemd 同学的家,让 Systemd 先生叫其他人起床。[注3]

Systemd 起床后,便会伸出她长长的手指,一根指头戳醒管理王国境内大部分机器的udev 先生,一根指头戳醒王国的门卫 logind 先生,一根指头戳醒负责记录王国一切大小事务的 史官 journald 先生……整个王国就这样慢慢地被 systemd 长长的指头戳醒了。大街上逐渐 变得熙熙攘攘,王国也就迎来了新的美好一天。[注4]

此处暂先不表,且说内核的成员们,她们才是王国最可怜的孩子,每天从早忙到晚,晚上倒 头就睡,从来没有放松的时候。王国热闹起来后,她们的活不仅没有变少,反而更多了。她 们都需要做些什么呢?诶呀,那可太多了,给我三天三夜都说不完。我就举几个见微知著的 小例子吧。

内核中有一名叫做“调度手”的员工,专门负责给王国的大家送饭。 Linux 王国很神奇,他 们不畜牲畜,也不种粮食。那王国的大家每天都吃什么呢?只能靠内核每天给大家送饭了。 那内核的粮食是从哪里来的呢?其实谁也不知道。内核里有一名敲钟手,她的任务就是每过 一段时间敲一下手里的钟报时;而她每敲一下钟,内核的粮仓里就会充满粮食,谁也不知道 是从哪来的。而调度手的任务,就是负责把粮仓里的粮食分给王国的大家。[注5]

这是一项十分严峻而繁琐的工作,需要同时拥有过硬的身体素质和数学能力。如果身体素质 不行,不能在敲钟手下一次正常敲钟前把粮仓里的粮食发放完,敲钟手就要被迫等待调度手 发放完才能敲钟,结果就会耽误整个王国的运行;如果数学能力不行,不能快速估算出王国 里谁的劳动强度高、需要吃更多的饭,就会导致有的人吃的太撑,饱食终日无所事事;有的 人吃的不够,长期饥饿甚至饿死。这些都是很严重的问题。[注6]

过去,内核一般会聘请 cfs 同学做调度手。她的全名叫 Completely Fair Schedular , 是一位非常严谨的同学,拥有过硬的数学素养,可以基本确保粮食的分配绝对公平。但她实 在是太严谨了,哪怕在王国的劳动人口很少的时候,也坚持要用一套复杂的算法来确保分配 的公平性。所以现在内核有时也会聘用 bfs 同学做调度手。[注7]

Bfs 同学的想法比较简单,她只会用一种很简单但很有效的方法快速把粮食发完(事实上, 她的全名—— Brain Fuck Schedular ,脑残调度手——也一定程度上体现出了她的这种处世 哲学)。在王国人口不多、且阶级划分明显的时候,这种方法相当有效;但如果王国人口众 多, bfs 的小脑瓜就处理不过来了,还是得 cfs 出面救场。这样,你就总是能看到一道来 无影去无踪的身影穿梭在 Linux 王国的大街小巷间,送给每个人恰到好处的美味佳肴,实 属一道不可多得的风景。

(未完待续)

注:

  1. 对于自由软件运动而言,这其实是一个很严肃的话题。参见 GNU 官网对于 Gnu/Linux 的说明。
  2. 参考:https://bbs.archlinux.org/viewtopic.php?pid=1149530#p1149530
  3. Linux 的实际启动流程比这要复杂的多,我所叙述的不一定准确。这方面网上的资料是 相对较多的。
  4. 说 systemd 启动了 udev/logind/journald 等进程并不准确,因为它们更多的是作为 systemd 本身的一部分存在的。但我实在懒得翻 systemd 的 target 列表了,所以就这 样吧……
  5. 这几段分别写了内核调度器和时钟。受限于拟人环境,很多地方写的并不准确,大家看 完就当图一乐吧QwQ
  6. 如果 CPU 调度器没能保证进程的 CPU 时间分配公平的话,是的确可能出现“进程饥饿” 和“进程饿死”的情况的。
  7. BFS 调度器是 linux-ck 的开发者所开发的 CPU 调度器,(据称)能够显著提升桌面性 能。 CFS 大概更适用于服务器等多 CPU 场景。

1.7.11. 历史笔记 <2022-05-17 Tue>

  1. 埃及与希腊与罗马

    怎么说呢,现在的我对这两个所谓“文明的开端”(对西方人而言)并没有什么十分良好的印 象。

    在过去,那段我还很喜欢历史的日子里,我对古埃及、古希腊历史是很着迷的。不同于对于 中国古代史抱有的那种崇敬感,我对埃及、希腊史是一种听故事式的喜欢。它们就像一股历 史的清流,激起我极大的好奇,给我带来难以言喻的精神满足。

    不过后来听说了一些传闻,譬如吉萨金字塔,如亚历山大大帝,如“一个雅典人”的伯罗奔尼 撒,还有公元十几世纪的一些拉丁文著作,难免又觉得有些奇奇怪怪,虽然不至于全信,但 也让人清醒了许多,进而觉得还是把这些历史当作应付考试的东西和平日的消遣学了吧。

    而且,按照西方人惯常的整活历史来看,我不知道的东西还多着呢。总之,小心意识形态。

    不过,希腊的政治倒着实令我很有兴趣。能把县级行政单位玩出花来,可谓是法国人的祖宗。 这也让我对奴隶制产生了浓厚的好奇,包括罗马帝国奇妙的繁荣,背后也肯定有着奴隶制的 神奇一面。教材不讲这些实在是太可惜了。

    关于奴隶制,我所知道的极为有限,东周各国的市场、井田之外的野人,是我仅有的具象的 认识,而且还和西方无关。后来又有了罗马亚历山大港中向货船上搬运货物的黑奴,也有被 当作货物的白奴,但这也是少数。关于古罗马,我所知的更多的,还是那些可以用“宏伟”形 容的澡堂、奢靡无度的贵族、五彩斑斓的布匹、以铅调味的毒酒、焚烧的基督徒尸体(前 期),以及持着短投矛、训练有素、满身蒜臭的职业军人,和他们以军队为主、以扩张维生 的畸形体制。相比之下,十二铜表法仿佛都成了可有可无之物——可能事实上也是如此。

    另外给我印象很深刻的是苏格兰的两道长城——最北边那条叫安东尼长城,南边那个叫哈啥啥 来着?——我初中酷爱看地图,当时看到这两道长城立刻就惊叹了:世界上竟然还有这么短的 “长城”!后来我靠着这两道长城做对了无数地图题,简直是判断罗马帝国的不二法门。

    但是,直到今天我也不知道,从罗马行省大执政官的高阁或高塔上向下望去,到底是一番什 么样的景色:道路有没有铺上石板?巷角有没有便溺的恶臭?街上更多的是穿着布衣的平民, 还是白肤色或黑肤色的奴隶?这才是我作为一个现代人,所最为好奇的。

  2. 阿拉伯

    说起来阿拉伯,尤其是阿拉伯帝国时期,我了解的是真的少,也就是课本上那个程度——穆罕 默德的大半辈子、哈里发、政教合一、狭长的,左边是个勺子右边是个锤子的国土、通商、 文化交流、数字、翻译东西方典籍、保存古希腊罗马文明什么的。其它的都是旁敲侧击得来 的认识:西班牙的穆斯林、崇高的大图书馆、越发繁荣的亚历山大港、住在广东的肥胖的大 食人,诸如此类。可见,我对那方土地的认识着实浅薄。

    阿拉伯帝国之后的阿拉伯地区,历史倒是很丰富的。但在我的刻意遗忘下,现在我能说出来 的东西也不多了。如果说古希腊是历史的清流,它就是历史的泥石流。种族仇杀、同态复仇、 血腥的王位继承制、少数民族军队、混乱的民族关系…… 给我印象最深刻的,是一张照片, 照片里亚美尼亚族的少女赤着身子,一排排地被钉死在木架上——那是奥斯曼土耳其的所作所 为。每当看到奥斯曼那八爪鱼似的国土,我就会忍不住想到这些,课堂上也会偶尔恍惚。所 以我愿意忘记它也不奇怪了。

  3. 日本

    日本的幕府政治给我以一种什么样的感觉呢?单从政治上讲其实是乏善可陈的——不仅是“乏 善可陈”,更可以称之为“乏可陈”了,一定要说政治的话,我对它的变迁倒是有些兴趣。我 们大概都听说过织田信长、丰臣秀吉、德川家康,乃至于更之前的鸟羽法皇等一干人物。日 本的政治其实是很动荡的,比李朝两班还令人眼花缭乱,相当有的磕。日本的文化作品往往 把那段时间渲染得很是出彩,堪比日本版三国演义,但事实上我们都知道,日本的战国时期 和中国的战国时期可不是一回事,与其说是战国,不如说是战村,织田信长哪怕是掀了本州 岛,充其量也就是县官打架,传说中的堺市,恐怕也只是集市,日本那帮公司怕不是把银魂 的场景素材搬到信长的野望上去了。当然,游戏方面我是外行,不便多言。这样一看,顿时 就觉得无趣许多。这也能看出我万历那个抗日援朝的水平了,现代化的国家组织能力啊……今 天的中国也有待提升。

    最令我印象深刻的其实是日本的文化。没办法,一想到幕府,脑海中便浮现出江户时代的村 舍与良田,浮现出矮矮的山坡上的神社,御币上撒落下白色的花,仿佛有一张大结界,把一 切隔开。当然了,这只是幻想乡——正如其名。这么看来,东方能够经久不衰,不是没有原因 的,它准确地切中了人们最真实的期待。走出幻想,真实的幕府日本,在我的梦中出现最多 的,还是丑陋的髡头,拔眉毛、涂黑齿的女人,乱伦的和尚,十五岁上战场的孩子。不是什 么好地方。封建时代永远是黑暗的。

    哪怕明治维新之后的日本,也没给我留下什么好印象。我偶尔会梦见阿依努,一个日本的原 始族群。最开始我是在欧陆风云的地图上看到它的,文明等级很低,却占据着整个北海道岛。 后来我一直在后悔没有把它收成海外藩属国(当时我玩的大明),这样在日本攻占它的时候 我就有宣称出兵了。后来我偶尔会梦见它,梦见阿依努参天的高树,在明治时代一片片地倒 下,变成海船消失在海雾里,留下一片光秃秃的冰冷大地。

    我看到过明治天皇那顶高耸得滑稽的帽子,实在令我印象深刻。但我从未看到过阿依努部族 的素未谋面的朋友。

1.8. 日志

1.8.1. 尝试文学编程 emacs init [2022-01-26 Wed 23:48]   emacs

看前景,文学编程对于我这种脑瓜不好使的小笨蛋来说大概会很有用?qwq

简单学了一下后发现并不难,虽然要熟练可能不容易,但搞懂怎么用倒也不是问题。

org-babel 的功能十分强大,对于我这种轻度写代码,连补全都没什么要求的人来说已经绰 绰有余。很可惜的是其中的很多功能我可能经常用不上,例如插入结果什么的。

在我的感觉中,文学编程更像是一种展示用的工具,而不是真正的生产环境。

不过谁在乎呢?用着开心不是最重要的嘛哈哈

老和枯燥的代码打交道多没意思啊,还是写东西好玩。

先用 elisp 试试水,效果好的话以后的代码都这么写。

1.8.2. 决定入坑 Haskell [2022-01-28 Fri 10:07]   explore

能够在 sdf 找到 Haskell 的解释器实在是太让我意外了。

在 sdf 上重构个人网站是我目前在编程上最迫切的需求,也可以说是我的主要动力之 一。既然 sdf 支持 Haskell ,那我不介意试它一试。仰其大名已久了。

1.8.3. 关于资本问题的一点想法? [2022-01-28 Fri 14:59]   reading

今早儿刷到这篇文章,再次挑起了我的想法。

其实仔细想一想,除了增加作文素材以及充当生活的调剂品之外,它好像也没有什么 更多的用处了。作者在尽自己作为知识分子应尽的义务,我们小老百姓们照样过着一 如既往挨压榨对生活,对变革的呼喊在不知不觉间已经成为平凡的日常了,哪里还有 一点变革的热血的态度。

古来万事东流水。

我可能被虚无主义感染了?我好像在想,这日子就这样过下去吧,该热爱的时候就这 样热爱,该奋斗的时候就这样奋斗,该呐喊的时候就这样呐喊,等到该变革的时候再 按照它该有的样子变革。说不定这样我就能幸福啦?

1.8.4. 表达自我 [2022-01-30 Sun 16:40]   nonsense

今天画了新的头像,又折腾了半天终端配色方案。我自己都不知道自己在较什么劲, 非要把昨晚梦到的那些美好的东西搬进现实来——哪怕最终只能搬进来虚幻的影子。

大概是我“表达自我”的欲望在作祟吧——我也不知道为什么,只是隐隐约约觉得,这样 碌碌地生活着实在是太不值得了,于是便想寻求一点什么,哪怕走的是一条完全错误 的道路。

小时候觉得自己很特别,不愿意相信自己的平庸;长大点被迫面对现实,却又抱着不 切实际的幻想去追求特别;再长大点,发现追求特别也是没有意义的——因为所有人都 在这么做,甚至做得比我好得多,于是在精神的碌碌之中沉沦,直到现在。我终于理 解——

不是理解每个人都有自己的功用——那是一句正确的废话,是捂住一只眼睛看到的“真相”。

正如鲁迅之言,希望与绝望皆为虚妄。既然是虚妄,那谁还管它那么多呢?

我自觉碌碌,就要追寻自我价值的表达;自觉不幸,就要追寻幸福;自觉孤单,就要 去追寻此心安处。我追就是了,管他三七二十一。

哪怕治不好我的难过,也能让我睡得更踏实些。

拜拜了您呐。

1.8.5. 年诗 [2022-01-31 Mon 03:21]   nonsense

寻觅过三秋,飘飖尽九州。

心安非此处,何故惹停留。

注:尽九州:指儿时辗转事。

1.8.6. 生活感悟? [2022-02-04 Fri 11:09]   nonsense

才明白为什么很多生活感悟记不下来,因为它们一直在我心里呢。

今天和同学聊网络小说,说到喜欢看慢慢更的小说,觉得这就好像幸福的日子一直在陪你过 下去一样,你可能不在它身边,但它一直在等着你……你说吧,这种东西,记下来呢?不记下 来呢?

所以为什么要记下来呢?

想到这里有点难过。我知道为什么,但我不需要写出来,因为也没有什么用。

还是那句,心安非此处,何故惹停留。

1.8.7. 用正确的态度 [2022-02-05 Sat 10:57]   nonsense

我觉得,做事的态度对做一件事有至关重要的作用。如何端正一个可靠的、正确的、长远的 态度是一件十分重要的事。

——我相信大部分人看到这段开场白,都一定会觉得下面会是一段毫无意义的场面话了。

事实上,就好比写作,我不知道我应该用一个什么样的态度去面对它。

完成任务?那其实不是写作,也创造不出什么意义;

实现理想?实现什么理想呢?若说表达自我,那它的意义是虚幻的;若说经世济民,我还没 那个资格呢。

单纯地想要书写点什么?我还没有那么高尚,这么点理由可没办法把我拽下手机和作业。

或者说,把写作当成自我的树洞,或是一个心灵的归属?但我知道这不是一条坦途,而我那 不惜一切拥抱它的热情,大概也在一次次的失望中消磨得不剩多少了吧。

——同样的,不愿投入其它的理由,可能也有这方面的原因。

这就好像我伸出一只孤单的手,想要抓住什么,但总还有一些、一些距离一样。

说真的,我不知道该怎么办,大部分的道理我都明白,可它们中的大多数除了让我更深刻而 理智地认识到自己的无力外并无大用。奇迹的救赎更不会从天而降——对我而言更是如此。

再安静下来,仔细地想一想,我自己身上的、不足为外人道的问题同样很多很多。大概这就 是青春独有的景色?

忽然想起了所谓“青春伤痛文学”。我不相信当一个人长大以后,从青年迈入中年,他就会变 得多么智慧了,智慧到可以笑看少年时“无谓”的思索。我更愿意认为那其实是一种成长中混 杂着悲哀的妥协的结果——否则也不会有什么“中年危机”一说了。——从儿童到少年或许同样如 此。

我固然可以把日子这样过下去,但不应如此。

暂且这样吧。

1.8.8. 关于手动降智 [2022-02-06 Sun 14:54]   nonsense

今天再次翻了翻寒假作业单,看到了那项积累作业(等假期结束了我恐怕就会把这一坨以不 知道什么形式交上去),于是又想起来了那个与我而言老生常谈的问题。

对我来说,这个问题我已经思考了不知道多少遍,但我还从未把它写下来过。趁着这个机会 写出来吧,一举两得之事,何乐而不为~

在写作方面,我自诩一个对自己要求比较“高”的人:除了那些迫不得已的场合(期末考试), 我一般不会写那些我很熟悉的、我已经知道答案、知道怎么做的事物,而总是在探索一些我 比较前沿的想法。这话很难以理解。

好比上学期写的存在感作文,我深刻明白“存在感”这个词只是一个笼统的概括,它背后其实 代表着多种不同的、复杂的情感,每种情感都有它独特的心理内涵——单拿外部给予的“存在 感”而言,有肤浅的被人认识、收到追捧,有深层点的所做之事受到认可,还有,比如——

让我想象一下,我和好朋友们凑在一起走着,我走在最边缘,他们可能在聊很有趣的话题, 似乎完全没有在乎到我。这时候我的意识忽然拔高,我看到了这样一个行走着的孩子,他微 笑着,静静地听着身边的同学吵吵闹闹,送出属于他的一份温暖的关注的目光——这是一种怎 样的感觉?谁也不能说这不是一种“存在感”。

于是我知道,它很复杂,我就不能把它笼统地看作一个东西,笼统地抽成两个方面,仿佛一 个蹒跚学步的幼童在用“睥睨天下”的气势作出那样一番论述——尽管大部分情况下这么做其实 能收获一个不错的分数,但我觉得那个分数更像是对我的嘲弄。

——啊,这个想法是错误的。我不应该这样想,那是冲动的,没有用一种和谐的目光看待这个 世界。这是数秒的思考就能得出的结论——所以上文那样的观点永远不会出现在我今后的作文 里了。大概就是这种感觉。

嗯,让我坦诚一点。这样的做法显然不算太好。于是我便又想出来数个理由去推翻它,说服 我不这么做,可随后又有数个理由从我头脑中冒出来,牵制住这些理由——这其中也不乏一些 不足为外人道的东西,譬如我对自己的幻想、期待,如我在数年的成长中建立起的一些不那 么正确但却一直支撑着我的执念和顽固认识……想到最后,我也无法确定怎样是更好的。

这其实不是我想的多,只是因为我缺乏一个能够压倒一切的坚定理由。这就好比战场打仗, 飞机在天上缠斗,坦克在平原角力,步兵在街市巷战,最后血流漂杵;但如果哪一方有原子 弹的话,伤亡直接就是零了:压根打不起来,一方直接赢了。

……我是怎么扯这么远的?

这种执念在我的作文上造成的最大影响,其实在记叙文。记叙文中,故事的主角经常需要有 一个心路的转变,往往是从一个作者认为错误的理念转变为一个作者认为正确的理念。这种 类型的记叙文大概相当热门——但我往往不认为这种虚假的“治病救人”有什么意义。

所以我很少写记叙文,这种类型的大概更是从未写过——除非我哪一天可以在考场上数分钟的 钻磨中恍然大悟,彻底推翻我原有的一种思考,建立一种新的思考,并且刚好可以虚构一个 符合题意的故事套进去,在八百字的写作过程中完成我与主角共同的蜕变——那就不是我在写 文了,是文在写我(双关)。

这种想法或许确实不太对?我在长一些的故事里确实不会这么做。

它不好吗?谁知道呢。人生又不是分数能够评定的——是评定,而非决定。

先这样吧~

1.8.9. 想明白了一件事 [2022-02-08 Tue 13:10]   nonsense

生命的意义是一个伟大的命题,要想明白它对我来说是一件不可能完成的任务。但我又迫切 地需要一种这样的信念支撑我不断前进。那我该怎么办呢?

很简单,我可以退而求其次嘛。怎么个退法?这可以通过对这个伟大命题的溯源来找到答案。

人为什么会产生关于生命的思考?仓廪实的话我们都很熟悉,是物质与精神的丰富充实让我 们有条件产生这样的思考。于是,一条曲线救国的路线就浮现了出来:我们的奋斗,不一定 是要实现所谓“生命的意义”,我们还可以努力让更多人生活的更好,让更多人能更好地思考 生命的意义,这对我来说也会是一件拥有同样意义的事——简单来说就是甩锅嘛!甩锅的轻松 愉快谁不爱呢~

诶,快乐了

1.8.10. 关于缺点 [2022-02-09 Wed 14:22]   nonsense

所谓人无完人,每个人身上都或多或少会有一些缺点。好比我,我总能看到我身上有很多很 多的缺点。

针对这一坨缺点,我的第一个解决方案是改,有多少改多少。在这方面我的信心与成功经验 一向充沛。但后来我发现了诸多问题:

一个是,缺点永无止境,改完一个冒出一堆,轮番上阵群殴,令人招架不得;

再一个,所谓真理是具体的有条件的,很多“缺点”,它并不是那么坏,甚至还有一些对我未 来可预见的的人生是有好处的。而且它们的评判标准往往还和我的价值观相连。

另外,有的“缺点”环环相扣,想要彻底改掉必须斩草除根,否则就是野火烧不尽春风吹又生, 这中间又有很多非人力所能及的地方……

以上总总令人心中郁郁。

我也不是没有想过像某些鸡汤说的,“接受自己的缺点”呀,“与自己和解”呀什么的,但是, 嗯,由于各种加在一起很复杂的因素,像个人要求啊、对自己能力的自信啊、愧疚感啊、紧 迫感啊,还有文化影响之类说不清道不明的东西,我不觉得这会是一条好路。

另一个方法就是把缺点当作萌点一类的东西来看待,但这个经常是用于对待他人的啦,排除 不算。

其实或许我只是需要有人对我说一句你辛苦了,做得很好,路还很长之类的——那其实是一个 普通人在祈求一个奇迹,而这样的奇迹往往是很少的。一个平凡人的思考或许就是这样的, 理性的道理和感性的诉求搅在一起,傻傻分不开。

先这样过下去吧,还能怎么办呢?幸福不会从天上掉下来,也不会从脑子里冒出来,我好像 能感受到,好像也曾拥有过,好像也不曾拥有过。我好像应该努力抓住些什么,又好像应该 豁达地放弃些什么。归根结底,我对它可能还是不够了解,或者不够习惯吧——我不希望是后 一种。

1.8.11. 又到了交作业的日子 [2022-02-20 Sun 19:23]   nonsense

因为网站还没建好(准确来说是懒,说得好听点是不太明白建它的意义了),所以我还是需 要把这些乱七八糟的东西打出来交上去滥竽充数。

然后我才发现,啊我前些天写的东西……它们好像不太对劲,我现在可能不那么认为了。

事实上,这样的事情已经在过去的十数年里无数次发生了。

它大概才是浇灭我创作热情的最重要原因。

我也可以说这是成长的历程,是有价值的东西。可是真的是这样吗?它真的有价值吗?恐怕 不一定。

我大概不应该执着于留下些什么,这其实没什么意义。

确实如此。我已经慢慢意识到这点了。

就这样吧。

哦对了,加缪的《荒谬之墙》是本好书,它解答了我这个月写下的东西中百分之八十的问题, 但它带来的新问题同样不少……

唔,就这样吧,一天天傻不拉叽的,多少能配得上一声单纯?qaq

1.8.12. 配置了以下 latex 环境 <2022-06-07 Tue>

可以用 emacs 写数学作业了,开心

可惜中文字体还有些问题, dvisvgm 生成的 svg 是纯白色的,不知道为什么

1.9. 记录

1.9.1. 洋务运动研学课笔记 <2021-09-17 Fri>

  1. Westernization movement 洋务运动
    1. 信息
      1. 皇帝谱系

        道光 1820-1850 咸丰 1850-1861 同治 1861-1875 光绪 1875-1908

    2. 何为“洋务”
      1. “洋务”的出现

        原称“夷务”,《北京条约》后改洋务——“不敢斥言夷字” 但许多清臣仍坚持用“夷”字

        1. 何为“夷务”

          “夷务”原指外国的侵略及由此引起的中外战争与交涉,包括抵抗迎战、谈判、签订条约,代表外交问题

        2. “洋务”

          与“借法”、“仿行”共举,代表内政问题

      2. “洋务”内涵的变化
        1. 过去

          办理洋务被视为使人沾染污名的工具

        2. 七八十年代之交

          情形变化: “可获优缺,擢高官” 士大夫以抵抗侵略的态度谈论洋务

        3. 甲午之后

          “洋务”含义完全转变,指借法事项

    3. 基本史实
    4. 研究史(截至抗战前期)
      1. 梁启超
        1. “四界说”

          同治初,变法“荜路开山”,中国近代变革之始 光绪甲中,萌芽时期,“朝士皆耻言西学” 马江败后(中法战争),知道必要性,“谈洋务不以为深耻……盖渐知西学,而肯讲求” 甲午东事败后,“旧法之不足恃”,更深入地学习

        2. 对洋务运动的态度

          根本上肯定了洋务运动的历史地位,把它看成维新运动的先导 对具体的变法措施,给予否定的评价,认为在封建势力的阻挠下,洋务派兴办的事业收效甚微

      2. 高博彦

        不得不做

      3. 吕思勉

        “所举者军事器械之末”,大多数人恶之

    5. 中体西用的积极意义

      突破了旧的正统意识形态框架,为新的意识形态形成奠定基础 西学在“中体西用”中合法化、合理化,突破传统文化的一元结构模式

1.9.2. 2021春节朋友圈 <2022-02-01 Tue>

眨眼间又到春节啦!大家虎年快乐!要快乐一整年!

感谢2021年,感谢我的家长,感谢原高一12班的老师和同学们,感谢高二14班的老师和同学们,感谢高二年级学生会的老师和同学们,感谢指导我诗词的老师和前辈们,感谢槐雪诗社的大家,感谢Emacs 中文论坛的大家,感谢 sdf.org 社区的大家,特别感谢 6auY57u06bqS== ,5YWw5qyj== , 5aKo5ra15a2m6ZW/== , 5buW5oCd5pix== , 6YOt6ZmF5rO9== ,5a2Z5LiA== 等挚友的一路陪伴!(人名经过 base64 编码)

讲给你们一个我珍藏的小魔术,你现在眨一下眼,记住你对着屏幕眨的这次眼,然后悄悄地等一年,明年这个时候你再眨一下,之后你也能感觉到一年一眨眼就过去啦!

下半年来我没再往朋友圈里发我新写的诗了。先说好不是我没写,是我忽然觉得,我好像没有必要从朋友圈里的大家这儿寻找我和我的诗的意义罢了。不过既然是除旧迎新,那我多少也应该对过去的一年做一些总结吧。

这半学期我还能找到的所有诗我都附在下面。不过其中有一首诗是咏伟大的 Emacs 编辑器 的,写的是很可以的,但原文发在社区帖子里,这里只贴出链接: https://emacs-china.org/t/topic/18977/25?u=3vau 此外还有讽刺班里太热而写在班门上的两首十六字令,没有留档,后来布置考场的时候散佚了,如果哪位同学记下了请联系我;半首写足联的七律,合格模拟考时写在了考卷上,考完后也不知被我扔哪里去了;前天夜里做梦时还梦到一联特别好的,但早晨一不小心睡了个回笼觉,也只能说甚是遗憾……

原本今年不打算写些什么,但那一觉过后心里就总是过意不去,乱涂两笔,算是雁过留声:

寻觅过三秋,飘飖尽九州。

心安非此处,何故惹停留。

注:尽九州:指儿时辗转事。

想在这里表达些什么,但又忽然觉得不合适起来;正好我的网站这两天就快修缮好了,欢迎大家到时候去那里和我聊聊天,让2022少一点孤单~

留下来的句子大多也不怎么样,只希望大家瞥过某些,能忽然开心一下,也就够啦

再祝新春快乐!!

廿九记安装linux

  莫疑繁此为何妨,夜觅阑珊守尺方。

  代码行行映人肃,个中点点入心长。

  肠肥恶见三牲貌,腹馁唯知一口张。

  当敬人间才与苦,方成诸夏景还长。

初三感夜

  夜望灯家胜莽林,少读凿壁复难吟。

  广厦一览悟甘苦,课本百读明旧新。

  当祭千秋累白骨,终成九载化仁心。

  长歌何幸得生此,窃禄无酬愧不禁。

廿三读洛神赋

怅尔亭亭痴尔嗔,春当靥靥水当温。

长歌难免长嗟去,洛比徒将羡洛神。

和王强老师

  夜望灯家胜莽林,少读凿壁复难吟。

  广厦一览悟甘苦,课本百读明旧新。

  当祭千秋累白骨,终成九载化仁心。

  长歌何幸得生此,窃禄无酬愧不禁。

长相思・夜思赠某人

思三更,忆三更,捱去一宵仍未平,离愁点点生。

错一庚,漏一庚,冬雨轻弹角羽声,难为明月情。

无题

浩渺纷繁岂可察,平芜谡谡肆倾轧。

逍遥只为归吾所,犹敬当年司马家。

1.9.3. 高维麒关于 tty 中文问题的回信 <2022-02-03 Thu>

From: "Rosario S.E." <rosa@sdf.org> To: 高维麒Carl <gaoweiqicarl1@163.com> Cc: "rosa@sdf.org" <rosa@sdf.org> Subject: Re: tty显示中文那是啥来着 In-Reply-To: <4473825d.190e.17ebcef4473.Coremail.gaoweiqicarl1@163.com> References: <4473825d.190e.17ebcef4473.Coremail.gaoweiqicarl1@163.com> User-Agent: Wanderlust/2.15.9 (Almost Unreal) SEMI-EPG/1.14.7 (Harue) FLIM-LB/1.14.9 (Gojō) APEL-LB/10.8 EasyPG/1.0.0 Emacs/29.0.50 (x8664-apple-darwin19.6.0) MULE/6.0 (HANACHIRUSATO) X-Face: HmiA-nBeZRtgo$%VbIAw1yEVa^!aGyni?]i;8vMVn3OS&eyoq]easuqsHB1?]vNr|CYT ={QYa1`;$52w]XH3RD*ZN2dPekHY+*0M-y"5,|u9\K=y"U'Fr/{^;oBH7u]SW6qE{DVz>!~s5w!N, R04%);{([$hrnG}!\~bpii>b!(B,VH:`nfn@G#}YixVd@@.cRn4]a:;U0gT8@JrH{2Y2NZVGoi-u@ Q:1}`Gep^HO+?lV4vkNU[ppDGxH^~)NGkOd72DaqsZ+x6{<y.$r[}UVh<'i:WV:O`SCr?q]:U_ pQTCH3MA:O?(zoecBOMZYG+%'r7urtAIL()eLC"yu(6WM0D8bYFb;)RgQWAz –text follows this line "–

cjktty 啊,没有博客链接,你去网上搜 cjktty ,最新的回复也只在说最老的东西,真正 有了解的人都已经暗搓搓用上了[滑稽]

aur 安装 linux-xanmod-tt-uksm-cjkttylinux-xanmod-tt-uksm-cjktty-headers

安装前按照这篇wiki的方法配置好 makepkg 加速编译——你好歹是八核,怎么着也得打个 j9 吧。

我之前给你发过一份我在 Gentoo 上用的 make.conf ,一些 CFLAG 你可以照着那个配——O3 就算了,稳妥起见你还是 O2 吧。但除此之外什么 -fno-math-error 是可以放心打上的。

我猜那份 make.conf 你肯定丢了哈哈 这是新的~

之后无论你是用 pikaur 还是克隆下 PKGBUILD 再 makepkg -si 都可以。安装完毕后重 启,进 GRUB 的时候检查一下启动选项,第一个应该已经变成新的内核了。

Archwiki 不提供对非官方内核的任何指导2333 毕竟玩内核的人到底还是小众,不过不玩玩 怎么知道它有多舒服呢?~

tty 下的中文输入法,很久以前是有的,但现在你要配置恐怕得麻烦死,你百度一下会有 “教程”,不过别抱太大希望。我比较推荐的解决方案是 emacs —— emacs 甚至内置了一个中 文输入法(尽管难用得要命),进入 emacs 后按下 Ctrl + \ ,输入 chinese-py 使 用。

你要是感兴趣我给你详细介绍介绍 emacs 强大的输入法系统,我这封信就是使用 emacs-rime 打出来的,输入体验比 macOS 的系统输入法还强——你应该看到了,这封信的中 文和英文之间是有空格的,我在任意中文后打一个空格,输入法就会切换到英文,再按一下 空格返回中文,兼顾美观和体验。想强制输入中文只需按一下 M-j 。还有这个每行宽度 80 ,在 emacs 下只需要按下 M-q 就可以自动把当前段落格式化为你设置的行宽(默认80)。

对了, cjktty 有一个小问题,要用它就不能换用不同字号的 tty 字体了,否则中文会乱 码—— cjktty 只支持 8*16 的中文字符QwQ 。不过你要嫌默认字体不好看,同字号的字体还 是可以换的,不会有影响。

先说这些,想到什么再写给你。

哦对了, neofetch 一下给我看看呗, neofetch 就是这玩意,如果你想要个不带色彩预览 版的可以用 screenfetch ,输出信息是差不多的——我对你电脑又臭又长的 dmidecode 不感 兴趣[狗头]

1.9.4. 代理相关的一系列名词的关系 <2022-03-03 Thu>

(摘自某次微信聊天记录,胡诌,勿信,备用)

关于clash、v2ray、VPN、代理这些名词的关系呢,其实是这样的:VPN全称是“虚拟私人网 络”,是名词,指一种网络模式;代理是动词,也用以指代这种行为,就是我访问某个地址 的时候,我把访问请求发给另一台机器,让它替我访问,再把结果发给我,它是VPN的具体 实现形式,而这个“另一台机器”被称为VPS,大概就是您说您先前用的那种;

但是,一人一台VPS明显不适合翻墙产业分工做大,因为一台VPS的性能很强,可以同时代理 很多人的流量,因此会有浪费,不利于供应方;而一台VPS也太容易被封了,不利于需求方, 因此最好的产业模式是供应方统一管理多台VPS,再让大量用户一起使用这些VPS。但这时用 户就会面临一个配置繁琐的问题:比如我用的这个机场拥有二十多个节点,也就是二十多台 VPS,我要是每换一台VPS就得重新配置一遍服务器地址、端口、用户名、密码,实在是过于 麻烦。

此外,用户还有另一个需求:只有访问被墙的网站,我才需要过代理;如果我要访问百度, 这时候还过代理,那就是浪费代理流量了。于是,在这些复杂的需求下,一个“分配方案”就 被催生了——这就是clash/v2ray/ShadowSocks(SS)/SS2它们。代理软件(CFW、CFA它们)把 这些复杂的协议和规则嵌套在一起,并且控制操作系统自带的代理接口,就能实现快速导入、 连接、切换、更新节点,自动绕过国内网站等功能。当然实际要更加复杂一点,比如v2ray 还有搭建VPS的功能什么的……

1.9.5. Emacs查看已安装字体的方法

(insert (cl-loop for i in (font-family-list) with r = "" do (setq r (concat r ",
              " i)) finally (cl-return r)))

1.9.6. 埃及分数 elisp 版

(defun egyptian-fraction (nr dr)
  (let ((ef)
        (str)
        (nr (float nr))
        (dr (float dr)))
    (while (not (= nr 0))
      (let ((x (ceiling (/ dr nr))))
        (push x ef)
        (setq nr (- (* x nr) dr)
              dr (* x dr))))
    (setq ef (reverse ef))
    (dotimes (i (length ef))
      (if (= i (- (length ef) 1))
          (setq str (concat
                     str
                     (format " 1/%d"
                             (nth i ef))))
        (setq str (concat
                   str
                   (format " 1/%d + "
                           (nth i ef))))))
    (message str)))

;; (egyptian-fraction 6 14)

1.9.7. 发布本文件到 SDF 服务器

(org-html-export-to-html)
(async-shell-command "scp ~/rosa.html sdf:~/html/index.html ; scp ~/rosa.org sdf:~/html")
;; (async-shell-command "scp ~/rosa.html rosa@sdf.org:~/html/index.html ; scp ~/rosa.org rosa@sdf.org:~/html")
;; (async-shell-command "scp C:\Users\Administrator\AppData\Roaming\rosa.html rosa@sdf.org:~/html/index.html; scp C:\Users\Administrator\AppData\Roaming\rosa.org rosa@sdf.org:~/html/rosa.org")
;; (async-shell-command "scp ~/res/* sdf:~/html/res/")

1.9.8. tty 截图函数

(defun rosa/tty-shot ()
  (interactive)
  (shell-command
   (format "sudo fbgrab %s.png"
           (mapconcat #'number-to-string (current-time) ""))))

(global-set-key (kbd "<f5>") #'rosa/tty-shot)

Date: 文章始著于2020年12月

Author: Rosario S.E.

Created: 2022-06-23 Thu 20:28

Validate