aboutsummaryrefslogtreecommitdiff
path: root/modules/textadept/adeptsense.lua
diff options
context:
space:
mode:
Diffstat (limited to 'modules/textadept/adeptsense.lua')
-rw-r--r--modules/textadept/adeptsense.lua307
1 files changed, 307 insertions, 0 deletions
diff --git a/modules/textadept/adeptsense.lua b/modules/textadept/adeptsense.lua
index 90093b54..4ecd1743 100644
--- a/modules/textadept/adeptsense.lua
+++ b/modules/textadept/adeptsense.lua
@@ -5,6 +5,313 @@
module('_m.textadept.adeptsense', package.seeall)
-- Markdown:
+-- ## 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. Adeptsense can also display documentation for such
+-- entities 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](../manual/7_Modules.html#language_specific).
+-- 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 a class can have,
+-- and fields are class or instance properties. For example an "Apple" class may
+-- have a "color" field and an "eat" 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
+--
+-- In the language-specific module, create an Adeptsense module.
+--
+-- $> # from either _HOME or _USERHOME:
+-- $> cd modules/lua/
+-- $> textadept adeptsense.lua
+--
+-- The Adeptsense should look like the following:
+--
+-- --- Adeptsense for the lua module.
+-- module('_m.lua.adeptsense', package.seeall)
+-- sense = _m.textadept.adeptsense.new('lua')
+--
+-- Where 'lua' is replaced by your language's name.
+--
+-- Then edit the module's `init.lua` to include the Adeptsense.
+--
+-- $> textadept init.lua
+--
+-- require 'lua.adeptsense' -- add this line
+-- require 'lua.commands'
+-- require 'lua.snippets'
+--
+-- #### 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`](#syntax)
+-- table documentation for all options.
+--
+-- ##### syntax.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.
+--
+-- ##### syntax.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.
+--
+-- ##### syntax.symbol_chars
+--
+-- In addition to the usual `[%w_%.]` symbol characters, Lua also allows symbols
+-- to contain a `:`.
+--
+-- sense.syntax.symbol_chars = '[%w_.:]'
+--
+-- ##### syntax.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 = {}
+--
+-- ##### syntax.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`](#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](http://ctags.sourceforge.net/)
+-- 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 to generate a fake set of tags that is more useful.
+-- This utility (`scripts/adeptsensedoc.lua`) is found in the source release.
+-- 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 = 'functions',
+-- F = 'fields',
+-- m = 'classes',
+-- t = 'fields',
+-- }
+--
+-- To load a default set of Ctags, use [`load_ctags()`](#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`](#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()`](#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.
+--
+-- #### Overriding Adeptsense Functions
+--
+-- Sometimes Adeptsense's default behavior is not sufficient. Maybe the
+-- `type_declarations` and `type_assignments` tables used by the
+-- [`get_class()`](#get_class) function are not granular enough. Maybe some
+-- symbols can contain more than just the `syntax.symbol_chars` used by
+-- [`get_symbol()`](#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()`](#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`](#ctags_kinds), it passes an unknown tag to the
+-- [`handle_ctag()`](#handle_ctag) function. In this case, package (`p`) tags
+-- are 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`](#get_completions) function needs to be overridden to
+-- use the `import` table's completions when necessary.
+--
+-- function sense:get_completions(symbol, only_fields, only_funs)
+-- if not buffer:get_cur_line():find('^%s*import') then
+-- return self.super.get_completions(self, symbol, only_fields, only_funs)
+-- 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. When this happens,
+-- [`handle_clear()`](#handle_clear) is called.
+--
+-- function sense:handle_clear()
+-- self.imports = {}
+-- end
+--
-- ## Settings
--
-- * `FUNCTIONS`: XPM image for adeptsense functions.