diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2022-04-21 23:14:48 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2022-04-21 23:14:48 +0800 |
commit | a8606be439ce9e3357d18943d2e75d721253c3c3 (patch) | |
tree | bce83668a1ca876952c717af125032df4e910016 /script/parser/luadoc.lua | |
parent | a19f2ee7394c5f5a10342a37497fbd7b7587316c (diff) | |
download | lua-language-server-a8606be439ce9e3357d18943d2e75d721253c3c3.zip |
luadoc supports `--[[@xxx]]`
Diffstat (limited to 'script/parser/luadoc.lua')
-rw-r--r-- | script/parser/luadoc.lua | 1049 |
1 files changed, 510 insertions, 539 deletions
diff --git a/script/parser/luadoc.lua b/script/parser/luadoc.lua index d329985f..b7849524 100644 --- a/script/parser/luadoc.lua +++ b/script/parser/luadoc.lua @@ -2,10 +2,11 @@ local m = require 'lpeglabel' local re = require 'parser.relabel' local guide = require 'parser.guide' local parser = require 'parser.newparser' +local util = require 'utility' local TokenTypes, TokenStarts, TokenFinishs, TokenContents, TokenMarks local Ci, Offset, pushWarning, NextComment, Lines -local parseType +local parseType, parseTypeUnit ---@type any local Parser = re.compile([[ Main <- (Token / Sp)* @@ -336,104 +337,6 @@ local function parseSigns(parent) return signs end -local function parseClass(parent) - local result = { - type = 'doc.class', - parent = parent, - fields = {}, - } - result.class = parseName('doc.class.name', result) - if not result.class then - pushWarning { - type = 'LUADOC_MISS_CLASS_NAME', - start = getFinish(), - finish = getFinish(), - } - return nil - end - result.start = getStart() - result.finish = getFinish() - result.signs = parseSigns(result) - if not checkToken('symbol', ':', 1) then - return result - end - nextToken() - - result.extends = {} - - while true do - local extend = parseName('doc.extends.name', result) - or parseTable(result) - if not extend then - pushWarning { - type = 'LUADOC_MISS_CLASS_EXTENDS_NAME', - start = getFinish(), - finish = getFinish(), - } - return result - end - result.extends[#result.extends+1] = extend - result.finish = getFinish() - if not checkToken('symbol', ',', 1) then - break - end - nextToken() - end - return result -end - -local function parseTypeUnitArray(parent, node) - if not checkToken('symbol', '[]', 1) then - return nil - end - nextToken() - local result = { - type = 'doc.type.array', - start = node.start, - finish = getFinish(), - node = node, - parent = parent, - } - node.parent = result - return result -end - -local function parseTypeUnitSign(parent, node) - if not checkToken('symbol', '<', 1) then - return nil - end - nextToken() - local result = { - type = 'doc.type.sign', - start = node.start, - finish = getFinish(), - node = node, - parent = parent, - signs = {}, - } - node.parent = result - while true do - local sign = parseType(result) - if not sign then - pushWarning { - type = 'LUA_DOC_MISS_SIGN', - start = getFinish(), - finish = getFinish(), - } - break - end - result.signs[#result.signs+1] = sign - if checkToken('symbol', ',', 1) then - nextToken() - else - break - end - end - nextSymbolOrError '>' - result.finish = getFinish() - return result -end - local function parseDots(tp, parent) if not checkToken('symbol', '...', 1) then return @@ -527,8 +430,6 @@ local function parseTypeUnitFunction(parent) return typeUnit end -local parseTypeUnit - local function parseFunction(parent) local _, content = peekToken() if content == 'async' then @@ -551,6 +452,509 @@ local function parseFunction(parent) end end +local docSwitch = util.switch() + : case 'class' + : call(function () + local result = { + type = 'doc.class', + fields = {}, + } + result.class = parseName('doc.class.name', result) + if not result.class then + pushWarning { + type = 'LUADOC_MISS_CLASS_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.start = getStart() + result.finish = getFinish() + result.signs = parseSigns(result) + if not checkToken('symbol', ':', 1) then + return result + end + nextToken() + + result.extends = {} + + while true do + local extend = parseName('doc.extends.name', result) + or parseTable(result) + if not extend then + pushWarning { + type = 'LUADOC_MISS_CLASS_EXTENDS_NAME', + start = getFinish(), + finish = getFinish(), + } + return result + end + result.extends[#result.extends+1] = extend + result.finish = getFinish() + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + return result + end) + : case 'type' + : call(function () + return parseType() + end) + : case 'alias' + : call(function () + local result = { + type = 'doc.alias', + } + result.alias = parseName('doc.alias.name', result) + if not result.alias then + pushWarning { + type = 'LUADOC_MISS_ALIAS_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.start = getStart() + result.signs = parseSigns(result) + result.extends = parseType(result) + if not result.extends then + pushWarning { + type = 'LUADOC_MISS_ALIAS_EXTENDS', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.finish = getFinish() + return result + end) + : case 'param' + : call(function () + local result = { + type = 'doc.param', + } + result.param = parseName('doc.param.name', result) + or parseDots('doc.param.name', result) + if not result.param then + pushWarning { + type = 'LUADOC_MISS_PARAM_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + if checkToken('symbol', '?', 1) then + nextToken() + result.optional = true + end + result.start = result.param.start + result.finish = getFinish() + result.extends = parseType(result) + if not result.extends then + pushWarning { + type = 'LUADOC_MISS_PARAM_EXTENDS', + start = getFinish(), + finish = getFinish(), + } + return result + end + result.finish = getFinish() + result.firstFinish = result.extends.firstFinish + return result + end) + : case 'return' + : call(function () + local result = { + type = 'doc.return', + returns = {}, + } + while true do + local docType = parseType(result) + if not docType then + break + end + if not result.start then + result.start = docType.start + end + if checkToken('symbol', '?', 1) then + nextToken() + docType.optional = true + end + docType.name = parseName('doc.return.name', docType) + result.returns[#result.returns+1] = docType + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + if #result.returns == 0 then + return nil + end + result.finish = getFinish() + return result + end) + : case 'field' + : call(function () + local result = { + type = 'doc.field', + } + try(function () + local tp, value = nextToken() + if tp == 'name' then + if value == 'public' + or value == 'protected' + or value == 'private' then + result.visible = value + result.start = getStart() + return true + end + end + return false + end) + result.field = parseName('doc.field.name', result) + or parseIndexField('doc.field.name', result) + if not result.field then + pushWarning { + type = 'LUADOC_MISS_FIELD_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + if not result.start then + result.start = result.field.start + end + if checkToken('symbol', '?', 1) then + nextToken() + result.optional = true + end + result.extends = parseType(result) + if not result.extends then + pushWarning { + type = 'LUADOC_MISS_FIELD_EXTENDS', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.finish = getFinish() + return result + end) + : case 'generic' + : call(function () + local result = { + type = 'doc.generic', + generics = {}, + } + while true do + local object = { + type = 'doc.generic.object', + parent = result, + } + object.generic = parseName('doc.generic.name', object) + if not object.generic then + pushWarning { + type = 'LUADOC_MISS_GENERIC_NAME', + start = getFinish(), + finish = getFinish(), + } + return nil + end + object.start = object.generic.start + if not result.start then + result.start = object.start + end + if checkToken('symbol', ':', 1) then + nextToken() + object.extends = parseType(object) + end + object.finish = getFinish() + result.generics[#result.generics+1] = object + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + result.finish = getFinish() + return result + end) + : case 'vararg' + : call(function () + local result = { + type = 'doc.vararg', + } + result.vararg = parseType(result) + if not result.vararg then + pushWarning { + type = 'LUADOC_MISS_VARARG_TYPE', + start = getFinish(), + finish = getFinish(), + } + return + end + result.start = result.vararg.start + result.finish = result.vararg.finish + return result + end) + : case 'overload' + : call(function () + local tp, name = peekToken() + if tp ~= 'name' + or (name ~= 'fun' and name ~= 'async') then + pushWarning { + type = 'LUADOC_MISS_FUN_AFTER_OVERLOAD', + start = getFinish(), + finish = getFinish(), + } + return nil + end + local result = { + type = 'doc.overload', + } + result.overload = parseFunction(result) + if not result.overload then + return nil + end + result.overload.parent = result + result.start = result.overload.start + result.finish = result.overload.finish + return result + end) + : case 'deprecated' + : call(function () + return { + type = 'doc.deprecated', + start = getFinish(), + finish = getFinish(), + } + end) + : case 'meta' + : call(function () + return { + type = 'doc.meta', + start = getFinish(), + finish = getFinish(), + } + end) + : case 'version' + : call(function () + local result = { + type = 'doc.version', + versions = {}, + } + while true do + local tp, text = nextToken() + if not tp then + pushWarning { + type = 'LUADOC_MISS_VERSION', + start = getFinish(), + finish = getFinish(), + } + break + end + if not result.start then + result.start = getStart() + end + local version = { + type = 'doc.version.unit', + parent = result, + start = getStart(), + } + if tp == 'symbol' then + if text == '>' then + version.ge = true + elseif text == '<' then + version.le = true + end + tp, text = nextToken() + end + if tp ~= 'name' then + pushWarning { + type = 'LUADOC_MISS_VERSION', + start = getStart(), + finish = getFinish(), + } + break + end + version.version = tonumber(text) or text + version.finish = getFinish() + result.versions[#result.versions+1] = version + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + if #result.versions == 0 then + return nil + end + result.finish = getFinish() + return result + end) + : case 'see' + : call(function () + local result = { + type = 'doc.see', + } + result.name = parseName('doc.see.name', result) + if not result.name then + return nil + end + result.start = result.name.start + result.finish = result.name.finish + if checkToken('symbol', '#', 1) then + nextToken() + result.field = parseName('doc.see.field', result) + result.finish = getFinish() + end + return result + end) + : case 'diagnostic' + : call(function () + local result = { + type = 'doc.diagnostic', + } + local nextTP, mode = nextToken() + if nextTP ~= 'name' then + pushWarning { + type = 'LUADOC_MISS_DIAG_MODE', + start = getFinish(), + finish = getFinish(), + } + return nil + end + result.mode = mode + result.start = getStart() + result.finish = getFinish() + if mode ~= 'disable-next-line' + and mode ~= 'disable-line' + and mode ~= 'disable' + and mode ~= 'enable' then + pushWarning { + type = 'LUADOC_ERROR_DIAG_MODE', + start = result.start, + finish = result.finish, + } + end + + if checkToken('symbol', ':', 1) then + nextToken() + result.names = {} + while true do + local name = parseName('doc.diagnostic.name', result) + if not name then + pushWarning { + type = 'LUADOC_MISS_DIAG_NAME', + start = getFinish(), + finish = getFinish(), + } + return result + end + result.names[#result.names+1] = name + if not checkToken('symbol', ',', 1) then + break + end + nextToken() + end + end + + result.finish = getFinish() + + return result + end) + : case 'module' + : call(function () + local result = { + type = 'doc.module', + start = getFinish(), + finish = getFinish(), + } + local tp, content = peekToken() + if tp == 'string' then + result.module = content + nextToken() + result.start = getStart() + result.finish = getFinish() + result.smark = getMark() + else + pushWarning { + type = 'LUADOC_MISS_MODULE_NAME', + start = getFinish(), + finish = getFinish(), + } + end + return result + end) + : case 'async' + : call(function () + return { + type = 'doc.async', + start = getFinish(), + finish = getFinish(), + } + end) + : case 'nodiscard' + : call(function () + return { + type = 'doc.nodiscard', + start = getFinish(), + finish = getFinish(), + } + end) + +local function parseTypeUnitArray(parent, node) + if not checkToken('symbol', '[]', 1) then + return nil + end + nextToken() + local result = { + type = 'doc.type.array', + start = node.start, + finish = getFinish(), + node = node, + parent = parent, + } + node.parent = result + return result +end + +local function parseTypeUnitSign(parent, node) + if not checkToken('symbol', '<', 1) then + return nil + end + nextToken() + local result = { + type = 'doc.type.sign', + start = node.start, + finish = getFinish(), + node = node, + parent = parent, + signs = {}, + } + node.parent = result + while true do + local sign = parseType(result) + if not sign then + pushWarning { + type = 'LUA_DOC_MISS_SIGN', + start = getFinish(), + finish = getFinish(), + } + break + end + result.signs[#result.signs+1] = sign + if checkToken('symbol', ',', 1) then + nextToken() + else + break + end + end + nextSymbolOrError '>' + result.finish = getFinish() + return result +end + local function parseString(parent) local tp, content = peekToken() if not tp or tp ~= 'string' then @@ -789,406 +1193,6 @@ function parseType(parent) return result end -local function parseAlias() - local result = { - type = 'doc.alias', - } - result.alias = parseName('doc.alias.name', result) - if not result.alias then - pushWarning { - type = 'LUADOC_MISS_ALIAS_NAME', - start = getFinish(), - finish = getFinish(), - } - return nil - end - result.start = getStart() - result.signs = parseSigns(result) - result.extends = parseType(result) - if not result.extends then - pushWarning { - type = 'LUADOC_MISS_ALIAS_EXTENDS', - start = getFinish(), - finish = getFinish(), - } - return nil - end - result.finish = getFinish() - return result -end - -local function parseParam() - local result = { - type = 'doc.param', - } - result.param = parseName('doc.param.name', result) - or parseDots('doc.param.name', result) - if not result.param then - pushWarning { - type = 'LUADOC_MISS_PARAM_NAME', - start = getFinish(), - finish = getFinish(), - } - return nil - end - if checkToken('symbol', '?', 1) then - nextToken() - result.optional = true - end - result.start = result.param.start - result.finish = getFinish() - result.extends = parseType(result) - if not result.extends then - pushWarning { - type = 'LUADOC_MISS_PARAM_EXTENDS', - start = getFinish(), - finish = getFinish(), - } - return result - end - result.finish = getFinish() - result.firstFinish = result.extends.firstFinish - return result -end - -local function parseReturn() - local result = { - type = 'doc.return', - returns = {}, - } - while true do - local docType = parseType(result) - if not docType then - break - end - if not result.start then - result.start = docType.start - end - if checkToken('symbol', '?', 1) then - nextToken() - docType.optional = true - end - docType.name = parseName('doc.return.name', docType) - result.returns[#result.returns+1] = docType - if not checkToken('symbol', ',', 1) then - break - end - nextToken() - end - if #result.returns == 0 then - return nil - end - result.finish = getFinish() - return result -end - -local function parseField() - local result = { - type = 'doc.field', - } - try(function () - local tp, value = nextToken() - if tp == 'name' then - if value == 'public' - or value == 'protected' - or value == 'private' then - result.visible = value - result.start = getStart() - return true - end - end - return false - end) - result.field = parseName('doc.field.name', result) - or parseIndexField('doc.field.name', result) - if not result.field then - pushWarning { - type = 'LUADOC_MISS_FIELD_NAME', - start = getFinish(), - finish = getFinish(), - } - return nil - end - if not result.start then - result.start = result.field.start - end - if checkToken('symbol', '?', 1) then - nextToken() - result.optional = true - end - result.extends = parseType(result) - if not result.extends then - pushWarning { - type = 'LUADOC_MISS_FIELD_EXTENDS', - start = getFinish(), - finish = getFinish(), - } - return nil - end - result.finish = getFinish() - return result -end - -local function parseGeneric() - local result = { - type = 'doc.generic', - generics = {}, - } - while true do - local object = { - type = 'doc.generic.object', - parent = result, - } - object.generic = parseName('doc.generic.name', object) - if not object.generic then - pushWarning { - type = 'LUADOC_MISS_GENERIC_NAME', - start = getFinish(), - finish = getFinish(), - } - return nil - end - object.start = object.generic.start - if not result.start then - result.start = object.start - end - if checkToken('symbol', ':', 1) then - nextToken() - object.extends = parseType(object) - end - object.finish = getFinish() - result.generics[#result.generics+1] = object - if not checkToken('symbol', ',', 1) then - break - end - nextToken() - end - result.finish = getFinish() - return result -end - -local function parseVararg() - local result = { - type = 'doc.vararg', - } - result.vararg = parseType(result) - if not result.vararg then - pushWarning { - type = 'LUADOC_MISS_VARARG_TYPE', - start = getFinish(), - finish = getFinish(), - } - return - end - result.start = result.vararg.start - result.finish = result.vararg.finish - return result -end - -local function parseOverload() - local tp, name = peekToken() - if tp ~= 'name' - or (name ~= 'fun' and name ~= 'async') then - pushWarning { - type = 'LUADOC_MISS_FUN_AFTER_OVERLOAD', - start = getFinish(), - finish = getFinish(), - } - return nil - end - local result = { - type = 'doc.overload', - } - result.overload = parseFunction(result) - if not result.overload then - return nil - end - result.overload.parent = result - result.start = result.overload.start - result.finish = result.overload.finish - return result -end - -local function parseDeprecated() - return { - type = 'doc.deprecated', - start = getFinish(), - finish = getFinish(), - } -end - -local function parseMeta() - return { - type = 'doc.meta', - start = getFinish(), - finish = getFinish(), - } -end - -local function parseVersion() - local result = { - type = 'doc.version', - versions = {}, - } - while true do - local tp, text = nextToken() - if not tp then - pushWarning { - type = 'LUADOC_MISS_VERSION', - start = getFinish(), - finish = getFinish(), - } - break - end - if not result.start then - result.start = getStart() - end - local version = { - type = 'doc.version.unit', - parent = result, - start = getStart(), - } - if tp == 'symbol' then - if text == '>' then - version.ge = true - elseif text == '<' then - version.le = true - end - tp, text = nextToken() - end - if tp ~= 'name' then - pushWarning { - type = 'LUADOC_MISS_VERSION', - start = getStart(), - finish = getFinish(), - } - break - end - version.version = tonumber(text) or text - version.finish = getFinish() - result.versions[#result.versions+1] = version - if not checkToken('symbol', ',', 1) then - break - end - nextToken() - end - if #result.versions == 0 then - return nil - end - result.finish = getFinish() - return result -end - -local function parseSee() - local result = { - type = 'doc.see', - } - result.name = parseName('doc.see.name', result) - if not result.name then - return nil - end - result.start = result.name.start - result.finish = result.name.finish - if checkToken('symbol', '#', 1) then - nextToken() - result.field = parseName('doc.see.field', result) - result.finish = getFinish() - end - return result -end - -local function parseDiagnostic() - local result = { - type = 'doc.diagnostic', - } - local nextTP, mode = nextToken() - if nextTP ~= 'name' then - pushWarning { - type = 'LUADOC_MISS_DIAG_MODE', - start = getFinish(), - finish = getFinish(), - } - return nil - end - result.mode = mode - result.start = getStart() - result.finish = getFinish() - if mode ~= 'disable-next-line' - and mode ~= 'disable-line' - and mode ~= 'disable' - and mode ~= 'enable' then - pushWarning { - type = 'LUADOC_ERROR_DIAG_MODE', - start = result.start, - finish = result.finish, - } - end - - if checkToken('symbol', ':', 1) then - nextToken() - result.names = {} - while true do - local name = parseName('doc.diagnostic.name', result) - if not name then - pushWarning { - type = 'LUADOC_MISS_DIAG_NAME', - start = getFinish(), - finish = getFinish(), - } - return result - end - result.names[#result.names+1] = name - if not checkToken('symbol', ',', 1) then - break - end - nextToken() - end - end - - result.finish = getFinish() - - return result -end - -local function parseModule() - local result = { - type = 'doc.module', - start = getFinish(), - finish = getFinish(), - } - local tp, content = peekToken() - if tp == 'string' then - result.module = content - nextToken() - result.start = getStart() - result.finish = getFinish() - result.smark = getMark() - else - pushWarning { - type = 'LUADOC_MISS_MODULE_NAME', - start = getFinish(), - finish = getFinish(), - } - end - return result -end - -local function parseAsync() - return { - type = 'doc.async', - start = getFinish(), - finish = getFinish(), - } -end - -local function parseNoDiscard() - return { - type = 'doc.nodiscard', - start = getFinish(), - finish = getFinish(), - } -end - local function convertTokens() local tp, text = nextToken() if not tp then @@ -1202,41 +1206,7 @@ local function convertTokens() } return nil end - if text == 'class' then - return parseClass() - elseif text == 'type' then - return parseType() - elseif text == 'alias' then - return parseAlias() - elseif text == 'param' then - return parseParam() - elseif text == 'return' then - return parseReturn() - elseif text == 'field' then - return parseField() - elseif text == 'generic' then - return parseGeneric() - elseif text == 'vararg' then - return parseVararg() - elseif text == 'overload' then - return parseOverload() - elseif text == 'deprecated' then - return parseDeprecated() - elseif text == 'meta' then - return parseMeta() - elseif text == 'version' then - return parseVersion() - elseif text == 'see' then - return parseSee() - elseif text == 'diagnostic' then - return parseDiagnostic() - elseif text == 'module' then - return parseModule() - elseif text == 'async' then - return parseAsync() - elseif text == 'nodiscard' then - return parseNoDiscard() - end + return docSwitch(text) end local function trimTailComment(text) @@ -1261,7 +1231,8 @@ end local function buildLuaDoc(comment) local text = comment.text - local _, startPos = text:find('^%-%s*@') + local startPos = (comment.type == 'comment.short' and text:match '^%-%s*@()') + or (comment.type == 'comment.long' and text:match '@()') if not startPos then return { type = 'doc.comment', @@ -1272,9 +1243,9 @@ local function buildLuaDoc(comment) } end - local doc = text:sub(startPos + 1) + local doc = text:sub(startPos) - parseTokens(doc, comment.start + startPos + 1) + parseTokens(doc, comment.start + startPos) local result = convertTokens() if result then result.range = comment.finish |