cdoc_parser.lua 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. local cdoc_grammar = require('scripts.cdoc_grammar')
  2. local c_grammar = require('src.nvim.generators.c_grammar')
  3. --- @class nvim.cdoc.parser.param
  4. --- @field name string
  5. --- @field type string
  6. --- @field desc string
  7. --- @class nvim.cdoc.parser.return
  8. --- @field name string
  9. --- @field type string
  10. --- @field desc string
  11. --- @class nvim.cdoc.parser.note
  12. --- @field desc string
  13. --- @class nvim.cdoc.parser.brief
  14. --- @field kind 'brief'
  15. --- @field desc string
  16. --- @class nvim.cdoc.parser.fun
  17. --- @field name string
  18. --- @field params nvim.cdoc.parser.param[]
  19. --- @field returns nvim.cdoc.parser.return[]
  20. --- @field desc string
  21. --- @field deprecated? true
  22. --- @field since? string
  23. --- @field attrs? string[]
  24. --- @field nodoc? true
  25. --- @field notes? nvim.cdoc.parser.note[]
  26. --- @field see? nvim.cdoc.parser.note[]
  27. --- @class nvim.cdoc.parser.State
  28. --- @field doc_lines? string[]
  29. --- @field cur_obj? nvim.cdoc.parser.obj
  30. --- @field last_doc_item? nvim.cdoc.parser.param|nvim.cdoc.parser.return|nvim.cdoc.parser.note
  31. --- @field last_doc_item_indent? integer
  32. --- @alias nvim.cdoc.parser.obj
  33. --- | nvim.cdoc.parser.fun
  34. --- | nvim.cdoc.parser.brief
  35. --- If we collected any `---` lines. Add them to the existing (or new) object
  36. --- Used for function/class descriptions and multiline param descriptions.
  37. --- @param state nvim.cdoc.parser.State
  38. local function add_doc_lines_to_obj(state)
  39. if state.doc_lines then
  40. state.cur_obj = state.cur_obj or {}
  41. local cur_obj = assert(state.cur_obj)
  42. local txt = table.concat(state.doc_lines, '\n')
  43. if cur_obj.desc then
  44. cur_obj.desc = cur_obj.desc .. '\n' .. txt
  45. else
  46. cur_obj.desc = txt
  47. end
  48. state.doc_lines = nil
  49. end
  50. end
  51. --- @param line string
  52. --- @param state nvim.cdoc.parser.State
  53. local function process_doc_line(line, state)
  54. line = line:gsub('^%s+@', '@')
  55. local parsed = cdoc_grammar:match(line)
  56. if not parsed then
  57. if line:match('^ ') then
  58. line = line:sub(2)
  59. end
  60. if state.last_doc_item then
  61. if not state.last_doc_item_indent then
  62. state.last_doc_item_indent = #line:match('^%s*') + 1
  63. end
  64. state.last_doc_item.desc = (state.last_doc_item.desc or '')
  65. .. '\n'
  66. .. line:sub(state.last_doc_item_indent or 1)
  67. else
  68. state.doc_lines = state.doc_lines or {}
  69. table.insert(state.doc_lines, line)
  70. end
  71. return
  72. end
  73. state.last_doc_item_indent = nil
  74. state.last_doc_item = nil
  75. local kind = parsed.kind
  76. state.cur_obj = state.cur_obj or {}
  77. local cur_obj = assert(state.cur_obj)
  78. if kind == 'brief' then
  79. state.cur_obj = {
  80. kind = 'brief',
  81. desc = parsed.desc,
  82. }
  83. elseif kind == 'param' then
  84. state.last_doc_item_indent = nil
  85. cur_obj.params = cur_obj.params or {}
  86. state.last_doc_item = {
  87. name = parsed.name,
  88. desc = parsed.desc,
  89. }
  90. table.insert(cur_obj.params, state.last_doc_item)
  91. elseif kind == 'return' then
  92. cur_obj.returns = { {
  93. desc = parsed.desc,
  94. } }
  95. state.last_doc_item_indent = nil
  96. state.last_doc_item = cur_obj.returns[1]
  97. elseif kind == 'deprecated' then
  98. cur_obj.deprecated = true
  99. elseif kind == 'nodoc' then
  100. cur_obj.nodoc = true
  101. elseif kind == 'since' then
  102. cur_obj.since = parsed.desc
  103. elseif kind == 'see' then
  104. cur_obj.see = cur_obj.see or {}
  105. table.insert(cur_obj.see, { desc = parsed.desc })
  106. elseif kind == 'note' then
  107. state.last_doc_item_indent = nil
  108. state.last_doc_item = {
  109. desc = parsed.desc,
  110. }
  111. cur_obj.notes = cur_obj.notes or {}
  112. table.insert(cur_obj.notes, state.last_doc_item)
  113. else
  114. error('Unhandled' .. vim.inspect(parsed))
  115. end
  116. end
  117. --- @param item table
  118. --- @param state nvim.cdoc.parser.State
  119. local function process_proto(item, state)
  120. state.cur_obj = state.cur_obj or {}
  121. local cur_obj = assert(state.cur_obj)
  122. cur_obj.name = item.name
  123. cur_obj.params = cur_obj.params or {}
  124. for _, p in ipairs(item.parameters) do
  125. local param = { name = p[2], type = p[1] }
  126. local added = false
  127. for _, cp in ipairs(cur_obj.params) do
  128. if cp.name == param.name then
  129. cp.type = param.type
  130. added = true
  131. break
  132. end
  133. end
  134. if not added then
  135. table.insert(cur_obj.params, param)
  136. end
  137. end
  138. cur_obj.returns = cur_obj.returns or { {} }
  139. cur_obj.returns[1].type = item.return_type
  140. for _, a in ipairs({
  141. 'fast',
  142. 'remote_only',
  143. 'lua_only',
  144. 'textlock',
  145. 'textlock_allow_cmdwin',
  146. }) do
  147. if item[a] then
  148. cur_obj.attrs = cur_obj.attrs or {}
  149. table.insert(cur_obj.attrs, a)
  150. end
  151. end
  152. cur_obj.deprecated_since = item.deprecated_since
  153. -- Remove some arguments
  154. for i = #cur_obj.params, 1, -1 do
  155. local p = cur_obj.params[i]
  156. if p.name == 'channel_id' or vim.tbl_contains({ 'lstate', 'arena', 'error' }, p.type) then
  157. table.remove(cur_obj.params, i)
  158. end
  159. end
  160. end
  161. local M = {}
  162. --- @param filename string
  163. --- @return {} classes
  164. --- @return nvim.cdoc.parser.fun[] funs
  165. --- @return string[] briefs
  166. function M.parse(filename)
  167. local funs = {} --- @type nvim.cdoc.parser.fun[]
  168. local briefs = {} --- @type string[]
  169. local state = {} --- @type nvim.cdoc.parser.State
  170. local txt = assert(io.open(filename, 'r')):read('*all')
  171. local parsed = c_grammar.grammar:match(txt)
  172. for _, item in ipairs(parsed) do
  173. if item.comment then
  174. process_doc_line(item.comment, state)
  175. else
  176. add_doc_lines_to_obj(state)
  177. if item[1] == 'proto' then
  178. process_proto(item, state)
  179. table.insert(funs, state.cur_obj)
  180. end
  181. local cur_obj = state.cur_obj
  182. if cur_obj and not item.static then
  183. if cur_obj.kind == 'brief' then
  184. table.insert(briefs, cur_obj.desc)
  185. end
  186. end
  187. state = {}
  188. end
  189. end
  190. return {}, funs, briefs
  191. end
  192. -- M.parse('src/nvim/api/vim.c')
  193. return M