Vim 中文速查表/Cheatsheet(全网最完善)

March 7th, 2018 No comments

春节期间整理了一份 Vim 中文速查表,免得经常东搜索西搜索的:

https://github.com/skywind3000/awesome-cheatsheets/blob/master/editors/vim.txt

看了一下,应该是现在 Vim 所有中英文速查表里最完善的一份,有时候速查表比看书搜网页高效多了。

Categories: 随笔 Tags:

GDB 从裸奔到穿戴整齐

February 28th, 2018 No comments

无数次被问道:你在终端下怎么调试更高效?或者怎么在 Vim 里调试?好吧,今天统一回答下,我从来不在 vim 里调试,因为它还不成熟。那除了命令行 GDB 裸奔以外,终端下还有没有更高效的方法?能够让我事半功倍?

当然有,选择恰当的工具和方法,让 GDB 调试效率成倍的提升并没有任何问题。当然,前提条件是你至少会在使用最原始的 GDB。

裸奔状态:原始的 GDB 命令行

穿上各种衣服前,至少得先学会裸奔,找份简单的 GDB cheat sheet 对照一下:

生产环境中出现崩溃时,因线上服务器一般没有开发环境,也无配套源代码,所以程序崩溃后,如果你懒得把 core 文件拖回到开发机检查,可以先在线上服务器先简单gdb看一下。

GDB命令密密麻麻,常用的也就表格上那几条,比如进去以后第一步先用 bt 查看一下调用栈,info local查看一下本地变量,再配合 up/down 在整个调用栈的不同层次之间上下移动一下,检查各处局部变量的值,print 一下某个表达式,即便没代码,看下符号和反汇编,一般也能调试个七七八八。

碰到复杂点的 BUG,必须配合源代码了,那你得把 core 文件拉到开发环境中,再用 gdb 对照源代码调试,配合 list [行号] 指令查看当前运行的源代码,再配合其他方法进行调试。

那么这时候,如果调试复杂度继续上升,你需要不断的断点,每次 next / step 单步完后你都需要 list 一下前后源代码,或者用 disassemble [函数名/地址] 查看一下指令的话,不少人会感觉到抓狂,这时我们需要给裸奔的 GDB 穿条内裤了。

穿上内裤的 GDB – TUI

这是 gdb 自带的文本界面,使用 gdb -tui 命令启动,比如:

gdb -tui hello

即可打开我们的文本界面:

上方是源代码窗口,下面是 gdb 终端,窗口管理快捷键模仿 emacs,使用 c-x o 进行窗口切换,如果你还想查看指令窗口,可以输入:layout split

然后你单步的时候上面的源代码窗口会跟着滚动,比之前不停的 list 方便不少吧,要看前后源代码可以继续 c-x o 切换窗口后上下滚动。

有时候你 up/down 切换了栈帧后,上面的代码也许来不及更新,那么你用 update 指令,让上面的代码窗口定位到你最新的执行位置,有时也需要 CTRL-L 重绘下整个界面。

在 gdb 自带的 tui 模式中,你不但可以随时查看代码/指令,还可以查看寄存器。好吧,其实 gdb再简陋,本身还是配套了一条内裤的,只是很多人忘记拿出来穿了。

穿上内衣:gdbinit

如果上述文本 TUI的信息不够丰富的话,也许你会对 .gdbinit 感兴趣,~/.gdbinit 是一个 gdb配置脚本,可以设定一些由 python 编写的插件,比如 peda:

在 ~/.gdbinit 上面配置了 peda 后,可以看到命令提示符就从 (gdb) 变成了 gdb-peda$ 。每敲一个单步命令,peda 都会显示出无比丰富的信息,你还可以配置添加更多,配合高亮,你可以得到一个加强版的命令行。

