-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
37a91a5
commit c46b521
Showing
1 changed file
with
84 additions
and
272 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,274 +1,86 @@ | ||
-- https://github.com/neovim/nvim-lspconfig/blob/master/scripts/docgen.lua | ||
-- https://github.com/neovim/nvim-lspconfig/blob/master/LICENSE.md | ||
require 'lspconfig' | ||
local configs = require 'lspconfig.configs' | ||
local util = require 'lspconfig.util' | ||
local inspect = vim.inspect | ||
local uv = vim.loop | ||
local fn = vim.fn | ||
local tbl_flatten = vim.tbl_flatten | ||
|
||
local function template(s, params) | ||
return (s:gsub('{{([^{}]+)}}', params)) | ||
end | ||
|
||
local function map_list(t, func) | ||
local res = {} | ||
for i, v in ipairs(t) do | ||
local x = func(v, i) | ||
if x ~= nil then | ||
table.insert(res, x) | ||
end | ||
end | ||
return res | ||
end | ||
|
||
local function indent(n, s) | ||
local prefix | ||
if type(n) == 'number' then | ||
if n <= 0 then | ||
return s | ||
end | ||
prefix = string.rep(' ', n) | ||
else | ||
assert(type(n) == 'string', 'n must be number or string') | ||
prefix = n | ||
end | ||
local lines = vim.split(s, '\n', true) | ||
for i, line in ipairs(lines) do | ||
lines[i] = prefix .. line | ||
end | ||
return table.concat(lines, '\n') | ||
end | ||
|
||
local function make_parts(fns) | ||
return tbl_flatten(map_list(fns, function(v) | ||
if type(v) == 'function' then | ||
v = v() | ||
end | ||
return { v } | ||
end)) | ||
end | ||
|
||
local function make_section(indentlvl, sep, parts) | ||
return indent(indentlvl, table.concat(make_parts(parts), sep)) | ||
end | ||
|
||
local function readfile(path) | ||
assert(util.path.is_file(path)) | ||
return io.open(path):read '*a' | ||
end | ||
|
||
local function sorted_map_table(t, func) | ||
local keys = vim.tbl_keys(t) | ||
table.sort(keys) | ||
return map_list(keys, function(k) | ||
return func(k, t[k]) | ||
end) | ||
end | ||
|
||
local lsp_section_template = [[ | ||
## {{template_name}} | ||
{{preamble}} | ||
**Snippet to enable the language server:** | ||
```lua | ||
require'lspconfig'.{{template_name}}.setup{} | ||
``` | ||
{{commands}} | ||
**Default values:** | ||
{{default_values}} | ||
]] | ||
|
||
local function require_all_configs() | ||
-- Configs are lazy-loaded, tickle them to populate the `configs` singleton. | ||
for _, v in ipairs(vim.fn.glob('lua/lspconfig/server_configurations/*.lua', 1, 1)) do | ||
local module_name = v:gsub('.*/', ''):gsub('%.lua$', '') | ||
configs[module_name] = require('lspconfig.server_configurations.' .. module_name) | ||
end | ||
end | ||
|
||
local function make_lsp_sections() | ||
return make_section( | ||
0, | ||
'\n', | ||
sorted_map_table(configs, function(template_name, template_object) | ||
local template_def = template_object.document_config | ||
local docs = template_def.docs | ||
|
||
local params = { | ||
template_name = template_name, | ||
preamble = '', | ||
commands = '', | ||
default_values = '', | ||
} | ||
|
||
params.commands = make_section(0, '\n\n', { | ||
function() | ||
if not template_def.commands or #vim.tbl_keys(template_def.commands) == 0 then | ||
return | ||
end | ||
return '**Commands:**\n' | ||
.. make_section(0, '\n', { | ||
sorted_map_table(template_def.commands, function(name, def) | ||
if def.description then | ||
return string.format('- %s: %s', name, def.description) | ||
end | ||
return string.format('- %s', name) | ||
end), | ||
}) | ||
end, | ||
}) | ||
|
||
params.default_values = make_section(2, '\n\n', { | ||
function() | ||
if not template_def.default_config then | ||
return | ||
end | ||
return make_section(0, '\n', { | ||
sorted_map_table(template_def.default_config, function(k, v) | ||
local description = ((docs or {}).default_config or {})[k] | ||
if description and type(description) ~= 'string' then | ||
description = inspect(description) | ||
elseif not description and type(v) == 'function' then | ||
description = 'see source file' | ||
end | ||
return string.format('- `%s` : \n```lua\n%s\n```', k, description or inspect(v)) | ||
end), | ||
}) | ||
end, | ||
}) | ||
|
||
if docs then | ||
local tempdir = os.getenv 'DOCGEN_TEMPDIR' or uv.fs_mkdtemp '/tmp/nvim-lsp.XXXXXX' | ||
local preamble_parts = make_parts { | ||
function() | ||
if docs.description and #docs.description > 0 then | ||
return docs.description | ||
end | ||
end, | ||
function() | ||
local package_json_name = util.path.join(tempdir, template_name .. '.package.json') | ||
if docs.package_json then | ||
if not util.path.is_file(package_json_name) then | ||
os.execute(string.format('curl -v -L -o %q %q', package_json_name, docs.package_json)) | ||
end | ||
if not util.path.is_file(package_json_name) then | ||
print(string.format('Failed to download package.json for %q at %q', template_name, docs.package_json)) | ||
os.exit(1) | ||
return | ||
end | ||
local data = fn.json_decode(readfile(package_json_name)) | ||
-- The entire autogenerated section. | ||
return make_section(0, '\n', { | ||
-- The default settings section | ||
function() | ||
local default_settings = (data.contributes or {}).configuration | ||
if not default_settings.properties then | ||
return | ||
end | ||
-- The outer section. | ||
return make_section(0, '\n', { | ||
'This server accepts configuration via the `settings` key.', | ||
'<details><summary>Available settings:</summary>', | ||
'', | ||
-- The list of properties. | ||
make_section( | ||
0, | ||
'\n\n', | ||
sorted_map_table(default_settings.properties, function(k, v) | ||
local function tick(s) | ||
return string.format('`%s`', s) | ||
end | ||
local function bold(s) | ||
return string.format('**%s**', s) | ||
end | ||
|
||
-- https://github.github.com/gfm/#backslash-escapes | ||
local function excape_markdown_punctuations(str) | ||
local pattern = | ||
'\\(\\*\\|\\.\\|?\\|!\\|"\\|#\\|\\$\\|%\\|\'\\|(\\|)\\|,\\|-\\|\\/\\|:\\|;\\|<\\|=\\|>\\|@\\|\\[\\|\\\\\\|\\]\\|\\^\\|_\\|`\\|{\\|\\\\|\\|}\\)' | ||
return fn.substitute(str, pattern, '\\\\\\0', 'g') | ||
end | ||
|
||
-- local function pre(s) return string.format("<pre>%s</pre>", s) end | ||
-- local function code(s) return string.format("<code>%s</code>", s) end | ||
if not (type(v) == 'table') then | ||
return | ||
end | ||
return make_section(0, '\n', { | ||
'- ' .. make_section(0, ': ', { | ||
bold(tick(k)), | ||
function() | ||
if v.enum then | ||
return tick('enum ' .. inspect(v.enum)) | ||
end | ||
if v.type then | ||
return tick(table.concat(tbl_flatten { v.type }, '|')) | ||
end | ||
end, | ||
}), | ||
'', | ||
make_section(2, '\n\n', { | ||
{ v.default and 'Default: ' .. tick(inspect(v.default, { newline = '', indent = '' })) }, | ||
{ v.items and 'Array items: ' .. tick(inspect(v.items, { newline = '', indent = '' })) }, | ||
{ excape_markdown_punctuations(v.description) }, | ||
}), | ||
}) | ||
end) | ||
), | ||
'', | ||
'</details>', | ||
}) | ||
end, | ||
}) | ||
end | ||
end, | ||
} | ||
if not os.getenv 'DOCGEN_TEMPDIR' then | ||
os.execute('rm -rf ' .. tempdir) | ||
end | ||
-- Insert a newline after the preamble if it exists. | ||
if #preamble_parts > 0 then | ||
table.insert(preamble_parts, '') | ||
end | ||
params.preamble = table.concat(preamble_parts, '\n') | ||
end | ||
|
||
return template(lsp_section_template, params) | ||
end) | ||
) | ||
end | ||
|
||
local function make_implemented_servers_list() | ||
return make_section( | ||
0, | ||
'\n', | ||
sorted_map_table(configs, function(k) | ||
return template('- [{{server}}](#{{server}})', { server = k }) | ||
end) | ||
) | ||
end | ||
|
||
local function generate_readme(template_file, params) | ||
vim.validate { | ||
lsp_server_details = { params.lsp_server_details, 's' }, | ||
implemented_servers_list = { params.implemented_servers_list, 's' }, | ||
} | ||
local input_template = readfile(template_file) | ||
local readme_data = template(input_template, params) | ||
|
||
local writer = io.open('doc/server_configurations.md', 'w') | ||
writer:write(readme_data) | ||
writer:close() | ||
uv.fs_copyfile('doc/server_configurations.md', 'doc/server_configurations.txt') | ||
end | ||
|
||
require_all_configs() | ||
generate_readme('scripts/README_template.md', { | ||
implemented_servers_list = make_implemented_servers_list(), | ||
lsp_server_details = make_lsp_sections(), | ||
}) | ||
local preamble_parts = make_parts { | ||
function() | ||
if docs.description and #docs.description > 0 then | ||
return docs.description | ||
end | ||
end, | ||
function() | ||
local package_json_name = util.path.join(tempdir, template_name .. '.package.json') | ||
if docs.package_json then | ||
if not util.path.is_file(package_json_name) then | ||
os.execute(string.format('curl -v -L -o %q %q', package_json_name, docs.package_json)) | ||
end | ||
if not util.path.is_file(package_json_name) then | ||
print(string.format('Failed to download package.json for %q at %q', template_name, docs.package_json)) | ||
os.exit(1) | ||
return | ||
end | ||
local data = fn.json_decode(readfile(package_json_name)) | ||
-- The entire autogenerated section. | ||
return make_section(0, '\n', { | ||
-- The default settings section | ||
function() | ||
local default_settings = (data.contributes or {}).configuration | ||
if not default_settings.properties then | ||
return | ||
end | ||
-- The outer section. | ||
return make_section(0, '\n', { | ||
'This server accepts configuration via the `settings` key.', | ||
'<details><summary>Available settings:</summary>', | ||
'', | ||
-- The list of properties. | ||
make_section( | ||
0, | ||
'\n\n', | ||
sorted_map_table(default_settings.properties, function(k, v) | ||
local function tick(s) | ||
return string.format('`%s`', s) | ||
end | ||
local function bold(s) | ||
return string.format('**%s**', s) | ||
end | ||
|
||
-- https://github.github.com/gfm/#backslash-escapes | ||
local function excape_markdown_punctuations(str) | ||
local pattern = | ||
'\\(\\*\\|\\.\\|?\\|!\\|"\\|#\\|\\$\\|%\\|\'\\|(\\|)\\|,\\|-\\|\\/\\|:\\|;\\|<\\|=\\|>\\|@\\|\\[\\|\\\\\\|\\]\\|\\^\\|_\\|`\\|{\\|\\\\|\\|}\\)' | ||
return fn.substitute(str, pattern, '\\\\\\0', 'g') | ||
end | ||
|
||
-- local function pre(s) return string.format("<pre>%s</pre>", s) end | ||
-- local function code(s) return string.format("<code>%s</code>", s) end | ||
if not (type(v) == 'table') then | ||
return | ||
end | ||
return make_section(0, '\n', { | ||
'- ' .. make_section(0, ': ', { | ||
bold(tick(k)), | ||
function() | ||
if v.enum then | ||
return tick('enum ' .. inspect(v.enum)) | ||
end | ||
if v.type then | ||
return tick(table.concat(tbl_flatten { v.type }, '|')) | ||
end | ||
end, | ||
}), | ||
'', | ||
make_section(2, '\n\n', { | ||
{ v.default and 'Default: ' .. tick(inspect(v.default, { newline = '', indent = '' })) }, | ||
{ v.items and 'Array items: ' .. tick(inspect(v.items, { newline = '', indent = '' })) }, | ||
{ excape_markdown_punctuations(v.description) }, | ||
}), | ||
}) | ||
end) | ||
), | ||
'', | ||
'</details>', | ||
}) | ||
end, | ||
}) | ||
end | ||
end, | ||
} |