diff options
author | 2010-08-21 01:14:24 -0400 | |
---|---|---|
committer | 2010-08-21 01:14:24 -0400 | |
commit | f41a788cb424b31fd687253e8db3fd448a693b4a (patch) | |
tree | d733e35486108a29afcac5d84a9adfd03dcb30c4 /modules/lua/commands.lua | |
parent | 0c1718a568f4db2661ab3fc960beba22ce5ff00c (diff) | |
download | textadept-f41a788cb424b31fd687253e8db3fd448a693b4a.tar.gz textadept-f41a788cb424b31fd687253e8db3fd448a693b4a.zip |
Added Lua autocompletion support.
Diffstat (limited to 'modules/lua/commands.lua')
-rw-r--r-- | modules/lua/commands.lua | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/modules/lua/commands.lua b/modules/lua/commands.lua index 0489ea56..f1902764 100644 --- a/modules/lua/commands.lua +++ b/modules/lua/commands.lua @@ -4,6 +4,68 @@ -- Commands for the lua module. module('_m.lua.commands', package.seeall) +-- Markdown: +-- ## Key Commands +-- +-- + `Alt+l, m`: Open this module for editing. +-- + `Alt+l, g`: Goto file being 'require'd on the current line. +-- + `Shift+Return`: Try to autocomplete an `if`, `for`, etc. statement with +-- `end`. +-- + `.`: When to the right of a known identifier, show an autocompletion list +-- of fields. +-- + `:`: When to the right of a known identifier, show an autocompletion list +-- of functions. +-- + `Tab`: When the caret is to the right of a `(` in a known function call, +-- show a calltip with documentation for the function. +-- +-- ## Autocompletion of Fields and Functions +-- +-- This module parses files in the `api_files` table for LuaDoc documentation. +-- Currently all Textadept and Lua identifiers are supported. +-- +-- #### Syntax +-- +-- Fields are recognized as Lua line comments of the form ``-- * `field_name` ``. +-- Functions are recognized as Lua functions of the form `function func(args)` +-- or `function namespace:func(args)`. `.`-completion shows autocompletion for +-- both fields and functions using the first function syntax. `:`-completion +-- shows completions for functions only. Any LuaDoc starting with `---` and +-- including any subsequent comments up until the function declaration will be +-- shown with a calltip when requested. +-- +-- In order to be recognized, all comments and functions MUST begin the line. +-- +-- Syntatically valid Lua code is not necessary for parsing; only the patterns +-- described above are necessary. +-- +-- For example: +-- +-- In file `~/.textadept/modules/foo/foo.luadoc`: +-- +-- -- * `bar`: Bar field. +-- +-- --- LuaDoc for bar. +-- function baz() +-- +-- --- +-- -- LuaDoc for foobar. +-- -- @param barfoo First arg. +-- function foobar(barfoo) +-- +-- --- +-- -- LuaDoc for foo:barbaz. +-- -- @param foo Foo table. +-- function foo:barbaz() +-- +-- In file `_HOME/modules/lua/commands.lua` below `api_files` declaration: +-- +-- api_files['foo'] = _USERHOME..'/modules/foo/foo.luadoc' +-- +-- In any Lua file: +-- +-- foo. -- shows autocompletion list with [bar, baz, foobar]. +-- foo: -- shows autocompletion list with [foobar, barbaz]. + local m_editing, m_run = _m.textadept.editing, _m.textadept.run -- Comment string tables use lexer names. m_editing.comment_string.lua = '--' @@ -73,6 +135,111 @@ function goto_required() end end +--- +-- LuaDoc to load API from. +-- Keys are Lua table names with LuaDoc file values. +-- @class table +-- @name api_files +local api_files = { + ['args'] = _HOME..'/core/args.lua', + ['buffer'] = _HOME..'/core/.buffer.luadoc', + ['events'] = _HOME..'/core/events.lua', + ['gui'] = _HOME..'/core/.gui.luadoc', + ['gui.find'] = _HOME..'/core/.find.luadoc', + ['gui.command_entry'] = _HOME..'/core/.command_entry.luadoc', + ['l'] = _HOME..'/lexers/lexer.lua', + ['view'] = _HOME..'/core/.view.luadoc', +} +-- Add API for loaded textadept modules. +for p, _ in pairs(package.loaded) do + if p:find('^_m%.textadept%.') then + api_files[p] = _HOME..'/modules/textadept/'..p:match('[^%.]+$')..'.lua' + end +end +-- Add Lua API +local lua = { 'coroutine', 'debug', 'io', 'math', 'os', 'string', 'table' } +for _, m in ipairs(lua) do + api_files[m] = _HOME..'/modules/lua/api/'..m..'.luadoc' +end +api_files[''] = _HOME..'/modules/lua/api/_G.luadoc' + +local lfs = require 'lfs' + +-- Load API. +local apis = {} +local current_doc = '' +local f_args = {} +for word, api_file in pairs(api_files) do + if lfs.attributes(api_file) then + apis[word] = { fields = {}, funcs = {} } + for line in io.lines(api_file) do + if line:match('^%-%- %* `([^`]+)`') then -- field + local fields = apis[word].fields + fields[#fields + 1] = line:match('^%-%- %* `([^`]+)`')..'?2' + elseif line:match('^function ') then -- function + local f, n = line:match('^function [%w_]+:(([%w_]+)%([^)]*%))') + if not f then + f, n = line:match('^function (([%w_]+)%([^)]*%))') + local fields = apis[word].fields + fields[#fields + 1] = n..'?1' + end + local funcs = apis[word].funcs + funcs[#funcs + 1] = n..'?1' + if f and #current_doc > 0 then + f = f..current_doc + current_doc = '' + end + local c = line:find(':') and ':' or '.' + if word == '' then c = '' end + f_args[word..c..n] = f + elseif line:match('^%-%-%-? (.+)$') then + current_doc = current_doc..'\n'..line:match('^%-%-%-? (.+)$') + elseif #current_doc > 0 then + current_doc = '' + end + end + table.sort(apis[word].fields) + table.sort(apis[word].funcs) + end +end + +local f_xpm = '/* XPM */\nstatic char *function[] = {\n/* columns rows colors chars-per-pixel */\n"16 16 5 1",\n" c black",\n". c #E0BC38",\n"X c #F0DC5C",\n"o c #FCFC80",\n"O c None",\n/* pixels */\n"OOOOOOOOOOOOOOOO",\n"OOOOOOOOOOOOOOOO",\n"OOOOOOOOOOOOOOOO",\n"OOOOOOOOOO OOOO",\n"OOOOOOOOO oo OO",\n"OOOOOOOO ooooo O",\n"OOOOOOO ooooo. O",\n"OOOO O XXoo.. O",\n"OOO oo XXX... O",\n"OO ooooo XX.. OO",\n"O ooooo. X. OOO",\n"O XXoo.. O OOOO",\n"O XXX... OOOOOOO",\n"O XXX.. OOOOOOOO",\n"OO X. OOOOOOOOO",\n"OOOO OOOOOOOOOO"\n};' +local v_xpm = '/* XPM */\nstatic char *field[] = {\n/* columns rows colors chars-per-pixel */\n"16 16 5 1",\n" c black",\n". c #8C748C",\n"X c #9C94A4",\n"o c #ACB4C0",\n"O c None",\n/* pixels */\n"OOOOOOOOOOOOOOOO",\n"OOOOOOOOOOOOOOOO",\n"OOOOOOOOOOOOOOOO",\n"OOOOOOOOOOOOOOOO",\n"OOOOOOOOOOOOOOOO",\n"OOOOOOOOOOOOOOOO",\n"OOOOOOOOO OOOOO",\n"OOOOOOOO oo OOO",\n"OOOOOOO ooooo OO",\n"OOOOOO ooooo. OO",\n"OOOOOO XXoo.. OO",\n"OOOOOO XXX... OO",\n"OOOOOO XXX.. OOO",\n"OOOOOOO X. OOOO",\n"OOOOOOOOO OOOOO",\n"OOOOOOOOOOOOOOOO"\n};' + +--- +-- [Local function] Returns word specified by patt behind the caret. +-- @param patt Lua pattern containing word characters. +-- @param pos Optional position to start from. +-- @return word. +local function prev_word(patt, pos) + local e = pos or buffer.current_pos - 1 + local s = e - 1 + while s >= 0 and string.char(buffer.char_at[s]):find(patt) do s = s - 1 end + return buffer:text_range(s + 1, e) +end + +--- +-- [Local function] Shows autocompletion list. +-- @param len Length passed to buffer:auto_c_show. +-- @param completions Table of completions. +-- @see buffer:auto_c_show. +local function auto_c_show(len, completions) + buffer:clear_registered_images() + buffer:register_image(1, f_xpm) + buffer:register_image(2, v_xpm) + buffer:auto_c_show(len, table.concat(completions, ' ')) +end + +events.connect('char_added', + function(c) -- show autocomplete list or calltip + if c == 46 or c == 58 then -- '.' or ':' + if buffer:get_lexer() ~= 'lua' then return end + local word = prev_word('[%w_%.]') + if word == '' or not apis[word] then return end + auto_c_show(0, c == 46 and apis[word].fields or apis[word].funcs) + end + end) + -- Lua-specific key commands. local keys = _G.keys if type(keys) == 'table' then @@ -83,5 +250,20 @@ if type(keys) == 'table' then g = { goto_required }, }, ['s\n'] = { try_to_autocomplete_end }, + [not MAC and 'c\n' or 'esc'] = { function() -- complete API + local part = prev_word('[%w_]', buffer.current_pos) + local pos = buffer.current_pos - #part - 1 + if pos > 0 then + local word = prev_word('[%w_%.]', pos) + if word == '' or not apis[word] then return false end -- handle normally + local c = buffer.char_at[pos] + auto_c_show(#part, c == 46 and apis[word].fields or apis[word].funcs) + end + end }, + ['\t'] = { function() -- show API calltip + local func = prev_word('[%w_%.:]') + if not f_args[func] then return false end -- handle normally + buffer:call_tip_show(buffer.current_pos, f_args[func]) + end }, } end |