不过这个我用的并不多,因为它会影响 TUI 模式的界面,我不喜欢在我的 gdb 里搞太多乱七八糟的东西。同时,当我们的调试需求继续变得更加复杂,你要不停的断点,频繁的单步,这些传统命令行里不停的输入各种指令显然已经让我们疲惫不堪了,能否直接在界面上查看源代码并且直接在代码上快捷键断点,快捷键单步呢?

可以啊,试试 cgdb。

穿上外套的 GDB:cgdb

CGDB 类似 gdb tui 分为终端窗口和代码窗口:

Read more…

Categories: 随笔 Tags:

终端软件里正确设置 ALT 键和 BACKSPACE 键

February 23rd, 2018 No comments

不管你在终端下使用 vim/neovim, emacs, nano 或者 zsh,你都会碰到使用 ALT 键的情况(终端下叫做 meta键),而由于历史原因,大部分终端软件的默认设置都无法正确使用 ALT 键。

要在终端下正确使用 ALT键最简单的做法是:首先将终端软件的 “使用 Alt键作为 Meta键” 的功能打开,意思是如果你在终端下按下 ALT+X,那么终端软件将会发送 <ESC>x 两个字节过去,字节码为:0x27, 0x78。

SecureCRT:终端设置

XShell4 终端设置:

其他终端软件里:

  • Putty/MinTTY 默认ALT+X 就是发送 <ESC>x过去
  • Mac下面的 iTerm2/Terminal.app 需要跟 XShell / SecureCRT一样设置一下
  • Ubuntu 下面的 GnomeTerminal 默认也是发送 <ESC>x过去的
  • 任意平台下面的 xterm 可以配置 ~/.Xdefaults 来设置这个行为。

这样的话,终端里的软件就能识别你的 ALT 组合键了,设置好以后,你可以在终端下使用命令:

showkey -a

来查看自己的设置正确不,是不是按下 ALT_a 后正确发送了 0x1b, 0x61 两个字节过去了?

功能键超时

那么终端里按下 ALTX 和先按 ESC 键再按 X键有什么区别呢?答案是没有区别,你在 emacs 中快速先后按下 ESC 和 v 的话,emacs 就会以为你按了 ALTv,然后给你来个上翻页了。

远程主机一般靠超时来区别到底是 ALTX 还是先按 ESC 再按 X 键,如果你 100 毫秒内先后发送了 ESC 和 X 过来,远程主机会识别成 ALTX 键,否则识别成 ESC 和 X 两个键,这个超时时间可以设置,一般设置成 100ms 或者 50ms ,但不要低于 25ms,否则网络一卡可能有概率会判断错误。

在 Vim/NeoVim 中你可以通过 ttimeoutlen 来设置功能键超时检测为 50 毫秒,比如:

:set ttimeout ttimeoutlen=50

而 tmux 中,也有类似配置,比如:

set-option -g escape-time 50

就连 ncurses 库中也有类似功能键超时检测的设置,来区别到底是功能键/组合键,还是数个单独的按键。这里你可能觉得这样靠超时检测很不可靠,那你要问 VT100, VT220 的标准制定者了。不过我个人常年设置成 50 毫秒后,连接到各种国内外速度不一的主机上并没有被网速慢把功能键卡成两个键的情况,应该是终端软件中本身也在尽量保证同一组按键序列能够尽量同时发送,所以我链接到国外 rtt=400 左右的主机上工作时,尽管网速经常不稳定,也没有发生错误识别的问题,那其他网速正常的主机就更不用担心了。

更友好的终端设置

上面在 SecureCRT / XShell 中设置了将 alt 键作为发送 +ESC x 的 meta 键后,你会发现,终端软件中固有的一些 ALT 组合键全部失效了,比如原来在终端中 ALT1 到 ALT9 可以切换终端的 TAB,ALT_B 可以打开链接管理器,这下都全部用不了了,这是件比较坑爹的事情,能不能保留有限的几个 ALT 组合键给终端软件使用,剩下的全部当作 meta 键呢?答案是可以的,先取消终端里 ALT 当作 meta 键的设置,恢复成默认状态,然后打开终端软件 keymap 设置窗口,将你不需要保留的 ALT 组合键全部设置成发送 +ESC x 字符串。

