我們都知道 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