今天腦子發熱,想來用vim達成類似XCode的自動補完功能(auto-completion).
先從國外網友提供的.ycm_extra_conf.py開始折騰。
import os
import ycm_core
flags = [
'-resource-dir',
'/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/6.0',
'-isysroot',
'/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk',
'-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include',
'-F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks',
'-fblocks',
'-fobjc-runtime=macosx-10.10.0',
'-fencode-extended-block-signature',
'-fobjc-arc',
'-fobjc-exceptions',
'-fexceptions',
'-x',
'objective-c'
]
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
def FlagsForFile( filename, **kwargs ):
return {
'flags': flags,
'do_cache': True
}
先把YouCompleteMe安裝好,再把這個檔案放在專案的根目錄下面
用VIM打開.m檔的時候,就會出現一個對話框,問要不要執行.ycm_extra_conf.py
這個程式,這時候選Load
就能載入了
如果沒意外的話,配合國外網友提供的測試檔案
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
int retVal;
@autoreleasepool {
NSString *string = @"hihi";
NSLog(@"%@", string);
retVal = 0;
}
return retVal;
}
就能看到auto completion的功能了
等等,UIKit去那了…
…
國外網友提供的是Mac app的設定,如果是iOS的話
要改成iOS的SDK
import os
import ycm_core
flags = [
'-arch i386',
'-fblocks',
'-fobjc-runtime=ios-8.0.0',
'-fencode-extended-block-signature',
'-fobjc-arc',
'-fobjc-exceptions',
'-fexceptions',
'-x',
'objective-c',
]
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
def FlagsForFile( filename, **kwargs ):
return {
'flags': flags,
'do_cache': True
}
在VIM輸入:YcmDiag
,就能看到
'UIKit/UIKit.h' file not found
找不到UIKit是吧?
在flags
加入
'-mios-simulator-version-min=7.0'
指定deployment target是ios simulator 7.0
再試一次:YcmDiag
…
/System/Library/Frameworks/Security.framework/Versions/A/Headers/cssmtype.h|142 col 5 error| 'CSSM_GUID' is unavailable: not available on iOS
/System/Library/Frameworks/Security.framework/Versions/A/Headers/cssmtype.h|143 col 5 error| 'CSSM_VERSION' is unavailable: not available on iOS
出現了一大堆看不懂的東西
沒關係,再繼續trial-and-error
在flags裡加入
'-isysroot',
'/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk',
指定system root directory
(這啥?)
繼續:YcmDiag
看不懂的東西不見了,但是出現其它更看不懂的東西
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|29 col 32 error| missing ',' between enumerators (FixIt available)
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|30 col 40 error| missing ',' between enumerators (FixIt available)
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|30 col 41 error| redefinition of enumerator '__AVAILABILITY_INTERNAL__IPHONE_9_0'
沒關係,再繼續trial-and-error
在flags裡加入
'-MMD',
這個-MMD
是什麼呢?
看一下說明:
-MMD
Like -MD except mention only user header files, not system header files.
說明上寫和-MD
很像
那-MD
又是什麼東東?
-MD
-MD is equivalent to -M -MF file, except that -E is not implied. The driver determines file based on whether an -o option is given. If it is, the driver uses its argument but with a suffix of .d, otherwise it takes the name of the input file, removes any directory components and suffix, and applies a .d suffix.
說明上寫和-M
是幾乎一樣的東西
那-M
又是什麼東東?
-M
Instead of outputting the result of preprocessing, output a rule suitable for make describing the dependencies of the main source file. The preprocessor outputs one make rule containing the object file name for that source file, a colon, and the names of all the included files, including those coming from -include or -imacros command-line options.
Unless specified explicitly (with -MT or -MQ), the object file name consists of the name of the source file with any suffix replaced with object file suffix and with any leading directory parts removed. If there are many included files then the rule is split into several lines using ‘\’-newline. The rule has no commands.
好…還是看不懂
自己動手試試吧
找一個.m
檔
執行
clang -M hello.m
出現了
hello.o: hello.m \
/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h \
/System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h \
/usr/include/sys/types.h /usr/include/sys/appleapiopts.h \
/usr/include/sys/cdefs.h /usr/include/sys/_symbol_aliasing.h \
/usr/include/sys/_posix_availability.h /usr/include/machine/types.h \
/usr/include/i386/types.h /usr/include/i386/_types.h \
/usr/include/sys/_types/_int8_t.h /usr/include/sys/_types/_int16_t.h \
/usr/include/sys/_types/_int32_t.h /usr/include/sys/_types/_int64_t.h \
/usr/include/sys/_types/_intptr_t.h \
...
從結果判斷,-M
的功能是把所有這個.m檔出現的header files都列出來
那-MD
和-MMD
呢?
執行
clang -MD hello.m
出現
Undefined symbols for architecture x86_64:
"___CFConstantStringClassReference", referenced from:
CFString in hello-eb80c0.o
"_objc_autoreleasePoolPop", referenced from:
_main in hello-eb80c0.o
"_objc_autoreleasePoolPush", referenced from:
_main in hello-eb80c0.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
出現link error
搞不懂,放棄
繼續:YcmDiag
之前的看不懂的東西都不見了,但是出來一個新的stdarg.h
找不到
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h|12 col 10 error| 'stdarg.h' file not found
這怎麼辦呢?
去問Google大神
I get errors about some headers being missing (stddef.h, stdarg.h)
Some header files (stddef.h, stdarg.h, and others) are shipped with Clang — these are called builtin includes. Clang searches for them in a directory relative to the location of the clang binary. If you moved the clang binary, you need to move the builtin headers, too.
More information can be found in the Builtin includes section.
上面說Clang有自己的stdarg.h
所以加入在flag裡加入
'-I/Library/Developer/CommandLineTools/usr/bin/../lib/clang/7.0.0/include',
再來:YcmDiags
,結果stdarg.h
找不到的問題解決了,但是之前更看不懂的問題又出現了…
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|29 col 32 error| missing ',' between enumerators (FixIt available)
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFNumberFormatter.h|30 col 40 error| missing ',' between enumerators (FixIt available)
看起來是clang看不懂ios9特有的macro
那就不用YouCompleteMe的libclang,改用XCode的clang好了
執行
cd ~/.vim/bundle/YouCompleteMe
./install.py --clang-completer --system-libclang
繼續:YcmDiag
之前的錯誤訊息幾乎不見了,只剩下最後一個
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h|92 col 10 error| 'CoreFoundation/CFUserNotification.h' file not found
訊息說問題出在CoreFoundation.h
第92行
就去那邊看看
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
#include <CoreFoundation/CFUserNotification.h>
#include <CoreFoundation/CFXMLNode.h>
#include <CoreFoundation/CFXMLParser.h>
#endif
原來是include了CFUserNotification.h
但是iOS的framework裡面根本沒有這個檔案呀
問題可能是出在TARGET_OS_MAC
有被定義,也可能是TARGET_OS_IPHONE
沒有被定義
來寫一個簡單的測試程式,來看看那些MACRO有被定義
#import <Foundation/Foundation.h>
int foo() {
return TARGET_OS_MAC;
return TARGET_OS_EMBEDDED;
return TARGET_OS_IPHONE;
}
執行:w z.m
回到command line,執行
clang -E z.m
出現
int foo() {
return 1;
return 0;
return 0;
}
所以TARGET_OS_MAC
的值是1,其它的是0
那我們直接把TARGET_OS_MAC
設成0就好了
執行
clang -DTARGET_OS_MAC=0 -E z.m
出現
int foo() {
return 1;
return 0;
return 0;
}
…
還是沒變
所以是有別的地方改了這個MACRO的值
用silver searcher來找一下元兇是誰
cd /Applications/Xcode.app/Contents
ag 'define TARGET_OS_MAC'
出現
Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/TargetConditionals.h
92: #define TARGET_OS_MAC 1
213: #define TARGET_OS_MAC 1
339: #define TARGET_OS_MAC 1
Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/TargetConditionals.h
92: #define TARGET_OS_MAC 1
213: #define TARGET_OS_MAC 1
339: #define TARGET_OS_MAC 1
不對呀,有好幾個SDK,該用那一個?
應該來指定要使用的SDK
執行
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -E z.m
出現
int foo() {
return 1;
return 0;
return 1;
}
TARGET_OS_IPHONE變成1了
問題解決
回去:YcmDiag
,看看是不是行了
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h|92 col 10 error| 'CoreFoundation/CFUserNotification.h' file not found
...
問題還在
來Debug一下,輸入:YcmDebugInfo
Printing YouCompleteMe debug information...
-- Server has Clang support compiled in: True
-- Clang version: Apple LLVM version 7.0.0 (clang-700.0.72)
-- Flags for /Users/Lono/tmp/a/z.m loaded from /Users/Lono/tmp/a/.ycm_extra_conf.py:
-- ['-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.0/include', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk', '-isystem', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1', '-isystem', '/usr/local/include', '-isystem', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include', '-isystem', '/System/Library/Frameworks', '-isystem', '/usr/include','-isystem', '/Library/Frameworks']
咦! 出現一些在.ycm_extra_conf.py
裡沒有的flags
這可能是YouCompleteMe自己設的flags
再來ag一下
cd ~/.vim/bundle/YouCompleteMe
ag /usr/local/include
找到可疑檔案
~/.vim/bundle/YouCompleteMe/third_party/ycmd/ycmd/completers/cpp/flags.py
原來YouCompleteMe自己有放一些default的flags進去
把這些flags移掉
繼續:YcmDiag
出現
Forcing compilation, this will block Vim until done.
No warnings or errors detected
…
終於設定完成(淚~~)
最後的.ycm_extra.conf.py
import os
import ycm_core
flags = [
'-D__IPHONE_OS_VERSION_MIN_REQUIRED=70000',
'-resource-dir',
'/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.0',
'-mios-simulator-version-min=7.0',
'-arch i386',
'-fblocks',
'-fobjc-runtime=ios-7.0.0',
'-fencode-extended-block-signature',
'-fobjc-arc',
'-fobjc-exceptions',
'-fexceptions',
'-x',
'objective-c',
'-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks',
'-I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Foundation.framework/Headers',
'-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include',
'-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.0/include',
'-I/Library/Developer/CommandLineTools/usr/include',
'-isysroot',
'/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk',
'-MMD',
]
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
def FlagsForFile( filename, **kwargs ):
return {
'flags': flags,
'do_cache': True
}
2015/11/08
今天:YcmDiag竟然出現
unknown argument: '-fencode-extended-block-signature'
真奇怪,之前都沒問題
可能和clang的版本有關(clang-700.0.72)
只好把這個argument移掉了(???)
把clang_complete裡的cindex.py、init.py、enumerations.py抓下來放在./clang
的資料夾下,再指定libclang.dylib的位置(下面的path變數),就可以開始玩了
import sys
from clang.cindex import Index, SourceLocation, Cursor, File, CursorKind, TypeKind, Config, LibclangError
def dumpnode(node, indent):
print ' ' * indent, node.kind, node.spelling
for i in node.get_children():
dumpnode(i, indent+2)
def srcrangestr(x):
return '%s:%d:%d - %s:%d:%d' % (x.start.file, x.start.line, x.start.column, x.end.file, x.end.line, x.end.column)
def dumptoken(node):
for x in node.get_tokens():
print x.kind
print " " + srcrangestr(x.extent)
print " '" + str(x.spelling) + "'"
def init():
conf = Config()
# here we use the libclang.dylib from the vim plugin -- YouCompleteMe
path = "/Users/<UserName>/.vim/bundle/YouCompleteMe/third_party/ycmd"
Config.set_library_path(path)
conf.set_library_path(path)
try:
conf.get_cindex_library()
except LibclangError as e:
print "Error: " + str(e)
def main():
init()
index = Index.create()
print sys.argv[1]
tu = index.parse(sys.argv[1], args=['-x', 'objective-c'])
dumpnode(tu.cursor, 0)
dumptoken(tu.cursor)
if __name__ == '__main__':
main()
玩法:
python xxx.py MyObject.m
就可以看到parse後的node和token名稱了