如何使用Universal Ctags标记引用整个文件的变量及Ember.js组件
我之前处理过类似的场景——比如有些模块整个文件就是为了导出一个全局变量或者单例对象,这时候确实需要让Ctags把这个变量和整个文件绑定起来。核心就是通过自定义正则规则来告诉Ctags怎么识别这种特殊的变量。
举个实际的例子,比如在JavaScript项目里,很多文件会用export default MyModule或者module.exports = MyUtils来导出整个模块的核心对象。你可以这么配置:
先打开你的Ctags配置文件(一般是~/.ctags或者~/.config/ctags/config),添加下面的规则:
--langdef=javascript --langmap=javascript:.js # 匹配ES模块的默认导出 --regex-javascript=/^export\s+default\s+([A-Za-z0-9_]+)/\1/v,var,exported default file-level variable/ # 匹配CommonJS的module.exports --regex-javascript=/^module\.exports\s*=\s*([A-Za-z0-9_]+)/\1/v,var,module exports file-level variable/
如果碰到匿名导出的情况(比如module.exports = { ... }),你还可以直接把文件名作为这个“文件级变量”的标识:
--regex-javascript=/^module\.exports\s*=\s*\{/\__filename__/v,var,anonymous file-level export object/
配置好之后,运行ctags --options=~/.ctags -R .重新生成tags,之后你在引用这个变量的时候,就能直接跳转到整个文件了。
要是你用的是其他语言,思路完全一样:找到文件里代表整个文件核心导出的代码行,写个正则匹配它,然后把标识符(或者文件名)标记为关联整个文件的变量就行。
Ember这种靠文件名定义组件的机制确实有点坑,我当初折腾了好一会儿才搞定。核心思路是给Ctags加跨文件的自定义规则,让.hbs模板里的组件引用能精准跳转到对应的js文件。
具体步骤来啦:
- 先让Ctags识别.hbs文件,在配置里加:
--langdef=handlebars --langmap=handlebars:.hbs - 匹配.hbs里的组件引用——不管是标签形式的
<example-component>还是helper形式的{{example-component}},都要能抓出来:
这里的--regex-handlebars=/<([a-z0-9-]+)(\s|>)/\1/c,component,component tag reference/ --regex-handlebars=/{{([a-z0-9-]+)(\s|}})/\1/c,component,component helper reference/c是自定义的kind标识,代表component,方便和其他变量、函数区分开。 - 关键一步:让Ctags把组件文件本身标记为这个组件的定义。因为Ember组件没有显式的“定义名”,所以直接用文件名作为标识符:
这样--regex-javascript=/^.*$/\__filename__/c,component,component file definition//app/components/example-component.js就会被标记为example-component的组件定义。如果你用的是pod结构(组件在app/components/example-component/component.js),只需要调整规则里的路径逻辑就行。 - 最后运行
ctags --options=~/.ctags -R app/生成tags,之后在模板里点击组件名,就能直接跳转到对应的js文件啦!
哦对了,如果你项目里的组件是用类定义的(比如export default class ExampleComponent extends Component),也可以匹配类名再转换成kebab-case和模板里的组件名对应,但用文件名的方式更直接,适配所有Ember组件写法。
内容的提问来源于stack exchange,提问作者Mike Hearn

