如何维护hithesis(一)

由于上传到CTAN需要用文学化编程这种形式,所以hithesis中所有的格式、文档、格式中的注释、文档中的注释等等所有乱七八糟的东西都放在了hithesis.dtx文件中。 运行latex hithesis.ins会把混合源码和文档的hithesis.dtx文件打开,类似解压缩。 如果需要添加新的文档类,例如添加hithesisbeamer.cls文档类,需要修改 hithesis.ins文件,并把hithesisbeamer.cls放入hithesis.dtx文件中。 如果不添加新的文档类,那么维护hithesis主要的任务就是修改这个hithesis.dtx文件。

如果将代码和文档打乱放入一个文本文件中,如何才能区分哪里是文档,哪里不是文档呢?一个直观的方法就是再发明一个注释符号%,所有以这个符号开头的内容都是文档的部分,剩余非% 符号的是代码部分,不同的代码文件用这个符号进行区分。

%<*foo>
foo 文件的代码部分
%</foo>

文学化编程的目的就是能够直接生成一本书,书里含有100%完整的代码(包含LaTeX包中所有源码文件)和其中的说明。LaTeX作者高德纳认为通过看书可以达到了解代码的目的。

推荐使用emacs编写和维护hithesis,根据某掌门的多年修为判定,在编辑器领域 emacs将独领风骚几百年。emacs在维护hithesis时的优势非常明显。 例如,某掌门修改一个bug之后需要在修改位置添加文档注释,还需要添加一个改动索引。 改动之前:

\RequirePackage{xltxtra}
\def\hit@def@fontsize#1#2{

改动后:

...
\RequirePackage{xltxtra}

%    \end{macrocode}
% 设置深圳校区开题报告的正文行距与毕业论文模板一致
% \changes{v3.0.4}{2020/07/29}{设置深圳校区开题报告的正文行距与毕业论文模板一致}
% 此处修改与正文行距保持一致
% \changes{v3.0.5}{2020/09/13}{此处修改与正文行距保持一致}
%    \begin{macrocode}

\renewcommand\normalsize{
  \@setfontsize\normalsize{12bp}{20.50398bp}
  \abovedisplayskip=8pt
  \abovedisplayshortskip=8pt
  \belowdisplayskip=\abovedisplayskip
  \belowdisplayshortskip=\abovedisplayshortskip}

\def\hit@def@fontsize#1#2{
...

添加了6行代码:

\renewcommand\normalsize{
  \@setfontsize\normalsize{12bp}{20.50398bp}
  \abovedisplayskip=8pt
  \abovedisplayshortskip=8pt
  \belowdisplayskip=\abovedisplayskip
  \belowdisplayshortskip=\abovedisplayshortskip}

在对应位置添加了文档注释:

%    \end{macrocode}
% 设置深圳校区开题报告的正文行距与毕业论文模板一致
% \changes{v3.0.4}{2020/07/29}{设置深圳校区开题报告的正文行距与毕业论文模板一致}
% 此处修改与正文行距保持一致
% \changes{v3.0.5}{2020/09/13}{此处修改与正文行距保持一致}
%    \begin{macrocode}

其中需要添加版本号和修改日期,如果其他非自由软件,无法自由修改编辑器的基本上无法做到自动生成版本号和日期。结果就是维护一个很大的.dtx文件十分难受,因为不清楚当前最新的版本号是多少,需要从头到位手动浏览文件查看最新的版本号。 对于这种场景,emacs的小伙伴可以使用snippet再加上几行代码轻松搞定。 snippet部分:

# -*- mode: snippet -*-
# name: comment inline with change
# key:cmic
# --
%    \end{macrocode}
%    ${1:此处添加的规则}
%    \changes{${2:$$(yas-auto-next (yas-choose-value (get-version-candidates)))}}{`(format-time-string "%Y/%m/%d")`}{${3:$1}}
%    \begin{macrocode}$0

可以自动化生成版本号和修改时间,这样维护hithesis.dtx就非常简单,只需要关注如何修改即可。 版本号的生成方法是使用正则式抽取当前编辑的文件中所有版本号,然后排序,在 snippet中生成备选目录。 以下是对应的emacs配置的Lisp代码。

(defun dictionary-lessp (str1 str2)
  "return t if STR1 is < STR2 when doing a dictionary compare
(splitting the string at numbers and doing numeric compare with them)"
  (let ((str1-components (dict-split str1))
        (str2-components (dict-split str2)))
    (dict-lessp str1-components str2-components)))
(defun string-digits-lessp (s1 s2)
  (let ((lt t) (gt nil) (eq nil) (d1 0) (d2 0))
    (loop
      (when (and (= start1 end1) (= start2 end2)) (return eq))
      (when (= start1 end1) (return lt))
      (when (= start2 end2) (return gt))
      ;;
      (when
          (and
          (> start1 d1)
          (> start2 d2)
          (digit-char-p (char s1 start1))
          (digit-char-p (char s2 start2)))
        (setq d1 (1+ start1)) (setq d2 (1+ start2))
        (loop
        (when (or (= d1 end1) (not (digit-char-p (char s1 d1)))) (return))
        (incf d1))
        (loop
        (when (or (= d2 end2) (not (digit-char-p (char s2 d2)))) (return))
        (incf d2))
        (when (> (- d1 start1) (- d2 start2)) (return gt))
        (when (< (- d1 start1) (- d2 start2)) (return lt)))
      ;;
      (when (char-lessp (char s1 start1) (char s2 start2)) (return lt))
      (when (char-greaterp (char s1 start1) (char s2 start2)) (return gt))
      (incf start1) (incf start2))))
(defun dict-lessp (slist1 slist2)
  "compare the two lists of strings & numbers"
  (cond ((null slist1)
          (not (null slist2)))
        ((null slist2)
          nil)
        ((and (numberp (car slist1))
              (stringp (car slist2)))
          t)
        ((and (numberp (car slist2))
              (stringp (car slist1)))
          nil)
        ((and (numberp (car slist1))
              (numberp (car slist2)))
          (or (< (car slist1) (car slist2))
              (and (= (car slist1) (car slist2))
                  (dict-lessp (cdr slist1) (cdr slist2)))))
        (t
          (or (string-lessp (car slist1) (car slist2))
              (and (string-equal (car slist1) (car slist2))
                  (dict-lessp (cdr slist1) (cdr slist2)))))))
(defun dict-split (str)
  "split a string into a list of number and non-number components"
  (save-match-data
    (let ((res nil))
      (while (and str (not (string-equal "" str)))
        (let ((p (string-match "[0-9]+" str)))
          (cond ((null p)
                  (setq res (cons str res))
                  (setq str nil))
                ((= p 0)
                  (setq res (cons (string-to-number (match-string 0 str)) res))
                  (setq str (substring str (match-end 0))))
                (t
                  (setq res (cons (substring str 0 (match-beginning 0)) res))
                  (setq str (substring str (match-beginning 0)))))))
      (reverse res))))
(defun get-version-candidates ()
  (nreverse
    (sort (flatten-list (delete-dups
                        (with-helm-current-buffer
                          (s-match-strings-all "v[0-9]+\\.[0-9]+\\.[0-9]+" (buffer-string)))))
        'dictionary-lessp)
    ))
石见 石页 /
在共享协议(CC)下发布于
类别: technology 
标签: hithesis  LaTeX  emacs  中