Emacs才是世界上最强的IDE - 用linum+智能显示行号
稍微现代点的编辑器都有行号显示功能,行号显示是在buffer的左侧一栏显示当前buffer每行的行号,比较直观,但其实在以鼠标为中心的编辑器、IDE中,其实这个行号显示功能不大,但是在以纯键盘为中心的编辑器,如vi, Emacs中这个行号显示功能就非常方便了,它可以帮助你迅速定位到某行,知道了行号,直接通过M-x goto-line到达某行,非常方便。
但是Emacs比较“土”,Emacs23之前一直没有内置的行号显示功能,但是“土”归“土”,但是它很听话,你要怎样控制它就怎样控制它,所以诞生了一堆外置的行号显示插件,比如wb-line-number, setnu, setnu-plus, display-line-number, 虽然很多,但是都有一些小bug,不是很方便。Emacs23出来后,我欣喜的看到,它已经内置了行号显示功能,用的是linum-mode,既然是内置的,当然性能比较好,bug也比较少。它每次只显示当前buffer可视区域内的行号,而且用了一个小缓存的技巧,保证了性能,即使打开大文件也没问题。它的行号显示格式是通过linum-format来控制的,该值为’dynamic的话,就会动态控制行号的宽度,比如当前buffer最大行是3位数,那么行号的宽度就是3,这个format还可以设为固定的宽度,比如设置成”%4d”,那么始终显示成4个字符宽度。一般的编辑器用的都是那个’dynamic, 动态控制行号显示的宽度。
但是这个动态显示宽度还不够智能,比如当前buffer最大行的行号是3位数,我显示前面几行行号的时候,比如从1到50,没必要也用3个字符宽度来显示,只需要用2位就可以了,所以我写了一个linum+,来弥补linum这个缺点,linum+主要是重新定义了下linum里面更新行号的函数linum-update-window,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | ;;;###autoload (defun linum+-generate-linum-format (format-type limit) "Generate line number format by FORMAT-TYPE, LIMIT is `window-end' of win." (cond ((stringp format-type) format-type) ((or (listp format-type) (vectorp format-type) (eq format-type 'dynamic) (eq format-type 'smart)) (let* ((dynamic-width (or (vectorp format-type) (eq format-type 'smart))) (old-format (if (eq format-type 'dynamic) linum+-dynamic-format (if (eq format-type 'smart) linum+-smart-format format-type))) (w (length (number-to-string (line-number-at-pos (if dynamic-width limit (point-max)))))) (new-format (if (listp old-format) (car old-format) (if (vectorp old-format) (aref old-format 0) old-format)))) (format new-format w))))) ;;;###autoload (defun linum-update-window (win) "Update line numbers for the portion visible in window WIN." (goto-char (window-start win)) (let* ((line (line-number-at-pos)) (limit (window-end win t)) (fmt (linum+-generate-linum-format linum-format limit)) (width 0)) (run-hooks 'linum-before-numbering-hook) ;; Create an overlay (or reuse an existing one) for each ;; line visible in this window, if necessary. (while (and (not (eobp)) (<= (point) limit)) (let* ((str (if fmt (propertize (format fmt line) 'face 'linum) (funcall linum-format line))) (visited (catch 'visited (dolist (o (overlays-in (point) (point))) (when (equal-including-properties (overlay-get o 'linum-str) str) (unless (memq o linum-overlays) (push o linum-overlays)) (setq linum-available (delq o linum-available)) (throw 'visited t)))))) (setq width (max width (length str))) (unless visited (let ((ov (if (null linum-available) (make-overlay (point) (point)) (move-overlay (pop linum-available) (point) (point))))) (push ov linum-overlays) (overlay-put ov 'before-string (propertize " " 'display `((margin left-margin) ,str))) (overlay-put ov 'linum-str str)))) (forward-line) (setq line (1+ line))) (set-window-margins win width))) |
其实这个linum-update-window改的很简单,主要是根据当前最大行号来制定一个宽度。
linum+.el全文件在这里下载。
因为只有行号从n位数过度到n+1位数的时候,行号宽度才会发生改变,这种机会并不多,所以我这个linum+性能基本上和linum一样。
linum中, linum-format有可以有三种形式, string, ‘dynamic, 或者函数, 你使用了linum+以后,增加了三种形式, 分别是’smart, 含有一个字符串的list, 和含有一个字符串的vector, string和函数还和linum中的作用一样, ‘dynamic还是根据当前buffer最大行来控制最大的行号宽度, ’smart则是智能根据当前可使范围内最大行号来控制行号显示宽度的, 如果是list的话, 则是根据list中的字符串来和当前最大行号的宽度一起作为format函数的参数来生成当前行号的格式,然后再用这个格式来显示行号,比如当前buffer显示的行数是从1到50,那么最大的行号是50,其宽度是2,list的值为’(“%%%dd|”), 那么当前buffer每行行号的显示格式就是:
1 2 | (format "%%%dd|" 2) =>"%2d|" |
得到”%2d|”,然后当前buffer每行的行号显示格式就是”%2d|”了。
当linum-format为vector的时候, 处理过程基本和list的一样, 唯一的差别就是行号的格式的时候, “%d”是用当前buffer最大行号的宽度去替换的, 比如当前最大行号是125, 其宽度是3, 那么当前buffer每行行号的显示格式就是:
1 2 | (format "%%%dd|" 3) =>"%2d|" |
得到”%3d|”,然后当前buffer每行的行号显示格式就是”%2d|”了。
其实, linum+内部实现时, ‘dynamic和’(“%%%dd|”)作用一样, ’smart和["%%%dd|"]作用一样. 你不喜欢’smart后面的”|”的话, 把linum-format设置为["%%%dd"]就可以了.
如果你喜欢linum中的’dynamic, 根据buffer最大的行号生成固定的宽度, 而不喜欢linum+中’smart那样动态变化的宽度, 你仍然可以把linum-format配置成’dynamic, 或者配置成list, 比如’(“%%%dd”), 这样既可以享受固定的宽度, 又可以享受可配置的格式(linum中linum-format为’dynamic, 行号的格式你是控制不了的)
下面是linum+的截图:

