autocomplete-lua

Extensible autocomplete+ provider for Lua

39,512

12

Bug Reports

0.9.2

MIT

GitHub

This package provides the following services:

This package consumes the following services:

atom-autocomplete-lua

Atom Autocomplete+ provider for Lua.

This package aims to parse Lua files with oxyc/luaparse and generate scope and type-aware completions with syntactic analysis.

Features

Available Providers

Configuration

Besides what you can configure in Atom preferences, atom-autocomplete-lua looks for a .luacompleterc file in the parent directories of the current file.

If you need to define additional global symbols for your specific Lua environment, place a .luacompleterc file in your project root. It's a JSON file with roughly the following structure:

{
  "global": {
    "type": "table",
    "fields": {
      "my_global_var": { /* type definition */ },
      /* ... */
    }
  },
  "namedTypes": {
    "my_named_type": { /* type definition */ },
    /* ... */
  }
  "luaVersion": "5.2",
  "packagePath": "./?.lua",
  "cwd": "path/to/lua/module/root"
}

All options are optional. Here's what each option does:

Option Default Description
global { type: 'table', fields: {} } The type definition of the global environment. Define additional fields on this table to declare globals available in your Lua environment. Read the Type definitions section for more info.
namedTypes {} To avoid deep nesting and allow multiple places to reference a single type, you can define named types. Read the Named types section for more info.
luaVersion "5.2" The version of Lua your code is targeting. Valid values are "5.1", "5.2", "5.3" and "luajit-2.0".
packagePath "./?.lua" The value of LUA_PATH used when resolving required modules.
cwd . The current directory used to resolve relative paths in packagePath. If cwd is relative, it's considered relative to the parent directory of .luacompleterc.

Type definitions

The general format of a type definition is:

{
  "type": "type_name", // one of "function", "table", "number", "boolean", "string" or "unknown", "ref"
  "description": "Optional short Markdown description of your symbol",
  "descriptionPlain": "Optional short plain text description of your symbol (if you don't want Markdown for some reason)",
  "link": "http://optional.link/to/full/api/docs"
}

Tables

Tables ("type": "table") have 2 more properties:

Example:

{ // Type definition for a student
  "type": "table",
  "fields": {
    "name": { type: "string" },
    "surname": { type: "string" },
    "height": { type: "number" },
  },
  "metatable": {
    "type": "table",
    "fields": {
      "__index": {
        "type": "table",
        "fields": { "skip_rope": { "type": "function" } }
      }
    }
  }
}

Functions

Functions ("type": "function") can have a few more optional properties:

Argument names are of the form { "name": "arg_name", "displayName": "display_name" }, where displayName is optional.

displayName will be displayed in the autocomplete dropdown, while name will be part of the inserted snippet. This is useful for things like optional arguments:

{
  "type": "function",
  "args": [{ "name": "arg1" }, { "name": "arg2", "displayName": "[arg2]" }],
}

will produce f(arg1, [arg2]) in the dropdown and f(arg1, arg2) after being inserted.

In rare cases, displayName might be unsuitable for you. argsDisplay and argsDisplayOmitSelf can be used to manually specify the comma-separated list of arguments.

