-- Copyright 2007-2018 Mitchell mitchell.att.foicica.com. See LICENSE. local M = {} --[[ This comment is for LuaDoc. --- -- The ansi_c module. -- It provides utilities for editing C code. module('_M.ansi_c')]] -- Autocompletion and documentation. --- -- List of ctags files to use for autocompletion in addition to the current -- project's top-level *tags* file or the current directory's *tags* file. -- @class table -- @name tags M.tags = { _HOME..'/modules/ansi_c/tags', _HOME..'/modules/ansi_c/lua_tags', _USERHOME..'/modules/ansi_c/tags' } local XPM = textadept.editing.XPM_IMAGES local xpms = setmetatable({ c = XPM.CLASS, d = XPM.SLOT, e = XPM.VARIABLE, f = XPM.METHOD, g = XPM.TYPEDEF, m = XPM.VARIABLE, s = XPM.STRUCT, t = XPM.TYPEDEF, v = XPM.VARIABLE }, {__index = function() return 0 end}) textadept.editing.autocompleters.ansi_c = function() -- Retrieve the symbol behind the caret. local line, pos = buffer:get_cur_line() local symbol, op, part = line:sub(1, pos):match('([%w_]-)([%.%->]*)([%w_]*)$') if symbol == '' and part == '' then return nil end -- nothing to complete if op ~= '' and op ~= '.' and op ~= '->' then return nil end -- Attempt to identify the symbol type. if symbol ~= '' then local buffer = buffer local decl = '([%w_]+)[%s%*&]+'..symbol:gsub('(%p)', '%%%1')..'[^%w_]' for i = buffer:line_from_position(buffer.current_pos) - 1, 0, -1 do local class = buffer:get_line(i):match(decl) if class then symbol = class break end end end -- Search through ctags for completions for that symbol. local tags_files = {} for i = 1, #M.tags do tags_files[#tags_files + 1] = M.tags[i] end tags_files[#tags_files + 1] = (io.get_project_root(buffer.filename) or lfs.currentdir())..'/tags' local name_patt = '^'..part local sep = string.char(buffer.auto_c_type_separator) ::rescan:: local list = {} for i = 1, #tags_files do if lfs.attributes(tags_files[i]) then for tag_line in io.lines(tags_files[i]) do local name = tag_line:match('^%S+') if (name:find(name_patt) and not name:find('^!') and not list[name]) or name == symbol then local fields = tag_line:match(';"\t(.*)$') if (fields:match('class:(%S+)') or fields:match('enum:(%S+)') or fields:match('struct:(%S+)') or '') == symbol then list[#list + 1] = string.format('%s%s%d', name, sep, xpms[fields:sub(1, 1)]) list[name] = true elseif name == symbol and fields:match('typeref:') then -- For typeref, change the lookup symbol to the referenced name and -- rescan tags files. symbol = fields:match('[^:]+$') goto rescan end end end end end return #part, list end textadept.editing.api_files.ansi_c = { _HOME..'/modules/ansi_c/api', _HOME..'/modules/ansi_c/lua_api', _USERHOME..'/modules/ansi_c/api' } -- Commands. --- -- Table of C-specific key bindings. -- -- + `Shift+Enter` (`⇧↩` | `S-Enter`) -- Add ';' to the end of the current line and insert a newline. -- @class table -- @name _G.keys.ansi_c keys.ansi_c = { ['s\n'] = function() buffer:line_end() buffer:add_text(';') buffer:new_line() end, } -- Snippets. if type(snippets) == 'table' then --- -- Table of C-specific snippets. -- @class table -- @name _G.snippets.ansi_c snippets.ansi_c = { func = '%1(int) %2(name)(%3(args)) {\n\t%0\n\treturn %4(0);\n}', vfunc = 'void %1(name)(%2(args)) {\n\t%0\n}', ['if'] = 'if (%1) {\n\t%0\n}', eif = 'else if (%1) {\n\t%0\n}', ['else'] = 'else {\n\t%0\n}', ['for'] = 'for (%1; %2; %3) {\n\t%0\n}', ['fori'] = 'for (%1(int) %2(i) = %3(0); %2 %4(<) %5(count); %2%6(++)) {\n'.. '\t%0\n}', ['while'] = 'while (%1) {\n\t%0\n}', ['do'] = 'do {\n\t%0\n} while (%1);', sw = 'switch (%1) {\n\tcase %2:\n\t\t%0\n\t\tbreak;\n}', case = 'case %1:\n\t%0\n\tbreak;', st = 'struct %1(name) {\n\t%0\n};', td = 'typedef %1(int) %2(name_t);', tds = 'typedef struct %1(name) {\n\t%0\n} %1%2(_t);', def = '#define %1(name) %2(value)', inc = '#include "%1"', Inc = '#include <%1>', pif = '#if %1\n%0\n#endif', main = 'int main(int argc, const char **argv) {\n\t%0\n\treturn 0;\n}', printf = 'printf("%1(%s)\\n", %2);', } end return M