diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2019-05-19 18:45:21 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-19 18:45:21 +0200 |
commit | 8e941c59ec239724c00f240deaa5a86ee0e0e1ba (patch) | |
tree | 6f1d2093145e4851211822b810c6aba7589fd1fe | |
parent | 974b43fd7940cd807e5a6b67c77cb2e0462b11a4 (diff) | |
parent | fab81cfb04b345fa7465fa099204d50a496f1819 (diff) | |
download | rneovim-8e941c59ec239724c00f240deaa5a86ee0e0e1ba.tar.gz rneovim-8e941c59ec239724c00f240deaa5a86ee0e0e1ba.tar.bz2 rneovim-8e941c59ec239724c00f240deaa5a86ee0e0e1ba.zip |
Merge #9740 from KillTheMule/luadoc
-rw-r--r-- | runtime/doc/if_lua.txt | 285 | ||||
-rw-r--r-- | runtime/lua/vim/shared.lua | 128 | ||||
-rwxr-xr-x | scripts/gen_vimdoc.py | 313 | ||||
-rw-r--r-- | scripts/lua2dox.lua | 666 | ||||
-rwxr-xr-x | scripts/lua2dox_filter | 87 | ||||
-rw-r--r-- | src/Doxyfile | 6 | ||||
-rw-r--r-- | src/nvim/lua/vim.lua | 83 | ||||
-rw-r--r-- | test/helpers.lua | 25 |
8 files changed, 1240 insertions, 353 deletions
diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index b97341e319..7f90074ff0 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -30,7 +30,7 @@ finds and loads Lua modules. The conventions are similar to VimL plugins, with some extra features. See |lua-require-example| for a walkthrough. ============================================================================== -Importing modules *lua-require* +Importing Lua modules *lua-require* Nvim automatically adjusts `package.path` and `package.cpath` according to effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is @@ -258,7 +258,77 @@ position are restricted when the command is executed in the |sandbox|. ============================================================================== -vim.* *lua-vim* *lua-stdlib* +luaeval() *lua-luaeval* *lua-eval* + *luaeval()* + +The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is +"luaeval". "luaeval" takes an expression string and an optional argument used +for _A inside expression and returns the result of the expression. It is +semantically equivalent in Lua to: +> + local chunkheader = "local _A = select(1, ...) return " + function luaeval (expstr, arg) + local chunk = assert(loadstring(chunkheader .. expstr, "luaeval")) + return chunk(arg) -- return typval + end + +Lua nils, numbers, strings, tables and booleans are converted to their +respective VimL types. An error is thrown if conversion of any other Lua types +is attempted. + +The magic global "_A" contains the second argument to luaeval(). + +Example: > + :echo luaeval('_A[1] + _A[2]', [40, 2]) + 42 + :echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123') + foo + +Lua tables are used as both dictionaries and lists, so it is impossible to +determine whether empty table is meant to be empty list or empty dictionary. +Additionally lua does not have integer numbers. To distinguish between these +cases there is the following agreement: + +0. Empty table is empty list. +1. Table with N incrementally growing integral numbers, starting from 1 and + ending with N is considered to be a list. +2. Table with string keys, none of which contains NUL byte, is considered to + be a dictionary. +3. Table with string keys, at least one of which contains NUL byte, is also + considered to be a dictionary, but this time it is converted to + a |msgpack-special-map|. + *lua-special-tbl* +4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point + value: + - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to + a floating-point 1.0. Note that by default integral lua numbers are + converted to |Number|s, non-integral are converted to |Float|s. This + variant allows integral |Float|s. + - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty + dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is + converted to a dictionary `{'a': 42}`: non-string keys are ignored. + Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3. + are errors. + - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well + as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not + form a 1-step sequence from 1 to N are ignored, as well as all + non-integral keys. + +Examples: > + + :echo luaeval('math.pi') + :function Rand(x,y) " random uniform between x and y + : return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y}) + : endfunction + :echo Rand(1,10) + +Note that currently second argument to `luaeval` undergoes VimL to lua +conversion, so changing containers in lua do not affect values in VimL. Return +value is also always converted. When converting, |msgpack-special-dict|s are +treated specially. + +============================================================================== +Lua standard modules *lua-stdlib* The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes various functions and sub-modules. It is always loaded, thus require("vim") @@ -292,7 +362,7 @@ Note that underscore-prefixed functions (e.g. "_os_proc_children") are internal/private and must not be used by plugins. ------------------------------------------------------------------------------ -vim.api.* functions +VIM.API *lua-api* `vim.api` exposes the full Nvim |API| as a table of Lua functions. @@ -302,55 +372,18 @@ For example, to use the "nvim_get_current_line()" API function, call print(tostring(vim.api.nvim_get_current_line())) ------------------------------------------------------------------------------ -vim.* builtin functions - -vim.deepcopy({object}) *vim.deepcopy* - Performs a deep copy of the given object, and returns that copy. - For a non-table object, that just means a usual copy of the object, - while for a table all subtables are copied recursively. - -vim.gsplit({s}, {sep}, {plain}) *vim.gsplit* - Split a given string by a separator. Returns an iterator of the - split components. The separator can be a lua pattern, see - https://www.lua.org/pil/20.2.html - Setting {plain} to `true` turns off pattern matching, as it is passed - to `string:find`, see - http://lua-users.org/wiki/StringLibraryTutorial - - Parameters:~ - {s} String: String to split - {sep} String: Separator pattern. If empty, split by chars. - {plain} Boolean: If false, match {sep} verbatim - - Return:~ - Iterator of strings, which are the components of {s} after - splitting - -vim.split({s}, {sep}, {plain}) *vim.split* - Split a given string by a separator. Returns a table containing the - split components. The separator can be a lua pattern, see - https://www.lua.org/pil/20.2.html - Setting {plain} to `true` turns off pattern matching, as it is passed - to `string:find`, see - http://lua-users.org/wiki/StringLibraryTutorial - - Parameters:~ - {s} String: String to split - {sep} String: Separator pattern. If empty, split by chars. - {plain} Boolean: If false, match {sep} verbatim - - Return:~ - Table of strings, which are the components of {s} after - splitting +VIM *lua-util* + +vim.inspect({object}, {options}) *vim.inspect* + Return a human-readable representation of the passed object. See + https://github.com/kikito/inspect.lua + for details and possible options. vim.stricmp(a, b) *lua-vim.stricmp* Function used for case-insensitive string comparison. Takes two string arguments and returns 0, 1 or -1 if strings are equal, a is greater then b or a is lesser then b respectively. -vim.trim({string}) *vim.trim* - Returns the string with all leading and trailing whitespace removed. - vim.type_idx *lua-vim.type_idx* Type index for use in |lua-special-tbl|. Specifying one of the values from |lua-vim.types| allows typing the empty table (it is @@ -386,86 +419,106 @@ vim.types *lua-vim.types* `vim.types.dictionary` will not change or that `vim.types` table will only contain values for these three types. ------------------------------------------------------------------------------- -vim.* runtime functions +============================================================================== +Lua module: vim *lua-vim* -Those functions are only available after the runtime files have been loaded. -In particular, they are not available when using `nvim -u NONE`. +trim({s}) *vim.trim()* + Trim whitespace (Lua pattern "%%s") from both sides of a + string. -vim.inspect({object}, {options}) *vim.inspect* - Return a human-readable representation of the passed object. See - https://github.com/kikito/inspect.lua - for details and possible options. + Parameters: ~ + {s} String to trim -============================================================================== -luaeval() *lua-luaeval* *lua-eval* - *luaeval()* + Return: ~ + String with whitespace removed from its beginning and end -The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is -"luaeval". "luaeval" takes an expression string and an optional argument used -for _A inside expression and returns the result of the expression. It is -semantically equivalent in Lua to: -> - local chunkheader = "local _A = select(1, ...) return " - function luaeval (expstr, arg) - local chunk = assert(loadstring(chunkheader .. expstr, "luaeval")) - return chunk(arg) -- return typval - end + See also: ~ + https://www.lua.org/pil/20.2.html -Lua nils, numbers, strings, tables and booleans are converted to their -respective VimL types. An error is thrown if conversion of any other Lua types -is attempted. -The magic global "_A" contains the second argument to luaeval(). -Example: > - :echo luaeval('_A[1] + _A[2]', [40, 2]) - 42 - :echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123') - foo -Lua tables are used as both dictionaries and lists, so it is impossible to -determine whether empty table is meant to be empty list or empty dictionary. -Additionally lua does not have integer numbers. To distinguish between these -cases there is the following agreement: +deepcopy({orig}) *vim.deepcopy()* + Returns a deep copy of the given object. Non-table objects are + copied as in a typical Lua assignment, whereas table objects + are copied recursively. -0. Empty table is empty list. -1. Table with N incrementally growing integral numbers, starting from 1 and - ending with N is considered to be a list. -2. Table with string keys, none of which contains NUL byte, is considered to - be a dictionary. -3. Table with string keys, at least one of which contains NUL byte, is also - considered to be a dictionary, but this time it is converted to - a |msgpack-special-map|. - *lua-special-tbl* -4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point - value: - - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to - a floating-point 1.0. Note that by default integral lua numbers are - converted to |Number|s, non-integral are converted to |Float|s. This - variant allows integral |Float|s. - - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty - dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is - converted to a dictionary `{'a': 42}`: non-string keys are ignored. - Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3. - are errors. - - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well - as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not - form a 1-step sequence from 1 to N are ignored, as well as all - non-integral keys. + Parameters: ~ + {orig} Table to copy -Examples: > + Return: ~ + New table of copied keys and (nested) values. - :echo luaeval('math.pi') - :function Rand(x,y) " random uniform between x and y - : return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y}) - : endfunction - :echo Rand(1,10) +gsplit({s}, {sep}, {plain}) *vim.gsplit()* + Splits a string at each instance of a separator. -Note that currently second argument to `luaeval` undergoes VimL to lua -conversion, so changing containers in lua do not affect values in VimL. Return -value is also always converted. When converting, |msgpack-special-dict|s are -treated specially. + Parameters: ~ + {s} String to split + {sep} Separator string or pattern + {plain} If `true` use `sep` literally (passed to + String.find) -============================================================================== - vim:tw=78:ts=8:et:ft=help:norl: + Return: ~ + Iterator over the split components + + See also: ~ + |vim.split()| + https://www.lua.org/pil/20.2.html + http://lua-users.org/wiki/StringLibraryTutorial + +split({s}, {sep}, {plain}) *vim.split()* + Splits a string at each instance of a separator. + + Examples: > + split(":aa::b:", ":") --> {'','aa','','bb',''} + split("axaby", "ab?") --> {'','x','y'} + split(x*yz*o, "*", true) --> {'x','yz','o'} +< + + Parameters: ~ + {s} String to split + {sep} Separator string or pattern + {plain} If `true` use `sep` literally (passed to + String.find) + + Return: ~ + List-like table of the split components. + + See also: ~ + |vim.gsplit()| + +tbl_contains({t}, {value}) *vim.tbl_contains()* + Checks if a list-like (vector) table contains `value` . + + Parameters: ~ + {t} Table to check + {value} Value to compare + + Return: ~ + true if `t` contains `value` + +tbl_extend({behavior}, {...}) *vim.tbl_extend()* + Merges two or more map-like tables. + + Parameters: ~ + {behavior} Decides what to do if a key is found in more + than one map: + - "error": raise an error + - "keep": use value from the leftmost map + - "force": use value from the rightmost map + {...} Two or more map-like tables. + + See also: ~ + |extend()| + +tbl_flatten({t}) *vim.tbl_flatten()* + Creates a copy of a list-like table such that any nested + tables are "unrolled" and appended to the result. + + Parameters: ~ + {t} List-like table + + Return: ~ + Flattened copy of the given list-like table. + + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 9dca51ce9a..07f9f52e5c 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -1,9 +1,107 @@ ---- Shared functions --- - Used by Nvim and tests --- - Can run in vanilla Lua (do not require a running instance of Nvim) +-- Functions shared by Nvim and its test-suite. +-- +-- The singular purpose of this module is to share code with the Nvim +-- test-suite. If, in the future, Nvim itself is used to run the test-suite +-- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua + + +--- Returns a deep copy of the given object. Non-table objects are copied as +--- in a typical Lua assignment, whereas table objects are copied recursively. +--- +--@param orig Table to copy +--@returns New table of copied keys and (nested) values. +local function deepcopy(orig) + error() +end +local function _id(v) + return v +end +local deepcopy_funcs = { + table = function(orig) + local copy = {} + for k, v in pairs(orig) do + copy[deepcopy(k)] = deepcopy(v) + end + return copy + end, + number = _id, + string = _id, + ['nil'] = _id, + boolean = _id, +} +deepcopy = function(orig) + return deepcopy_funcs[type(orig)](orig) +end + +--- Splits a string at each instance of a separator. +--- +--@see |vim.split()| +--@see https://www.lua.org/pil/20.2.html +--@see http://lua-users.org/wiki/StringLibraryTutorial +--- +--@param s String to split +--@param sep Separator string or pattern +--@param plain If `true` use `sep` literally (passed to String.find) +--@returns Iterator over the split components +local function gsplit(s, sep, plain) + assert(type(s) == "string") + assert(type(sep) == "string") + assert(type(plain) == "boolean" or type(plain) == "nil") + local start = 1 + local done = false + + local function _pass(i, j, ...) + if i then + assert(j+1 > start, "Infinite loop detected") + local seg = s:sub(start, i - 1) + start = j + 1 + return seg, ... + else + done = true + return s:sub(start) + end + end --- Checks if a list-like (vector) table contains `value`. + return function() + if done then + return + end + if sep == '' then + if start == #s then + done = true + end + return _pass(start+1, start) + end + return _pass(s:find(sep, start, plain)) + end +end + +--- Splits a string at each instance of a separator. +--- +--- Examples: +--- <pre> +--- split(":aa::b:", ":") --> {'','aa','','bb',''} +--- split("axaby", "ab?") --> {'','x','y'} +--- split(x*yz*o, "*", true) --> {'x','yz','o'} +--- </pre> +-- +--@see |vim.gsplit()| +--- +--@param s String to split +--@param sep Separator string or pattern +--@param plain If `true` use `sep` literally (passed to String.find) +--@returns List-like table of the split components. +local function split(s,sep,plain) + local t={} for c in gsplit(s, sep, plain) do table.insert(t,c) end + return t +end + +--- Checks if a list-like (vector) table contains `value`. +--- +--@param t Table to check +--@param value Value to compare +--@returns true if `t` contains `value` local function tbl_contains(t, value) if type(t) ~= 'table' then error('t must be a table') @@ -17,13 +115,14 @@ local function tbl_contains(t, value) end --- Merges two or more map-like tables. --- +--- --@see |extend()| --- --- behavior: Decides what to do if a key is found in more than one map: --- "error": raise an error --- "keep": use value from the leftmost map --- "force": use value from the rightmost map +--- +--@param behavior Decides what to do if a key is found in more than one map: +--- - "error": raise an error +--- - "keep": use value from the leftmost map +--- - "force": use value from the rightmost map +--@param ... Two or more map-like tables. local function tbl_extend(behavior, ...) if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then error('invalid "behavior": '..tostring(behavior)) @@ -46,7 +145,11 @@ local function tbl_extend(behavior, ...) return ret end --- Flattens a list-like table: unrolls and appends nested tables to table `t`. +--- Creates a copy of a list-like table such that any nested tables are +--- "unrolled" and appended to the result. +--- +--@param t List-like table +--@returns Flattened copy of the given list-like table. local function tbl_flatten(t) -- From https://github.com/premake/premake-core/blob/master/src/base/table.lua local result = {} @@ -66,6 +169,9 @@ local function tbl_flatten(t) end local module = { + deepcopy = deepcopy, + gsplit = gsplit, + split = split, tbl_contains = tbl_contains, tbl_extend = tbl_extend, tbl_flatten = tbl_flatten, diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index cdcab817ad..e491ff59b0 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -48,26 +48,65 @@ DEBUG = ('DEBUG' in os.environ) INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ) INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ) -doc_filename = 'api.txt' -# String used to find the start of the generated part of the doc. -section_start_token = '*api-global*' -# Required prefix for API function names. -api_func_name_prefix = 'nvim_' - -# Section name overrides. -section_name = { - 'vim.c': 'Global', +text_width = 78 +script_path = os.path.abspath(__file__) +base_dir = os.path.dirname(os.path.dirname(script_path)) +out_dir = os.path.join(base_dir, 'tmp-{mode}-doc') +filter_cmd = '%s %s' % (sys.executable, script_path) +seen_funcs = set() +lua2dox_filter = os.path.join(base_dir, 'scripts', 'lua2dox_filter') + +CONFIG = { + 'api': { + 'filename': 'api.txt', + # String used to find the start of the generated part of the doc. + 'section_start_token': '*api-global*', + # Section ordering. + 'section_order' : [ + 'vim.c', + 'buffer.c', + 'window.c', + 'tabpage.c', + 'ui.c', + ], + # List of files/directories for doxygen to read, separated by blanks + 'files': os.path.join(base_dir, 'src/nvim/api'), + # file patterns used by doxygen + 'file_patterns': '*.h *.c', + # Only function with this prefix are considered + 'func_name_prefix': 'nvim_', + # Section name overrides. + 'section_name': { + 'vim.c': 'Global', + }, + # Module name overrides (for Lua). + 'module_override': {}, + # Append the docs for these modules, do not start a new section. + 'append_only' : [], + }, + 'lua': { + 'filename': 'if_lua.txt', + 'section_start_token': '*lua-vim*', + 'section_order' : [ + 'vim.lua', + 'shared.lua', + ], + 'files': ' '.join([ + os.path.join(base_dir, 'src/nvim/lua/vim.lua'), + os.path.join(base_dir, 'runtime/lua/vim/shared.lua'), + ]), + 'file_patterns': '*.lua', + 'func_name_prefix': '', + 'section_name': {}, + 'module_override': { + 'shared': 'vim', # `shared` functions are exposed on the `vim` module. + }, + 'append_only' : [ + 'shared.lua', + ], + }, } -# Section ordering. -section_order = ( - 'vim.c', - 'buffer.c', - 'window.c', - 'tabpage.c', - 'ui.c', -) - param_exclude = ( 'channel_id', ) @@ -77,13 +116,6 @@ annotation_map = { 'FUNC_API_ASYNC': '{async}', } -text_width = 78 -script_path = os.path.abspath(__file__) -base_dir = os.path.dirname(os.path.dirname(script_path)) -src_dir = os.path.join(base_dir, 'src/nvim/api') -out_dir = os.path.join(base_dir, 'tmp-api-doc') -filter_cmd = '%s %s' % (sys.executable, script_path) -seen_funcs = set() # Tracks `xrefsect` titles. As of this writing, used only for separating # deprecated functions. @@ -286,7 +318,7 @@ def render_node(n, text, prefix='', indent='', width=62): elif n.nodeName == 'listitem': for c in n.childNodes: text += indent + prefix + render_node(c, text, indent=indent+(' ' * len(prefix)), width=width) - elif n.nodeName == 'para': + elif n.nodeName in ('para', 'heading'): for c in n.childNodes: text += render_node(c, text, indent=indent, width=width) if is_inline(n): @@ -400,7 +432,7 @@ def parse_parblock(parent, prefix='', width=62, indent=''): # }}} -def parse_source_xml(filename): +def parse_source_xml(filename, mode): """Collects API functions. Returns two strings: @@ -415,9 +447,12 @@ def parse_source_xml(filename): deprecated_functions = [] dom = minidom.parse(filename) + compoundname = get_text(dom.getElementsByTagName('compoundname')[0]) for member in dom.getElementsByTagName('memberdef'): if member.getAttribute('static') == 'yes' or \ - member.getAttribute('kind') != 'function': + member.getAttribute('kind') != 'function' or \ + member.getAttribute('prot') == 'private' or \ + get_text(get_child(member, 'name')).startswith('_'): continue loc = find_first(member, 'location') @@ -444,7 +479,13 @@ def parse_source_xml(filename): annotations = filter(None, map(lambda x: annotation_map.get(x), annotations.split())) - vimtag = '*{}()*'.format(name) + if mode == 'lua': + fstem = compoundname.split('.')[0] + fstem = CONFIG[mode]['module_override'].get(fstem, fstem) + vimtag = '*{}.{}()*'.format(fstem, name) + else: + vimtag = '*{}()*'.format(name) + params = [] type_length = 0 @@ -454,6 +495,10 @@ def parse_source_xml(filename): declname = get_child(param, 'declname') if declname: param_name = get_text(declname).strip() + elif mode == 'lua': + # that's how it comes out of lua2dox + param_name = param_type + param_type = '' if param_name in param_exclude: continue @@ -521,7 +566,7 @@ def parse_source_xml(filename): if 'Deprecated' in xrefs: deprecated_functions.append(func_doc) - elif name.startswith(api_func_name_prefix): + elif name.startswith(CONFIG[mode]['func_name_prefix']): functions.append(func_doc) xrefs.clear() @@ -547,115 +592,129 @@ def gen_docs(config): Doxygen is called and configured through stdin. """ - p = subprocess.Popen(['doxygen', '-'], stdin=subprocess.PIPE) - p.communicate(config.format(input=src_dir, output=out_dir, - filter=filter_cmd).encode('utf8')) - if p.returncode: - sys.exit(p.returncode) - - sections = {} - intros = {} - sep = '=' * text_width - - base = os.path.join(out_dir, 'xml') - dom = minidom.parse(os.path.join(base, 'index.xml')) - - # generate docs for section intros - for compound in dom.getElementsByTagName('compound'): - if compound.getAttribute('kind') != 'group': - continue - - groupname = get_text(find_first(compound, 'name')) - groupxml = os.path.join(base, '%s.xml' % compound.getAttribute('refid')) - - desc = find_first(minidom.parse(groupxml), 'detaileddescription') - if desc: - doc = parse_parblock(desc) - if doc: - intros[groupname] = doc - - for compound in dom.getElementsByTagName('compound'): - if compound.getAttribute('kind') != 'file': - continue - - filename = get_text(find_first(compound, 'name')) - if filename.endswith('.c'): - functions, deprecated = parse_source_xml( - os.path.join(base, '%s.xml' % compound.getAttribute('refid'))) - - if not functions and not deprecated: + for mode in CONFIG: + output_dir = out_dir.format(mode=mode) + p = subprocess.Popen(['doxygen', '-'], stdin=subprocess.PIPE) + p.communicate( + config.format( + input=CONFIG[mode]['files'], + output=output_dir, + filter=filter_cmd, + file_patterns=CONFIG[mode]['file_patterns']) + .encode('utf8') + ) + if p.returncode: + sys.exit(p.returncode) + + sections = {} + intros = {} + sep = '=' * text_width + + base = os.path.join(output_dir, 'xml') + dom = minidom.parse(os.path.join(base, 'index.xml')) + + # generate docs for section intros + for compound in dom.getElementsByTagName('compound'): + if compound.getAttribute('kind') != 'group': continue - if functions or deprecated: - name = os.path.splitext(os.path.basename(filename))[0] - if name == 'ui': - name = name.upper() - else: - name = name.title() - - doc = '' - - intro = intros.get('api-%s' % name.lower()) - if intro: - doc += '\n\n' + intro - - if functions: - doc += '\n\n' + functions - - if INCLUDE_DEPRECATED and deprecated: - doc += '\n\n\nDeprecated %s Functions: ~\n\n' % name - doc += deprecated + groupname = get_text(find_first(compound, 'name')) + groupxml = os.path.join(base, '%s.xml' % compound.getAttribute('refid')) + desc = find_first(minidom.parse(groupxml), 'detaileddescription') + if desc: + doc = parse_parblock(desc) if doc: - filename = os.path.basename(filename) - name = section_name.get(filename, name) - title = '%s Functions' % name - helptag = '*api-%s*' % name.lower() - sections[filename] = (title, helptag, doc) + intros[groupname] = doc - if not sections: - return - - docs = '' - - i = 0 - for filename in section_order: - if filename not in sections: - continue - title, helptag, section_doc = sections.pop(filename) - - i += 1 - docs += sep - docs += '\n%s%s' % (title, helptag.rjust(text_width - len(title))) - docs += section_doc - docs += '\n\n\n' + for compound in dom.getElementsByTagName('compound'): + if compound.getAttribute('kind') != 'file': + continue - if sections: - # In case new API sources are added without updating the order dict. - for title, helptag, section_doc in sections.values(): + filename = get_text(find_first(compound, 'name')) + if filename.endswith('.c') or filename.endswith('.lua'): + functions, deprecated = parse_source_xml( + os.path.join(base, '%s.xml' % + compound.getAttribute('refid')), mode) + + if not functions and not deprecated: + continue + + if functions or deprecated: + name = os.path.splitext(os.path.basename(filename))[0] + if name == 'ui': + name = name.upper() + else: + name = name.title() + + doc = '' + + intro = intros.get('api-%s' % name.lower()) + if intro: + doc += '\n\n' + intro + + if functions: + doc += '\n\n' + functions + + if INCLUDE_DEPRECATED and deprecated: + doc += '\n\n\nDeprecated %s Functions: ~\n\n' % name + doc += deprecated + + if doc: + filename = os.path.basename(filename) + name = CONFIG[mode]['section_name'].get(filename, name) + + if mode == 'lua': + title = 'Lua module: {}'.format(name.lower()) + helptag = '*lua-{}*'.format(name.lower()) + else: + title = '{} Functions'.format(name) + helptag = '*api-{}*'.format(name.lower()) + sections[filename] = (title, helptag, doc) + + if not sections: + return + + docs = '' + + i = 0 + for filename in CONFIG[mode]['section_order']: + if filename not in sections: + raise RuntimeError('found new module "{}"; update the "section_order" map'.format(filename)) + title, helptag, section_doc = sections.pop(filename) i += 1 - docs += sep - docs += '\n%s%s' % (title, helptag.rjust(text_width - len(title))) + if filename not in CONFIG[mode]['append_only']: + docs += sep + docs += '\n%s%s' % (title, helptag.rjust(text_width - len(title))) docs += section_doc docs += '\n\n\n' - docs = docs.rstrip() + '\n\n' - docs += ' vim:tw=78:ts=8:ft=help:norl:\n' + docs = docs.rstrip() + '\n\n' + docs += ' vim:tw=78:ts=8:ft=help:norl:\n' + + doc_file = os.path.join(base_dir, 'runtime', 'doc', + CONFIG[mode]['filename']) - doc_file = os.path.join(base_dir, 'runtime/doc', doc_filename) - delete_lines_below(doc_file, section_start_token) - with open(doc_file, 'ab') as fp: - fp.write(docs.encode('utf8')) - shutil.rmtree(out_dir) + delete_lines_below(doc_file, CONFIG[mode]['section_start_token']) + with open(doc_file, 'ab') as fp: + fp.write(docs.encode('utf8')) + + shutil.rmtree(output_dir) def filter_source(filename): - """Filters the source to fix macros that confuse Doxygen.""" - with open(filename, 'rt') as fp: - print(re.sub(r'^(ArrayOf|DictionaryOf)(\(.*?\))', - lambda m: m.group(1)+'_'.join( - re.split(r'[^\w]+', m.group(2))), - fp.read(), flags=re.M)) + name, extension = os.path.splitext(filename) + if extension == '.lua': + p = subprocess.run([lua2dox_filter, filename], stdout=subprocess.PIPE) + op = ('?' if 0 != p.returncode else p.stdout.decode('utf-8')) + print(op) + else: + """Filters the source to fix macros that confuse Doxygen.""" + with open(filename, 'rt') as fp: + print(re.sub(r'^(ArrayOf|DictionaryOf)(\(.*?\))', + lambda m: m.group(1)+'_'.join( + re.split(r'[^\w]+', m.group(2))), + fp.read(), flags=re.M)) # Doxygen Config {{{ @@ -663,13 +722,15 @@ Doxyfile = ''' OUTPUT_DIRECTORY = {output} INPUT = {input} INPUT_ENCODING = UTF-8 -FILE_PATTERNS = *.h *.c +FILE_PATTERNS = {file_patterns} RECURSIVE = YES INPUT_FILTER = "{filter}" EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */private/* EXCLUDE_SYMBOLS = +EXTENSION_MAPPING = lua=C +EXTRACT_PRIVATE = NO GENERATE_HTML = NO GENERATE_DOCSET = NO diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua new file mode 100644 index 0000000000..ae76a36d50 --- /dev/null +++ b/scripts/lua2dox.lua @@ -0,0 +1,666 @@ +--[[-------------------------------------------------------------------------- +-- Copyright (C) 2012 by Simon Dales -- +-- simon@purrsoft.co.uk -- +-- -- +-- This program is free software; you can redistribute it and/or modify -- +-- it under the terms of the GNU General Public License as published by -- +-- the Free Software Foundation; either version 2 of the License, or -- +-- (at your option) any later version. -- +-- -- +-- This program is distributed in the hope that it will be useful, -- +-- but WITHOUT ANY WARRANTY; without even the implied warranty of -- +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- +-- GNU General Public License for more details. -- +-- -- +-- You should have received a copy of the GNU General Public License -- +-- along with this program; if not, write to the -- +-- Free Software Foundation, Inc., -- +-- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -- +----------------------------------------------------------------------------]] +--[[! +\file +\brief a hack lua2dox converter +]] + +--[[! +\mainpage + +Introduction +------------ + +A hack lua2dox converter +Version 0.2 + +This lets us make Doxygen output some documentation to let +us develop this code. + +It is partially cribbed from the functionality of lua2dox +(http://search.cpan.org/~alec/Doxygen-Lua-0.02/lib/Doxygen/Lua.pm). +Found on CPAN when looking for something else; kinda handy. + +Improved from lua2dox to make the doxygen output more friendly. +Also it runs faster in lua rather than Perl. + +Because this Perl based system is called "lua2dox"., I have decided to add ".lua" to the name +to keep the two separate. + +Running +------- + +<ol> +<li> Ensure doxygen is installed on your system and that you are familiar with its use. +Best is to try to make and document some simple C/C++/PHP to see what it produces. +You can experiment with the enclosed example code. + +<li> Run "doxygen -g" to create a default Doxyfile. + +Then alter it to let it recognise lua. Add the two following lines: + +\code{.bash} +FILE_PATTERNS = *.lua + +FILTER_PATTERNS = *.lua=lua2dox_filter +\endcode + + +Either add them to the end or find the appropriate entry in Doxyfile. + +There are other lines that you might like to alter, but see futher documentation for details. + +<li> When Doxyfile is edited run "doxygen" + +The core function reads the input file (filename or stdin) and outputs some pseudo C-ish language. +It only has to be good enough for doxygen to see it as legal. +Therefore our lua interpreter is fairly limited, but "good enough". + +One limitation is that each line is treated separately (except for long comments). +The implication is that class and function declarations must be on the same line. +Some functions can have their parameter lists extended over multiple lines to make it look neat. +Managing this where there are also some comments is a bit more coding than I want to do at this stage, +so it will probably not document accurately if we do do this. + +However I have put in a hack that will insert the "missing" close paren. +The effect is that you will get the function documented, but not with the parameter list you might expect. +</ol> + +Installation +------------ + +Here for linux or unix-like, for any other OS you need to refer to other documentation. + +This file is "lua2dox.lua". It gets called by "lua2dox_filter"(bash). +Somewhere in your path (e.g. "~/bin" or "/usr/local/bin") put a link to "lua2dox_filter". + +Documentation +------------- + +Read the external documentation that should be part of this package. +For example look for the "README" and some .PDFs. + +]] + +-- we won't use our library code, so this becomes more portable + +-- require 'elijah_fix_require' +-- require 'elijah_class' +-- +--! \brief ``declare'' as class +--! +--! use as: +--! \code{.lua} +--! TWibble = class() +--! function TWibble.init(this,Str) +--! this.str = Str +--! -- more stuff here +--! end +--! \endcode +--! +function class(BaseClass, ClassInitialiser) + local newClass = {} -- a new class newClass + if not ClassInitialiser and type(BaseClass) == 'function' then + ClassInitialiser = BaseClass + BaseClass = nil + elseif type(BaseClass) == 'table' then + -- our new class is a shallow copy of the base class! + for i,v in pairs(BaseClass) do + newClass[i] = v + end + newClass._base = BaseClass + end + -- the class will be the metatable for all its newInstanceects, + -- and they will look up their methods in it. + newClass.__index = newClass + + -- expose a constructor which can be called by <classname>(<args>) + local classMetatable = {} + classMetatable.__call = + function(class_tbl, ...) + local newInstance = {} + setmetatable(newInstance,newClass) + --if init then + -- init(newInstance,...) + if class_tbl.init then + class_tbl.init(newInstance,...) + else + -- make sure that any stuff from the base class is initialized! + if BaseClass and BaseClass.init then + BaseClass.init(newInstance, ...) + end + end + return newInstance + end + newClass.init = ClassInitialiser + newClass.is_a = + function(this, klass) + local thisMetatable = getmetatable(this) + while thisMetatable do + if thisMetatable == klass then + return true + end + thisMetatable = thisMetatable._base + end + return false + end + setmetatable(newClass, classMetatable) + return newClass +end + +-- require 'elijah_clock' + +--! \class TCore_Clock +--! \brief a clock +TCore_Clock = class() + +--! \brief get the current time +function TCore_Clock.GetTimeNow() + if os.gettimeofday then + return os.gettimeofday() + else + return os.time() + end +end + +--! \brief constructor +function TCore_Clock.init(this,T0) + if T0 then + this.t0 = T0 + else + this.t0 = TCore_Clock.GetTimeNow() + end +end + +--! \brief get time string +function TCore_Clock.getTimeStamp(this,T0) + local t0 + if T0 then + t0 = T0 + else + t0 = this.t0 + end + return os.date('%c %Z',t0) +end + + +--require 'elijah_io' + +--! \class TCore_IO +--! \brief io to console +--! +--! pseudo class (no methods, just to keep documentation tidy) +TCore_IO = class() +-- +--! \brief write to stdout +function TCore_IO_write(Str) + if (Str) then + io.write(Str) + end +end + +--! \brief write to stdout +function TCore_IO_writeln(Str) + if (Str) then + io.write(Str) + end + io.write("\n") +end + + +--require 'elijah_string' + +--! \brief trims a string +function string_trim(Str) + return Str:match("^%s*(.-)%s*$") +end + +--! \brief split a string +--! +--! \param Str +--! \param Pattern +--! \returns table of string fragments +function string_split(Str, Pattern) + local splitStr = {} + local fpat = "(.-)" .. Pattern + local last_end = 1 + local str, e, cap = string.find(Str,fpat, 1) + while str do + if str ~= 1 or cap ~= "" then + table.insert(splitStr,cap) + end + last_end = e+1 + str, e, cap = string.find(Str,fpat, last_end) + end + if last_end <= #Str then + cap = string.sub(Str,last_end) + table.insert(splitStr, cap) + end + return splitStr +end + + +--require 'elijah_commandline' + +--! \class TCore_Commandline +--! \brief reads/parses commandline +TCore_Commandline = class() + +--! \brief constructor +function TCore_Commandline.init(this) + this.argv = arg + this.parsed = {} + this.params = {} +end + +--! \brief get value +function TCore_Commandline.getRaw(this,Key,Default) + local val = this.argv[Key] + if not val then + val = Default + end + return val +end + + +--require 'elijah_debug' + +------------------------------- +--! \brief file buffer +--! +--! an input file buffer +TStream_Read = class() + +--! \brief get contents of file +--! +--! \param Filename name of file to read (or nil == stdin) +function TStream_Read.getContents(this,Filename) + -- get lines from file + local filecontents + if Filename then + -- syphon lines to our table + --TCore_Debug_show_var('Filename',Filename) + filecontents={} + for line in io.lines(Filename) do + table.insert(filecontents,line) + end + else + -- get stuff from stdin as a long string (with crlfs etc) + filecontents=io.read('*a') + -- make it a table of lines + filecontents = TString_split(filecontents,'[\n]') -- note this only works for unix files. + Filename = 'stdin' + end + + if filecontents then + this.filecontents = filecontents + this.contentsLen = #filecontents + this.currentLineNo = 1 + end + + return filecontents +end + +--! \brief get lineno +function TStream_Read.getLineNo(this) + return this.currentLineNo +end + +--! \brief get a line +function TStream_Read.getLine(this) + local line + if this.currentLine then + line = this.currentLine + this.currentLine = nil + else + -- get line + if this.currentLineNo<=this.contentsLen then + line = this.filecontents[this.currentLineNo] + this.currentLineNo = this.currentLineNo + 1 + else + line = '' + end + end + return line +end + +--! \brief save line fragment +function TStream_Read.ungetLine(this,LineFrag) + this.currentLine = LineFrag +end + +--! \brief is it eof? +function TStream_Read.eof(this) + if this.currentLine or this.currentLineNo<=this.contentsLen then + return false + end + return true +end + +--! \brief output stream +TStream_Write = class() + +--! \brief constructor +function TStream_Write.init(this) + this.tailLine = {} +end + +--! \brief write immediately +function TStream_Write.write(this,Str) + TCore_IO_write(Str) +end + +--! \brief write immediately +function TStream_Write.writeln(this,Str) + TCore_IO_writeln(Str) +end + +--! \brief write immediately +function TStream_Write.writelnComment(this,Str) + TCore_IO_write('// ZZ: ') + TCore_IO_writeln(Str) +end + +--! \brief write to tail +function TStream_Write.writelnTail(this,Line) + if not Line then + Line = '' + end + table.insert(this.tailLine,Line) +end + +--! \brief outout tail lines +function TStream_Write.write_tailLines(this) + for k,line in ipairs(this.tailLine) do + TCore_IO_writeln(line) + end + TCore_IO_write('// Lua2DoX new eof') +end + +--! \brief input filter +TLua2DoX_filter = class() + +--! \brief allow us to do errormessages +function TLua2DoX_filter.warning(this,Line,LineNo,Legend) + this.outStream:writelnTail( + '//! \todo warning! ' .. Legend .. ' (@' .. LineNo .. ')"' .. Line .. '"' + ) +end + +--! \brief trim comment off end of string +--! +--! If the string has a comment on the end, this trims it off. +--! +local function TString_removeCommentFromLine(Line) + local pos_comment = string.find(Line,'%-%-') + local tailComment + if pos_comment then + Line = string.sub(Line,1,pos_comment-1) + tailComment = string.sub(Line,pos_comment) + end + return Line,tailComment +end + +--! \brief get directive from magic +local function getMagicDirective(Line) + local macro,tail + local macroStr = '[\\@]' + local pos_macro = string.find(Line,macroStr) + if pos_macro then + --! ....\\ macro...stuff + --! ....\@ macro...stuff + local line = string.sub(Line,pos_macro+1) + local space = string.find(line,'%s+') + if space then + macro = string.sub(line,1,space-1) + tail = string_trim(string.sub(line,space+1)) + else + macro = line + tail = '' + end + end + return macro,tail +end + +--! \brief check comment for fn +local function checkComment4fn(Fn_magic,MagicLines) + local fn_magic = Fn_magic + -- TCore_IO_writeln('// checkComment4fn "' .. MagicLines .. '"') + + local magicLines = string_split(MagicLines,'\n') + + local macro,tail + + for k,line in ipairs(magicLines) do + macro,tail = getMagicDirective(line) + if macro == 'fn' then + fn_magic = tail + -- TCore_IO_writeln('// found fn "' .. fn_magic .. '"') + else + --TCore_IO_writeln('// not found fn "' .. line .. '"') + end + end + + return fn_magic +end +--! \brief run the filter +function TLua2DoX_filter.readfile(this,AppStamp,Filename) + local err + + local inStream = TStream_Read() + local outStream = TStream_Write() + this.outStream = outStream -- save to this obj + + if (inStream:getContents(Filename)) then + -- output the file + local line + local fn_magic -- function name/def from magic comment + + outStream:writelnTail('// #######################') + outStream:writelnTail('// app run:' .. AppStamp) + outStream:writelnTail('// #######################') + outStream:writelnTail() + + while not (err or inStream:eof()) do + line = string_trim(inStream:getLine()) + -- TCore_Debug_show_var('inStream',inStream) + -- TCore_Debug_show_var('line',line ) + if string.sub(line,1,2)=='--' then -- its a comment + if string.sub(line,3,3)=='@' then -- it's a magic comment + local magic = string.sub(line,4) + outStream:writeln('/// @' .. magic) + fn_magic = checkComment4fn(fn_magic,magic) + elseif string.sub(line,3,3)=='-' then -- it's a nonmagic doc comment + local comment = string.sub(line,4) + outStream:writeln('/// '.. comment) + elseif string.sub(line,3,4)=='[[' then -- it's a long comment + line = string.sub(line,5) -- nibble head + local comment = '' + local closeSquare,hitend,thisComment + while (not err) and (not hitend) and (not inStream:eof()) do + closeSquare = string.find(line,']]') + if not closeSquare then -- need to look on another line + thisComment = line .. '\n' + line = inStream:getLine() + else + thisComment = string.sub(line,1,closeSquare-1) + hitend = true + + -- unget the tail of the line + -- in most cases it's empty. This may make us less efficient but + -- easier to program + inStream:ungetLine(string_trim(string.sub(line,closeSquare+2))) + end + comment = comment .. thisComment + end + if string.sub(comment,1,1)=='@' then -- it's a long magic comment + outStream:write('/*' .. comment .. '*/ ') + fn_magic = checkComment4fn(fn_magic,comment) + else -- discard + outStream:write('/* zz:' .. comment .. '*/ ') + fn_magic = nil + end + else + outStream:writeln('// zz:"' .. line .. '"') + fn_magic = nil + end + elseif string.find(line,'^function') or string.find(line,'^local%s+function') then + -- it's a function + local pos_fn = string.find(line,'function') + -- function + -- ....v... + if pos_fn then + -- we've got a function + local fn_type + if string.find(line,'^local%s+') then + fn_type = ''--'static ' -- static functions seem to be excluded + else + fn_type = '' + end + local fn = TString_removeCommentFromLine(string_trim(string.sub(line,pos_fn+8))) + if fn_magic then + fn = fn_magic + fn_magic = nil + end + + if string.sub(fn,1,1)=='(' then + -- it's an anonymous function + outStream:writelnComment(line) + else + -- fn has a name, so is interesting + + -- want to fix for iffy declarations + local open_paren = string.find(fn,'[%({]') + local fn0 = fn + if open_paren then + fn0 = string.sub(fn,1,open_paren-1) + -- we might have a missing close paren + if not string.find(fn,'%)') then + fn = fn .. ' ___MissingCloseParenHere___)' + end + end + + local dot = string.find(fn0,'[%.:]') + if dot then -- it's a method + local klass = string.sub(fn,1,dot-1) + local method = string.sub(fn,dot+1) + --TCore_IO_writeln('function ' .. klass .. '::' .. method .. ftail .. '{}') + --TCore_IO_writeln(klass .. '::' .. method .. ftail .. '{}') + outStream:writeln( + '/*! \\memberof ' .. klass .. ' */ ' + .. method .. '{}' + ) + else + -- add vanilla function + + outStream:writeln(fn_type .. 'function ' .. fn .. '{}') + end + end + else + this:warning(inStream:getLineNo(),'something weird here') + end + fn_magic = nil -- mustn't indavertently use it again + elseif string.find(line,'=%s*class%(') then + -- it's a class declaration + local tailComment + line,tailComment = TString_removeCommentFromLine(line) + local equals = string.find(line,'=') + local klass = string_trim(string.sub(line,1,equals-1)) + local tail = string_trim(string.sub(line,equals+1)) + -- class(wibble wibble) + -- ....v. + local parent = string.sub(tail,7,-2) + if #parent>0 then + parent = ' :public ' .. parent + end + outStream:writeln('class ' .. klass .. parent .. '{};') + else + -- we don't know what this line means, so we can probably just comment it out + if #line>0 then + outStream:writeln('// zz: ' .. line) + else + outStream:writeln() -- keep this line blank + end + end + end + + -- output the tail + outStream:write_tailLines() + else + outStream:writeln('!empty file') + end +end + +--! \brief this application +TApp = class() + +--! \brief constructor +function TApp.init(this) + local t0 = TCore_Clock() + this.timestamp = t0:getTimeStamp() + this.name = 'Lua2DoX' + this.version = '0.2 20130128' + this.copyright = 'Copyright (c) Simon Dales 2012-13' +end + +function TApp.getRunStamp(this) + return this.name .. ' (' .. this.version .. ') ' + .. this.timestamp +end + +function TApp.getVersion(this) + return this.name .. ' (' .. this.version .. ') ' +end + +function TApp.getCopyright(this) + return this.copyright +end + +local This_app = TApp() + +--main +local cl = TCore_Commandline() + +local argv1 = cl:getRaw(2) +if argv1 == '--help' then + TCore_IO_writeln(This_app:getVersion()) + TCore_IO_writeln(This_app:getCopyright()) + TCore_IO_writeln([[ + run as: + lua2dox_filter <param> + -------------- + Param: + <filename> : interprets filename + --version : show version/copyright info + --help : this help text]]) +elseif argv1 == '--version' then + TCore_IO_writeln(This_app:getVersion()) + TCore_IO_writeln(This_app:getCopyright()) +else + -- it's a filter + local appStamp = This_app:getRunStamp() + local filename = argv1 + + local filter = TLua2DoX_filter() + filter:readfile(appStamp,filename) +end + + +--eof diff --git a/scripts/lua2dox_filter b/scripts/lua2dox_filter new file mode 100755 index 0000000000..6cb16ef060 --- /dev/null +++ b/scripts/lua2dox_filter @@ -0,0 +1,87 @@ +#!/bin/bash + +########################################################################### +# Copyright (C) 2012 by Simon Dales # +# simon@purrsoft.co.uk # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the # +# Free Software Foundation, Inc., # +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +########################################################################### +LANG="" + +##! \brief test executable to see if it exists +test_executable(){ + P_EXE="$1" + ######### + WHICH=`which ${P_EXE}` + if test -z "${WHICH}" + then + echo "not found \"${P_EXE}\"" + else + EXE="${P_EXE}" + fi + } + +##! \brief sets the lua interpreter +set_lua(){ + test_executable 'texlua' + if test -z "${EXE}" + then + test_executable 'lua' + fi + #echo "final EXE=\"${EXE}\"" + } + +##! \brief makes canonical name of file +##! +##! Note that "readlink -f" doesn't work in MacOSX +##! +do_readlink(){ + pushd . > /dev/null + TARGET_FILE=$1 + + cd `dirname $TARGET_FILE` + TARGET_FILE=`basename $TARGET_FILE` + + # Iterate down a (possible) chain of symlinks + while [ -L "$TARGET_FILE" ] + do + TARGET_FILE=`readlink $TARGET_FILE` + cd `dirname $TARGET_FILE` + TARGET_FILE=`basename $TARGET_FILE` + done + + PHYS_DIR=`pwd -P` + RESULT=$PHYS_DIR + popd > /dev/null + } + +##main +set_lua +if test -z "${EXE}" +then + echo "no lua interpreter available" +else + BASENAME=`basename "$0"` + do_readlink "$0" + DIRNAME="${RESULT}" + + LUASCRIPT="${DIRNAME}/lua2dox.lua ${BASENAME}" + #echo "lua[${LUASCRIPT}]" + + ${EXE} ${LUASCRIPT} $@ +fi +# +##eof diff --git a/src/Doxyfile b/src/Doxyfile index de31c8355f..461fafe99d 100644 --- a/src/Doxyfile +++ b/src/Doxyfile @@ -243,7 +243,7 @@ OPTIMIZE_OUTPUT_VHDL = NO # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. -EXTENSION_MAPPING = +EXTENSION_MAPPING = lua=C # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable @@ -672,7 +672,7 @@ INPUT_ENCODING = UTF-8 # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl -FILE_PATTERNS = *.h *.c +FILE_PATTERNS = *.h *.c *.lua # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. @@ -758,7 +758,7 @@ INPUT_FILTER = # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. -FILTER_PATTERNS = +FILTER_PATTERNS = *.lua=scripts/lua2dox_filter # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 1a7aec6cc6..38a8795680 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -1,10 +1,10 @@ -- Nvim-Lua stdlib: the `vim` module (:help lua-stdlib) -- -- Lua code lives in one of three places: --- 1. The runtime (`runtime/lua/vim/`). For "nice to have" features, e.g. --- the `inspect` and `lpeg` modules. --- 2. The `vim.shared` module: code shared between Nvim and its test-suite. --- 3. Compiled-into Nvim itself (`src/nvim/lua/`). +-- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the +-- `inspect` and `lpeg` modules. +-- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests. +-- 3. src/nvim/lua/: Compiled-into Nvim itself. -- -- Guideline: "If in doubt, put it in the runtime". -- @@ -154,75 +154,17 @@ local function _update_package_paths() last_nvim_paths = cur_nvim_paths end -local function gsplit(s, sep, plain) - assert(type(s) == "string") - assert(type(sep) == "string") - assert(type(plain) == "boolean" or type(plain) == "nil") - - local start = 1 - local done = false - - local function pass(i, j, ...) - if i then - assert(j+1 > start, "Infinite loop detected") - local seg = s:sub(start, i - 1) - start = j + 1 - return seg, ... - else - done = true - return s:sub(start) - end - end - - return function() - if done then - return - end - if sep == '' then - if start == #s then - done = true - end - return pass(start+1, start) - end - return pass(s:find(sep, start, plain)) - end -end - -local function split(s,sep,plain) - local t={} for c in gsplit(s, sep, plain) do table.insert(t,c) end - return t -end - +--- Trim whitespace (Lua pattern "%%s") from both sides of a string. +--- +--@see https://www.lua.org/pil/20.2.html +--@param s String to trim +--@returns String with whitespace removed from its beginning and end local function trim(s) - assert(type(s) == "string", "Only strings can be trimmed") - local result = s:gsub("^%s+", ""):gsub("%s+$", "") + assert(type(s) == 'string', 'Only strings can be trimmed') + local result = s:gsub('^%s+', ''):gsub('%s+$', '') return result end -local deepcopy - -local function id(v) - return v -end - -local deepcopy_funcs = { - table = function(orig) - local copy = {} - for k, v in pairs(orig) do - copy[deepcopy(k)] = deepcopy(v) - end - return copy - end, - number = id, - string = id, - ['nil'] = id, - boolean = id, -} - -deepcopy = function(orig) - return deepcopy_funcs[type(orig)](orig) -end - local function __index(t, key) if key == 'inspect' then t.inspect = require('vim.inspect') @@ -240,9 +182,6 @@ local module = { _os_proc_info = _os_proc_info, _system = _system, trim = trim, - split = split, - gsplit = gsplit, - deepcopy = deepcopy, } setmetatable(module, { diff --git a/test/helpers.lua b/test/helpers.lua index cc5f05bdee..3311f3ef97 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -336,30 +336,6 @@ local function shallowcopy(orig) return copy end -local deepcopy - -local function id(v) - return v -end - -local deepcopy_funcs = { - table = function(orig) - local copy = {} - for k, v in pairs(orig) do - copy[deepcopy(k)] = deepcopy(v) - end - return copy - end, - number = id, - string = id, - ['nil'] = id, - boolean = id, -} - -deepcopy = function(orig) - return deepcopy_funcs[type(orig)](orig) -end - local REMOVE_THIS = {} local function mergedicts_copy(d1, d2) @@ -728,7 +704,6 @@ local module = { check_logs = check_logs, concat_tables = concat_tables, dedent = dedent, - deepcopy = deepcopy, dictdiff = dictdiff, eq = eq, expect_err = expect_err, |