一直以来都没有记笔记的习惯,同样也没有写日志的习惯。学习了很多技术,虽然会使用,却不会进行总结归纳,也缺乏相应的表达能力;经历的很多事,可是转眼却忘记事情的细节。每天浑浑噩噩的生活,多年以后,也许都无法回顾自己的一生。所以从现在起,我开始记录自己生活的点点滴滴。

笔记工具选择

主流的笔记工具从记录类型来看主要分为两类,一类是富文本记录,一类是纯文本记录。从存储位置来看也分为两类,一类是云端存储,一类是本地存储,从收费模式来看,一类是免费记录,一类是收费记录。

我所知道的主流的笔记工具有:

  1. OneNote

  2. Evernote

  3. 有道云笔记

其中OneNote是富文本记录,同时它不支持标记语法(如Markdown,orgmode)。这个工具,我目前主要用来进行资料的收集,如果OneNote来进行编辑和发布的话,我觉得相当不方便。

其它两个工具,我没有尝试,但应该都支持Markdown标记语法,不过我不太喜欢云端得东西,最关键的是,我偏向于用Orgmode来进行编辑。所以最终确定的笔记工具,我其实没得选,只能选择支持orgmode的emacs了。

另外用纯文本还有一个好处是,这个文件可以通过任何文本编辑器打开编辑,不会受限于特定的工具。我记得很早以前,我使用过以记录的wiki的工具,这个工具是采用sqlite来存储的,多年以后,这个工具从互联网上消失了,我再也下载不到了,我曾经数据再也无法查看了,虽然打开sqlite的工具有很多,但是sqlite各个表之间的逻辑,数据如何整合成易读的形式,没有这个wiki工具,我是没法实现的。好在这个工具是采用标准的sqlite来存储的,如果采用的是其自定义的二进制格式的话,曾经记录的数据,如果没有对应的工具,估计再也无法查看了。

目录设计

emacs本身没有层次化笔记管理的功能,这个层次化,我选择通过文件系统的目录的来实现。虽然可以将所有文件存入一个目录,甚至存入一个文件,然后通过编写emacs插件来解析这个文件,从而实现层次化的视觉效果,但是这样做有点得不偿失。通过目录来管理笔记层次会更通用,即使脱离emacs也可以进行管理。

目录的结构设计其实也困扰我很久,不知道用什么样的目录结构更合适。首先是考虑笔记的分类,目前我将笔记分为3个类别:

  1. Life,这个笔记主要记录生活的点点滴滴

  2. Story,这个笔记可以记录自己写的小说

  3. Tech,这个笔记主要记录技术方面的总结

大的分类分好后,有考虑到,如果以后写作量爆棚,即使一天写一篇文章,10年的时间也只有3650多个文件了,这对于单个目录来说应该是完全可以管理的,更何况一天一篇文章也很难实现。

小说这个类别比较特殊,这个就以不同的小说名作为子目录好了,同时每部小说独占一个资源目录。

因为是纯文本文件,所有文本和图片类的资源只能分开存储。那么资源类得文件如果和文本文件混合在一起,就会显得很乱,所以资源类得文件需要和文本隔离开来。

那么资源类文件,所有分类共享一个目录,还是每个类别共享一个目录,或是每篇文章一个目录呢?对于这个问题,我是这么考虑的:

  1. 所有类别共享一个资源目录,如果所有类别共享一个目录,那么不同类别的笔记就不能独立迁移了。比如如果有一天我想把Tech笔记分享给其他人,同时我不想让她看到我的Life信息,因为文本目录了,所以我直接复制文本目录给她即可,但是资源怎么办?如果不给,就会导致笔记信息不全,如果要给,我就得从资源中剔除Life相关的资源。如果数量特别大的话,会很麻烦。所以所有类别共享一个资源目录不可取。

  2. 每篇文章独占一个资源目录,因为并非每篇笔记都会附带资源,所以如何建立资源目录呢?如果在新建每篇笔记的时候就建立资源目录,那么就会存在大量的空目录。如果每次都是需要的时候再去建立目录,就会比较繁琐。所以每篇笔记独占一个资源目录也不可取。