那么一个个设置可能有些麻烦,对于 SecureCRT 的话,我生成了一个配置文件:

A   VK_A                    "\033a"
A   VK_D                    "\033d"
A   VK_E                    "\033e"
A   VK_G                    "\033g"
A   VK_H                    "\033h"
....
AS  VK_A                    "\033A"
AS  VK_B                    "\033B"
AS  VK_C                    "\033C"
AS  VK_D                    "\033D"

可以到 这里 下载现成的,在 keymap editor 窗口中加载进去即可。

这份 keymap 配置除了保留了 SecureCRT 常用的 ALT1 – ALT9 ,ALTB, ALTR 和 ALTI 外,其他的 alt 组合都设置成了 +ESC x 的 meta 键序列。并且将 ALTSHIFT1 到 ALTSHIFT_9 映射到了终端里的 +ESC 1 到 +ESC 9 ,也就是说你的 ALT+数字 被保留给软件切换TAB用了,而 ALT+SHIFT+数字 被映射成了终端链接中的 ALT+数字,这样在终端里碰到需要 ALT+数字 的地方,可以用 ALT+SHIFT+数字 来代替。

设置好以后,可以继续使用 Linux 下的 showkey -a 命令查看一下是否正常。

Vim里识别 ALT 键

前面在终端软件里配置好 ALT键,但是 Vim 的话,由于历史原因,需要在你的 vimrc 里加一段键盘码配置:

function! Terminal_MetaMode(mode)
    set ttimeout
    if $TMUX != ''
        set ttimeoutlen=30
    elseif &ttimeoutlen > 80 || &ttimeoutlen <= 0
        set ttimeoutlen=80
    endif
    if has('nvim') || has('gui_running')
        return
    endif
    function! s:metacode(mode, key)
        if a:mode == 0
            exec "set <M-".a:key.">=\e".a:key
        else
            exec "set <M-".a:key.">=\e]{0}".a:key."~"
        endif
    endfunc
    for i in range(10)
        call s:metacode(a:mode, nr2char(char2nr('0') + i))
    endfor
    for i in range(26)
        call s:metacode(a:mode, nr2char(char2nr('a') + i))
        call s:metacode(a:mode, nr2char(char2nr('A') + i))
    endfor
    if a:mode != 0
        for c in [',', '.', '/', ';', '[', ']', '{', '}']
            call s:metacode(a:mode, c)
        endfor
        for c in ['?', ':', '-', '_']
            call s:metacode(a:mode, c)
        endfor
    else
        for c in [',', '.', '/', ';', '{', '}']
            call s:metacode(a:mode, c)
        endfor
        for c in ['?', ':', '-', '_']
            call s:metacode(a:mode, c)
        endfor
    endif
endfunc

call Terminal_MetaMode(0)

然后你就可以正确在 Vim 映射 ALT 键了,具体原理见 :help set-termcap 以及:

http://www.skywind.me/blog/archives/1846

其他的诸如 emacs, nano 和 neovim 等都不需要额外设置。

终端里正确设置 BS 键

还是 VT100 的历史原因,BACKSPACE 键和 CTRL-H 给混淆起来了,默认情况下,终端里不管按 CTRL-H 还是 BACKSPACE 时都是发送 ASCII 码为 0x08 的 ^H 过去。导致我们想在 Vim/Emacs 中映射 CTRL-H 去干别的事情时会影响到 BACKSPACE 键的使用。

