发现你的Emacs Axis

众所周知,键帽按久了就会掉色,我决定多买一份我最常用的几个按键的键帽。

鉴于我平时码字用的唯一指定软件是Emacs,所以,我要让Emacs告诉我我按哪些键的次数最多。也就是写一个在Emacs里统计按键次数的程序。Emacs有内建的sqlite支持,因此我决定把在Emacs里所有的击键数据都存到一个sqlite数据库里面,便于长时间统计,顺便省掉了写统计界面的麻烦,用sqlite-mode读数据库就好了:index_pointing_up::nerd_face:

(defgroup axis '()
  "Statistics of how you play Emacs."
  :group 'applications)

(defcustom axis-db-location
  (concat user-emacs-directory "axis-data.sqlite")
  "A path to axis-db, which is a sqlite database."
  :group 'axis
  :type 'string)

(defvar axis-db nil
  "SQLite database connection for command-key tracking.")

(defun axis-db-init ()
  "Initialize the SQLite database with performance optimizations."
  (setq axis-db (sqlite-open axis-db-location))
  ;; Apply performance PRAGMAs
  (sqlite-execute axis-db "PRAGMA journal_mode = WAL;")
  (sqlite-execute axis-db "PRAGMA synchronous = NORMAL;")
  (sqlite-execute axis-db "PRAGMA cache_size = 2000;")
  ;; Create table if it doesn't exist
  (sqlite-execute
   axis-db
   "CREATE TABLE IF NOT EXISTS command_keys (
       command TEXT,
       key TEXT,
       count INTEGER,
       PRIMARY KEY (command, key)
     );"))

(defun axis-db-close ()
  "Close the SQLite database connection."
  (when axis-db
    (sqlite-close axis-db)
    (setq axis-db nil)))

(defun axis-record-command-and-keys ()
  "Record the current command and keys into the database."
  (when (and (commandp this-command)
             (symbolp this-command))
    (let ((key-str (key-description (this-command-keys))))
      (sqlite-execute
       axis-db
       "INSERT INTO command_keys (command, key, count) VALUES (?, ?, 1)
        ON CONFLICT(command, key) DO UPDATE SET count = count + 1;"
       (list (symbol-name this-command) key-str)))))

(defun axis-show-command-summary ()
  "Display commands and their associated keys with usage counts."
  (interactive)
  (let ((data
         (sqlite-select
          axis-db
          "SELECT command, SUM(count) FROM command_keys GROUP BY command
           ORDER BY SUM(count) DESC;"))
        )
    (with-output-to-temp-buffer "*Axis Commands & Keys*"
      (princ "Command Usage Statistics:\n\n")
      (dolist (entry data)
        (princ (format "Command: %s - %d times\n" (car entry) (cadr entry)))
        ))))

;;;###autoload
(define-minor-mode axis-mode
  "Record statistics of input commands and keys."
  :init-value nil

  (if axis-mode
      (progn
        (unless axis-db (axis-db-init))
        (add-hook 'pre-command-hook 'axis-record-command-and-keys)
        (message "Axis mode enabled."))
    (progn
      (remove-hook 'pre-command-hook 'axis-record-command-and-keys)
      (axis-db-close)
      (message "Axis mode disabled.")))
  )

(provide ‘axis-mode)

(axis-mode)添加到你的启动配置里一段时间,然后在你下次购买/保养键盘的时候调用axis-show-command-summary,就可以通过分析统计数据找到最需要关心的键帽了!

P. S. 最初的主意是,我想要找到最常用的命令子序列(我把它叫做Emacs Riff),这样可以知道哪些命令适合封装成一个函数,哪些封装纯属多余。但是,我没来得及想出好的子序列分类算法用来高效的处理10^5-10^7规模的序列数据,这里就抛砖引玉了。也许我们只通过单点的统计情况也可以推断出路径的分布情况?

8 个赞

这个很实用,点赞。

试试去,zsbd

之前见过一个类似:keyfreq

pbt和abs二色键帽不会掉色,只会打油。pbt热升华键帽也要很久才会掉色。

写了会 org,这是执行次数在 10 次以上的。

Command Usage Statistics:

Command: org-self-insert-command - 3093 times
Command: mwheel-scroll - 724 times
Command: previous-line - 434 times
Command: forward-char - 433 times
Command: next-line - 348 times
Command: org-delete-backward-char - 342 times
Command: backward-char - 208 times
Command: self-insert-command - 188 times
Command: org-return - 75 times
Command: org-beginning-of-line - 71 times
Command: org-end-of-line - 63 times
Command: org-delete-char - 60 times
Command: mouse-drag-region - 51 times
Command: w32-drag-n-drop - 51 times
Command: save-buffer - 50 times
Command: mouse-set-point - 42 times
Command: org-export-dispatch - 35 times
Command: move-beginning-of-line - 31 times
Command: completion-preview-insert - 27 times
Command: org-open-line - 24 times
Command: org-kill-line - 22 times
Command: delete-backward-char - 21 times
Command: other-window - 20 times
Command: org-edit-special - 18 times
Command: org-edit-src-exit - 18 times
Command: recenter-top-bottom - 18 times
Command: vertico-exit - 17 times
Command: forward-page - 13 times
Command: backward-kill-word - 12 times
Command: yank - 12 times
Command: yy/insert-zws - 12 times
Command: isearch-printing-char - 11 times
Command: set-mark-command - 11 times

当前的主键是(command, key),所以数据库的大小很难超过10M。可以试试一直开着,累计个一年半载再拿出来看看。

sqlite-mode-open-file访问axis-db-location位置的数据库文件可以查询到更详细的统计数据。sqlite作为后端省了不少麻烦,缓存和并发什么的都不用考虑了。

1 个赞

尝试分析:

看起来你用鼠标的次数并不少,主要用来滚轮翻页和定位,而且不常用到forward-wordbackward-word这些命令。

按了50次save-buffer,平均每敲32个字就保存一次,你是被软件崩溃导致内容丢失折磨过的人。

插入和删除字符次数的比例大概是10比1,你的思维混乱度是 \log \frac{10}{9} \approx 0.1053

3 个赞

666,还能分析的 :rofl:

哈哈哈 这是用AI 分析的吧

AI应该想不出来 c= \log \frac{\text{chars interted}}{\text{chars interted}-\text{chars deleted}} 这种奇怪的算法吧。

1 个赞

666,你这个观察很有趣。

提一个 typo:最后一行应该是

(provide 'axis-mode)

编译报错:

(error "Loading file axis-mode.elc failed to provide feature ‘axis-mode’")
1 个赞

谢谢,已修改