loading...
奇怪了,为什么很多大牛都对Emacs感觉良好,但对于Vim就不感冒。但Vim在贫民阶层是很受欢迎的啊。
[回复]
ahei 回复:
四月 4th, 2010 at 3:28 下午
@haowan, 正如大家所说, vi相对来说更容易学习吧, 所以”贫民”阶层用的很多, 还是有不少大牛用vi的, 不过我见到的vi大牛也都同时用Emacs,
[回复]
Vim的最大优势就是击键频率
[回复]
从1变到10,只有10行的距离;从10到100,只有90行的距离;从100变到1000,却有900行的距离,因此把3位数宽度设置为行号显示最小宽度,从效率上来讲应该是比较好的。你这样做其实有点儿得不偿失。
[回复]
ahei 回复:
四月 18th, 2010 at 12:59 下午
@nichen, 你这样说是有一定道理的, 不过还有另外一种情况, 且看看前面的网友的回复:
@tmpistemp, 而且,如果觉得两位数三位数的变化不太值得的话,我感觉三位和四位的就很值得了.如果共有1100行的话,为那100行显示4列行号就很不值得了,更不用说11111的情况.很好.
[回复]
tmpistemp 回复:
十月 19th, 2010 at 6:29 下午
@nichen, 这位仁兄理解的可能有失误,博主写的脚本是让显示行号位数跟已经显示出来的区域的行号相适应,而不是与整个文件本身的总行数相适应
[回复]
emacs 23里面已经可以显示行号咯 (global-linum-mode t)
请教黑哥,如何才能把这些 minor mode 链接到 major mode中阿 比如 c++ mode
这样只有打开major mode的时候才启动那些minor mode
毕竟 看个小说都显示行号 有点……
[回复]
ahei 回复:
十一月 15th, 2010 at 9:04 下午
@soey, add-hook
[回复]
soey 回复:
十一月 15th, 2010 at 9:46 下午
@ahei, 回得好快阿,感动~~支持一个~
[回复]
cookieu 回复:
八月 14th, 2011 at 7:00 下午
@soey, 具体怎么hook呢?不会elisp呀
[回复]
不知为什么就想学这个。。。我平时不大型编程,就是用Matlab编写些算法,可是看到这个http://blogs.mathworks.com/desktop/2009/09/14/matlab-emacs-integration-is-back/后坚定了我学习Emacs的决心。。。。学个3个月看能不能代替EmEditor。。。
[回复]
我有个问题:这个行号显示的背景颜色和buffer的背景颜色是一样的,这样有点不爽。有没有办法单独设置行号所在的背景颜色?
[回复]
ahei 回复:
十二月 18th, 2010 at 3:02 下午
@思无邪, 设置linum的背景颜色就可以了
[回复]
思无邪 回复:
十二月 18th, 2010 at 11:08 下午
@ahei,
能否详细一点,刚用emacs几天,不太会。
我google了“emacs linum background”找了好久,也没找到。
另外,在linum与buffer之间还有个Line Wrapper条,我发现这个小条的背景颜色与color-mode有关,在有些color-mode下,它的颜色与buffer的颜色是不一样的。
[回复]
ahei 回复:
十二月 19th, 2010 at 12:21 上午
@思无邪,
[回复]
思无邪 回复:
十二月 19th, 2010 at 1:08 上午
@ahei,
非常感谢
学到了一个set-face-命令
还有个小问题:比如我打开一个只有5行的文件,这个时候,linum前5行–也就是有行号显示的地方–前景色与背景色都是所想要的;但是5行之后没有行号显示的地方,其颜色和没设置之前一样。
我试了下
(set-face-background ‘border “red”)
貌似不行
[回复]
思无邪 回复:
十二月 19th, 2010 at 1:21 上午
@ahei,
google了很久,貌似应该设置关于left-margin的属性,但
(set-face-background ‘left-margin “red”)
无效
[回复]
ahei 回复:
十二月 19th, 2010 at 11:09 上午
@思无邪, 后面的行号不显示,背景当然一样, left-margin是一个普通变量,不是face,当然不能设置背景
[回复]
思无邪 回复:
十二月 19th, 2010 at 12:22 下午
@ahei,
那么没办法让Linum所在的窄条背景色保持一致了?
[回复]
ahei 回复:
十二月 19th, 2010 at 12:27 下午
@思无邪, 你别设置linum的背景色不就ok了?
[回复]
思无邪 回复:
十二月 19th, 2010 at 3:07 下午
@ahei,
囧…
[回复]
ahei 回复:
十二月 19th, 2010 at 8:44 下午
@思无邪, 你为什么要改变linum的背景颜色呢?默认挺好的阿
[回复]
思无邪 回复:
十二月 19th, 2010 at 10:58 下午
@ahei,
关键是这个颜色和buffer的颜色一样的,感觉不太好,容易混绕
这个行号在windows下是不是有问题啊?滚动起来卡到爆.
[回复]
ahei 回复:
十二月 30th, 2010 at 8:55 下午
@telancs, 还好阿,不慢的
[回复]
telancs 回复:
十二月 31st, 2010 at 6:58 下午
@ahei,
(global-linum-mode t)
这不是显示行号的配置语句吗.
你可以试下配置就这一行,打开一个百行左右的文件,拖到滚动条.
反正我是卡到爆了,好多台都试过了,只要是windows,就没辙,ubuntu下没这个问题.
[回复]
ahei 回复:
十二月 31st, 2010 at 7:44 下午
@telancs, 我经常用的,都没问题的
[回复]
telancs 回复:
一月 1st, 2011 at 2:58 下午
@ahei,
那真是奇怪了,不可能只有我用的有问题啊…囧
[回复]
ahei 回复:
一月 1st, 2011 at 6:08 下午
@telancs, 你可以用二分调试法查下原因
[回复]
telancs 回复:
一月 1st, 2011 at 9:37 下午
@ahei,
感谢你的耐心回复了,反正先不用行好的了…无奈.
[回复]
这个补丁已经进入debian了还是进入emacs了?怎么我没有安装这个文件,但是直接能用了?奇怪啊奇怪。。。
[回复]
ahei 回复:
八月 13th, 2011 at 4:49 下午
@tmttmm, 内置进emacs了
[回复]
这个或许可能用到
(defun my-linum-on () ; linum should turn off in non-editor buffer
(unless (or (minibufferp)
(equal frame-title-format “Speedbar 1.0″)
(equal (string-match “\\*.*\\*” (buffer-name)) 0))
(linum-mode 1)))
(define-globalized-minor-mode my-global-linum-mode linum-mode my-linum-on)
(my-global-linum-mode 1)
[回复]
你好,我使用text-scale-increase调大字体的时候line number字体也跟着变大了,但是用于显示line number的区域没有跟着调整,所以没法显示完整的line number,我注意到下面的mode line字体大小没有跟着变化。请问则么解决这个问题:
1)如何让line number的字体大小固定
或者
2)让line number显示区域跟随变化。
谢谢。
[回复]
ahei 回复:
十二月 10th, 2011 at 12:58 下午
@Shiquan Wang, 这个可能要改linum-plus了
[回复]