手动美化 mode-line 第二季

之前 @xcodebuild 有发过一个帖子介绍 如何手动美化mode-line: 手动美化mode-line

不过那个方案还不够完美,比如 anzu, window-numbering, flycheck, persp-name 等信息并没有显示在 mode-line 里面。

而 Spaceline 虽然好看,但是性能一直有问题,而且有 bug,会导致 Emacs 卡死。

由于实在是受不了 Spaceline 的这个 bug,我这次准备把 spaceline 的功能都 port 过来。

代码如下:

  (defun zilongshanren/update-persp-name ()
    (when (bound-and-true-p persp-mode)
      ;; There are multiple implementations of
      ;; persp-mode with different APIs
      (progn
             (or (not (string= persp-nil-name (safe-persp-name (get-frame-persp))))
                 "Default")
             (let ((name (safe-persp-name (get-frame-persp))))
               (propertize (concat "[" name "] ")
                           'face 'font-lock-preprocessor-face
                           'help-echo "Current Layout name.")))))


  (defun spaceline--unicode-number (str)
    "Return a nice unicode representation of a single-digit number STR."
    (cond
     ((string= "1" str) "➊")
     ((string= "2" str) "➋")
     ((string= "3" str) "➌")
     ((string= "4" str) "➍")
     ((string= "5" str) "➎")
     ((string= "6" str) "➏")
     ((string= "7" str) "➐")
     ((string= "8" str) "➑")
     ((string= "9" str) "➒")
     ((string= "0" str) "➓")))

  (defun window-number-mode-line ()
    "The current window number. Requires `window-numbering-mode' to be enabled."
    (when (bound-and-true-p window-numbering-mode)
      (let* ((num (window-numbering-get-number))
             (str (when num (int-to-string num))))
        (spaceline--unicode-number str))))

  (defun mode-line-fill (face reserve)
    "Return empty space using FACE and leaving RESERVE space on the right."
    (unless reserve
      (setq reserve 20))
    (when (and window-system (eq 'right (get-scroll-bar-mode)))
      (setq reserve (- reserve 3)))
    (propertize " "
                'display `((space :align-to
                                  (- (+ right right-fringe right-margin) ,reserve)))
                'face face))

  (defun buffer-encoding-abbrev ()
    "The line ending convention used in the buffer."
    (let ((buf-coding (format "%s" buffer-file-coding-system)))
      (if (string-match "\\(dos\\|unix\\|mac\\)" buf-coding)
          (match-string 1 buf-coding)
        buf-coding)))

  (setq my-flycheck-mode-line
        '(:eval
          (pcase flycheck-last-status-change
            (`not-checked nil)
            (`no-checker (propertize " -" 'face 'warning))
            (`running (propertize " ✷" 'face 'success))
            (`errored (propertize " !" 'face 'error))
            (`finished
             (let* ((error-counts (flycheck-count-errors flycheck-current-errors))
                    (no-errors (cdr (assq 'error error-counts)))
                    (no-warnings (cdr (assq 'warning error-counts)))
                    (face (cond (no-errors 'error)
                                (no-warnings 'warning)
                                (t 'success))))
               (propertize (format "[%s/%s]" (or no-errors 0) (or no-warnings 0))
                           'face face)))
            (`interrupted " -")
            (`suspicious '(propertize " ?" 'face 'warning)))))

  (setq-default mode-line-format
                (list
                 " %1"
                 '(:eval (propertize
                          (window-number-mode-line)
                          'face
                          'font-lock-type-face))
                 " "
                 '(:eval (zilongshanren/update-persp-name))

                 "%1 "
                 ;; the buffer name; the file name as a tool tip
                 '(:eval (propertize "%b " 'face 'font-lock-keyword-face
                                     'help-echo (buffer-file-name)))


                 " [" ;; insert vs overwrite mode, input-method in a tooltip
                 '(:eval (propertize (if overwrite-mode "Ovr" "Ins")
                                     'face 'font-lock-preprocessor-face
                                     'help-echo (concat "Buffer is in "
                                                        (if overwrite-mode
                                                            "overwrite"
                                                          "insert") " mode")))

                 ;; was this buffer modified since the last save?
                 '(:eval (when (buffer-modified-p)
                           (concat ","  (propertize "Mod"
                                                    'face 'font-lock-warning-face
                                                    'help-echo "Buffer has been modified"))))

                 ;; is this buffer read-only?
                 '(:eval (when buffer-read-only
                           (concat ","  (propertize "RO"
                                                    'face 'font-lock-type-face
                                                    'help-echo "Buffer is read-only"))))
                 "] "

                 ;; anzu
                 anzu--mode-line-format

                 ;; relative position, size of file
                 "["
                 (propertize "%p" 'face 'font-lock-constant-face) ;; % above top
                 "/"
                 (propertize "%I" 'face 'font-lock-constant-face) ;; size
                 "] "

                 ;; the current major mode for the buffer.
                 '(:eval (propertize "%m" 'face 'font-lock-string-face
                                     'help-echo buffer-file-coding-system))

                 "%1 "
                 my-flycheck-mode-line
                 "%1 "
                 ;; evil state
                 '(:eval evil-mode-line-tag)

                 ;; minor modes
                 minor-mode-alist
                 " "
                 ;; git info
                 `(vc-mode vc-mode)

                 " "

                 ;; global-mode-string goes in mode-line-misc-info
                 mode-line-misc-info

                 (mode-line-fill 'mode-line 20)

                 ;; line and column
                 "(" ;; '%02' to set to 2 chars at least; prevents flickering
                 (propertize "%02l" 'face 'font-lock-type-face) ","
                 (propertize "%02c" 'face 'font-lock-type-face)
                 ") "

                 '(:eval (buffer-encoding-abbrev))
                 mode-line-end-spaces
                 ;; add the time, with the date and the emacs uptime in the tooltip
                 ;; '(:eval (propertize (format-time-string "%H:%M")
                 ;;                     'help-echo
                 ;;                     (concat (format-time-string "%c; ")
                 ;;                             (emacs-uptime "Uptime:%hh"))))
                 ))

最终美化的效果如下图:

上面的 mode-line-format 变量大家还可以随便玩,比如移动 segment 的位置,比如加入新的 string 显示在 mode-line 上面。

温馨提示

mode-line-format 这个变量里面的 :eval 会在每次 mode-line 更新的时候都会被调用,所以请不要把耗时的函数调用放在里面,避免 Emacs 变卡,切记!

Spacemacs 用户需要尝试,可以把 spaceline 这个 package 先 exclude 掉,然后把上面的配置添加到 user-config 函数里面即可。

Happy hacking :smile:

6 个赞

另外,我还把 which-function-mode 从 mode-line 中移除了,我喜欢用 header-line-format:

  ;; http://emacsredux.com/blog/2014/04/05/which-function-mode/
  (which-func-mode)
  ;; when editing js file, this feature is very useful
  (setq-default header-line-format
                '((which-func-mode ("" which-func-format " "))))
  (setq-default mode-line-misc-info
                (assq-delete-all 'which-function-mode mode-line-misc-info))

还有我的 evil-state-tag 的设置如下:

    (setq evil-normal-state-tag   (propertize "[N]" 'face '((:background "DarkGoldenrod2" :foreground "black")))
          evil-emacs-state-tag    (propertize "[E]" 'face '((:background "SkyBlue2" :foreground "black")))
          evil-insert-state-tag   (propertize "[I]" 'face '((:background "chartreuse3") :foreground "white"))
          evil-motion-state-tag   (propertize "[M]" 'face '((:background "plum3") :foreground "white"))
          evil-visual-state-tag   (propertize "[V]" 'face '((:background "gray" :foreground "black")))
          evil-operator-state-tag (propertize "[O]" 'face '((:background "purple"))))

4 个赞

好东西,抄过来玩玩。

看见了这些代码分享,很可惜不会用她,估计许多人都有这个遗憾吧。楼主可否就如何使用细说下

我直接把 zilongshanren 的 ui layer 复制一份作为自己的一个 layer,很方便。然后 exclude 了 spaceline ,省得自己折腾画虎不足。

zilongshanren 的 ui laye 哪儿有?请教

这个就是了:

谢谢!谢谢热心人!好人一生平安

有什么办法可以快速更新mode-line吗? 每次都要重启有点难受, Google update和raload都没结果

查 elpa 怎么用

anzu–mode-line-format is void

如果设置 这个anzu–mode-line-format就会报下面的错,我用的是purcell的配置,但是注释掉就不会报错,但是number号显示不好看,数字显示两次了。

Symbol's value as variable is void: anzu--mode-line-format

挺好看的,刚用上了

|(...)||代表光标位置,两者选一然后C-x C-eeval这个表达式(evil-insert-state或者纯净emacs下)。evil-normal-state下第二种可用,第一种有点差别。第一种似乎是错的,sexp也会闪一下但是不会生效。

  • 在这里eval(setq-default mode-line-format ...)改变default,打开新的buffer会显示刚刚修改过的mode-line样式。
  • setq-default改成setq后eval,可以在当前buffer看到变化(但不会影响其他buffer因为只改了buffer-local的mode-line-format)

我把这段(setq-default mode-line-format ...)

(with-eval-after-load 'anzu
  (with-eval-after-load 'evil-anzu
    (with-eval-after-load 'which-func
)))

括起来了。这样不漂亮但是能解决。有个小问题是这样spacemacs home buffer会是自定义之前的mode-line。

我不明白的是,这里还有比如my-flycheck-mode-line,没有出现symbol’s value void的问题

用上了 挺好的 可是我的 naya-mode 没了 怎么才能兼容一下

怎么现实 naya-mode呀 用上就失效了

(:eval (list (nyan-create)))加入mode-line-format就行。 试试执行这个:

(setq mode-line-format
      (list
       '(:eval (list (nyan-create)))
       ))

好的 我试一下 谢谢

加进去 成功了 感谢 但是这个位置有点偏能不能调一下 我还加了 all-the-icons 来显示major-mode 但是也有点歪

'(:eval (propertize (all-the-icons-icon-for-mode major-mode)))

E681E2F2-AB95-439B-9C39-F1C929E3E5AD

歪了

改了这里也没用 height v-adjust。。。求指点 感谢

你是说上下?我也不清楚。如果v-adjust有用的话,应该是:v-adjust