到此我们关于Buffer 和window 的内容就全部介绍完了。按照惯例本节对之前内容进行总结并完成一个综合性的实战练习
知识回顾
首先我们学习关于buffer管理的一些函数
get-buffer-create和get-bufferkill-bufferset-bufferwitch-current-bufferbuffer-namebuffer-live-p
接着我们学习了一些控制buffer属性相关的函数
buffer-namerename-bufferbuffer-file-name和set-visited-file-namebuffer-modified-p和set-buffer-modified-pbuffer-read-onlyinhibit-read-only
后面我们进入关于Window的内容,window有下列函数
split-windowset-window-bufferwindow-bufferselect-windowselected-windowwalk-windowsdelete-window和quit-windowdisplay-buffer和pop-to-buffer
之后介绍了如何在buffer中贴上一个标识的标签,通过 overlay机制
make-overlayoverlay-put和overlay-getoverlay-start和overlay-endoverlays-at和overlays-in
最后我们介绍了如何跨buffer进行文本提取,主要是一个函数 insert-buffer-substring
综合练习
今天的综合练习,我们尝试将这些内容结合起来,我们完成一个简单的交互式的日志buffer。它具有下面的功能:
- 对外提供一个简单的 log 函数,它支持
ERROR、WARNING、DEBUG、INFO四种类型的日志 - 代码种每调用一次
log函数都会往*App-Log*buffer种记录一条日志 - 日志的格式为 ERROR[line-num]: message
- 我们可以通过日志中记录的信息快速回到对应写日志的位置
我们先实现一个对外的日志函数, 主要用来记录日志,并保存一些隐藏信息
(defun my/log(level message)
(let* ((level-sym (if (stringp level) (intern (upcase level)) level))
(src-buf (current-buffer))
(file-or-buf (or (buffer-file-name) (buffer-name)))
(line-num (line-number-at-pos))
(log-buf (get-buffer-create "*App-Log*")))
(with-current-buffer log-buf
(let ((inhibit-read-only t))
(goto-char (point-max))
(let ((start-pos (point)))
;; 格式 [LEVEL][buffer-name][line-number]:message
(insert (format "[%s][%s][%d]: %s\n"
level-sym file-or-buf line-num message))
;;为了便于后续跳转回来,这里我们保存一些隐藏信息
(add-text-properties start-pos (point)
`(log-file ,file-or-buf
log-line ,line-num
log-buf ,src-buf)))))))文本属性我们目前没有介绍到,属于朝纲的内容,但是相对来放在这里并不难理解。另外注意这里我们使用的是反引号而不是列表中常用的单引号。单引号的作用是禁止对表达式求值,但是我们实际需要对后面的 file-or-buf这些变量进行求值。所以我们使用反引号表示将这个作为列表的模版,对逗号后面的表达式求值。
然后我们添加一个从log 信息中跳转到对应位置的函数,该函数主要作用是从日志文件中读取隐藏数据,并跳转
(defun my/log-jump-to-source()
(interactive)
(let ((file (get-text-property (point) 'log-file))
(line (get-text-property (point) 'log-line))
(src-buf (get-text-property (point) 'log-buf)))
(message "%s %s %d" file (buffer-name src-buf) line)
(if (not (and file line))
(message "没有有效的日志源信息")
(if (and src-buf (buffer-live-p src-buf))
;;弹出或切换到源buffer
(pop-to-buffer src-buf)
(find-file file))
;;移动到对应行
(goto-char (point-min))
(forward-line (1- line)))))在跳转的时候,我认为在跳转回源buffer时,应该重点仍然在日志上,我们只是简单的看看报错的日志在哪里。也就是要保留日志buffer、短暂的显示源buffer。所以就排除了使用 split-window 的想法,但是我们可能需要跳转之后短暂的修改代码,所以我认为使用 pop-to-buffer 比较合适。
到此我们简单的日志工具就完成了。虽然它不那么完美,例如我们记录的其实并不是调用log函数时的行,和buffer,而是执行时的当前buffer和光标所在位置。实际上我们可以通过宏来解决,就像c/c++ 中的 __FILE__、__LINE__ 一样。读者可以查阅相关资料尝试解决这些问题。
评论 (0)