Rewriting YouCompleteMe from scratch in Swift

01 Jul 2018


It’s been just over a year since iCompleteMe’s inception. It’s a hard fork of YouCompleteMe, a powerful code completion system for Vim.

iCompleteMe

The iCompleteMe project spawned as an attempt to implement Swift code completion via sourcekitd into YouCompleteMe.

The project started as an attempt to build a Swift code completion system ontop of sourcekitd for Vim. YouCompleteMe’s stable and widely used codebase, made it a good choice for a platform. But, it wasn’t possible to integrate Swift code completion into YouCompleteMe in an ad-hoc way: it needed to be integrated directly into the core source code.

After code completion was working good, it seemed like a great fit to upstream to YouCompleteMe. Until it wasn’t. A hard fork ended up being the solution. The YouCompleteMe team is absolutely a pleasure to work with - the fork spawned for technical reasons..

YouCompleteMe

YouCompleteMe consists of several features in addition to code completion including diagnostics rendering, go to definition, and more. The system vertically integrates several different backends like Jedi for Python and Racerd for Rust.

Here’s a simplified summary of how YouCompleteMe completion works:

  • Vim autocmds and callbacks trigger completion in YouCompleteMe’s VimScript
  • The VimScript calls into the completion system. ( Written in Python ).
  • The Python code returns code completions to Vim via VimScript
  • In the Vim Python process, YouCompleteMe gets code completions by delegating to a backend HTTP server, ycmd. Some completions are computed in the Vim process directly.
  • ycmd delegates to Completer’s which all except the clang one, make HTTP requests to language level servers.
  • To get semantic completion, the actual language severs interface with program language tools ( like the Swift compiler )

One design advantage of YouCompleteMe is that the semantic code completion engines are run in isolated process. Since ycmd is decoupled from Vim, it is reusable in other contexts, like emacs. icmd has recently been integrated into emacs as well.

Now for the challenges.

Maintainability and Support

The system is a massive polygot project encompassing VimScript, Python 2 ( and 3 ), C++, Boost, and all of third party code: C#, Rust, Java, and more.

As all of these technologies evolve, the project needs to be updated to deal with changes. iCompleteMe succeeded in reducing the surface by focusing exclusively on Swift.

A search of YouCompleteMe’s Github issues ( and @jerrymarino’s ), shows a dedidicated and perpetual effort to hold it all together.

Installation

The standard installation process of YouCompleteMe expects that the user’s machine can compile the transitive dependency graph. Building a complex program like this in arbitrary environments is not for the faint hearted. Installation is one of the main challenges the project faces.

Namespacing

Since Vim plugins run in a shared process, there was a huge conflict with YouCompleteMe. Vim plugin code, global variables and import paths all conflicted. Most of the YouCompleteMe code running in Vim’s process had to be namespaced to iCompleteMe. This namespacing and literal source renaming hardened the forking and renders most patches difficult to apply without significant tooling.

Python

YouCompleteMe supports both 2 and 3. Keeping python working is effort, even for Vim its self. For example, using 3.7 pops warnings about deprecated functionality used in Vim’s core.

Argument placeholders

There was push back on having custom VimScript for Swift completion. For function completions, it inserts placeholders where the arguments belong. As the user tab’s, argument placeholders are automatically deleted as the cursor automatically jumps to the argument. For Xcode users, this behavior is idiomatic. For YouCompleteMe, this feature isn’t implemented. Clang’s completion API has long exposed placeholders for methods

The philosophy of all encompassing YouCompleteMe can’t support language specific behaviors: the antithesis of a unified experience across all languages.

Looking forward

iCompleteMe inherited both the good parts and bad parts of YouCompleteMe. Swift brings a lot to the table for Vim plugin development, and SwiftForVim makes it a reality.

Many problems of YouCompleteMe could be solved by writing the core engine and plugin in a single language and distributing it as statically compiled native binary. This approach would make installation trivial and reduce maintenance overhead.

The impending Swift rewrite of YouCompleteMe could improve a lot more.

Here’s a few ideas:

  • Leverage the safety and type system of Swift to make it stable.
  • Build features in modules that can be reused in other plugins.
  • Use a library based approach to reduce complexity, server abstractions, and processes.
  • Writing it as functional as possible could make it easier to reason about.
  • Strip out all features configuration options. Users instead create variants with permissibly licensed core libraries.
  • Move away from an monolithic project and ownership model. A single monolithic code completion system is hard to support and evolve.

Starting up and progress

Things went into motion this early this summer.

1) Break the problem into small reasonable pieces and prototype.

2) SwiftPackageManager.vim is the test ground for prototyping starting with the diagnostics feature.

3) Implement Swift support ( Factor SwiftForVim ) out of SwiftPackageManager.vim

This is just the beginning.

Published on 01 Jul 2018 Find me on Twitter!