Progress with libclang! (in C++, not Python)

Feb 23, 2013

As you might remember, I gave up on porting SublimeClang over to Emacs. After raising two bugs on irony-mode, one of which ended up with the author (who seems to be a really nice guy, by the way) cloning my repo to see why it wasn’t working, I finally managed to get auto-completion up and running with irony-mode. And it turned out to be too slow. I mean, for me it was even slower than brianjcj’s auto-complete-clang plugin, which uses the clang binary to drive completions. By the end of it, I was in a slump and was even contemplating abandoning Emacs and switching over to ST2 (for which I own a license, don’t ask me why).

However, being the stubborn ass that I am, I figured I’d try writing my own plugin from scratch. So, I checked out the trunk versions of llvm and clang (guide here) and thankfully everything compiled and linked just fine. Then, I tried the following snippet:

args = ['-I/home/amey/Developer/GamePlay/gameplay/src',
        '-I/home/amey/Developer/GamePlay/external-deps/bullet/include',
        '-I/home/amey/Developer/GamePlay/external-deps/openal/include',
        '-I/home/amey/Developer/GamePlay/external-deps/oggvorbis/include',
        '-I/home/amey/Developer/GamePlay/external-deps/libpng/include',
        '-I/home/amey/Developer/GamePlay/external-deps/zlib/include',
        '-I/home/amey/Developer/GamePlay/external-deps/lua/include',
        '-I/home/amey/Developer/GamePlay/external-deps/glew/include',
        '-I/home/amey/Developer/llvm-3.2.src/Release+Asserts/lib/clang/3.2/include']
index = clang.cindex.Index.create()
tu = index.parse("/home/amey/Developer/GamePlay/Projects/Genesis/src/GenesisGame.cpp", args)
cc = tu.codeComplete("home/amey/Developer/GamePlay/Projects/Genesis/src/GenesisGame.cpp", 32, 15)

The snippet worked fine. tu.diagnostics had one error, which is understandable since the supplied GenesisGame.cpp had a lightNode-> in it, with line 32 and column 15 poised at the point after the “->”. But what does cc.results.numResults give me? A big, fat zero (which means that libclang found zero completions).

Refusing to give up, I decided to write a test program in C++ instead. Browsing through tools/clang/include/clang-c/Index.h, I found the C++ equivalents to the functions I’d been using in Python. The header is impressively documented, with very few ambiguous grey areas. So, I ended up with the following snippet:

int main() {
    const char *args[] = {
        "-I/home/amey/Developer/GamePlay/gameplay/src",
        "-I/home/amey/Developer/GamePlay/external-deps/bullet/include",
        "-I/home/amey/Developer/GamePlay/external-deps/openal/include",
        "-I/home/amey/Developer/GamePlay/external-deps/oggvorbis/include",
        "-I/home/amey/Developer/GamePlay/external-deps/libpng/include",
        "-I/home/amey/Developer/GamePlay/external-deps/zlib/include",
        "-I/home/amey/Developer/GamePlay/external-deps/lua/include",
        "-I/home/amey/Developer/GamePlay/external-deps/glew/include",
        "-I/home/amey/Developer/llvm-3.2.src/Release+Asserts/lib/clang/3.2/include"
    };
    CXIndex index = clang_createIndex(1, 1);
    CXTranslationUnit tu = clang_parseTranslationUnit(index, "/home/amey/Developer/GamePlay/Projects/Genesis/src/GenesisGame.cpp", args, 9, NULL, 0, 0);

    CXCodeCompleteResults *res = clang_codeCompleteAt(tu,
        "/home/amey/Developer/GamePlay/Projects/Genesis/src/GenesisGame.cpp",
        32, 16, NULL, 0, 0);
    std::cout << "Number of results: " << res->NumResults << std::endl;
    clang_disposeCodeCompleteResults(res);

    return 0;
}

Running this gave me around 1800 results. Which was both encouraging and disheartening, because I pretty much was expecting it to give me about 20 results, all of them accessible variables/functions from the Node class.

Dumping the results was kind of easy. I say kind of because res->Results[i].CompletionString gives an object of type CXCompletionString, which is of type void *. I don’t have a lot of experience with this sort of coding, I’m used to neatly-packaged classes with methods that give me what I’m looking for. Instead, clang has a bunch of functions that accept CXCompletionString variables that return useful information about the completion.

After flailing around for a bit trying to extract chunks from each CompletionString, I ended up with code that would dump all the completions as plaintext strings. To my dismay, none of them had anything to do with the Node object! In fact, most of them seemed to be variable types, which got me thinking. There was only one reason why I could be getting variable types as completions, and that was because libclang thought that I was trying to declare a new variable at the cursor (Emacs cursor, not clang cursor) position I had provided. And the only possible reason that could happen was that I had supplied the wrong line and column values. Turns out that Emacs is weird in this regard. Line numbers start at 1, but column numbers start at 0. So, remember to add 1 to the column number you get from (what-cursor-position).

With this sorted out, I finally started getting decent completion results. Instead of flailing around some more, I decided to borrow SublimeClang’s C++ code for filtering among the returned completions to find relevant ones, and the resulting program finally shows about 30 completions, all of them public variables and functions in the Node class. Here’s the final code, if you want to try it out for yourself then change the args, fileName, lineNum and colNum variables in main. Build the binary with g++ complete.cpp -lclang. Keep in mind that you’ll need an installation of libclang for it to work in this manner, you’re free to link with a local library or whatever else pleases you.