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
9 changed files with 244 additions and 66 deletions

View File

@@ -1,12 +1,9 @@
" Maintainer: Anmol Sethi <anmol@aubble.com> " 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$' if &shell =~# 'fish$'
let s:man_cmd = 'man -P cat ^/dev/null' let s:man_cmd = 'man ^/dev/null'
else else
let s:man_cmd = 'man -P cat 2>/dev/null' let s:man_cmd = 'man 2>/dev/null'
endif endif
let s:man_find_arg = "-w" let s:man_find_arg = "-w"
@@ -26,7 +23,7 @@ endtry
" by the user. count defaults to 0 which is a valid section and " 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 " count1 defaults to 1 which is also a valid section. Only when they
" are equal was the count explicitly set. " 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 if a:0 > 2
call s:error('too many arguments') call s:error('too many arguments')
return return
@@ -49,48 +46,52 @@ function! man#open_page(count, count1, ...) abort
" user explicitly set a count " user explicitly set a count
let sect = string(a:count) let sect = string(a:count)
endif endif
let [sect, name] = s:verify_exists(sect, name) let [sect, name, path] = s:verify_exists(sect, name)
catch catch
call s:error(v:exception) call s:error(v:exception)
return return
endtry endtry
call s:push_tag() call s:push_tag()
let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')') let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')')
let found_man = s:find_man() if a:mods !~# 'tab' && s:find_man()
if getbufvar(bufname, 'manwidth') ==# s:manwidth() if s:manwidth() ==# getbufvar(bufname, 'manwidth')
if found_man silent execute 'buf' bufname
silent execute 'buf' bufnr(bufname) call man#set_window_local_options()
else
execute 'split' bufname
endif
keepjumps 1 keepjumps 1
return return
endif endif
if found_man
noautocmd execute 'edit' bufname noautocmd execute 'edit' bufname
elseif s:manwidth() ==# getbufvar(bufname, 'manwidth')
execute a:mods 'split' bufname
call man#set_window_local_options()
keepjumps 1
return
else else
noautocmd execute 'split' bufname noautocmd execute a:mods 'split' bufname
endif endif
call s:read_page(sect, name) call s:read_page(path)
endfunction endfunction
function! man#read_page(ref) abort function! man#read_page(ref) abort
try try
let [sect, name] = s:extract_sect_and_name_ref(a:ref) 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 catch
call s:error(v:exception) call s:error(v:exception)
return return
endtry endtry
call s:read_page(sect, name)
endfunction endfunction
function! s:read_page(sect, name) abort function! s:read_page(path) abort
setlocal modifiable setlocal modifiable
setlocal noreadonly setlocal noreadonly
keepjumps %delete _ keepjumps %delete _
let b:manwidth = s:manwidth() 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 " remove all the backspaced text
silent execute 'keeppatterns keepjumps %substitute,.\b,,e'.(&gdefault?'':'g') silent execute 'keeppatterns keepjumps %substitute,.\b,,e'.(&gdefault?'':'g')
while getline(1) =~# '^\s*$' while getline(1) =~# '^\s*$'
@@ -134,7 +135,7 @@ function! s:verify_exists(sect, name) abort
if a:name =~# '\/' if a:name =~# '\/'
" We do not need to extract the section/name from the path if the name is " We do not need to extract the section/name from the path if the name is
" just a path. " just a path.
return ['', a:name] return ['', a:name, path]
endif endif
" We need to extract the section from the path because sometimes " We need to extract the section from the path because sometimes
" the actual section of the manpage is more specific than the section " 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 " 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 " ':Man PRIntf', we still want the name of the buffer to be 'printf' or
" whatever the correct capitilization is. " 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 endfunction
let s:tag_stack = [] let s:tag_stack = []
@@ -203,6 +205,15 @@ function! s:manwidth() abort
return $MANWIDTH return $MANWIDTH
endfunction 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 function! s:man_args(sect, name) abort
if empty(a:sect) if empty(a:sect)
return shellescape(a:name) 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')) return uniq(sort(map(globpath(s:mandirs,'man?/'.name.'*.'.sect.'*', 0, 1), 's:format_candidate(v:val, sect)'), 'i'))
endfunction endfunction
function! s:format_candidate(c, sect) abort function! s:format_candidate(path, sect) abort
if a:c =~# '\.\%(pdf\|in\)$' " invalid extensions if a:path =~# '\.\%(pdf\|in\)$' " invalid extensions
return return
endif 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 if sect ==# a:sect
return name return name
elseif sect =~# a:sect.'[^.]\+$' 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 <bang> (See the '-bang' attribute) Expands to a ! if the
command was executed with a ! modifier, otherwise command was executed with a ! modifier, otherwise
expands to nothing. 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>* *<register>*
<reg> (See the '-register' attribute) The optional register, <reg> (See the '-register' attribute) The optional register,
if specified. Otherwise, expands to nothing. <register> if specified. Otherwise, expands to nothing. <register>

View File

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

View File

@@ -5,9 +5,9 @@ if exists('g:loaded_man')
endif endif
let g:loaded_man = 1 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 augroup man
autocmd! autocmd!

View File

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

View File

@@ -1327,24 +1327,24 @@ static char_u * do_one_cmd(char_u **cmdlinep,
case 'c': if (!checkforcmd(&ea.cmd, "confirm", 4)) case 'c': if (!checkforcmd(&ea.cmd, "confirm", 4))
break; break;
cmdmod.confirm = TRUE; cmdmod.confirm = true;
continue; continue;
case 'k': if (checkforcmd(&ea.cmd, "keepmarks", 3)) { case 'k': if (checkforcmd(&ea.cmd, "keepmarks", 3)) {
cmdmod.keepmarks = TRUE; cmdmod.keepmarks = true;
continue; continue;
} }
if (checkforcmd(&ea.cmd, "keepalt", 5)) { if (checkforcmd(&ea.cmd, "keepalt", 5)) {
cmdmod.keepalt = TRUE; cmdmod.keepalt = true;
continue; continue;
} }
if (checkforcmd(&ea.cmd, "keeppatterns", 5)) { if (checkforcmd(&ea.cmd, "keeppatterns", 5)) {
cmdmod.keeppatterns = TRUE; cmdmod.keeppatterns = true;
continue; continue;
} }
if (!checkforcmd(&ea.cmd, "keepjumps", 5)) if (!checkforcmd(&ea.cmd, "keepjumps", 5))
break; break;
cmdmod.keepjumps = TRUE; cmdmod.keepjumps = true;
continue; continue;
/* ":hide" and ":hide | cmd" are not modifiers */ /* ":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)) || *p == NUL || ends_excmd(*p))
break; break;
ea.cmd = p; ea.cmd = p;
cmdmod.hide = TRUE; cmdmod.hide = true;
continue; continue;
case 'l': if (checkforcmd(&ea.cmd, "lockmarks", 3)) { case 'l': if (checkforcmd(&ea.cmd, "lockmarks", 3)) {
cmdmod.lockmarks = TRUE; cmdmod.lockmarks = true;
continue; continue;
} }
@@ -5156,6 +5156,24 @@ static char_u *uc_split_args(char_u *arg, size_t *lenp)
return buf; 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. * Check for a <> code in a user command.
* "code" points to the '<'. "len" the length of the <> (inclusive). * "code" points to the '<'. "len" the length of the <> (inclusive).
@@ -5180,8 +5198,8 @@ uc_check_code (
char_u *p = code + 1; char_u *p = code + 1;
size_t l = len - 2; size_t l = len - 2;
int quote = 0; int quote = 0;
enum { ct_ARGS, ct_BANG, ct_COUNT, ct_LINE1, ct_LINE2, ct_REGISTER, enum { ct_ARGS, ct_BANG, ct_COUNT, ct_LINE1, ct_LINE2, ct_MODS,
ct_LT, ct_NONE } type = ct_NONE; ct_REGISTER, ct_LT, ct_NONE } type = ct_NONE;
if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') { if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') {
quote = (*p == 'q' || *p == 'Q') ? 1 : 2; quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
@@ -5189,23 +5207,26 @@ uc_check_code (
l -= 2; l -= 2;
} }
++l; l++;
if (l <= 1) if (l <= 1) {
type = ct_NONE; type = ct_NONE;
else if (STRNICMP(p, "args>", l) == 0) } else if (STRNICMP(p, "args>", l) == 0) {
type = ct_ARGS; type = ct_ARGS;
else if (STRNICMP(p, "bang>", l) == 0) } else if (STRNICMP(p, "bang>", l) == 0) {
type = ct_BANG; type = ct_BANG;
else if (STRNICMP(p, "count>", l) == 0) } else if (STRNICMP(p, "count>", l) == 0) {
type = ct_COUNT; type = ct_COUNT;
else if (STRNICMP(p, "line1>", l) == 0) } else if (STRNICMP(p, "line1>", l) == 0) {
type = ct_LINE1; type = ct_LINE1;
else if (STRNICMP(p, "line2>", l) == 0) } else if (STRNICMP(p, "line2>", l) == 0) {
type = ct_LINE2; type = ct_LINE2;
else if (STRNICMP(p, "lt>", l) == 0) } else if (STRNICMP(p, "lt>", l) == 0) {
type = ct_LT; 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; type = ct_REGISTER;
} else if (STRNICMP(p, "mods>", l) == 0) {
type = ct_MODS;
}
switch (type) { switch (type) {
case ct_ARGS: case ct_ARGS:
@@ -5313,6 +5334,87 @@ uc_check_code (
break; 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: case ct_REGISTER:
result = eap->regname ? 1 : 0; result = eap->regname ? 1 : 0;
if (quote) if (quote)

View File

@@ -36,6 +36,7 @@ NEW_TESTS = \
test_help_tagjump.res \ test_help_tagjump.res \
test_langmap.res \ test_langmap.res \
test_syntax.res \ test_syntax.res \
test_usercommands.res \
test_timers.res \ test_timers.res \
test_viml.res \ test_viml.res \
test_visual.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 // 1901 NA
// 1900, // 1900,
// 1899 NA // 1899 NA
// 1898, 1898,
// 1897, // 1897,
// 1896, // 1896,
// 1895, // 1895,