因此得按照 VT220 的新标准修改一下 BACKSPACE 的设置,让它发送 ASCII 码 0x7f 即 ^? 过去:

  • SecureCRT: Session Options -> Terminal -> Emulation -> Mapped Keys, 勾选 Backspace sends delete
  • XShell: Properties -> Terminal -> Keyboard 里,把<BS>设置成 127,而 <DEL>设置成 VT220 Del
  • Putty: 好像默认是 ^? 的不过需要到:Configuration -> Terminal -> Keyboard 下面下确认下 The Backspace key 是 Control-? (127)
  • Terminal.app: 好像默认是发送 ^? 的,你也可以到 Profiles Advanced 下面确认下 “Delete sends Control-H” 没有勾选。
  • iTerm2: 默认也是发送 ^? 的,可以到 Profiles -> Keys下面确认一下 “Delete key sends ^H” 没有被勾选。
  • Gnome-Terminal: 默认发送 ^? 的,参见具体文本配置文件。
  • MinTTY: 设置 vt220/xterm 的话,默认发送 ^? 的,似乎还不能改。

你要深究原因的话,可以见:

http://www.skywind.me/blog/archives/1857

修改好以后可以继续运行:

showkey -a

检查一下,你的 BACKSPACE 键被按下时是否正确发送了 0x7f 字符过去。设置成功的话,终端下 CTRL-h 和 backspace 就不会出现混淆问题了。

Categories: 随笔 Tags:

基础优化-最不坏的哈希表

December 8th, 2017 No comments

哈希表性能优化的方法有很多,比如:

  • 使用双 hash 检索冲突
  • 使用开放+封闭混合寻址法组织哈希表
  • 使用跳表快速定位冲突
  • 使用 LRU 缓存最近访问过的键值,不管表内数据多大,短时内访问的总是那么几个
  • 使用更好的分配器来管理 keyvaluepair 这个节点对象

上面只要随便选两条你都能得到一个比 unordered_map 快不少的哈希表,类似的方法还有很多,比如使用除以质数来归一化哈希值(x86下性能最好,整数除法非常快,但非x86就不行了,arm还没有整数除法指令,要靠软件模拟,代价很大)。

哈希表最大的问题就是过分依赖哈希函数得到一个正态分布的哈希值,特别是开放寻址法(内存更小,速度更快,但是更怕哈希冲突),一旦冲突多了,或者 load factor 上去了,性能就急剧下降。

Python 的哈希表就是开放寻址的,速度是快了,但是面对哈希碰撞攻击时,挂的也是最惨的,早先爆出的哈希碰撞漏洞,攻击者可以通过哈希碰撞来计算成千上万的键值,导致 Python / Php / Java / V8 等一大批语言写成的服务完全瘫痪。

后续 Python 推出了修正版本,解决方案是增加一个哈希种子,用随机数来初始化它,这都不彻底,开放寻址法对hash函数的好坏仍然高度敏感,碰到特殊的数据,性能下降很厉害。

经过最近几年的各种事件,让人们不得不把目光从“如何实现个更快的哈希表”转移到 “如何实现一个最不坏的哈希表”来,以这个新思路重新思考 hash 表的设计。

哈希表定位主要靠下面一个操作:

index_pos = hash(key) % index_size;

来决定一个键值到底存储在什么地方,虽然 hash(key) 返回的数值在 0-0xffffffff 之前,但是索引表是有大小限制的,在 hash 值用 index_size 取模以后,大量不同哈希值取模后可能得到相同的索引位置,所以即使哈希值不一样最终取模后还是会碰撞

第一种思路是尽量避免冲突,比如双哈希,比如让索引大小 index_size 保持质数式增长,但是他们都太过依赖于哈希函数本身;所以第二种思路是把注意力放在碰撞发生了该怎么处理上,比如多层哈希,开放+封闭混合寻址法,跳表,封闭寻址+平衡二叉树。

优化方向

今天我们就来实现一下最后一种,也是最彻底的做法:封闭寻址+平衡二叉树,看看最终性能如何?能否达到我们的要求?实现起来有哪些坑?其原理简单说起来就是,将原来封闭寻址的链表,改为平衡二叉树:

传统的封闭寻址哈希表,也是 Linux / STL 等大部分哈希表的实现,碰到碰撞时链表一长就挂掉,所谓哈希表+平衡二叉树就是:

