mirror of
https://github.com/neovim/neovim.git
synced 2026-04-20 22:35:33 +00:00
refactor(help): move local-additions to Lua #37831
Problem: - ~200 line function of hard-to-maintain C code. - Local Addition section looks messy because of the varying description formats. Solution: - Move code to Lua. - Have a best-effort approach where short descriptions are right aligned, giving a cleaner look. Long descriptions are untouched.
This commit is contained in:
committed by
GitHub
parent
29c81ba27e
commit
b5ce7e74dc
@@ -143,7 +143,7 @@ Standard plugins ~
|
||||
See |standard-plugin-list|.
|
||||
|
||||
Local additions ~
|
||||
*local-additions*
|
||||
*local-additions*
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Bars example *bars*
|
||||
|
||||
@@ -122,10 +122,77 @@ function M.escape_subject(word)
|
||||
-- E.g. '`command`,' --> 'command' (backticks are removed too, but '``' stays '``')
|
||||
word = word:gsub([[^'([^']*)'.*]], [['%1']])
|
||||
word = word:gsub([[^{([^}]*)}.*]], '{%1}')
|
||||
word = word:gsub([[^`([^`]+)`.*]], '%1')
|
||||
word = word:gsub([[.*`([^`]+)`.*]], '%1')
|
||||
end
|
||||
|
||||
return word
|
||||
end
|
||||
|
||||
---Populates the |local-additions| section of a help buffer with references to locally-installed
|
||||
---help files. These are help files outside of $VIMRUNTIME (typically from plugins) whose first
|
||||
---line contains a tag (e.g. *plugin-name.txt*) and a short description.
|
||||
---
|
||||
---For each help file found in 'runtimepath', the first line is extracted and added to the buffer
|
||||
---as a reference (converting '*tag*' to '|tag|'). If a translated version of a help file exists
|
||||
---in the same language as the current buffer (e.g. 'plugin.nlx' alongside 'plugin.txt'), the
|
||||
---translated version is preferred over the '.txt' file.
|
||||
function M.local_additions()
|
||||
local buf = vim.api.nvim_get_current_buf()
|
||||
local bufname = vim.fs.basename(vim.api.nvim_buf_get_name(buf))
|
||||
|
||||
-- "help.txt" or "help.??x" where ?? is a language code, see |help-translated|.
|
||||
local lang = bufname:match('^help%.(%a%a)x$')
|
||||
if bufname ~= 'help.txt' and not lang then
|
||||
return
|
||||
end
|
||||
|
||||
-- Find local help files
|
||||
---@type table<string, string>
|
||||
local plugins = {}
|
||||
local pattern = lang and ('doc/*.{txt,%sx}'):format(lang) or 'doc/*.txt'
|
||||
for _, docpath in ipairs(vim.api.nvim_get_runtime_file(pattern, true)) do
|
||||
if not vim.fs.relpath(vim.env.VIMRUNTIME, docpath) then
|
||||
-- '/path/to/doc/plugin.txt' --> 'plugin'
|
||||
local plugname = vim.fs.basename(docpath):sub(1, -5)
|
||||
-- prefer language-specific files over .txt
|
||||
if not plugins[plugname] or vim.endswith(plugins[plugname], '.txt') then
|
||||
plugins[plugname] = docpath
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Format plugin list lines
|
||||
-- Default to 78 if 'textwidth' is not set (e.g. in sandbox)
|
||||
local textwidth = math.max(vim.bo[buf].textwidth, 78)
|
||||
local lines = {}
|
||||
for _, path in vim.spairs(plugins) do
|
||||
local fp = io.open(path, 'r')
|
||||
if fp then
|
||||
local tagline = fp:read('*l') or ''
|
||||
fp:close()
|
||||
---@type string, string
|
||||
local plugname, desc = tagline:match('^%*([^*]+)%*%s*(.*)$')
|
||||
if plugname and desc then
|
||||
-- left-align taglink and right-align description by inserting spaces in between
|
||||
local plug_width = vim.fn.strdisplaywidth(plugname)
|
||||
local desc_width = vim.fn.strdisplaywidth(desc)
|
||||
-- max(l, 1) forces at least one space for if the description is too long
|
||||
local spaces = string.rep(' ', math.max(textwidth - desc_width - plug_width - 2, 1))
|
||||
local fmt = string.format('|%s|%s%s', plugname, spaces, desc)
|
||||
table.insert(lines, fmt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add plugin list to local-additions section
|
||||
for linenr, line in ipairs(vim.api.nvim_buf_get_lines(buf, 0, -1, false)) do
|
||||
if line:find('*local-additions*', 1, true) then
|
||||
vim._with({ buf = buf, bo = { modifiable = true, readonly = false } }, function()
|
||||
vim.api.nvim_buf_set_lines(buf, linenr, linenr, true, lines)
|
||||
end)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
166
src/nvim/help.c
166
src/nvim/help.c
@@ -444,167 +444,17 @@ void prepare_help_buffer(void)
|
||||
set_buflisted(false);
|
||||
}
|
||||
|
||||
/// After reading a help file: if help.txt, populate *local-additions*
|
||||
/// Populate *local-additions* in help.txt
|
||||
void get_local_additions(void)
|
||||
{
|
||||
// In the "help.txt" and "help.abx" file, add the locally added help
|
||||
// files. This uses the very first line in the help file.
|
||||
char *const fname = path_tail(curbuf->b_fname);
|
||||
if (path_fnamecmp(fname, "help.txt") == 0
|
||||
|| (path_fnamencmp(fname, "help.", 5) == 0
|
||||
&& ASCII_ISALPHA(fname[5])
|
||||
&& ASCII_ISALPHA(fname[6])
|
||||
&& TOLOWER_ASC(fname[7]) == 'x'
|
||||
&& fname[8] == NUL)) {
|
||||
for (linenr_T lnum = 1; lnum < curbuf->b_ml.ml_line_count; lnum++) {
|
||||
char *line = ml_get_buf(curbuf, lnum);
|
||||
if (strstr(line, "*local-additions*") == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int lnum_start = lnum;
|
||||
|
||||
// Go through all directories in 'runtimepath', skipping
|
||||
// $VIMRUNTIME.
|
||||
char *p = p_rtp;
|
||||
while (*p != NUL) {
|
||||
copy_option_part(&p, NameBuff, MAXPATHL, ",");
|
||||
char *const rt = vim_getenv("VIMRUNTIME");
|
||||
if (rt != NULL
|
||||
&& path_full_compare(rt, NameBuff, false, true) != kEqualFiles) {
|
||||
int fcount;
|
||||
char **fnames;
|
||||
vimconv_T vc;
|
||||
|
||||
// Find all "doc/ *.txt" files in this directory.
|
||||
if (!add_pathsep(NameBuff)
|
||||
|| xstrlcat(NameBuff, "doc/*.??[tx]", // NOLINT
|
||||
sizeof(NameBuff)) >= MAXPATHL) {
|
||||
emsg(_(e_fnametoolong));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note: We cannot just do `&NameBuff` because it is a statically sized array
|
||||
// so `NameBuff == &NameBuff` according to C semantics.
|
||||
char *buff_list[1] = { NameBuff };
|
||||
if (gen_expand_wildcards(1, buff_list, &fcount,
|
||||
&fnames, EW_FILE|EW_SILENT) == OK
|
||||
&& fcount > 0) {
|
||||
char *s;
|
||||
char *cp;
|
||||
// If foo.abx is found use it instead of foo.txt in
|
||||
// the same directory.
|
||||
for (int i1 = 0; i1 < fcount; i1++) {
|
||||
const char *const f1 = fnames[i1];
|
||||
const char *const t1 = path_tail(f1);
|
||||
const char *const e1 = strrchr(t1, '.');
|
||||
if (e1 == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (path_fnamecmp(e1, ".txt") != 0
|
||||
&& path_fnamecmp(e1, fname + 4) != 0) {
|
||||
// Not .txt and not .abx, remove it.
|
||||
XFREE_CLEAR(fnames[i1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i2 = i1 + 1; i2 < fcount; i2++) {
|
||||
const char *const f2 = fnames[i2];
|
||||
if (f2 == NULL) {
|
||||
continue;
|
||||
}
|
||||
const char *const t2 = path_tail(f2);
|
||||
const char *const e2 = strrchr(t2, '.');
|
||||
if (e2 == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (e1 - f1 != e2 - f2
|
||||
|| path_fnamencmp(f1, f2, (size_t)(e1 - f1)) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (path_fnamecmp(e1, ".txt") == 0
|
||||
&& path_fnamecmp(e2, fname + 4) == 0) {
|
||||
// use .abx instead of .txt
|
||||
XFREE_CLEAR(fnames[i1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int fi = 0; fi < fcount; fi++) {
|
||||
if (fnames[fi] == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FILE *const fd = os_fopen(fnames[fi], "r");
|
||||
if (fd == NULL) {
|
||||
continue;
|
||||
}
|
||||
vim_fgets(IObuff, IOSIZE, fd);
|
||||
if (IObuff[0] == '*'
|
||||
&& (s = vim_strchr(IObuff + 1, '*'))
|
||||
!= NULL) {
|
||||
TriState this_utf = kNone;
|
||||
// Change tag definition to a
|
||||
// reference and remove <CR>/<NL>.
|
||||
IObuff[0] = '|';
|
||||
*s = '|';
|
||||
while (*s != NUL) {
|
||||
if (*s == '\r' || *s == '\n') {
|
||||
*s = NUL;
|
||||
}
|
||||
// The text is utf-8 when a byte
|
||||
// above 127 is found and no
|
||||
// illegal byte sequence is found.
|
||||
if ((uint8_t)(*s) >= 0x80 && this_utf != kFalse) {
|
||||
this_utf = kTrue;
|
||||
const int l = utf_ptr2len(s);
|
||||
if (l == 1) {
|
||||
this_utf = kFalse;
|
||||
}
|
||||
s += l - 1;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
// The help file is latin1 or utf-8;
|
||||
// conversion to the current
|
||||
// 'encoding' may be required.
|
||||
vc.vc_type = CONV_NONE;
|
||||
convert_setup(&vc,
|
||||
(this_utf == kTrue ? "utf-8" : "latin1"),
|
||||
p_enc);
|
||||
if (vc.vc_type == CONV_NONE) {
|
||||
// No conversion needed.
|
||||
cp = IObuff;
|
||||
} else {
|
||||
// Do the conversion. If it fails
|
||||
// use the unconverted text.
|
||||
cp = string_convert(&vc, IObuff, NULL);
|
||||
if (cp == NULL) {
|
||||
cp = IObuff;
|
||||
}
|
||||
}
|
||||
convert_setup(&vc, NULL, NULL);
|
||||
|
||||
ml_append(lnum, cp, 0, false);
|
||||
if (cp != IObuff) {
|
||||
xfree(cp);
|
||||
}
|
||||
lnum++;
|
||||
}
|
||||
fclose(fd);
|
||||
}
|
||||
FreeWild(fcount, fnames);
|
||||
}
|
||||
}
|
||||
xfree(rt);
|
||||
}
|
||||
linenr_T appended = lnum - lnum_start;
|
||||
if (appended) {
|
||||
mark_adjust(lnum_start + 1, (linenr_T)MAXLNUM, appended, 0, kExtmarkUndo);
|
||||
changed_lines_redraw_buf(curbuf, lnum_start + 1, lnum_start + 1, appended);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Error err = ERROR_INIT;
|
||||
Object res = NLUA_EXEC_STATIC("return require'vim._core.help'.local_additions()",
|
||||
(Array)ARRAY_DICT_INIT, kRetNilBool, NULL, &err);
|
||||
if (ERROR_SET(&err)) {
|
||||
emsg_multiline(err.msg, "lua_error", HLF_E, true);
|
||||
}
|
||||
api_free_object(res);
|
||||
api_clear_error(&err);
|
||||
}
|
||||
|
||||
/// ":exusage"
|
||||
|
||||
@@ -107,8 +107,8 @@ func Test_help_local_additions()
|
||||
help local-additions
|
||||
let lines = getline(line(".") + 1, search("^$") - 1)
|
||||
call assert_equal([
|
||||
\ '|mydoc-ext.txt| my extended awesome doc',
|
||||
\ '|mydoc.txt| my awesome doc'
|
||||
\ '|mydoc.txt| my awesome doc',
|
||||
\ '|mydoc-ext.txt| my extended awesome doc'
|
||||
\ ], lines)
|
||||
call delete('Xruntime/doc/mydoc-ext.txt')
|
||||
close
|
||||
@@ -124,17 +124,17 @@ func Test_help_local_additions()
|
||||
help local-additions@en
|
||||
let lines = getline(line(".") + 1, search("^$") - 1)
|
||||
call assert_equal([
|
||||
\ '|mydoc.txt| my awesome doc'
|
||||
\ '|mydoc.txt| my awesome doc'
|
||||
\ ], lines)
|
||||
close
|
||||
|
||||
help local-additions@ja
|
||||
let lines = getline(line(".") + 1, search("^$") - 1)
|
||||
call assert_equal([
|
||||
\ '|mydoc.txt| my awesome doc',
|
||||
\ '|help.txt| This is jax file',
|
||||
\ '|work.txt| This is jax file',
|
||||
\ '|work2.txt| This is jax file',
|
||||
\ '|help.txt| This is jax file',
|
||||
\ '|mydoc.txt| my awesome doc',
|
||||
\ '|work.txt| This is jax file',
|
||||
\ '|work2.txt| This is jax file',
|
||||
\ ], lines)
|
||||
close
|
||||
|
||||
|
||||
Reference in New Issue
Block a user