Skip to content
Oleksandr Yakushev edited this page May 20, 2024 · 7 revisions

By default, the information a completion library has is quite restricted. Usually it is only a prefix of a thing that user wants to complete. But big IDEs like Eclipse parse entire source files or even projects to make their completion more intelligent and environment-aware.

What is context?

Compliment tries to achieve something similar with a concept called context. Context is basically a top-level code form from where the completion was initiated. This code form undergoes several transformations on both client and server side to deliver the context to the completions source in a format that is convenient to use.

Aside this document you can also check context tests for additional understanding.

Client side

When user initiates completion in the editor, client-side Compliment plugin grabs the whole top-level form where the completion point is located. For example, completion was initiated in this code (█ represents cursor position):

(ns foo.bar
  (:use [clojure.string :only [join tr█]])
  (:import java.io.File))

Then the plugin replaces the completion prefix with unified prefix placeholder (__prefix__). This is done to communicate to server-side Compliment the location of the prefix.

(ns foo.bar
  (:use [clojure.string :only [join __prefix__]])
  (:import java.io.File))

Finally the modified context form is passed as a string to the server by calling (compliment.core/completions ...) together with the prefix.

Server side

Upon receiving the context Compliment performs the most significant transformation. It takes the code form (which is a tree) and turns it into a sequence of forms starting with immediate form containing __prefix__ and going outwards. Thus after parsing our example will become:

({:idx 1, :form [join __prefix__]}
 {:idx 2, :form [clojure.string :only [join __prefix__]]}
 {:idx 1, :form (:use [clojure.string :only [join __prefix__]])}
 {:idx 2, :form (ns foo.bar
                  (:use [clojure.string :only [join __prefix__]])
                  (:import java.io.File))})

Besides :form each map contains :idx value which represents the position of __prefix__ or the form that has it in the outer form.

For maps :idx works differently:

{:akey {someth█ 42}}

;;=>

({:idx 42, :map-role :key, :form {__prefix__ 42}}
 {:idx :akey, :map-role :value, :form {:akey {__prefix__ 42}}})

You can see that :map-role appears in the results that correspond to map forms. It can be either :key or :value depending on what position is prefix or prefix-containing form. Also :idx now shows the opposite value in prefix’s key-value pair.

If a map literal contains odd number of elements, a single nil is added at the end.

{:someth█}

;;=>

({:idx nil, :map-role :key, :form {__prefix__ nil}})

Context usage

A few examples of context usage can be found in default Compliment sources.

  • Vars completion in use/require: code, tests
  • Class members completion based on second arg: code, tests
  • Local bindings source works exclusively through contexts.
Clone this wiki locally