所以我最终的想法是每个类别共享一个资源目录。同时为了区分不同的资源,我又将资源分为:

  1. img

  2. video

  3. file

这个几种类型,目前看来应该只有img会被用到。最终的目录结构如下:

Notes/
|-- Life
|   |-- .assets
|   |   |-- file
|   |   |-- img
|   |   `-- video
|   |-- life-1.org
|   `-- life-2.org
|-- Story
|   |-- story-one
|   |   `-- .assets
|   |       |-- file
|   |       |-- img
|   |       `-- video
|   `-- story-two
|       `-- .assets
|           |-- file
|           |-- img
|           `-- video
`-- Tech
    |-- .assets
    |   |-- file
    |   |-- img
    |   `-- video
    |-- tech-1.org
    `-- tech-2.org

笔记管理

目录建立好以后,接下来要考虑的就是如何管理笔记了,我需要以下几种功能:

  1. 笔记列表,以表格的形式列出所有笔记,如

    Date Title Draft Catergory Tags
    2019-09-13 Orgmode Yes Tech emacs,orgmode,notes
  2. 新增笔记

  3. 查找笔记

  4. 删除笔记

  5. 修改笔记

  6. 发布笔记

其中,查找笔记需要支持按上述表格字段查找,也要支持全文检索查找。

另外,还需要提供对资源文件的管理:

  1. 直接拖动图片到笔记中,需要自动转存图片,同时自动插入图片路径

  2. 剪贴板粘贴图片,需要自动存储图片到资源目录,同时自动插入图片路径

  3. 如果是手工输入图片连接,需要提供快捷键对该链接进行处理,自动转存图片,同时修改链接

我很希望有上述这样的笔记管理工具,可是这样管理模块目前还没有实现,所以实际的管理方案目前折衷如下。

Treemacs & Deft

通过Treemacs在右边栏显示目录和文件列表,通过Deft显示文件标题和摘要内容。由于Deft的显示文件时间是根据系统文件修改时间来处理的,而我想的是通过笔记内部记录的时间来进行显示,所以我需要对Deft的代码进行一点修改,这个修改需要进行两个步骤:

  1. 根据笔记内部时间戳来获取时间 “#+DATE”

  2. 如果获取不到这个时间,那么退回Deft默认的时间获取方式。

我的每一篇文章的开头都会有这样的一行时间戳:

  #+DATE: 2019-09-14T22:31:42+0800

我需要提取其中的时间按照Deft默认的时间格式显示即可。

我建立的第一个函数是 deft-parse-mtime , 这个函数负责从文件内容中提取出带有时间戳的行。如果没有在文件中找到这样的行,就返回Deft默认的文件时间。

  (defun deft-parse-mtime (contents mtime)
    (let ((begin (string-match "^#\\+DATE:.+$" contents)))
      (if begin
          (deft-strip-mtime (substring contents begin (match-end 0)))
        mtime)))

第二个函数是 deft-strip-mtime , 这个函数负责把时间戳中多余的标识去除,如 #+DATE:2019-09-14T22:31:42+0800 -> 2019-09-14T22:31:42+0800 。同时 date-to-time 负责把字符串格式的时间转成 (high low) 秒的list表现形式。 date-to-time 这个函数在遇到不能解析的时间字符串是,会产生异常,不过我假设所有的时间都是合法的。毕竟我的文章的所有时间模板都是自动生成的。同时我此刻也不知道如何处理异常,就忽略了。

  (defun deft-strip-mtime (mtime)
    (date-to-time (deft-chomp (replace-regexp-in-string "#\\+DATE: *" "" mtime))))

最后,在Deft原有的函数中,我修改 deft-cache-newer-file 函数,在适当位置,我把上面的修改的代码替换原有的时间处理代码,就完成deft显示文件内部时间了。