将原来的链表(有序或者无序)换成平衡二叉树,这是复杂度最高的做法,同时也是最可靠的做法。发生碰撞时能将时间复杂度由 O(N) 降低到 O(logN),10个节点,链表的复杂度就是 10,而使用平衡二叉树的复杂度是 3;100个节点前者的时间是100,后者只有6.6 越往后差距约明显。

面临问题

树表混合结构(哈希表+平衡二叉树)的方法,Hash table – Wikipedia 上面早有说明,之所以一直没有进入各大语言/SDK的主流实现主要有四个问题:

  • 比起封闭寻址(STL,Java)来讲,节点少时,维持平衡(旋转等)会比有序链表更慢。
  • 比起开放寻址(python,v8实现)来讲,内存不紧凑,缓存不够友好。
  • 占用更多内存:一个平衡二叉树节点需要更多指针。
  • 设计比其他任何哈希表都要复杂。

所以虽然早就有人提出,但是一直都是一个边缘方法,并未进入主流实现。而最近两年随着各大语言暴露出来的各种哈希碰撞攻击,和原有设计基本无力应对坏一些的情况,于是又开始寻求这种树表混合结构是否还有优化的空间。

先来解决第一个问题,如果二叉树节点只有3-5个,那还不如使用有序链表,这是公认的事实,Java 8 最新实现的树表混合结构里,引入了一个 TREEIFY_THRESHOLD = 8 的常量,同一个索引内(或者叫同一个桶/slot/bucket内),冲突键值小于 8 的,使用链表,大于这个阈值时当前 index 内所有节点进行树化操作(treeify)。

Java 8 靠这个方法有效的解决了第一个问题和第三个问题,最终代替了原有 java4-7 一直在使用的 HashMap 老实现,那么我们要使用 Java 8 的方法么?

不用,今天我们换种实现。

Read more…

Categories: 编程技术 Tags:

AVL/RBTREE 实际比较

December 8th, 2017 No comments

网上对 AVL被批的很惨,认为性能不如 rbtree,这里给 AVL 树平反昭雪。最近优化了一下我之前的 AVL 树,总体跑的和 linux 的 rbtree 一样快了:

他们都比 std::map 快很多(即便使用动态内存分配,为每个新插入节点临时分配个新内存)。

项目代码在:skywind3000/avlmini
其他 AVL/RBTREE 评测也有类似的结论,见:STL AVL Map

谣言1:RBTREE的平均统计性能比 AVL 好

统计下来一千万个节点插入 AVL 共旋转 7053316 次(先左后右算两次),RBTREE共旋转 5887217 次,RBTREE看起来少是吧?应该很快?但是别忘了 RBTREE 再平衡的操作除了旋转外还有再着色,每次再平衡噼里啪啦的改一片颜色,父亲节点,叔叔,祖父,兄弟节点都要访问一圈,这些都是代价,再者平均树高比 AVL 高也成为各项操作的成本。

谣言2:RBTREE 一般情况只比 AVL 高一两层,这个代价忽略不计

纯粹谣言,随便随机一下,一百万个节点的 RBTREE 树高27,和一千万个节点的 AVL树相同,而一千万个节点的 RBTREE 树高 33,比 AVL 多了 6 层,这还不是最坏情况,最坏情况 AVL 只有 1.440 * log(n + 2) – 0.328, 而 RBTREE 是 2 * log(n + 1),也就是说同样100万个节点,AVL最坏情况是 28 层,rbtree 最坏可以到 39 层。

谣言3:AVL树删除节点是需要回溯到根节点

我以前也是这么写 AVL 树的,后来发现根据 AVL 的定义,可以做出两个推论,再平衡向上回溯时:

插入更新时:如当前节点的高度没有改变,则上面所有父节点的高度和平衡也不会改变。
删除更新时:如当前节点的高度没有改变且平衡值在 [-1, 1] 区间,则所有父节点的高度和平衡都不会改变。

