💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
我们都知道 Vim 和 Emacs 都是文本编辑器中的上古神器,你也许用 ctags,cscopes 配合 Vim 完成过大型 C 或者 C++ 的开发,你也许配合过其他插件,完成过 JavaScript,python 代码的开发,但是很少有人试过 iOS app 的开发吧,毕竟 iOS 的框架包含了很多东西,以及 Objective-C 天生很长的 API 名字,让我们没办法把此神器用起来,今天我就来给大家讲下我是怎么使用 Vim 开发 iOS App 的,当然 Emacs 也可以 # begin 使用 Vim 开发 iOS App 并不是特殊的爱好,而是被 Xcode 8 活生生的逼的,刚开始更新了 Xcode 8 以后,Xcode 8 把第三方插件给屏蔽了,导致没有 XVim 给我用了,没有 XVim 以后,发现异常不顺手,于是尝试用了一段时间的 AppCode,不得不说 AppCode 是一个非常好的 IDE,但是他有个很大的缺点,那就是 Java,JetBrain 家的东西都很不错,唯一缺点就是基于 Java,整个平台都略慢,然后我在不断的 google 过程中,发现了有人竟不知何谓恐惧,竟然使用 Vim 开发 iOS App,最后我也学会了这个新姿势 XDDDD 不过目前,只支持 Objective-C 代码的开发,swift 的话,没有解决工程文件自动补全的问题,因为目前大家使用的流行的 swift 自动补全工具 SourceKitten 并没有支持 workspace 所以暂时还没用起来 以及,目前不支持调试,因为发现 Vim 对调试的支持确实好糟糕... # 准备活动 工欲善其事,必先利其器,主角是 Vim 或者 Emacs,少了其他配角和龙套们,也没办法正负恐惧,我们来看看用到了些什么东西,让我们的 Vim 成为利器的,这里只是点下他们的名,文章后面会把链接奉上 - 首先我们来看主角队的同学们,他们是征服恐惧的主力 - macOS,没有神话,开发 iOS 还是只能在 Mac 上 - 支持 python 的 Vim,可以用 Vim8 或是 neoVim 食用更佳,我就用的 neoVim - YCMD,其实他是 YouCompleteMe + YouCompelteMeDeamon 的合体,自动补全、定义跳转等功能,就依赖他了 - 接下来我们来看看其他龙套们 - Vbundle,装插件用的,没他,龙套和主角都不用上场了 - unite + unite-outline + unite-outline-objc,提供了方法导航 - auto-pairs,自动补全右括号 - ctrlp,文件搜索跳转 - Ag,字符串搜索工具 - syntastic,语法检查工具 - vim-clang-format,代码格式工具 恩,需要的东西大概就是这些了,Vim 的配置文件,我是基于好久以前 square 开源的 maximum-awesome 的,所以,配合这个食用风味更佳,我的 dot file 也放到了 github 上,欢迎大家 star # 进入正题 龙套们,基本都可以通过配置 vbundle 来完成安装,之后只用配置对应的快捷键就好了,这里的正题,要搞定难搞的主 YCMD ## 难搞的主 YCMD ### 安装 YCMD YCMD 的安装很简单,主要是需要一定的配置 首先在 vim 的配置文件中加入下面的内容,更新配置文件并执行 `BundleInstall` 命令,让 Vbundle 把 YouCompleteMe 插件装上 ```vim Plugin 'Valloric/YouCompleteMe' ``` 然后到这个路径 `~/.vim/bundle/YouCompleteMe` 这里是 YouCompleteMe 安装的位置,在这里需要编译 YCM,一条命令就可以搞定 ```bash ./install.py --clang-completer --system-libclang ``` `--clang-completer` 告诉脚本我们需要 clang 的支持,`--system-libclang` 告诉编译脚本使用系统的 clang,因为之前 clang 升级 4.0 的时候,并没有已经编译好的包给我下载,所以这里不用系统 clang 的话,编译脚本会下载一个 clang 3.0,这样就无法支持 iOS 10.0 以后的 sdk 了,因为 iOS 10.0 以后的 sdk 为了支持 swift 引入了一些 clang 3.0 不支持的新语法,所以这里要加上 `--system-libclang` 然后等他编译完成,这样 YCMD 就配置好了,似乎这里看并不是很难搞,其实难搞的是如何在 iOS 项目中配置好自动提示 ### 为 Xcode 项目配置 YCMD 这里进入到了真正征服恐惧的地方了 #### YouCompleteMe 的原理 曾经有人说过,编辑器再怎么神器是没办法超过 IDE 的,因为 IDE 是通过编译、解析整个项目的所有文件,来达到语法错误提示,自动补全,定义跳转等高级功能的,而 YCMD 就是来弥补这一个差距的,YCMD 通过传入完整的编译参数,编译需要提示的文件,来实现自动补全,这样没办法超过 IDE 的部分就被抹平了 #### 配置一个项目 这里我们配置一个复杂的项目来练练手,首先 YCMD 是不可能通过 Xcode 的项目文件或是 workspace 文件获取到编译参数的,所以这一步需要手来,当然,将来可以做成自动的,因为目前手动的做其实很方便,所以现在还没有做成自动的 首先,YCMD 是通过每个项目路径下的 `.ycm_extra_conf.py` 脚本文件来获取编译参数的,这个脚本文件中有一个叫做 `FlagsForFile` 的函数,我们通过这个函数返回某一个特定文件需要的编译参数,一般情况下大部分文件的编译参数是一样的,我们来看一个配置的列子 ```python import os import ycm_core flags = [ '-resource-dir', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0', '-x objective-c', '-arch armv7', '-fmessage-length=0', # '-fmodules', # '-gmodules', '-fdiagnostics-show-note-include-stack', '-fmacro-backtrace-limit=0', '-D__arm__=1', '-D__IPHONE_OS_VERSION_MIN_REQUIRED=80000', '-std=gnu99', '-fobjc-arc', '-Wnon-modular-include-in-framework-module', '-Werror=non-modular-include-in-framework-module', '-Wno-trigraphs', '-fpascal-strings', '-Os', '-fno-common', '-Wno-missing-field-initializers', '-Wno-missing-prototypes', '-Werror=return-type', '-Wunreachable-code', '-Wno-implicit-atomic-properties', '-Werror=deprecated-objc-isa-usage', '-Werror=objc-root-class', '-Wno-arc-repeated-use-of-weak', '-Wduplicate-method-match', '-Wno-missing-braces', '-Wparentheses', '-Wswitch', '-Wunused-function', '-Wno-unused-label', '-Wno-unused-parameter', '-Wunused-variable', '-Wunused-value', '-Wempty-body', '-Wconditional-uninitialized', '-Wno-unknown-pragmas', '-Wno-shadow', '-Wno-four-char-constants', '-Wno-conversion', '-Wconstant-conversion', '-Wint-conversion', '-Wbool-conversion', '-Wenum-conversion', '-Wshorten-64-to-32', '-Wpointer-sign', '-Wno-newline-eof', '-Wno-selector', '-Wno-strict-selector-match', '-Wundeclared-selector', '-Wno-deprecated-implementations', '-DOBJC_OLD_DISPATCH_PROTOTYPES=0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk', '-fstrict-aliasing', '-Wprotocol', '-Wdeprecated-declarations', '-miphoneos-version-min=8.0', '-g', '-Wno-sign-conversion', '-Wno-infinite-recursion', '-fembed-bitcode-marker', '-I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform', '-I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include', '-I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks', '-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.0.0/include', '-F/Users/apple/Documents/Developer/CloudLifeWorkspace/iOS/Develop/Project_iOS/project', '-MMD', '-MT', '-MF', ] SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] HEADER_EXTENSIONS = [ '.hpp', '.hxx', '.hh', '.h' ] def FlagsForFile( filename, **kwargs ): staticFlags = flags return { 'flags': staticFlags, 'do_cache': True } ``` 这里上面的代码可以当作 `.ycmd_extra_conf` 文件的一个最小模板,也就是如果我们的项目里面没有子目录,没有第三方库,那么使用这个已经可以为 iOS 项目提供自动提示了 颤抖吧凡人,如此这般的编译条件,需要你能够把 Xcode 项目配置中的编译参数完完全全翻译出来才行,但是我也是凡人,所以这个不是我写的,而是有方法生成的,方法如下 把需要增加自动提示的项目用 Xcode 打开,然后编译,然后如下图所示,找到编译信息然后找到项目中的一个文件,注意不要是 Pod 中的文件,最右边,有个三条横线的按钮,点开他,没错,你看到了完整的编译参数,下图中 `EXPORT PATH =....` 下面的所有内容就是完整的编译参数 - Report View ![Xcode 中的 Report View][img01-xcode-build] - 戳这个按钮 ![Xcode 中的编译信息按钮][img03-xcode-buildinfo-button] - 我们要的编译信息 ![Xcode 中的编译信息][img02-xcode-buildinfo] 右键,copy,找到你熟悉的记事本,参数都是空格分隔的,所以,这里我们把它转换成上面代码中的形式,并把不需要的去掉,比如这里我注释了 `-fmodules` 和 `-gmodules` 因为这样编译没办法使用 module 把这些加入到配置文件中后,再打开 Vim 你就会发现自动提示变得非常好用了,不过这里还会有一个问题,如果编译的时候,有无法找到 `UIKit`,这是因为 YCMD 默认引入了 macOS SDK 的路径,导致了编译时 clang 认为我们编译目标是 macOS,所以如下图需要修改 `~/.vim/bundle/YouCompleteMe/third_party/ycmd/ycmd/completers/cpp/flags.py` 文件,去掉默认引入 macOS sdk 的编译参数,这样就好了 ![需要修改的 YCMD 文件][img04-do-not-include-macossdk] #### 效果图们 至此,最主要的问题已经被我们解决了,接下来看下效果如何 - 首先是自动提示 ![自动提示][img05-result-autocomplete] - 查找文件 ![文件查找][img06-ctrip-find-file] - 方法名称大纲 ![Outline][img07-outline] - 搜索字符串 ![搜索字符串][img08-Ag-find] - 语法检查 ![语法检查][img09-syntax-check] ## 好处 这么做自然不是为了花样炫技,更多的是为了探索 Vim 的使用,以及更多了解 Xcode 项目 - 完整的 Vim 环境,写代码再也不用碰鼠标了 - Vim 插件 + 不满的地方自己动手丰衣足食 - 快速,跟 Vim 比速度,笑话 - 方便快接的字符串搜索,替换操作 - 学了点 Python - 附赠一个 C/C++ 的开发环境 ## 已知问题 虽然目前已经可以达到写代码的程度了,但是还是有很多问题,如下: - 没办法 debug - 不支持 Xcode 中的 group 展示 - 头文件的提示有问题 - 不能使用 `@import` 的导入,会报语法错误 - xib,storyboard 自然是不行的,我的做法是 Xcode 里面拖 UI + 关联 Outlet + Debug,Vim 中做大量的代码编辑操作 - `[]` 方括号的匹配没有 Xcode 那么智能 - delegate 或是全局的自动提示,需要使用 ctrl + 空格打开,并且有时候会有点慢 - delegate 的自动提示有时候需要输入前面半部分的方法名才会有,比如上面截图的 tableView,需要先输入 `- (void)tableView:(UITableView *)tableView ` 再使用 ctrl + 空格才会有非常好的自动提示,当然输入前面这部分也会有一定的提示,整体上看能接受 上面的问题都是一直以来我没有解决的问题,大家要是发现有破解的方法,欢迎联系 # end 折腾这么多,相信大家已经可以用 Vim 敲 iOS 的代码了,虽然虽然当初开始折腾的时候,踩了很多坑,比如 clang 3 升级 clang 4 后,原来的配置都不能用了,但是收获挺多的也并不是这么一篇文章能够说完的,除了 Vim 大家也可以试试 Emacs 下的配置,我用的 Spacemacs,添加了 ycmd 的 layer,配置后也有了相同的效果 也欢迎大家丢砖 ## 参考资料 - [YouCompleteMe](http://valloric.github.io/YouCompleteMe/): 自动补全插件 - [Vbundle](https://github.com/VundleVim/Vundle.vim): 插件管理插件 - [Unite](https://github.com/Shougo/unite.vim): 一个通用的显示插件,可以用来显示各种东西,比如文件列表,buffer 列表,outline - [Unite-outline](https://github.com/Shougo/unite-outline): Unite 插件的 outline 插件 - [Unite-outline-objc](https://github.com/ryotakato/unite-outline-objc): Unite-outline 插件的 Objective-C 插件 - [auto-pairs](https://github.com/jiangmiao/auto-pairs): 自动补全括号的插件 - [ctrlp](https://github.com/ctrlpvim/ctrlp.vim): 文件查找插件 - [Ag](https://github.com/rking/ag.vim): 字符串查找插件 - [syntastic](https://github.com/vim-syntastic/syntastic): 语法检查插件 - [vim-clang-format](https://github.com/rhysd/vim-clang-format): clang format 格式化插件 - [VimAwesome](http://vimawesome.com): 方便的 Vim 插件导航网站 - [maximum-awesome](https://github.com/square/maximum-awesome): 一个比较有名的 Vim 配置,包括了上面的 Vbundle,Ag,ctrlp,syntastic 等插件,以及一些很方便的配置,我的配置文件是基于这个配置的,使用前先安装这个 - [我的配置文件](https://github.com/noark9/dotfiles): 我的配置文件,除了上文写的内容,还加了些 php,python,js 等开发配置,以及一些自己觉得用起来方便的配置 [img01-xcode-build]: http://noark9.github.io/img/dev-ios-with-vim/img01-xcode-build.png [img02-xcode-buildinfo]: http://noark9.github.io/img/dev-ios-with-vim/img02-xcode-buildinfo.png [img03-xcode-buildinfo-button]: http://noark9.github.io/img/dev-ios-with-vim/img03-xcode-buildinfo-button.png [img04-do-not-include-macossdk]: http://noark9.github.io/img/dev-ios-with-vim/img04-do-not-include-macossdk.png [img05-result-autocomplete]: http://noark9.github.io/img/dev-ios-with-vim/img05-result-autocomplete.png [img06-ctrip-find-file]: http://noark9.github.io/img/dev-ios-with-vim/img06-ctrip-find-file.png [img07-outline]: http://noark9.github.io/img/dev-ios-with-vim/img07-outline.png [img08-Ag-find]: http://noark9.github.io/img/dev-ios-with-vim/img08-Ag-find.png [img09-syntax-check]: http://noark9.github.io/img/dev-ios-with-vim/img09-syntax-check.png