diff options
author | 2013-05-28 16:35:47 -0400 | |
---|---|---|
committer | 2013-05-28 16:35:47 -0400 | |
commit | 78bd70ca3cd59ce6d76d1ef22c1622ffb048443a (patch) | |
tree | ed900f4550b43f72dacad5cd058a6e49035602e7 /modules | |
parent | d9ec6f46cb63906274c2bb58832cb0c3862d7536 (diff) | |
download | textadept-78bd70ca3cd59ce6d76d1ef22c1622ffb048443a.tar.gz textadept-78bd70ca3cd59ce6d76d1ef22c1622ffb048443a.zip |
Rewrote LuaDoc; modules/textadept/adeptsense.lua
Diffstat (limited to 'modules')
-rw-r--r-- | modules/textadept/adeptsense.lua | 410 |
1 files changed, 207 insertions, 203 deletions
diff --git a/modules/textadept/adeptsense.lua b/modules/textadept/adeptsense.lua index f0e7a320..d3c880c2 100644 --- a/modules/textadept/adeptsense.lua +++ b/modules/textadept/adeptsense.lua @@ -8,117 +8,117 @@ local M = {} -- -- ## 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 to write a new --- Adeptsense for a language and demonstrates how to create an Adeptsense for --- Lua. The document covers more advanced techniques later. --- --- ## Creating an Adeptsense --- --- Adeptsenses exist per-language so they should be defined in --- [language-specific modules][]. 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 and are useful references. --- 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. +-- Adeptsense is a form of autocompletion for programming. Object-oriented +-- programming languages often have class types with member methods. For +-- example, the Ruby and Java programming languages implement "String" classes +-- with member functions that return the uppercase versions of the strings. This +-- looks like: +-- +-- # Ruby | // Java +-- foo = "bar" | String foo = "bar"; +-- foo.upcase! | foo = foo.toUpperCase(); +-- +-- In both cases, Adeptsense recognizes that the variable "foo" is of type +-- "String" and displays a list of known methods in the "String" class (which +-- includes the appropriate one shown above) after the user types "foo.". Upon +-- request, Adeptsense also displays any known documentation for the symbol +-- under the caret, such as the "upcase!" and "toUpperCase" methods, in the form +-- of a calltip. +-- +-- The first part of this document deals with constructing a simple Adeptsense. +-- The second part deals with more advanced techniques for "fine-tuning" +-- Adeptsenses, such as overriding default behaviors and recognizing additional +-- language features. The last part shows you how to generate Adeptsense data +-- for any Lua modules you have so that Textadept can provide additional +-- completions and documentation while you write your scripts. +-- +-- ## Adeptsense Basics +-- +-- Adeptsenses exist per-language so [language-specific modules][] typically +-- define them. Before attempting to write an Adeptsense, first determine if the +-- module for your language has an Adeptsense. Textadept's official language +-- modules have Adeptsenses and are good reference sources. If your language is +-- similar to any of Textadept's, you may be able to copy and modify that +-- language's Adeptsense, saving some time and effort. -- -- [language-specific modules]: _M.html#Language-Specific.Modules -- --- ### Terminology --- --- Not all languages have "classes", "functions", and "fields" in the full sense --- of the word. Object Oriented Programming (OOP) normally refers to classes as --- objects with functions being class or instance methods, and fields being --- 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". +-- Creating a new instance of an Adeptsense from within a language-specific +-- module is easy. Just replace the '?' with the name of your language: -- --- ### Introduction +-- M.sense = _M.textadept.adeptsense.new('?') -- --- 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') +-- ### Terminology -- --- replacing 'lua' with your language's name. +-- Since some programming languages like Lua are not object-oriented, the terms +-- "classes" and "methods" may not mean anything to those languages. In an +-- attempt to cater to all languages, Adeptsense adopts the terms "class", +-- "function", and "field". However, "classes" are simply containers for +-- "functions" and "fields" while "functions" and "fields" are just entities +-- distinguishable only by an icon in the autocompletion list. The Lua +-- Adeptsense considers modules and tables as "classes", functions as +-- "functions", and module/table keys as "fields". -- -- ### 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. --- --- #### `self` --- --- The first syntax option is `syntax.self`. While Lua has a `self` identifier, --- Lua does not use that identifier in the usual sense for a class instance so --- it is 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: +-- Take a moment to think about your programming language and its syntax. How +-- do you declare "classes"? What characters can you use in identifiers? Is the +-- language statically typed? If so, how do you declare a variable's type? If +-- not, how might you infer a variable's type? You must answer all of these +-- questions in an Adeptsense's [`syntax`](#syntax) table. Please see its +-- documentation for details. +-- +-- #### Example Syntax Options +-- +-- So, how might you define syntax options for your language? Here are some +-- examples. +-- +-- **Lua** +-- +-- Lua is a dynamically typed language with no built-in object-oriented +-- features. As noted earlier, the Lua Adeptsense considers modules and tables +-- as "classes" and thus uses Lua 5.1's `module()` as a "class" declaration. Lua +-- allows alphanumerics and underscores in its identifiers and uses the '.' and +-- ':' characters to access the functions and fields of "classes". Since the +-- language is dynamically typed, it has no type declaration and instead relies +-- on type inference through assignment to entities like strings, other tables, +-- or the result of a function call like `io.open()`. +-- +-- M.sense.syntax.class_definition = 'module%s*%(?%s*[\'"]([%w_%.]+)' +-- M.sense.syntax.symbol_chars = '[%w_%.:]' +-- M.sense.syntax.type_declarations = {} +-- M.sense.syntax.type_assignments = { +-- ['^[\'"]'] = 'string', -- foo = 'bar' or foo = "bar" +-- ['^([%w_%.]+)%s*$'] = '%1', -- foo = _M.textadept.adeptsense +-- ['^io%.p?open%s*%b()%s*$'] = 'file' -- f = io.open('foo') +-- } -- --- sense.syntax.type_declarations = {} +-- The beginning '^' in these type assignment patterns is necessary since +-- Adeptsense starts matching on the right-hand side of an assignment. +-- Otherwise, `local foo = bar('baz')` might infer an incorrect type. -- --- #### `type_declarations_exclude` +-- **Java** -- --- Since Lua has no type declarations, no changes are necessary. +-- Java is a statically typed, object-oriented language. It has most of the +-- default syntax features that Adeptsense assumes except for parameterized list +-- types. -- --- #### `type_assignments` +-- local td = M.sense.syntax.type_declarations +-- td[#td + 1] = '(%u[%w_%.]+)%b<>%s+%_' -- List<Foo> bar -- --- 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. +-- The "%_" sequence in this pattern matches the symbol part of the type +-- declaration. -- -- ### 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: +-- Even though your Adeptsense now understands the basic syntax of your +-- programming language, it is not smart enough to parse code to generate lists +-- of function and field completions for classes. Instead, you must supply this +-- information to your Adeptsense's [`completions`](#completions) table. The +-- table contains string class names assigned to tables that themselves contain +-- `functions` and `fields` completion tables. Here is the general form: -- --- sense.completions = { +-- M.sense.completions = { -- ['class1'] = { -- functions = {'fun1', 'fun2', ...}, -- fields = {'f1', 'f2', ...} @@ -127,86 +127,94 @@ local M = {} -- ... -- } -- --- Obviously manually creating completion lists would be incredibly --- time-consuming so there is a shortcut method. +-- Obviously, manually creating completion lists is incredibly time-consuming so +-- Adeptsense provides 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 = { +-- Adeptsense recognizes the output from [Ctags][] and uses the output along +-- with your Adeptsense's [`ctags_kinds`](#ctags_kinds) to populate +-- `completions`. Ctags has a list of "kinds" for every language. View 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. After that, load your sets of tags using +-- [`load_ctags()`](#load_ctags). Here are some examples: +-- +-- **C/C++** +-- +-- local as = _M.textadept.adeptsense +-- M.sense.ctags_kinds = { +-- c = as.CLASS, d = as.FUNCTION, e = as.FIELD, f = as.FUNCTION, +-- g = as.CLASS, m = as.FIELD, s = as.CLASS, t = as.CLASS +-- } +-- M.sense:load_ctags(_HOME..'/modules/cpp/tags', true) +-- +-- **Lua** +-- +-- Unfortunately, Lua support in Ctags is poor. Instead, Textadept has a tool +-- (*modules/lua/adeptsensedoc.lua*) to generate a more useful set of tags. The +-- tool tags functions as `'f'`, module fields as `'F'`, modules as `'m'`, and +-- table keys as `'t'`. +-- +-- M.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()`](#load_ctags). --- --- sense:load_ctags(_HOME..'/modules/lua/tags', true) --- --- Textadept comes with a set of Ctags for its Lua API. +-- M.sense:load_ctags(_HOME..'/modules/lua/tags', true) -- -- [ctags]: http://ctags.sourceforge.net -- -- ### 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'} +-- Like with completion lists, Adeptsense is not smart enough to parse code to +-- generate API documentation. You must do so manually in a set of API files and +-- add them to your Adeptsense's [`api_files`](#api_files) table. The table's +-- documentation describes API file structure and format. -- -- ### 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). +-- At this point, your Adeptense understands your language's syntax, has a set +-- of completions for classes, and knows where to look up API documentation +-- from. The only thing left to do is to tell your Adeptsense what characters +-- trigger autocompletion. Use [`add_trigger()`](#add_trigger) to do this. Some +-- examples: -- --- sense:add_trigger('.') --- sense:add_trigger(':', false, true) +-- **C/C++** -- --- ### User-Settings +-- M.sense:add_trigger('.') +-- M.sense:add_trigger('->') -- --- 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: +-- **Lua** -- --- -- 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 +-- M.sense:add_trigger('.') +-- M.sense:add_trigger(':', false, true) -- -- ### 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. +-- Adeptsense is flexible enough to support code autocompletion and API +-- reference for many different programming languages. By simply setting some +-- basic syntax parameters in a new Adeptsense instance, loading a set of +-- completions and API documentation, and specifying characters that trigger +-- autocompletion, you created a powerful tool to help write code faster and +-- understand code better. -- --- ### Fine-Tuning +-- ## Advanced Techniques -- --- 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. +-- ### Fine-Tuning -- --- function sense:get_class(symbol) +-- Adeptsense's defaults are adequate enough to provide basic autocompletion for +-- a wide range of programming languages. However, sometimes you need more +-- control. For example, when determining the class of the symbol to +-- autocomplete, Adeptsense calls [`get_class()`](#get_class). This function +-- utilizes the Adeptsense's `syntax.type_declarations` and +-- `syntax.type_assignments` to help, but those tables may not be granular +-- enough for your language. (For example, in Ruby, everything is an object -- +-- even numbers. "0.to_s" is perfectly valid syntax.) Adeptsense allows you to +-- override its default functionality: +-- +-- function M.sense:get_class(symbol) -- if condition then -- return self.super.get_class(self, symbol) -- default behavior -- else @@ -214,14 +222,17 @@ local M = {} -- end -- end -- --- The default Adeptsense functions are called by using the `self.super` --- reference. +-- Use the `self.super` table to call on default functionality. -- --- #### Examples for Ruby +-- Below are some examples of overriding default functionality for the Ruby and +-- Java languages. -- --- 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. +-- **Ruby** +-- +-- As mentioned earlier, everything in Ruby is an object, including numbers. +-- Since numbers may exist by themselves, those instances do not have a type +-- declaration or type assignment. In that case, the Ruby Adeptsense's +-- `get_class()` needs to return "Integer" or "Float" if the symbol is a number. -- -- function sense:get_class(symbol) -- local class = self.super.get_class(self, symbol) @@ -233,34 +244,32 @@ local M = {} -- 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. +-- Since `syntax.symbol_chars` does not contain '+' and '-' characters, *symbol* +-- does not need to match 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: +-- Another consequence of the "everything is an object" rule is that, like +-- numbers, strings, arrays, hashes, etc. may also exist alone. (For example, +-- "[1, 2, 3]." needs to show Array completions.) Since symbols only contain +-- alphanumerics, '_', '?', and '!' characters, the Ruby Adeptsense needs to +-- override the default [`get_symbol()`](#get_symbol) functionality. -- -- function sense:get_symbol() -- local line, p = buffer:get_cur_line() --- if line:sub(1, p):match('%[.-%]%s*%.$') then return 'Array', '' end +-- if line:sub(1, p):match('%[.-%]%s*%.$') then +-- return 'Array', '' +-- end +-- -- More checks for strings, hashes, symbols, regexps, etc. -- 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 +-- **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 --- need to be handled. +-- Autocompletion of Java `import` statements is something nice to have. You can +-- construct an import completion list from Ctags's package tags. By default, +-- Adeptsense ignores any tags not mapped to classes, functions, or fields in +-- [`ctags_kinds`](#ctags_kinds) and passes the unknown tags to +-- [`handle_ctag()`](#handle_ctag). In this case, the Java Adeptsense needs to +-- handle package ('p') tags. -- -- 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 @@ -273,10 +282,10 @@ local M = {} -- 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. +-- Now that the Adeptsense has a set of import completions, the '.' trigger key +-- should only autocomplete packages on a line that starts with "import". Since +-- [`get_completions()`](#get_completions) is responsible for getting the set of +-- completions for a particular symbol, the Java Adeptsense must override it. -- -- function sense:get_completions(symbol, ofields, ofunctions) -- if not buffer:get_cur_line():find('^%s*import') then @@ -296,16 +305,15 @@ local M = {} -- 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. +-- The "?1" and "?2" appended to each completion entry tell Adeptsense which +-- icon to display for each entry in the autocompletion list. Entries do not +-- need to have icons. '1' is for fields and '2' is for functions. In this case, +-- the icons distinguish between a parent package and a package with no +-- children. -- --- 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()`](#handle_clear) is called. +-- Finally the Adeptsense should clear the `imports` table it created when the +-- Adeptsense is cleared so-as to free up memory immediately. This is done in +-- [`handle_clear()`](#handle_clear). -- -- function sense:handle_clear() -- self.imports = {} @@ -313,30 +321,26 @@ local M = {} -- -- ### 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: +-- When the user triggers autocompletion, Adeptsense uses the Adeptsense for the +-- language at the *caret position*, not necessarily the Adeptsense for the +-- parent language. For example, when editing CSS inside of an HTML file, the +-- user expects CSS completions. However, Textadept does not automatically load +-- child language Adeptsenses. The parent language must do this. For example: -- +-- -- In file *modules/hypertext/init.lua*. -- -- 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 +-- ## 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()`](#load_ctags) and [`api_files`](#api_files) respectively. +-- with `_HOME` being where you installed Textadept. LuaDoc outputs *tags* and +-- *api* files to the current directory. Load them via +-- [`load_ctags()`](#load_ctags) and [`api_files`](#api_files), respectively. -- -- [LuaDoc]: http://keplerproject.github.com/luadoc/ -- @@ -361,8 +365,8 @@ local M = {} -- -- Multiple documentation lines must be indented. -- </code></pre> -- --- Please note the latter ``-- * `field_name` `` syntax can appear anywhere --- inside a module, not just the module LuaDoc. +-- The latter ``-- * `field_name` `` syntax may appear anywhere inside a module, +-- not just the module LuaDoc. -- @field always_show_globals (bool) -- Include globals in the list of completions offered. -- Globals are classes, functions, and fields that do not belong to another @@ -950,7 +954,7 @@ api_files = {}, -- `'self'`. -- @field 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 +-- second, optional capture contains the class's 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 |