Textadept
- Home |
- Download |
- Lua API |
- Source |
- Language Modules |
- Stats |
- Wiki |
- Mailing List
Contents
_M.textadept.adeptsense
Code autocompletion and documentation support for programming languages.
Overview
Adeptsense is a form of autocompletion for programming. It has the means to supply a list of potential completions for classes, member functions and fields, packages, etc and display their documentation in the form of a calltip. This document provides the information necessary in order to write a new Adeptsense for a language. For illustrative purposes, an Adeptsense for Lua will be created. More advanced techniques are covered later.
Creating an Adeptsense
Adeptsenses exist per-language and are typically defined in a language-specific module. First check to see if the module for your language has an Adeptsense. If not, you will need to create one. The language modules included with Textadept have Adeptsenses so they can be used for reference. If your language is similar to any of those languages, you can copy and modify the existing language’s Adeptsense, saving some time and effort.
Terminology
Not all languages have “classes”, “functions”, and “fields” in the full sense of the word. Normally classes are referred to as objects in Object Oriented Programming (OOP), functions are class or instance methods,and fields are class or instance properties. For example a “Cat” class may have a “color” field and a “meow” function. To Adeptsense, the term “class” is simply a container for “function” and “field” completions. “functions” and “fields” are only distinguished by an icon in the completion list.
For Lua, consider modules and tables as “classes”, functions as “functions”, and module/table keys as “fields”.
Introduction
Open the language-specific module for editing and create a new instance of an Adeptsense.
$> # from either _HOME or _USERHOME:
$> cd modules/lua/
$> textadept init.lua
sense = _M.textadept.adeptsense.new('lua')
Where ‘lua’ is replaced by your language’s name.
Syntax Options
The syntax of different languages varies so the Adeptsense must be configured
for your language in order to function properly. See the syntax
table documentation for all options.
self
The first syntax option is syntax.self
. While Lua has a self
identifier,
it is not used in the usual sense for a class instance so it will just be
ignored.
class_definition
Next is syntax.class_definition
. Lua does not really have the “class”
concept most OOP programmers are used to, but modules do behave somewhat like
classes.
sense.syntax.class_definition = 'module%s*%(?%s*[\'"]([%w_%.]+)'
The “class”’s name is the identifier in quotes.
word_chars
Lua words already consist of the default %w_
so no changes are necessary.
symbol_chars
In addition to the usual [%w_%.]
symbol characters, Lua also allows symbols
to contain a ‘:’.
sense.syntax.symbol_chars = '[%w_%.:]'
type_declarations
Since Lua has no type declarations (e.g. int x
in C), the
syntax.type_declarations
table should be empty:
sense.syntax.type_declarations = {}
type_declarations_exclude
Since Lua has no type declarations, no changes are necessary.
type_assignments
Sometimes a type can be inferred by the right side of a variable assignment.
In the Lua code local foo = 'bar'
, the variable foo
has type string
.
Similarly, in local foo = _M.textadept.adeptsense
, foo
has type
_M.textadept.adeptsense
.
sense.syntax.type_assignments = {
['^[\'"]'] = 'string', -- foo = 'bar' or foo = "bar"
['^([%w_%.]+)'] = '%1' -- foo = _M.textadept.adeptsense
}
Note the ‘^’ in the pattern. The beginning of the right hand side of the
assignment should be matched, otherwise local foo = bar('baz')
could infer
an incorrect type.
Completion Lists
The completions
table contains the completion lists for
all classes. Each table entry key is the class’s name and the value is a
table of functions
and fields
lists. The general form is:
sense.completions = {
['class1'] = {
functions = {'fun1', 'fun2', ...},
fields = {'f1', 'f2', ...}
},
['class2'] = ...,
...
}
Obviously manually creating completion lists would be incredibly time-consuming so there is a shortcut method.
Ctags
Adeptsense recognizes the output from Ctags and can populate the
completions
table from it with a little bit of help. Ctags has a list of
“kinds” for every language. You can see them by running ctags --list-kinds
in your shell. Since Adeptsense only cares about classes, functions, and
fields, you need to let it know which kind of tag is which. Unfortunately,
Lua support in Ctags is not good at all. Instead, Textadept has a utility
(modules/lua/adeptsensedoc.lua) to generate a fake set of tags that is more
useful. Functions are tagged 'f'
and should be recognized as such; table
keys are tagged 't'
and should be recognized as fields; module fields,
'F'
, should be fields; and modules, 'm'
, should be classes:
sense.ctags_kinds = {
f = _M.textadept.adeptsense.FUNCTION,
F = _M.textadept.adeptsense.FIELD,
m = _M.textadept.adeptsense.CLASS,
t = _M.textadept.adeptsense.FIELD,
}
To load a default set of Ctags, use load_ctags()
.
sense:load_ctags(_HOME..'/modules/lua/tags', true)
Textadept comes with a set of Ctags for its Lua API.
API Documentation
Adeptsense can show API documentation for symbols from files specified in its
api_files
table. See the previous link for documentation on
how the API file should be structured.
sense.api_files = {_HOME..'/modules/lua/api'}
Triggers
Triggers are characters or character sequences that trigger an autocompletion
list to be displayed. Lua has two characters that can do so: ‘.’ and ‘:’. The
‘.’ should autocomplete both fields and functions while ‘:’ should
autocomplete only functions. This is specified using
add_trigger()
.
sense:add_trigger('.')
sense:add_trigger(':', false, true)
User-Settings
Finally, you should allow the users of your Adeptsense to supply their own Ctags and API files in addition to any default ones you loaded or specified earlier:
-- Load user tags and apidoc.
if lfs.attributes(_USERHOME..'/modules/lua/tags') then
sense:load_ctags(_USERHOME..'/modules/lua/tags')
end
if lfs.attributes(_USERHOME..'/modules/lua/api') then
sense.api_files[#sense.api_files + 1] = _USERHOME..'/modules/lua/api'
end
Summary
The above method of setting syntax options, Ctags kinds, and trigger characters for an Adeptsense is sufficient for most static and some dynamic languages. The rest of this document is devoted to more complex techniques.
Fine-Tuning
Sometimes Adeptsense’s default behavior is not sufficient. Maybe the
type_declarations
and type_assignments
tables used by the
get_class()
function are not granular enough. Maybe some
symbols can contain more than just the syntax.symbol_chars
used by
get_symbol()
. Adeptsense allows these functions to be
overridden.
function sense:get_class(symbol)
if condition then
return self.super.get_class(self, symbol) -- default behavior
else
-- different behavior
end
end
The default Adeptsense functions are called by using the self.super
reference.
Examples for Ruby
In Ruby, everything is an object – even numbers. Since numbers do not have
a type declaration, the get_class()
function should return
Integer
or Float
if the symbol is a number.
function sense:get_class(symbol)
local class = self.super.get_class(self, symbol)
if class then return class end
-- Integers and Floats.
if tonumber(symbol:match('^%d+%.?%d*$')) then
return symbol:find('%.') and 'Float' or 'Integer'
end
return nil
end
Note that there is no plus or minus prefix in the pattern. This is because
‘+’ or ‘-’ characters are not a part of syntax.symbol_chars
so a symbol
will not contain either of them.
Like numbers, the syntax for constructing strings, arrays, hashes, and the
like should also be considered as symbols. [foo].
should show a completion
list with array instance methods:
function sense:get_symbol()
local line, p = buffer:get_cur_line()
if line:sub(1, p):match('%[.-%]%s*%.$') then return 'Array', '' end
return self.super.get_symbol(self)
end
The Ruby module Adeptsense has a more complicated version of this function that handles strings, hashes, symbols, and regexps as well. Please refer to it for more information.
Examples for Java
Autocomplete of Java import
statements is something nice to have. Ctags
produces a tag for packages so it is rather straightforward to build an
import completion list.
Since Adeptsense ignores any tags not mapped to classes
, functions
, or
fields
in ctags_kinds
, it passes an unknown tag to the
handle_ctag()
function. In this case, package (‘p’) tags
need to be handled.
function sense:handle_ctag(tag_name, file_name, ex_cmd, ext_fields)
if ext_fields:sub(1, 1) ~= 'p' then return end -- not a package
if not self.imports then self.imports = {} end
local import = self.imports
for package in tag_name:gmatch('[^.]+') do
if not import[package] then import[package] = {} end
import = import[package]
end
import[#import + 1] = file_name:match('([^/\\]-)%.java$')
end
Now that we have a list of import completions, it should be activated by the
normal trigger (‘.’), but only on a line that contains an import
statement.
The get_completions
function needs to be overridden to
use the import
table’s completions when necessary.
function sense:get_completions(symbol, ofields, ofunctions)
if not buffer:get_cur_line():find('^%s*import') then
return self.super.get_completions(self, symbol, ofields, ofunctions)
end
if symbol == 'import' then symbol = '' end -- top-level import
local c = {}
local import = self.imports or {}
for package in symbol:gmatch('[^%.]+') do
if not import[package] then return nil end
import = import[package]
end
for k, v in pairs(import) do
c[#c + 1] = type(v) == 'table' and k..'?1' or v..'?2'
end
table.sort(c)
return c
end
Note that '?1'
and '?2'
are appended to each completion entry. These tell
Adeptsense which icon to display in the autocompletion list. If no icon
information is given, no icon is displayed. 1
is for fields and 2
is for
functions. In this case, the icons are used only to distinguish between a
parent package and a package with no children since parents have an
additional list of completions.
Finally since an imports
table was created, it should be cleared when the
Adeptsense is cleared to free up memory immediately. When this happens,
handle_clear()
is called.
function sense:handle_clear()
self.imports = {}
end
Child Language Adeptsenses
When Adeptsense completion is triggered, the Adeptsense for the language at the caret position is used, not necessarily the parent language’s Adeptsense. For example, when editing CSS inside of an HTML file, the user expects the CSS Adeptsense to be used. However, child language Adeptsenses are not loaded automatically and must be loaded by the parent language module. For the case of CSS in HTML, the HTML module’s init.lua must contain:
-- Load CSS Adeptsense.
if not _M.css then _M.css = require('css') end
You will have to do something similar if you are writing an Adeptsense for a child lexer language.
Generating Lua Adeptsense
You can generate Lua Adeptsense for your own modules using the Lua language module’s adeptsensedoc.lua module with LuaDoc:
luadoc -d . --doclet _HOME/modules/lua/adeptsensedoc [module(s)]
where _HOME
is where Textadept is installed. The tags and api files are
output to the current directory and can be loaded via
load_ctags()
and api_files
respectively.
Module Fields
Not only does the Lua Adeptsense generator recognize functions and tables within modules, but it also recognizes module fields and their types with a certain syntax:
---
-- Module documentation.
-- @field field_name (type)
-- Field documentation.
or
---
-- Module documentation
-- * `field_name` (type)
-- Field documentation.
-- Multiple documentation lines must be indented.
Please note the latter -- * `field_name`
syntax can appear anywhere
inside a module, not just the module LuaDoc.
Fields
CLASS
(string)
Ctags kind for Adeptsense classes.
FIELD
(string)
Ctags kind for Adeptsense fields.
FIELDS
(string)
XPM image for Adeptsense fields.
FUNCTION
(string)
Ctags kind for Adeptsense functions.
FUNCTIONS
(string)
XPM image for Adeptsense functions.
always_show_globals
(bool)
Include globals in the list of completions offered.
Globals are classes, functions, and fields that do not belong to another
class. They are contained in sense.completions['']
.
The default value is true
.
Functions
add_trigger
(sense, c, only_fields, only_functions)
Sets the trigger character(s) c for autocompletion.
If only_fields is true
, the trigger only completes fields. If
only_functions is true
, the trigger only completes functions.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.c
: The character(s) that triggers the autocompletion. You can have up to two characters.only_fields
: Optional flag indicating whether or not this trigger only completes fields. The default value isfalse
.only_functions
: Optional flag indicating whether or not this trigger only completes functions. The default value isfalse
.
Usage:
sense:add_trigger('.')
sense:add_trigger(':', false, true) -- only functions
sense:add_trigger('->')
clear
(sense)
Clears the Adeptsense for loading new Ctags or project files.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.
complete
(sense, only_fields, only_functions)
Shows an autocompletion list of functions (unless only_fields is true
)
and fields (unless only_funcs is true
) for the symbol behind the caret,
returning true
on success.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
. Ifnil
, uses the current language’s Adeptsense (if it exists).only_fields
: Optional flag indicating whether or not to return a list of only fields. The default value isfalse
.only_functions
: Optional flag indicating whether or not to return a list of only functions. The default value isfalse
.
Return:
true
on success orfalse
.
See also:
get_apidoc
(sense, symbol)
Returns a list of apidocs for symbol name.
The list contains a pos
key with the index of the apidoc to show.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.symbol
: The symbol name to get apidocs for.
Return:
- list of apidocs or
nil
get_class
(sense, symbol)
Returns the class name for symbol name.
If symbol is sense.syntax.self
and inside a class definition matching
sense.syntax.class_definition
, that class is returned. Otherwise the
buffer is searched backwards for a type declaration of symbol according to
the patterns in sense.syntax.type_declarations
or a type assignment of
symbol according to sense.syntax.type_assignments
.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.symbol
: The symbol name to get the class of.
Return:
- class or
nil
See also:
get_completions
(sense, symbol, only_fields, only_functions)
Returns a list of function (unless only_fields is true
) and field (unless
only_funcs is true
) completions for symbol name.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.symbol
: The symbol name to get completions for.only_fields
: Optional flag indicating whether or not to return a list of only fields. The default value isfalse
.only_functions
: Optional flag indicating whether or not to return a list of only functions. The default value isfalse
.
Return:
- completion_list or
nil
get_symbol
(sense)
Returns a full symbol (if any) and current symbol part (if any) behind the
caret.
For example: buffer.cur
would return 'buffer'
and 'cur'
. Returns empty
strings instead of nil
.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.
Return:
- symbol or
''
- part or
''
goto_ctag
(sense, kind, title)
Prompts the user to select a symbol to jump to from a list of all known symbols of kind kind (classes, functions, fields, etc.) shown in a filtered list dialog whose title text is title.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.kind
: The Ctag character kind (e.g.'f'
for a Lua function).title
: The title for the filtered list dialog.
handle_clear
(sense)
Called when clearing the Adeptsense. This function should be replaced with your own if you have any persistant objects that need to be deleted.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.
handle_ctag
(sense, tag_name, file_name, ex_cmd, ext_fields)
Called by load_ctags()
when a Ctag kind is not recognized.
The parameters are extracted from Ctags' tag format. This method should
be replaced with your own that is specific to the language.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.tag_name
: The tag name.file_name
: The name of the file the tag belongs to.ex_cmd
: Theex_cmd
returned by Ctags.ext_fields
: Theext_fields
returned by Ctags.
load_ctags
(sense, tag_file, nolocations)
Loads the Ctags file tag_file for autocompletions.
If nolocations is true
, sense:goto_ctag()
cannot be used with this set
of tags. It is recommended to pass -n
to ctags
in order to use line
numbers instead of text patterns to locate tags. This will greatly reduce
memory usage for a large number of symbols if nolocations
is false
.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
.tag_file
: The path of the Ctags file to load.nolocations
: Optional flag indicating whether or not to discard the locations of the tags for use bysense:goto_ctag()
. The default value isfalse
.
new
(lang)
Creates and returns a new Adeptsense for lang name. Only one sense can exist per language.
Parameters:
lang
: The lexer language name to create an Adeptsense for.
Usage:
local lua_sense = _M.textadept.adeptsense.new('lua')
Return:
- adeptsense
show_apidoc
(sense)
Shows a call tip with API documentation for the symbol behind the caret. If documentation is already being shown, cycles through multiple definitions.
Parameters:
sense
: The Adeptsense returned byadeptsense.new()
. Ifnil
, uses the current language’s Adeptsense (if it exists).
Return:
- list of apidocs on success or
nil
.
See also:
Tables
api_files
A list of api files used by show_apidoc()
.
Each line in the api file contains a symbol name (not the full symbol)
followed by a space character and then the symbol’s documentation. Since
there may be many duplicate symbol names, it is recommended to put the full
symbol and arguments, if any, on the first line. (e.g. Class.function(arg1,
arg2, ...)
). This allows the correct documentation to be shown based on the
current context. In the documentation, newlines are represented with “\n”. A
‘\’ before “\n” escapes the newline.
completions
A list containing lists of possible completions for known symbols.
Each symbol key has a table value that contains a list of field completions
with a fields
key and a list of functions completions with a functions
key. This table is normally populated by load_ctags()
, but can also be set
by the user.
ctags_kinds
A map of Ctags kinds to Adeptsense kinds.
Recognized kinds are FUNCTION
, FIELD
, and CLASS
. Classes are quite
simply containers for functions and fields so Lua modules would count as
classes. Any other kinds will be passed to handle_ctag()
for user-defined
handling.
Usage:
luasense.ctags_kinds = {f = _M.textadept.adeptsense.FUNCTION}
csense.ctags_kinds = {m = _M.textadept.adeptsense.FIELD, f = _M.textadept.adeptsense.FUNCTION, c = _M.textadept.adeptsense.CLASS, s = _M.textadept.adeptsense.CLASS}
javasense.ctags_kinds = {f = _M.textadept.adeptsense.FIELD, m = _M.textadept.adeptsense.FUNCTION, c = _M.textadept.adeptsense.CLASS, i = _M.textadept.adeptsense.CLASS}
See also:
inherited_classes
A map of classes and a list of their inherited classes, normally populated by
load_ctags()
.
locations
A list of the locations of known symbols, normally populated by
load_ctags()
.
syntax
Map of language-specific syntax settings.
Fields:
self
: The language’s syntax-equivalent ofself
. The default value is'self'
.class_definition
: A Lua pattern representing the language’s class definition syntax. The first capture returned must be the class name. A second, optional capture contains the class' superclass (if any). If no completions are found for the class name, completions for the superclass are shown (if any). Completions will not be shown for both a class and superclass unless defined in a previously loaded Ctags file. Also, multiple superclasses cannot be recognized by this pattern; use a Ctags file instead. The default value is'class%s+([%w_]+)'
.word_chars
: A Lua pattern of characters allowed in a word. The default value is'%w_'
.symbol_chars
: A Lua pattern of characters allowed in a symbol, including member operators. The pattern should be a character set. The default value is'[%w_%.]'
.type_declarations
: A list of Lua patterns used for determining the class type of a symbol. The first capture returned must be the class name. Use%_
to match the symbol. The default value is'(%u[%w_%.]+)%s+%_'
.type_declarations_exclude
: A table of types to exclude, even if they match atype_declarations
pattern. Each excluded type is a table key and has atrue
boolean value. For example,{Foo = true}
excludes any type whose name isFoo
. The default value is{}
.type_assignments
: A map of Lua patterns to class types for variable assignments, typically used for dynamically typed languages. For example,sense.syntax.type_assignments['^"'] = 'string'
would recognize string assignments in Lua so thefoo
infoo = "bar"
would be recognized as typestring
. The class type value may contain%n
pattern captures.
See also: