Merge #5210 'vim-patch:7.4.1898 + :Man modifiers support'.

This commit is contained in:
Justin M. Keyes 2016-08-25 21:54:00 -04:00
commit 7bc627b3c8
9 changed files with 244 additions and 66 deletions

View File

@ -1,12 +1,9 @@
" Maintainer: Anmol Sethi <anmol@aubble.com>
" Ensure Vim is not recursively invoked (man-db does this)
" by forcing man to use cat as the pager.
" More info here http://comments.gmane.org/gmane.editors.vim.devel/29085
if &shell =~# 'fish$'
let s:man_cmd = 'man -P cat ^/dev/null'
let s:man_cmd = 'man ^/dev/null'
else
let s:man_cmd = 'man -P cat 2>/dev/null'
let s:man_cmd = 'man 2>/dev/null'
endif
let s:man_find_arg = "-w"
@ -26,7 +23,7 @@ endtry
" by the user. count defaults to 0 which is a valid section and
" count1 defaults to 1 which is also a valid section. Only when they
" are equal was the count explicitly set.
function! man#open_page(count, count1, ...) abort
function! man#open_page(count, count1, mods, ...) abort
if a:0 > 2
call s:error('too many arguments')
return
@ -49,48 +46,52 @@ function! man#open_page(count, count1, ...) abort
" user explicitly set a count
let sect = string(a:count)
endif
let [sect, name] = s:verify_exists(sect, name)
let [sect, name, path] = s:verify_exists(sect, name)
catch
call s:error(v:exception)
return
endtry
call s:push_tag()
let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')')
let found_man = s:find_man()
if getbufvar(bufname, 'manwidth') ==# s:manwidth()
if found_man
silent execute 'buf' bufnr(bufname)
else
execute 'split' bufname
if a:mods !~# 'tab' && s:find_man()
if s:manwidth() ==# getbufvar(bufname, 'manwidth')
silent execute 'buf' bufname
call man#set_window_local_options()
keepjumps 1
return
endif
noautocmd execute 'edit' bufname
elseif s:manwidth() ==# getbufvar(bufname, 'manwidth')
execute a:mods 'split' bufname
call man#set_window_local_options()
keepjumps 1
return
endif
if found_man
noautocmd execute 'edit' bufname
else
noautocmd execute 'split' bufname
noautocmd execute a:mods 'split' bufname
endif
call s:read_page(sect, name)
call s:read_page(path)
endfunction
function! man#read_page(ref) abort
try
let [sect, name] = s:extract_sect_and_name_ref(a:ref)
let [sect, name] = s:verify_exists(sect, name)
" The third element is the path.
call s:read_page(s:verify_exists(sect, name)[2])
catch
call s:error(v:exception)
return
endtry
call s:read_page(sect, name)
endfunction
function! s:read_page(sect, name) abort
function! s:read_page(path) abort
setlocal modifiable
setlocal noreadonly
keepjumps %delete _
let b:manwidth = s:manwidth()
silent execute 'read!env MANWIDTH='.b:manwidth s:man_cmd s:man_args(a:sect, a:name)
" Ensure Vim is not recursively invoked (man-db does this)
" by forcing man to use cat as the pager.
" More info here http://comments.gmane.org/gmane.editors.vim.devel/29085
silent execute 'read !env MANPAGER=cat MANWIDTH='.b:manwidth s:man_cmd a:path
" remove all the backspaced text
silent execute 'keeppatterns keepjumps %substitute,.\b,,e'.(&gdefault?'':'g')
while getline(1) =~# '^\s*$'
@ -134,7 +135,7 @@ function! s:verify_exists(sect, name) abort
if a:name =~# '\/'
" We do not need to extract the section/name from the path if the name is
" just a path.
return ['', a:name]
return ['', a:name, path]
endif
" We need to extract the section from the path because sometimes
" the actual section of the manpage is more specific than the section
@ -142,7 +143,8 @@ function! s:verify_exists(sect, name) abort
" Also on linux, it seems that the name is case insensitive. So if one does
" ':Man PRIntf', we still want the name of the buffer to be 'printf' or
" whatever the correct capitilization is.
return s:extract_sect_and_name_path(path[:len(path)-2])
let path = path[:len(path)-2]
return s:extract_sect_and_name_path(path) + [path]
endfunction
let s:tag_stack = []
@ -203,6 +205,15 @@ function! s:manwidth() abort
return $MANWIDTH
endfunction
function! man#set_window_local_options() abort
setlocal nonumber
setlocal norelativenumber
setlocal foldcolumn=0
setlocal colorcolumn=0
setlocal nolist
setlocal nofoldenable
endfunction
function! s:man_args(sect, name) abort
if empty(a:sect)
return shellescape(a:name)
@ -271,11 +282,11 @@ function! man#complete(arg_lead, cmd_line, cursor_pos) abort
return uniq(sort(map(globpath(s:mandirs,'man?/'.name.'*.'.sect.'*', 0, 1), 's:format_candidate(v:val, sect)'), 'i'))
endfunction
function! s:format_candidate(c, sect) abort
if a:c =~# '\.\%(pdf\|in\)$' " invalid extensions
function! s:format_candidate(path, sect) abort
if a:path =~# '\.\%(pdf\|in\)$' " invalid extensions
return
endif
let [sect, name] = s:extract_sect_and_name_path(a:c)
let [sect, name] = s:extract_sect_and_name_path(a:path)
if sect ==# a:sect
return name
elseif sect =~# a:sect.'[^.]\+$'

View File

@ -1406,6 +1406,27 @@ The valid escape sequences are
<bang> (See the '-bang' attribute) Expands to a ! if the
command was executed with a ! modifier, otherwise
expands to nothing.
*<mods>*
<mods> The command modifiers, if specified. Otherwise, expands to
nothing. Supported modifiers are |aboveleft|, |belowright|,
|botright|, |browse|, |confirm|, |hide|, |keepalt|,
|keepjumps|, |keepmarks|, |keeppatterns|, |lockmarks|,
|noswapfile|, |silent|, |tab|, |topleft|, |verbose|, and
|vertical|.
Examples: >
command! -nargs=+ -complete=file MyEdit
\ for f in expand(<q-args>, 0, 1) |
\ exe '<mods> split ' . f |
\ endfor
function! SpecialEdit(files, mods)
for f in expand(a:files, 0, 1)
exe a:mods . ' split ' . f
endfor
endfunction
command! -nargs=+ -complete=file Sedit
\ call SpecialEdit(<q-args>, <q-mods>)
<
*<reg>* *<register>*
<reg> (See the '-register' attribute) The optional register,
if specified. Otherwise, expands to nothing. <register>

View File

@ -33,12 +33,7 @@ setlocal tabstop=8
setlocal softtabstop=8
setlocal shiftwidth=8
setlocal nonumber
setlocal norelativenumber
setlocal foldcolumn=0
setlocal colorcolumn=0
setlocal nolist
setlocal nofoldenable
call man#set_window_local_options()
if !exists('g:no_plugin_maps') && !exists('g:no_man_maps')
nmap <silent> <buffer> <C-]> <Plug>(Man)

View File

@ -5,9 +5,9 @@ if exists('g:loaded_man')
endif
let g:loaded_man = 1
command! -range=0 -complete=customlist,man#complete -nargs=+ Man call man#open_page(v:count, v:count1, <f-args>)
command! -range=0 -complete=customlist,man#complete -nargs=+ Man call man#open_page(v:count, v:count1, <q-mods>, <f-args>)
nnoremap <silent> <Plug>(Man) :<C-U>call man#open_page(v:count, v:count1, &filetype ==# 'man' ? expand('<cWORD>') : expand('<cword>'))<CR>
nnoremap <silent> <Plug>(Man) :<C-U>call man#open_page(v:count, v:count1, '', &filetype ==# 'man' ? expand('<cWORD>') : expand('<cword>'))<CR>
augroup man
autocmd!

View File

