aboutsummaryrefslogtreecommitdiff
path: root/scripts/lua2dox.lua
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lua2dox.lua')
-rw-r--r--scripts/lua2dox.lua544
1 files changed, 0 insertions, 544 deletions
diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua
deleted file mode 100644
index 0b3daa59b2..0000000000
--- a/scripts/lua2dox.lua
+++ /dev/null
@@ -1,544 +0,0 @@
------------------------------------------------------------------------------
--- 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. --
------------------------------------------------------------------------------
-
---[[!
-Lua-to-Doxygen converter
-
-Partially from lua2dox
-http://search.cpan.org/~alec/Doxygen-Lua-0.02/lib/Doxygen/Lua.pm
-
-RUNNING
--------
-
-This script "lua2dox.lua" gets called by "gen_vimdoc.py".
-
-DEBUGGING/DEVELOPING
----------------------
-
-1. To debug, run gen_vimdoc.py with --keep-tmpfiles:
- python3 scripts/gen_vimdoc.py -t treesitter --keep-tmpfiles
-2. The filtered result will be written to ./tmp-lua2dox-doc/….lua.c
-
-Doxygen must be on your system. You can experiment like so:
-
-- Run "doxygen -g" to create a default Doxyfile.
-- Then alter it to let it recognise lua. Add the following line:
- FILE_PATTERNS = *.lua
-- Then 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.
-
-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.
-
-There is 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.
-]]
-
-local TYPES = { 'integer', 'number', 'string', 'table', 'list', 'boolean', 'function' }
-
-local luacats_parser = require('src/nvim/generators/luacats_grammar')
-
-local debug_outfile = nil --- @type string?
-local debug_output = {}
-
---- write to stdout
---- @param str? string
-local function write(str)
- if not str then
- return
- end
-
- io.write(str)
- if debug_outfile then
- table.insert(debug_output, str)
- end
-end
-
---- write to stdout
---- @param str? string
-local function writeln(str)
- write(str)
- write('\n')
-end
-
---- an input file buffer
---- @class StreamRead
---- @field currentLine string?
---- @field contentsLen integer
---- @field currentLineNo integer
---- @field filecontents string[]
-local StreamRead = {}
-
---- @return StreamRead
---- @param filename string
-function StreamRead.new(filename)
- assert(filename, ('invalid file: %s'):format(filename))
- -- get lines from file
- -- syphon lines to our table
- local filecontents = {} --- @type string[]
- for line in io.lines(filename) do
- filecontents[#filecontents + 1] = line
- end
-
- return setmetatable({
- filecontents = filecontents,
- contentsLen = #filecontents,
- currentLineNo = 1,
- }, { __index = StreamRead })
-end
-
--- get a line
-function StreamRead:getLine()
- if self.currentLine then
- self.currentLine = nil
- return self.currentLine
- end
-
- -- get line
- if self.currentLineNo <= self.contentsLen then
- local line = self.filecontents[self.currentLineNo]
- self.currentLineNo = self.currentLineNo + 1
- return line
- end
-
- return ''
-end
-
--- save line fragment
---- @param line_fragment string
-function StreamRead:ungetLine(line_fragment)
- self.currentLine = line_fragment
-end
-
--- is it eof?
-function StreamRead:eof()
- return not self.currentLine and self.currentLineNo > self.contentsLen
-end
-
--- input filter
---- @class Lua2DoxFilter
-local Lua2DoxFilter = {
- generics = {}, --- @type table<string,string>
- block_ignore = false, --- @type boolean
-}
-setmetatable(Lua2DoxFilter, { __index = Lua2DoxFilter })
-
-function Lua2DoxFilter:reset()
- self.generics = {}
- self.block_ignore = false
-end
-
---- trim comment off end of string
----
---- @param line string
---- @return string, string?
-local function removeCommentFromLine(line)
- local pos_comment = line:find('%-%-')
- if not pos_comment then
- return line
- end
- return line:sub(1, pos_comment - 1), line:sub(pos_comment)
-end
-
---- @param parsed luacats.Return
---- @return string
-local function get_return_type(parsed)
- local elems = {} --- @type string[]
- for _, v in ipairs(parsed) do
- local e = v.type --- @type string
- if v.name then
- e = e .. ' ' .. v.name --- @type string
- end
- elems[#elems + 1] = e
- end
- return '(' .. table.concat(elems, ', ') .. ')'
-end
-
---- @param name string
---- @return string
-local function process_name(name, optional)
- if optional then
- name = name:sub(1, -2) --- @type string
- end
- return name
-end
-
---- @param ty string
---- @param generics table<string,string>
---- @return string
-local function process_type(ty, generics, optional)
- -- replace generic types
- for k, v in pairs(generics) do
- ty = ty:gsub(k, v) --- @type string
- end
-
- -- strip parens
- ty = ty:gsub('^%((.*)%)$', '%1')
-
- if optional and not ty:find('nil') then
- ty = ty .. '?'
- end
-
- -- remove whitespace in unions
- ty = ty:gsub('%s*|%s*', '|')
-
- -- replace '|nil' with '?'
- ty = ty:gsub('|nil', '?')
- ty = ty:gsub('nil|(.*)', '%1?')
-
- return '(`' .. ty .. '`)'
-end
-
---- @param parsed luacats.Param
---- @param generics table<string,string>
---- @return string
-local function process_param(parsed, generics)
- local name, ty = parsed.name, parsed.type
- local optional = vim.endswith(name, '?')
-
- return table.concat({
- '/// @param',
- process_name(name, optional),
- process_type(ty, generics, optional),
- parsed.desc,
- }, ' ')
-end
-
---- @param parsed luacats.Return
---- @param generics table<string,string>
---- @return string
-local function process_return(parsed, generics)
- local ty, name --- @type string, string
- if #parsed == 1 then
- ty, name = parsed[1].type, parsed[1].name or ''
- else
- ty, name = get_return_type(parsed), ''
- end
-
- local optional = vim.endswith(name, '?')
-
- return table.concat({
- '/// @return',
- process_type(ty, generics, optional),
- process_name(name, optional),
- parsed.desc,
- }, ' ')
-end
-
---- Processes "@…" directives in a docstring line.
----
---- @param line string
---- @return string?
-function Lua2DoxFilter:process_magic(line)
- line = line:gsub('^%s+@', '@')
- line = line:gsub('@package', '@private')
- line = line:gsub('@nodoc', '@private')
-
- if self.block_ignore then
- return '// gg:" ' .. line .. '"'
- end
-
- if not vim.startswith(line, '@') then -- it's a magic comment
- return '/// ' .. line
- end
-
- local magic_split = vim.split(line, ' ', { plain = true })
- local directive = magic_split[1]
-
- if
- vim.list_contains({
- '@cast',
- '@diagnostic',
- '@overload',
- '@meta',
- '@type',
- }, directive)
- then
- -- Ignore LSP directives
- return '// gg:"' .. line .. '"'
- elseif directive == '@defgroup' or directive == '@addtogroup' then
- -- Can't use '.' in defgroup, so convert to '--'
- return '/// ' .. line:gsub('%.', '-dot-')
- end
-
- if directive == '@alias' then
- -- this contiguous block should be all ignored.
- self.block_ignore = true
- return '// gg:"' .. line .. '"'
- end
-
- -- preprocess line before parsing
- if directive == '@param' or directive == '@return' then
- for _, type in ipairs(TYPES) do
- line = line:gsub('^@param%s+([a-zA-Z_?]+)%s+.*%((' .. type .. ')%)', '@param %1 %2')
- line = line:gsub('^@param%s+([a-zA-Z_?]+)%s+.*%((' .. type .. '|nil)%)', '@param %1 %2')
- line = line:gsub('^@param%s+([a-zA-Z_?]+)%s+.*%((' .. type .. '%?)%)', '@param %1 %2')
-
- line = line:gsub('^@return%s+.*%((' .. type .. ')%)', '@return %1')
- line = line:gsub('^@return%s+.*%((' .. type .. '|nil)%)', '@return %1')
- line = line:gsub('^@return%s+.*%((' .. type .. '%?)%)', '@return %1')
- end
- end
-
- local parsed = luacats_parser:match(line)
-
- if not parsed then
- return '/// ' .. line
- end
-
- local kind = parsed.kind
-
- if kind == 'generic' then
- self.generics[parsed.name] = parsed.type or 'any'
- return
- elseif kind == 'param' then
- return process_param(parsed --[[@as luacats.Param]], self.generics)
- elseif kind == 'return' then
- return process_return(parsed --[[@as luacats.Return]], self.generics)
- end
-
- error(string.format('unhandled parsed line %q: %s', line, parsed))
-end
-
---- @param line string
---- @param in_stream StreamRead
---- @return string
-function Lua2DoxFilter:process_block_comment(line, in_stream)
- local comment_parts = {} --- @type string[]
- local done --- @type boolean?
-
- while not done and not in_stream:eof() do
- local thisComment --- @type string?
- local closeSquare = line:find(']]')
- if not closeSquare then -- need to look on another line
- thisComment = line .. '\n'
- line = in_stream:getLine()
- else
- thisComment = line:sub(1, closeSquare - 1)
- done = true
-
- -- unget the tail of the line
- -- in most cases it's empty. This may make us less efficient but
- -- easier to program
- in_stream:ungetLine(vim.trim(line:sub(closeSquare + 2)))
- end
- comment_parts[#comment_parts + 1] = thisComment
- end
-
- local comment = table.concat(comment_parts)
-
- if comment:sub(1, 1) == '@' then -- it's a long magic comment
- return '/*' .. comment .. '*/ '
- end
-
- -- discard
- return '/* zz:' .. comment .. '*/ '
-end
-
---- @param line string
---- @return string
-function Lua2DoxFilter:process_function_header(line)
- local pos_fn = assert(line:find('function'))
- -- we've got a function
- local fn = removeCommentFromLine(vim.trim(line:sub(pos_fn + 8)))
-
- if fn:sub(1, 1) == '(' then
- -- it's an anonymous function
- return '// ZZ: ' .. line
- end
- -- fn has a name, so is interesting
-
- -- want to fix for iffy declarations
- if fn:find('[%({]') then
- -- we might have a missing close paren
- if not fn:find('%)') then
- fn = fn .. ' ___MissingCloseParenHere___)'
- end
- end
-
- -- Big hax
- if fn:find(':') then
- fn = fn:gsub(':', '.', 1)
-
- local paren_start = fn:find('(', 1, true)
- local paren_finish = fn:find(')', 1, true)
-
- -- Nothing in between the parens
- local comma --- @type string
- if paren_finish == paren_start + 1 then
- comma = ''
- else
- comma = ', '
- end
-
- fn = fn:sub(1, paren_start) .. 'self' .. comma .. fn:sub(paren_start + 1)
- end
-
- if line:match('local') then
- -- Special: tell gen_vimdoc.py this is a local function.
- return 'local_function ' .. fn .. '{}'
- end
-
- -- add vanilla function
- return 'function ' .. fn .. '{}'
-end
-
---- @param line string
---- @param in_stream StreamRead
---- @return string?
-function Lua2DoxFilter:process_line(line, in_stream)
- local line_raw = line
- line = vim.trim(line)
-
- if vim.startswith(line, '---') then
- return Lua2DoxFilter:process_magic(line:sub(4))
- end
-
- if vim.startswith(line, '--' .. '[[') then -- it's a long comment
- return Lua2DoxFilter:process_block_comment(line:sub(5), in_stream)
- end
-
- -- Hax... I'm sorry
- -- M.fun = vim.memoize(function(...)
- -- ->
- -- function M.fun(...)
- line = line:gsub('^(.+) = .*_memoize%([^,]+, function%((.*)%)$', 'function %1(%2)')
-
- if line:find('^function') or line:find('^local%s+function') then
- return Lua2DoxFilter:process_function_header(line)
- end
-
- if not line:match('^local') then
- local v = line_raw:match('^([A-Za-z][.a-zA-Z_]*)%s+%=')
- if v and v:match('%.') then
- -- Special: this lets gen_vimdoc.py handle tables.
- return 'table ' .. v .. '() {}'
- end
- end
-
- if #line > 0 then -- we don't know what this line means, so just comment it out
- return '// zz: ' .. line
- end
-
- return ''
-end
-
--- Processes the file and writes filtered output to stdout.
----@param filename string
-function Lua2DoxFilter:filter(filename)
- local in_stream = StreamRead.new(filename)
-
- local last_was_magic = false
-
- while not in_stream:eof() do
- local line = in_stream:getLine()
-
- local out_line = self:process_line(line, in_stream)
-
- if not vim.startswith(vim.trim(line), '---') then
- self:reset()
- end
-
- if out_line then
- -- Ensure all magic blocks associate with some object to prevent doxygen
- -- from getting confused.
- if vim.startswith(out_line, '///') then
- last_was_magic = true
- else
- if last_was_magic and out_line:match('^// zz: [^-]+') then
- writeln('local_function _ignore() {}')
- end
- last_was_magic = false
- end
- writeln(out_line)
- end
- end
-end
-
---- @class TApp
---- @field timestamp string|osdate
---- @field name string
---- @field version string
---- @field copyright string
---- this application
-local TApp = {
- timestamp = os.date('%c %Z', os.time()),
- name = 'Lua2DoX',
- version = '0.2 20130128',
- copyright = 'Copyright (c) Simon Dales 2012-13',
-}
-
-setmetatable(TApp, { __index = TApp })
-
-function TApp:getRunStamp()
- return self.name .. ' (' .. self.version .. ') ' .. self.timestamp
-end
-
-function TApp:getVersion()
- return self.name .. ' (' .. self.version .. ') '
-end
-
---main
-
-if arg[1] == '--help' then
- writeln(TApp:getVersion())
- writeln(TApp.copyright)
- writeln([[
- run as:
- nvim -l scripts/lua2dox.lua <param>
- --------------
- Param:
- <filename> : interprets filename
- --version : show version/copyright info
- --help : this help text]])
-elseif arg[1] == '--version' then
- writeln(TApp:getVersion())
- writeln(TApp.copyright)
-else -- It's a filter.
- local filename = arg[1]
-
- if arg[2] == '--outdir' then
- local outdir = arg[3]
- if
- type(outdir) ~= 'string'
- or (0 ~= vim.fn.filereadable(outdir) and 0 == vim.fn.isdirectory(outdir))
- then
- error(('invalid --outdir: "%s"'):format(tostring(outdir)))
- end
- vim.fn.mkdir(outdir, 'p')
- debug_outfile = string.format('%s/%s.c', outdir, vim.fs.basename(filename))
- end
-
- Lua2DoxFilter:filter(filename)
-
- -- output the tail
- writeln('// #######################')
- writeln('// app run:' .. TApp:getRunStamp())
- writeln('// #######################')
- writeln()
-
- if debug_outfile then
- local f = assert(io.open(debug_outfile, 'w'))
- f:write(table.concat(debug_output))
- f:close()
- end
-end