Fermat618's Blog

Happy coding

Fortran 中根据语义的补全

Fortran 中的 if 结构,函数声明等,都有不少的信息冗余。如

if (3 > 2) then
    !...
endif

real function add(a, b)  
...
end function

后面的 endif, end function 等,长度都不少。这样虽然提高了可读性,但手动敲下未免还是太麻烦。把时间花在敲入一个很长还必定会有的东西上面,很不划算。如果用 snippet 之类的方案,需要记住一个 key, 然后每次按 tab 展开,除了语言本身,又多了一个记忆 key 的负担。

于是,我想到了一个更为自动化的补全方案,就是让程序根据已经输入的内容,在敲入回车键换行时,自己判定这是不是一个未完成的 if 结构,然后自动在后面补全 endif.

因为这个功能绑定到了一个很重要的按键上,所以功能要尽量的不添乱 。所以,我想到把当前行按照程序语义给解析出来。如果能成功的解析,就补全后面的内容;如果不行,就执行普通的Enter.

解析程序的语义,还可以带来另外的好处。一个是自动加空白,如

do i=1,n

给自动化成为

do i = 1, n

有了空格之后更加的好读。另一个功能是把一些冗余的东西省掉,如

if (a > 3) then

有用的信息只有

if a > 3

因此,只需要敲下上面的文字,再回车,就可以补全成符合 Fortran 语法规范的形式。

代码在此 https://github.com/fermat618/fortran-construct-complete

 

vim打开文件时自动添加cscope连接

cscope是看源代码的得力工具。在编译进了cscope特性的vim中,可以在vim内部方便地使用cscope进行跳转. 使用cscope需要先生成一个默认文件名是cscope.out的交叉引用文件,在vim中使用cscope也需要使用:cscope add 命令指定交叉引用文件所在位置,也即所谓的建立cscope链接。

通常,cscope.out会在一个项目的根目录下面。当在这个目录下打开vim时,使用vim的cscope相关部分给出的示例设置

		if filereadable("cscope.out")
		    cs add cscope.out
		" else add database pointed to by environment
		elseif $CSCOPE_DB != ""
		    cs add $CSCOPE_DB
		endif

可以自动添加cscope.out文件。然而,当在一个项目的子目录下打开一个文件的时候,这份配置却不能正确添加cscope.out文件了。往往用cscope开始查找某个东西的时候,就提示说没有建立cscope链接,很是扫兴。

想做什么事被打断是很不爽的,所以就需要自动添加cscope.out文件了。比较直接的想法就是从当前目录一层一层地向上查找,如果找到了某个cscope.out文件,就把它添加进来。考虑到python3代码写起来比vim脚本爽多了,而我的vim又是自己编译几乎必带这个支持的,所以就在vimscript中用python3来实现了。

if has("cscope") && executable("cscope")
    if has('python3')
        py3 <<EOF
import os, os.path
from itertools import takewhile
def iterate(fun, x):
    yield x
    for element in iterate(fun, fun(x)):
        yield element
for path in takewhile(lambda x: x!='/', iterate(os.path.dirname, os.getcwd())):
    cscopefile = os.path.join(path, 'cscope.out')
    if os.access(cscopefile, os.R_OK): # file readable
        vim.command('cscope add ' + cscopefile +' '+ path)
        break
EOF
    endif
    if $CSCOPE_DB != ""
	cs add $CSCOPE_DB
    endif
endif

用python的好处是容易让代码成为自己想法的自然表达。程序的目的是在当前目录及其父目录中检查cscope.out文件的存在,再添加这个文件,那么这个程序自然的结构就是首先得到当前目录和它的父目录,然后再对这些目录进行一个一个地检查。生成父目录的方式就是对一个目录不停地用basename函数,想到一个生成某个函数对一个值不断应用而产生的序列的函数 iterate(), python 的itertools中竟然没有,那就自己定义一个吧。python的生成器使得程序既不会在当前目录下已经找到cscope.out的时候还去傻傻地生成当前目录的所有父目录的列表,还能让程序看起来很漂亮。用for in结构可比while漂亮。

最后再说下vim中的:cscope add命令。cscope.out文件中需要保存文件所在的位置,而这个位置通常是相对位置。如果vim的当前目录与生成cscope.out时的当前目录不一样,vim就会找不到cscope给出的文件位置。所以,就需要显示式指定cscope.out里面的路径是相对于哪个目录的,这就是cscope add命令的第二个参数。第一个参数自然就是cscope.out文件的路径了。