根据这两个推论,AVL的插入和删除大部分时候只需要向上回溯一两个节点即可,范围十分紧凑。

谣言4:虽然二者插入一万个节点总时间类似,但是rbtree树更平均,avl有时很快,有时慢很多,rbtree 只需要旋转两次重新染色就行了,比 avl 平均

完全说反了,avl是公认的比rbtree平均的数据结构,插入时间更为平均,rbtree才是不均衡,有时候直接插入就返回了(上面是黑色节点),有时候插入要染色几个节点但不旋转,有时候还要两次旋转再染色然后递归到父节点。该说法最大的问题是以为 rbtree 插入节点最坏情况是两次旋转加染色,可是忘记了一条,需要向父节点递归,比如:当前节点需要旋转两次重染色,然后递归到父节点再旋转两次重染色,再递归到父节点的父节点,直到满足 rbtree 的5个条件。这种说法直接把递归给搞忘记了,翻翻看 linux 的 rbtree 代码看看,再平衡时那一堆的 while 循环是在干嘛?不就是向父节点递归么?avl和rbtree 插入和删除的最坏情况都需要递归到根节点,都可能需要一路旋转上去,否则你设想下,假设你一直再树的最左边插入1000个新节点,每次都想再局部转两次染染色,而不去调整整棵树,不动根节点,可能么?只是说整个过程avl更加平均而已。

比较结论

Read more…

Categories: 编程技术 Tags:

如何优雅的使用 Vim

June 20th, 2017 2 comments

根据 Bram 前后几个关于高效使用 Vim的视频,大家每天需要花很多时间来编辑:代码、文档、邮件、日志 等等,除去这些外,还要分时间参加会议和人沟通,每个人的时间却都是不够的,优雅使用 Vim 无外乎:

  • 检测不高效的地方:你的整个工作流里,什么地方比较浪费时间?
  • 寻找一个更快的方式:官方文档,学习他人经验,自己编写 VimScript
  • 使它习惯化:开始使用,并且不断完善

以上三点反复循环,能让你的 Vim 越来越顺手。所以重点是根据自己的工作流不断迭代。而不是象大部分教程那样教你安装一大堆插件。插件都是别人写的为了解决通用需求而提炼的东西,和每个人的具体需求都有差别。上面这三点我屡试不爽,随着时间增长,有种越来越顺手的感觉,举几个我具体碰到的例子:

问题1:边开发边参考网上解决方案的问题

比如碰到问题搜到一段代码,需要试一下,一会又看会 Chrome ,一会又切回 GVim 里去写代码,反复 ALTTAB,有时候中间使用了一下资源管理器或者其他程序,ALTTAB 的顺序就会被打乱,你一切换就切跑了,十分低效。

于是我用 VimScript + 内嵌 Python 写了一个功能,按快捷键可以让 GVim 在透明/不透明两种状态间自由切换:

就是 VimScript 简单封装一个函数,里面用内嵌 Python 找到 GVim 的顶层 HWND,并设置透明度。平时默认不透明,需要参考其他资料时切换成透明,参考完了又快捷键切换回来,感觉比缘来切来切去顺畅很多。

问题2:浏览文档时的窗口滚动问题

比如你在抄写或者改写一段代码,窗口分为左右两个,左边是你引用参考的源代码,右边是你正在编辑的源代码。你抄着抄着,抄到左边最后一行了,或者你想前后看看正在引用的文本,你就需要将焦点从右边切换到左边,滚动,再切换交点回来,十分麻烦,于是撸一小段 VimScript 来解决这个问题:

