diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index f9f8a183ab..cf1131ee19 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -211,6 +211,18 @@ typedef struct { OptInt wo_winbl; #define w_p_winbl w_onebuf_opt.wo_winbl // 'winblend' + // A few options have local flags for kOptFlagInsecure. + uint32_t wo_wrap_flags; // flags for 'wrap' +#define w_p_wrap_flags w_onebuf_opt.wo_wrap_flags + uint32_t wo_stl_flags; // flags for 'statusline' +#define w_p_stl_flags w_onebuf_opt.wo_stl_flags + uint32_t wo_wbr_flags; // flags for 'winbar' +#define w_p_wbr_flags w_onebuf_opt.wo_wbr_flags + uint32_t wo_fde_flags; // flags for 'foldexpr' +#define w_p_fde_flags w_onebuf_opt.wo_fde_flags + uint32_t wo_fdt_flags; // flags for 'foldtext' +#define w_p_fdt_flags w_onebuf_opt.wo_fdt_flags + sctx_T wo_script_ctx[kWinOptCount]; // SCTXs for window-local options #define w_p_script_ctx w_onebuf_opt.wo_script_ctx } winopt_T; @@ -1302,12 +1314,6 @@ struct window_S { // transform a pointer to a "onebuf" option into a "allbuf" option #define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T)) - // A few options have local flags for kOptFlagInsecure. - uint32_t w_p_wrap_flags; // flags for 'wrap' - uint32_t w_p_stl_flags; // flags for 'statusline' - uint32_t w_p_wbr_flags; // flags for 'winbar' - uint32_t w_p_fde_flags; // flags for 'foldexpr' - uint32_t w_p_fdt_flags; // flags for 'foldtext' int *w_p_cc_cols; // array of columns to highlight or NULL uint8_t w_p_culopt_flags; // flags for cursorline highlighting diff --git a/src/nvim/option.c b/src/nvim/option.c index 3e6de605f2..0edc393e76 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -487,6 +487,7 @@ static void change_option_default(const OptIndex opt_idx, OptVal value) /// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination). static void set_option_default(const OptIndex opt_idx, int opt_flags) { + bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; OptVal def_val = get_option_default(opt_idx, opt_flags); set_option_direct(opt_idx, def_val, opt_flags, current_sctx.sc_sid); @@ -497,6 +498,10 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags) // The default value is not insecure. uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags); *flagsp = *flagsp & ~(unsigned)kOptFlagInsecure; + if (both) { + flagsp = insecure_flag(curwin, opt_idx, OPT_LOCAL); + *flagsp = *flagsp & ~(unsigned)kOptFlagInsecure; + } } /// Set all options (except terminal options) to their default value. @@ -1663,6 +1668,18 @@ uint32_t *insecure_flag(win_T *const wp, OptIndex opt_idx, int opt_flags) default: break; } + } else { + // For global value of window-local options, use flags in w_allbuf_opt. + switch (opt_idx) { + case kOptWrap: + return &wp->w_allbuf_opt.wo_wrap_flags; + case kOptFoldexpr: + return &wp->w_allbuf_opt.wo_fde_flags; + case kOptFoldtext: + return &wp->w_allbuf_opt.wo_fdt_flags; + default: + break; + } } // Nothing special, return global flags field. return &options[opt_idx].flags; @@ -3555,15 +3572,22 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value check_redraw(opt->flags); if (errmsg == NULL) { - uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags); opt->flags |= kOptFlagWasSet; - // When an option is set in the sandbox, from a modeline or in secure mode set the kOptFlagInsecure - // flag. Otherwise, if a new value is stored reset the flag. + uint32_t *flagsp = insecure_flag(curwin, opt_idx, opt_flags); + uint32_t *flagsp_local = scope_both ? insecure_flag(curwin, opt_idx, OPT_LOCAL) : NULL; + // When an option is set in the sandbox, from a modeline or in secure mode set the + // kOptFlagInsecure flag. Otherwise, if a new value is stored reset the flag. if (!value_checked && (secure || sandbox != 0 || (opt_flags & OPT_MODELINE))) { - *p |= kOptFlagInsecure; + *flagsp |= kOptFlagInsecure; + if (flagsp_local != NULL) { + *flagsp_local |= kOptFlagInsecure; + } } else if (value_replaced) { - *p &= ~(unsigned)kOptFlagInsecure; + *flagsp &= ~(unsigned)kOptFlagInsecure; + if (flagsp_local != NULL) { + *flagsp_local &= ~(unsigned)kOptFlagInsecure; + } } } @@ -4913,6 +4937,12 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_winbl = from->wo_winbl; to->wo_stc = copy_option_val(from->wo_stc); + to->wo_wrap_flags = from->wo_wrap_flags; + to->wo_stl_flags = from->wo_stl_flags; + to->wo_wbr_flags = from->wo_wbr_flags; + to->wo_fde_flags = from->wo_fde_flags; + to->wo_fdt_flags = from->wo_fdt_flags; + // Copy the script context so that we know were the value was last set. memmove(to->wo_script_ctx, from->wo_script_ctx, sizeof(to->wo_script_ctx)); check_winopt(to); // don't want NULL pointers diff --git a/test/old/testdir/test_fold.vim b/test/old/testdir/test_fold.vim index 6569e032f6..756cab15ed 100644 --- a/test/old/testdir/test_fold.vim +++ b/test/old/testdir/test_fold.vim @@ -1499,7 +1499,7 @@ endfunc " in a sandbox func Test_foldtext_in_modeline() func ModelineFoldText() - call feedkeys('aFoo', 'xt') + call writefile(['after'], 'Xmodelinefoldtext_write') return "folded text" endfunc let lines =<< trim END @@ -1510,24 +1510,78 @@ func Test_foldtext_in_modeline() END call writefile(lines, 'Xmodelinefoldtext', 'D') + func Check_foldtext_in_modeline(set_cmd) + call writefile(['before'], 'Xmodelinefoldtext_write', 'D') + split Xmodelinefoldtext + call cursor(1, 1) + normal! zf3j + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['before'], readfile('Xmodelinefoldtext_write')) + + split + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['before'], readfile('Xmodelinefoldtext_write')) + close + + setglobal foldtext=ModelineFoldText() + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['before'], readfile('Xmodelinefoldtext_write')) + + setglobal foldtext& + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['before'], readfile('Xmodelinefoldtext_write')) + + exe a:set_cmd 'foldtext=ModelineFoldText()' + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['after'], readfile('Xmodelinefoldtext_write')) + + call writefile(['before'], 'Xmodelinefoldtext_write') + exe 'sandbox' a:set_cmd 'foldtext=ModelineFoldText()' + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['before'], readfile('Xmodelinefoldtext_write')) + + exe a:set_cmd 'foldtext=ModelineFoldText()' + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['after'], readfile('Xmodelinefoldtext_write')) + bw! + endfunc + set modeline modelineexpr - split Xmodelinefoldtext + call Check_foldtext_in_modeline('setlocal') + call Check_foldtext_in_modeline('set') - call cursor(1, 1) - normal! zf3j + new Xa + sandbox setglobal foldenable foldtext=ModelineFoldText() + setlocal bufhidden=wipe + call writefile(['before'], 'Xmodelinefoldtext_write', 'D') + edit! Xb + call setline(1, ['func T()', ' let i = 1', 'endfunc']) | %fold call assert_equal('folded text', foldtextresult(1)) - call assert_equal(lines, getbufline('', 1, '$')) + call assert_equal(['before'], readfile('Xmodelinefoldtext_write')) + setglobal foldtext=ModelineFoldText() + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['before'], readfile('Xmodelinefoldtext_write')) + setlocal foldtext=ModelineFoldText() + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['after'], readfile('Xmodelinefoldtext_write')) + setlocal bufhidden=wipe + call writefile(['before'], 'Xmodelinefoldtext_write', 'D') + edit! Xc + call setline(1, ['func T()', ' let i = 1', 'endfunc']) | %fold + call assert_equal('folded text', foldtextresult(1)) + call assert_equal(['after'], readfile('Xmodelinefoldtext_write')) + bwipe! - bw! set modeline& modelineexpr& delfunc ModelineFoldText + delfunc Check_foldtext_in_modeline endfunc " Test for setting 'foldexpr' from the modeline and executing the expression " in a sandbox func Test_foldexpr_in_modeline() func ModelineFoldExpr() - call feedkeys('aFoo', 'xt') + call writefile(['after'], 'Xmodelinefoldexpr_write') return strlen(matchstr(getline(v:lnum),'^\s*')) endfunc let lines =<< trim END @@ -1541,15 +1595,69 @@ func Test_foldexpr_in_modeline() END call writefile(lines, 'Xmodelinefoldexpr', 'D') + func Check_foldexpr_in_modeline(set_cmd) + call writefile(['before'], 'Xmodelinefoldexpr_write', 'D') + split Xmodelinefoldexpr + call assert_equal(2, foldlevel(3)) + call assert_equal(['before'], readfile('Xmodelinefoldexpr_write')) + + split + call assert_equal(2, foldlevel(3)) + call assert_equal(['before'], readfile('Xmodelinefoldexpr_write')) + close + + setglobal foldexpr=ModelineFoldExpr() + call assert_equal(2, foldlevel(3)) + call assert_equal(['before'], readfile('Xmodelinefoldexpr_write')) + + setglobal foldexpr& + call assert_equal(2, foldlevel(3)) + call assert_equal(['before'], readfile('Xmodelinefoldexpr_write')) + + exe a:set_cmd 'foldexpr=ModelineFoldExpr()' + call assert_equal(2, foldlevel(3)) + call assert_equal(['after'], readfile('Xmodelinefoldexpr_write')) + + call writefile(['before'], 'Xmodelinefoldexpr_write') + exe 'sandbox' a:set_cmd 'foldexpr=ModelineFoldExpr()' + call assert_equal(2, foldlevel(3)) + call assert_equal(['before'], readfile('Xmodelinefoldexpr_write')) + + exe a:set_cmd 'foldexpr=ModelineFoldExpr()' + call assert_equal(2, foldlevel(3)) + call assert_equal(['after'], readfile('Xmodelinefoldexpr_write')) + bw! + endfunc + set modeline modelineexpr - split Xmodelinefoldexpr + call Check_foldexpr_in_modeline('setlocal') + call Check_foldexpr_in_modeline('set') + new Xa + sandbox setglobal foldenable foldmethod=expr foldexpr=ModelineFoldExpr() + setlocal bufhidden=wipe + call writefile(['before'], 'Xmodelinefoldexpr_write', 'D') + edit! Xb + call setline(1, lines[0:5]) call assert_equal(2, foldlevel(3)) - call assert_equal(lines, getbufline('', 1, '$')) + call assert_equal(['before'], readfile('Xmodelinefoldexpr_write')) + setglobal foldexpr=ModelineFoldExpr() + call assert_equal(2, foldlevel(3)) + call assert_equal(['before'], readfile('Xmodelinefoldexpr_write')) + setlocal foldexpr=ModelineFoldExpr() + call assert_equal(2, foldlevel(3)) + call assert_equal(['after'], readfile('Xmodelinefoldexpr_write')) + setlocal bufhidden=wipe + call writefile(['before'], 'Xmodelinefoldexpr_write', 'D') + edit! Xc + call setline(1, lines[0:5]) + call assert_equal(2, foldlevel(3)) + call assert_equal(['after'], readfile('Xmodelinefoldexpr_write')) + bwipe! - bw! set modeline& modelineexpr& delfunc ModelineFoldExpr + delfunc Check_foldexpr_in_modeline endfunc " Make sure a fold containing a nested fold is split correctly when using diff --git a/test/old/testdir/test_modeline.vim b/test/old/testdir/test_modeline.vim index a1050f82cb..628c4770db 100644 --- a/test/old/testdir/test_modeline.vim +++ b/test/old/testdir/test_modeline.vim @@ -382,10 +382,40 @@ func Test_modeline_nowrap_lcs_extends() \ 'ccc evil', \ 'ddd vim: nowrap', \ ], 'Xmodeline_nowrap', 'D') - call NewWindow(10, 20) + set noequalalways + 11new | 20vsplit + + func Check_modeline_nowrap(expect_insecure, expect_secure, set_cmd) + edit Xmodeline_nowrap + call assert_equal(a:expect_insecure, ScreenLines([1, 5], 20)) + + 5split + call assert_equal(a:expect_insecure, ScreenLines([1, 5], 20)) + call assert_equal(a:expect_insecure, ScreenLines([7, 11], 20)) + + exe a:set_cmd 'nowrap' + call assert_equal(a:expect_secure, ScreenLines([1, 5], 20)) + call assert_equal(a:expect_insecure, ScreenLines([7, 11], 20)) + + close + call assert_equal(a:expect_insecure, ScreenLines([1, 5], 20)) + + setglobal nowrap + call assert_equal(a:expect_insecure, ScreenLines([1, 5], 20)) + setglobal wrap + call assert_equal(a:expect_insecure, ScreenLines([1, 5], 20)) + + exe a:set_cmd 'nowrap' + call assert_equal(a:expect_secure, ScreenLines([1, 5], 20)) + + exe 'sandbox' a:set_cmd 'nowrap' + call assert_equal(a:expect_insecure, ScreenLines([1, 5], 20)) + + exe a:set_cmd 'nowrap' + call assert_equal(a:expect_secure, ScreenLines([1, 5], 20)) + endfunc setlocal nolist listchars= - edit Xmodeline_nowrap let expect_insecure = [ \ 'aaa ', \ 'bbb ', @@ -393,9 +423,6 @@ func Test_modeline_nowrap_lcs_extends() \ 'ddd >', \ '~ ', \ ] - call assert_equal(expect_insecure, ScreenLines([1, 5], 20)) - - setlocal nowrap let expect_secure = [ \ 'aaa ', \ 'bbb ', @@ -403,7 +430,8 @@ func Test_modeline_nowrap_lcs_extends() \ 'ddd ', \ '~ ', \ ] - call assert_equal(expect_secure, ScreenLines([1, 5], 20)) + call Check_modeline_nowrap(expect_insecure, expect_secure, 'setlocal') + call Check_modeline_nowrap(expect_insecure, expect_secure, 'set') setlocal list listchars=extends:+ let expect_secure = [ @@ -414,13 +442,65 @@ func Test_modeline_nowrap_lcs_extends() \ '~ ', \ ] call assert_equal(expect_secure, ScreenLines([1, 5], 20)) + call Check_modeline_nowrap(expect_insecure, expect_secure, 'setlocal') + call Check_modeline_nowrap(expect_insecure, expect_secure, 'set') - edit Xmodeline_nowrap - call assert_equal(expect_insecure, ScreenLines([1, 5], 20)) + " Other 'listchars' flags are not affected. + call writefile([ + \ "aa\ta", + \ "bb\tb", + \ "cc\tc evil", + \ "dd\td vim: nowrap lcs=tab\\:<->", + \ ], 'Xmodeline_nowrap') + let expect_insecure = [ + \ 'aa<---->a ', + \ 'bb<---->b ', + \ 'cc<---->c >', + \ 'dd<---->d >', + \ '~ ', + \ ] + let expect_secure = [ + \ 'aa<---->a ', + \ 'bb<---->b ', + \ 'cc<---->c ', + \ 'dd<---->d ', + \ '~ ', + \ ] + call Check_modeline_nowrap(expect_insecure, expect_secure, 'setlocal') + call Check_modeline_nowrap(expect_insecure, expect_secure, 'set') + + " Same behavior even if modeline sets "extends" to a space. + call writefile([ + \ "aa\ta", + \ "bb\tb", + \ "cc\tc evil", + \ "dd\td vim: nowrap lcs=tab\\:<->", + \ ], 'Xmodeline_nowrap') + call Check_modeline_nowrap(expect_insecure, expect_secure, 'setlocal') + call Check_modeline_nowrap(expect_insecure, expect_secure, 'set') + + sandbox setglobal nowrap + setglobal list listchars=eol:$ + setlocal bufhidden=wipe + enew! + call setline(1, ['aaa bbb']) + call assert_equal(['aaa >'], ScreenLines(1, 20)) + setglobal nowrap + call assert_equal(['aaa >'], ScreenLines(1, 20)) setlocal nowrap - call assert_equal(expect_secure, ScreenLines([1, 5], 20)) + call assert_equal(['aaa '], ScreenLines(1, 20)) + normal! 20zl + call assert_equal([' bbb$ '], ScreenLines(1, 20)) + setlocal bufhidden=wipe + enew! + call setline(1, ['ccc ddd']) + call assert_equal(['ccc '], ScreenLines(1, 20)) + normal! 20zl + call assert_equal([' ddd$ '], ScreenLines(1, 20)) - call CloseWindow() + bwipe! + delfunc Check_modeline_nowrap + set equalalways& endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_statusline.vim b/test/old/testdir/test_statusline.vim index bd2c596b57..6886700e83 100644 --- a/test/old/testdir/test_statusline.vim +++ b/test/old/testdir/test_statusline.vim @@ -641,4 +641,67 @@ func Test_statusline_highlight_group_cleared() call StopVimInTerminal(buf) endfunc +" Test for setting both global and local 'statusline' values in a sandbox +func Test_statusline_in_sandbox() + func SandboxStatusLine() + call writefile(['after'], 'Xsandboxstatusline_write') + return "status line" + endfunc + + func Check_statusline_in_sandbox(set_cmd0, set_cmd1) + only + 11new | 20vsplit + call setline(1, 'foo') + windo setlocal statusline=SomethingElse + wincmd t + setlocal statusline= + call writefile(['before'], 'Xsandboxstatusline_write', 'D') + + exe 'sandbox' a:set_cmd0 'statusline=%!SandboxStatusLine()' + call assert_equal('', &l:statusline) + sandbox setlocal statusline=%!SandboxStatusLine() + call assert_fails('redrawstatus', 'E48:') + call assert_equal(['before'], readfile('Xsandboxstatusline_write')) + wincmd b + call assert_fails('redrawstatus!', 'E48:') + call assert_equal(['before'], readfile('Xsandboxstatusline_write')) + wincmd t + + 5split + call assert_fails('redrawstatus!', 'E48:') + call assert_equal(['before'], readfile('Xsandboxstatusline_write')) + close + + setlocal statusline=%!SandboxStatusLine() | redrawstatus + call assert_equal('status line', Screenline(12)) + call assert_equal(['after'], readfile('Xsandboxstatusline_write')) + + call writefile(['before'], 'Xsandboxstatusline_write') + setlocal statusline= + call assert_fails('redrawstatus', 'E48:') + call assert_equal(['before'], readfile('Xsandboxstatusline_write')) + + 5split + call assert_fails('redrawstatus!', 'E48:') + call assert_equal(['before'], readfile('Xsandboxstatusline_write')) + + exe a:set_cmd1 'statusline=%!SandboxStatusLine()' | redrawstatus! + call assert_equal('', &l:statusline) + call assert_equal('status line', Screenline(6)) + call assert_equal('status line', Screenline(12)) + call assert_equal(['after'], readfile('Xsandboxstatusline_write')) + bwipe! + endfunc + + set noequalalways + call Check_statusline_in_sandbox('setglobal', 'setglobal') + call Check_statusline_in_sandbox('setglobal', 'set') + call Check_statusline_in_sandbox('set', 'setglobal') + call Check_statusline_in_sandbox('set', 'set') + + set equalalways& statusline& + delfunc SandboxStatusLine + delfunc Check_statusline_in_sandbox +endfunc + " vim: shiftwidth=2 sts=2 expandtab