For example, you can use it to customize comma placement in relation to [:

{
  "type": "function",
  "args": [{ "name": "self" }, { "name": "arg1" }, { "name": "arg2" }],
  "argsDisplay": "self[, arg1[, arg2]]",
  "argsDisplayOmitSelf": "[arg1[, arg2]]"
}

This will produce f(self[, arg1[, arg2]]) in the dropdown and f(self, arg1, arg2) after being inserted.

Function variants

Sometimes, you may have polymorphic functions which can be called with a number of different argument configurations. You might want to show all of these versions separately in the autocomplete dropdown.

You can provide multiple versions of the same function by moving link, description, args, argsDisplay and argsDisplayOmitSelf inside a variants array like so:

{ // Type definition for a get(url_or_filename) function
  "type": "function",
  "variants": [{
    "args": [{ name: "url" }],
    "description": "Fetches an URL and returns a string with the contents"
  }, {
    "args": [{ name: "filename" }],
    "description": "Read the file at filename and returns a string with the contents"
  }]
}

The autocomplete dropdown will show both get(url) and get(filename) with their corresponding descriptions.

Named types

There are often cases where you want to use the same type definition in multiple places. In these situations, you can use named types in your .luacompleterc.

Just use { "type": "ref", "name": "my_named_type" } instead of your type definition and define my_named_type in namedTypes.

Example .luacompleterc:

{
  "global": {
    "type": "table",
    "fields": {
      "make_a_cat": {
        "type": "function",
        "returnTypes": [{ "type": "ref", "name": "cat" }]
      },
      "make_a_cat_somehow_else": {
        "type": "function",
        "returnTypes": [{ "type": "ref", "name": "cat" }]
      },
    }
  },
  "namedTypes": {
    "cat": {
      "type": "table",
      "fields": {
        "color": { "type": "string" },
        "is_fluffy": { "type": "boolean" }
      }
    }
  }
}

Option providers

All the options provided in a .luacompleterc can be programmatically provided by plugin packages (like Defold IDE).

Start by adding this to your package.json:

"providedServices": {
  "autocomplete-lua.options-provider": {
    "versions": {
      "1.0.0": "getOptionProvider"
    }
  }
}

Then, in your package's JS object:

getOptionProvider () {
  return myProvider; // You can also return an array of providers if you have more
}

The provider is an object (or a class) with the following interface:

const myProvider = {
  priority: 20,

  getOptions (request, getPreviousOptions, utils, cache) {
    // Just return the options from the previous provider
    return getPreviousOptions().then(previousOptions => {
      return { options: previousOptions }
    })
  }

  dispose () {
    // Destroy stuff
  }
}

priority and getPreviousOptions()

Each time a completion is needed (roughly at each keystroke), autocomplete-lua sorts all the option providers by their priority and calls the getOptions() method of the highest priority option provider.

The option provider can choose to call the next-highest-in-priority provider by calling getPreviousOptions(). getPreviousOptions() returns a promise to the options object provided by the next-in-line provider.

There are 3 option providers that come with autocomplete-lua:

dispose()

Optional function. dispose() is called when your provider is not needed anymore.

getOptions(request, getPreviousOptions, utils, cache)

This function is called when your provider is expected to return a new set of options.

request comes directly from Autocomplete+, with the addition of request.filePath, the absolute path to the current file.

The return value is an object of the form { options }. The same object is passed to getOptions() as cache the next time the function is called on the same file, so you can store additional arbitrary properties on it that you'd like to receive in cache. Returning a promise to this object is also supported.

It's strongly encouraged to always return the same options object if nothing changed from the last call. Read on about utils.mergeOptionsCached() for a simple way to do this.

utils.reviveOptions(options)

Takes an options object and resolves all references to named types. (Replaces { type: 'ref', name: 'myRef' } with namedTypes.myRef). This should be called after you read the options object from permanent storage.

Returns the same options object.

utils.mergeOptions(previousOptions, newOptions)

Takes 2 options objects, merges them and returns the result.

Fields in newOptions overwrite fields in previousOptions. The global fields are deeply merged.

utils.mergeOptionsCached(previousOptions, newOptions, cache[, merger])

Uses mergeOptions() to merge previousOptions and newOptions if any of the two are different from cache.previousOptions and cache.newOptions, else returns cache.options.

If the merge takes place, merger(mergedOptions, previousOptions, newOptions) is called on the newly merged object to do additional custom merging work.

Returns an object { options, previousOptions, newOptions } suitable for returning as the cache object from getOptions().

A recommended pattern is the following:

getOptions = async (request, getPreviousOptions, utils, cache) => {
  if (providerIsNotApplicableToTheCurrentFile) {
    return { options: await getPreviousOptions() }
  }
  const newOptions = conjureABunchOfNewOptions()
  const previousOptions = await getPreviousOptions()
  return utils.mergeOptionsCached(previousOptions, newOptions, cache, mergedOptions => {
    mergedOptions.oneMoreThing = 'this thing'
  })
}

util.<typename>New()

Creates a new type definition for a <typename>. Available functions are: tableNew(), booleanNew(), functionNew(), numberNew(), unknownNew(), nilNew()

util.tableSet(table, key, value)

Sets the field identified by key in the table type definition table to the type definition value.

util.tableGet(table, key)

Gets the type definition corresponding to the field identified by key in the table type definition table.

util.tableSetMetatable(table, metatable)

Sets the metatable type definition in the table type definition table to the type definition metatable.

util.tableGetMetatable(table)

Gets the type definition of the metatable of the table type definition table.