@ -164,18 +164,18 @@ typedef struct expand {
/// flag. This needs to be saved for recursive commands, put them in a
/// structure for easy manipulation.
typedef struct {
int hide; ///< TRUE when ":hide" was used
int split; ///< flags for win_split()
int tab; ///< > 0 when ":tab" was used
int confirm; ///< TRUE to invoke yes/no dialog
int keepalt; ///< TRUE when ":keepalt" was used
int keepmarks; ///< TRUE when ":keepmarks" was used
int keepjumps; ///< TRUE when ":keepjumps" was used
int lockmarks; ///< TRUE when ":lockmarks" was used
int keeppatterns; ///< TRUE when ":keeppatterns" was used
bool noswapfile; ///< true when ":noswapfile" was used
bool browse; ///< TRUE to invoke file dialog
char_u *save_ei; ///< saved value of 'eventignore'
int split; ///< flags for win_split()
int tab; ///< > 0 when ":tab" was used
bool browse; ///< true to invoke file dialog
bool confirm; ///< true to invoke yes/no dialog
bool hide; ///< true when ":hide" was used
bool keepalt; ///< true when ":keepalt" was used
bool keepjumps; ///< true when ":keepjumps" was used
bool keepmarks; ///< true when ":keepmarks" was used
bool keeppatterns; ///< true when ":keeppatterns" was used
bool lockmarks; ///< true when ":lockmarks" was used
bool noswapfile; ///< true when ":noswapfile" was used
char_u *save_ei; ///< saved value of 'eventignore'
} cmdmod_T;
#endif // NVIM_EX_CMDS_DEFS_H

View File

@ -1327,24 +1327,24 @@ static char_u * do_one_cmd(char_u **cmdlinep,
case 'c': if (!checkforcmd(&ea.cmd, "confirm", 4))
break;
cmdmod.confirm = TRUE;
cmdmod.confirm = true;
continue;
case 'k': if (checkforcmd(&ea.cmd, "keepmarks", 3)) {
cmdmod.keepmarks = TRUE;
cmdmod.keepmarks = true;
continue;
}
if (checkforcmd(&ea.cmd, "keepalt", 5)) {
cmdmod.keepalt = TRUE;
cmdmod.keepalt = true;
continue;
}
if (checkforcmd(&ea.cmd, "keeppatterns", 5)) {
cmdmod.keeppatterns = TRUE;
cmdmod.keeppatterns = true;
continue;
}
if (!checkforcmd(&ea.cmd, "keepjumps", 5))
break;
cmdmod.keepjumps = TRUE;
cmdmod.keepjumps = true;
continue;
/* ":hide" and ":hide | cmd" are not modifiers */
@ -1352,11 +1352,11 @@ static char_u * do_one_cmd(char_u **cmdlinep,
|| *p == NUL || ends_excmd(*p))
break;
ea.cmd = p;
cmdmod.hide = TRUE;
cmdmod.hide = true;
continue;
case 'l': if (checkforcmd(&ea.cmd, "lockmarks", 3)) {
cmdmod.lockmarks = TRUE;
cmdmod.lockmarks = true;
continue;
}
@ -5156,6 +5156,24 @@ static char_u *uc_split_args(char_u *arg, size_t *lenp)
return buf;
}
static size_t add_cmd_modifier(char_u *buf, char *mod_str, bool *multi_mods)
{
size_t result = STRLEN(mod_str);
if (*multi_mods) {
result++;
}
if (buf != NULL) {
if (*multi_mods) {
STRCAT(buf, " ");
}
STRCAT(buf, mod_str);
}
*multi_mods = true;
return result;
}
/*
* Check for a <> code in a user command.
* "code" points to the '<'. "len" the length of the <> (inclusive).
@ -5180,8 +5198,8 @@ uc_check_code (
char_u *p = code + 1;
size_t l = len - 2;
int quote = 0;
enum { ct_ARGS, ct_BANG, ct_COUNT, ct_LINE1, ct_LINE2, ct_REGISTER,
ct_LT, ct_NONE } type = ct_NONE;
enum { ct_ARGS, ct_BANG, ct_COUNT, ct_LINE1, ct_LINE2, ct_MODS,
ct_REGISTER, ct_LT, ct_NONE } type = ct_NONE;
if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') {
quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
@ -5189,23 +5207,26 @@ uc_check_code (
l -= 2;
}
++l;
if (l <= 1)
l++;
if (l <= 1) {
type = ct_NONE;
else if (STRNICMP(p, "args>", l) == 0)
} else if (STRNICMP(p, "args>", l) == 0) {
type = ct_ARGS;
else if (STRNICMP(p, "bang>", l) == 0)
} else if (STRNICMP(p, "bang>", l) == 0) {
type = ct_BANG;
else if (STRNICMP(p, "count>", l) == 0)
} else if (STRNICMP(p, "count>", l) == 0) {
type = ct_COUNT;
else if (STRNICMP(p, "line1>", l) == 0)
} else if (STRNICMP(p, "line1>", l) == 0) {
type = ct_LINE1;
else if (STRNICMP(p, "line2>", l) == 0)
} else if (STRNICMP(p, "line2>", l) == 0) {
type = ct_LINE2;
else if (STRNICMP(p, "lt>", l) == 0)
} else if (STRNICMP(p, "lt>", l) == 0) {
type = ct_LT;
else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0)
} else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0) {
type = ct_REGISTER;
} else if (STRNICMP(p, "mods>", l) == 0) {
type = ct_MODS;
}
switch (type) {
case ct_ARGS:
@ -5313,6 +5334,87 @@ uc_check_code (
break;
}
case ct_MODS:
{
result = quote ? 2 : 0;
if (buf != NULL) {
if (quote) {
*buf++ = '"';
}
*buf = '\0';
}
bool multi_mods = false;
// :aboveleft and :leftabove
if (cmdmod.split & WSP_ABOVE) {
result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
}
// :belowright and :rightbelow
if (cmdmod.split & WSP_BELOW) {
result += add_cmd_modifier(buf, "belowright", &multi_mods);
}
// :botright
if (cmdmod.split & WSP_BOT) {
result += add_cmd_modifier(buf, "botright", &multi_mods);
}
typedef struct {
bool *set;
char *name;
} mod_entry_T;
static mod_entry_T mod_entries[] = {
{ &cmdmod.browse, "browse" },
{ &cmdmod.confirm, "confirm" },
{ &cmdmod.hide, "hide" },
{ &cmdmod.keepalt, "keepalt" },
{ &cmdmod.keepjumps, "keepjumps" },
{ &cmdmod.keepmarks, "keepmarks" },
{ &cmdmod.keeppatterns, "keeppatterns" },
{ &cmdmod.lockmarks, "lockmarks" },
{ &cmdmod.noswapfile, "noswapfile" }
};
// the modifiers that are simple flags
for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
if (*mod_entries[i].set) {
result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
}
}
// TODO(vim): How to support :noautocmd?
// TODO(vim): How to support :sandbox?
// :silent
if (msg_silent > 0) {
result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent",
&multi_mods);
}
// :tab
if (cmdmod.tab > 0) {
result += add_cmd_modifier(buf, "tab", &multi_mods);
}
// :topleft
if (cmdmod.split & WSP_TOP) {
result += add_cmd_modifier(buf, "topleft", &multi_mods);
}
// TODO(vim): How to support :unsilent?
// :verbose
if (p_verbose > 0) {
result += add_cmd_modifier(buf, "verbose", &multi_mods);
}
// :vertical
if (cmdmod.split & WSP_VERT) {
result += add_cmd_modifier(buf, "vertical", &multi_mods);
}
if (quote && buf != NULL) {
buf += result - 2;
*buf = '"';
}
break;
}
case ct_REGISTER:
result = eap->regname ? 1 : 0;
if (quote)

View File

@ -36,6 +36,7 @@ NEW_TESTS = \
test_help_tagjump.res \
test_langmap.res \
test_syntax.res \
test_usercommands.res \
test_timers.res \
test_viml.res \
test_visual.res \

View File

@ -0,0 +1,48 @@
" Tests for user defined commands
" Test for <mods> in user defined commands
function Test_cmdmods()
let g:mods = ''
command! -nargs=* MyCmd let g:mods .= '<mods> '
MyCmd
aboveleft MyCmd
belowright MyCmd
botright MyCmd
browse MyCmd
confirm MyCmd
hide MyCmd
keepalt MyCmd
keepjumps MyCmd
keepmarks MyCmd
keeppatterns MyCmd
lockmarks MyCmd
noswapfile MyCmd
silent MyCmd
tab MyCmd
topleft MyCmd
verbose MyCmd
vertical MyCmd
aboveleft belowright botright browse confirm hide keepalt keepjumps
\ keepmarks keeppatterns lockmarks noswapfile silent tab
\ topleft verbose vertical MyCmd
call assert_equal(' aboveleft belowright botright browse confirm ' .
\ 'hide keepalt keepjumps keepmarks keeppatterns lockmarks ' .
\ 'noswapfile silent tab topleft verbose vertical aboveleft ' .
\ 'belowright botright browse confirm hide keepalt keepjumps ' .
\ 'keepmarks keeppatterns lockmarks noswapfile silent tab topleft ' .
\ 'verbose vertical ', g:mods)
let g:mods = ''
command! -nargs=* MyQCmd let g:mods .= '<q-mods> '
vertical MyQCmd
call assert_equal('"vertical" ', g:mods)
delcommand MyCmd
delcommand MyQCmd
unlet g:mods
endfunction

View File

@ -378,7 +378,7 @@ static int included_patches[] = {
// 1901 NA
// 1900,
// 1899 NA
// 1898,
1898,
// 1897,
// 1896,
// 1895,