" 0:up, 1:down, 2:pgup, 3:pgdown, 4:top, 5:bottom
function! Tools_PreviousCursor(mode)
    if winnr('$') <= 1
        return
    endif
    noautocmd silent! wincmd p
    if a:mode == 0
        exec "normal! \<c-y>"
    elseif a:mode == 1
        exec "normal! \<c-e>"
    elseif a:mode == 2
        exec "normal! ".winheight('.')."\<c-y>"
    elseif a:mode == 3
        exec "normal! ".winheight('.')."\<c-e>"
    elseif a:mode == 4
        normal! gg
    elseif a:mode == 5
        normal! G
    elseif a:mode == 6
        exec "normal! \<c-u>"
    elseif a:mode == 7
        exec "normal! \<c-d>"
    elseif a:mode == 8
        exec "normal! k"
    elseif a:mode == 9
        exec "normal! j"
    endif
    noautocmd silent! wincmd p
endfunc

把这个函数绑定到 ALTU, ALTD 两个按键上,你正在编辑着当前文档时,不用退出 INSERT 模式,更不用切换窗口交点,直接 ALTU, ALTD,就可以上下滚动正在参考的文档内容了,有了这个改进后,我的工作又高效了那么一点点。

同理,Quickfix 窗口经常用来查看编译错误,或者 Grep 结果,我也写了一个专门针对 Quickfix 窗口的滚屏函数,不用切焦点随时浏览 Quickfix 内容。

Read more…

Categories: 随笔 Tags:

Emacs/Vim 深度比较

December 25th, 2016 2 comments

生命在于折腾,折腾完了 Atom Editor,开始跟着陈斌大婶和 purcell的配置折腾 Emacs,比较下。很多人都在比较键位,比较插件,这是十分肤浅的,我们比较点深入的东西:

代码结构

  • Emacs 源代码:eLisp 79%, C 21%
  • Vim 源代码:C 52%, VimScript 48%

从代码结构上来讲,Emacs的代码最多的是 elisp,C代码只是一个微内核,Vim 里C代码还是大头。当然不排除 24.X, 25.X以后 Emacs源代码里带了好几个重量级的包,而 Vim向来比较精简一些,官方没带啥大点的插件有关。去除自带插件后,Emacs的 elisp代码比例应该会下降很多,不过总体来说,Emacs有更多组件使用 elisp开发而成,也就是说可以被用户修改或者替换的地方比 Vim要多,当然速度也会相应慢一点(比如 Emacs新打开上万行的文件连续按住PageDown时cpu 100%占满),不过比较大 JB,Atom Editor来说,还是快不少。

系统接口

大框架基本类似:

  • Vim 可以操作: buffer, window, tabpage, 光标,marker, region 跳转表等等。
  • Emacs 可以操作:buffer, window, frame, 光标,marker,region 异步进程 等等。

Vim 有 local,Emacs有 mode,Vim有事件触发,Emacs有各种钩子,基本大框架类似的。

键位设置也都很灵活,会配置的话,可以把 Emacs键位全部弄成 Vim的,比如 Evil,或者Vim里面也可以配制成进去就自动进入插入模式,全部用 Emacs键位。

具体到比如 buffer 或者窗口里面,Emacs的窗口或者 buffer /window 属性更多一些,Vim也有一些 Emacs没有的基础设施,比如 jumplist, quickfix之类的,不过 Emacs也可以用插件实现,实现 jumplist没问题,比较独立,但每个插件实现一个类似的 quickfix的东西,实在是比较蛋疼。

脚本语言

VimScript 类似 Lua,但更啰嗦些,没啥好说的,要上手可能就是2-3天的样子,主要是实际编写中熟悉各种 Vim内部结构体系,以及api。

EmacsLisp 类似 clisp,稍微有些区别而已。clisp中的向量: (vector 1 2 3 4) 在 elisp中可以用中括号来表示:[1 2 3 4] 这样对减少小括号数量有一些帮助,代码读起来没那么拗口,可惜实际写 elisp的过程中,向量用的并不多,各种插件用的最多的还是 list,alist,plist 几种容器,小括号还是满天飞,如果不借助编辑器的匹配,缩进,还有彩虹括号(不同层次的括号用不同颜色的小插件)代码很难阅读,自己写好写点,看别人代码要对括号对半天(特别是他们把很多写一行的话)。

Lisp变量可视范围比较好用,局部变量可以 “遮盖住” 全局变量,比如你在 VimScript里面要临时跳转到一个新目录需要这么写:

let cd = haslocaldir()? "cd" : "lcd"
let saved = getcwd()
silent! exec cd "my new directory"
......
silent! exec cd saved

而 elisp里面,利用局部变量遮盖特性,简单的:

(let ((default-directory "my new directory"))
  .....
  )

在这个 let的作用范围内,buffer的全局变量 default-directory 被新定义的同名局部变量给遮盖住了,里面的代码(包括调用外面定义的函数)取这个名字的变量,都会取到你新定义的变量,作用范围结束就恢复了,这样比 vimscript方便不少,类似的用法还有 (with-selected-window new-window...) 把当前窗口设置成 new-window并做一些事情,当离开代码作用域以后,自动恢复成先前的窗口,如果VimScript来做这事情,又需要保存前窗口,然后跳转,最后又恢复。

VimScript在 vim6.0及以前的时代里比 EmacsLisp弱很多,连个 List,HashTable之类的容器都没有,怎么写复杂的程序嘛,Vim7.0以后补充了字典和列表还有三元式,简单面向对象等,终于可以写点复杂的东西了,而 Vim下很多传奇性的插件,其实也正是 7.0以后才出现的。Vim8.0以后又进一步补充了匿名函数和 partial function (类似简陋版的 curry 函数)等等特性,除了啰嗦外,写点复杂东西问题不大,比如前久有人用 VimScript写了一个 C编译器,可以在Vim里跑 C脚本。

Lisp里面的宏类似C++模版加强版,C++模版只有一层,定义好模版,然后生成代码。Lisp的宏可以先用程序生成模版,再由模版生成代码,类似模版的模版。不过实际使用中,为了不让自己的代码飞的太远,难以维护,大部分插件开发者在 elisp里面最多就是 require一下 common lisp的兼容包,用一些 common lisp里面诸如 loop, for, incf, case 之类的通用宏,自己定义也限于定义一些小工具,没有人丧心病狂的搞一些影响程序结构的东西,定义成另外一门语言。

大部分插件开发者实际上还是把它当作充满括号的 lua 来用,除去一些便利性外,麻烦也还是挺多的。

异步进程

早期的 Vim没有异步进程操作,导致很多插件没有 Emacs那么顺畅,比如生成 tag或者编译,Vim只有傻等着,Emacs可以同时做其他事情,Vim8.0以后异步接口和后台时钟等也比较完善了,假以时日各大常见插件能逐步发挥 8.0特性的时候,相信也能带来一致的体验,包括纯vimscript实现的 shell,以及 gdb集成,发邮件等,问题不大。

Read more…

Categories: 随笔 Tags:

Aix 折腾手记

December 8th, 2016 1 comment

早年开发工作主要在 FreeBSD进行,2006年后来切换到 Linux下,期间穿插使用了一下 Solaris,所以我的网络库一直都是只支持这三个系统。为了让网络库支持更多平台,网上购置了一台 IBM AIX 小型机,因为其他大部分非 Linux系统,今天基本都可以在虚拟机里面安装了,而 AIX系统,你真的没法虚拟。

弄了几天以后,发现真他妈的麻烦,强大是强大,但是真的太琐碎了,相比之下,Linux/FreeBSD之流基本是傻瓜了。不看说明直接操作 AIX的话,可能连开机都麻烦,或者关机没关对,下次直接启动不了。

文字终端就没什么好拍的了,先上一张图形桌面的靓照吧:

是的你没看错,这就是 AIX 7,2012年的操作系统,就是那么的霸道,四处透着古典 Unix的味道。这样的机器今天还跑在各大银行的机房里,AIX系统管理员也拿着比 Linux系统管理员多几倍的工资,虽然工作岗位比较稀少。

下面来感受一下你想正常开关机,安装软件的话要怎么弄:

Read more…

Categories: 随笔 Tags: