]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0499: modeline: allow to disable modelines with modelinestrict v9.2.0499
authorChristian Brabandt <cb@256bit.org>
Mon, 18 May 2026 18:59:03 +0000 (18:59 +0000)
committerChristian Brabandt <cb@256bit.org>
Mon, 18 May 2026 18:59:03 +0000 (18:59 +0000)
Problem:  Cannot disable modeline processing when loading a file
          (Mao-Yining, after v9.2.0350)
Solution: Allow to disable modeline processing even when
          'modelienstrict' is in effect.

fixes:  #20103
closes: #20229

Signed-off-by: Christian Brabandt <cb@256bit.org>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/options.txt
src/option.c
src/testdir/test_modeline.vim
src/version.c

index 2ba59824f627f8ccaf913ada36714ac025ddffa7..b7d74e9f8c95e91948a7e2987d7791209ede4e05 100644 (file)
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.2.  Last change: 2026 May 17
+*options.txt*  For Vim version 9.2.  Last change: 2026 May 18
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -6239,6 +6239,11 @@ A jump table for the options with a short description can be found at |Q_op|.
        This option cannot be set from a |modeline| or in the |sandbox|, for
        security reasons.
 
+       As an exception, `set nomodeline` is honored from within a modeline
+       even when 'modelinestrict' is on.  Other forms (`set modeline=0`,
+       `set modeline!`, `set invmodeline`) are still silently ignored.
+       This lets a file disable further modeline processing for itself.
+
        The behaviour of 'modeline', 'modelinestrict' and 'modelineexpr' is
        as follows:
 
index 31f84c98d1ac5d789abe4d396ee4b28482491241..d15cc3f3c5481a0fdc92017f241fbe53c276c6fa 100644 (file)
@@ -1589,7 +1589,12 @@ is_modeline_whitelisted(char *name)
  * if it can be changed.
  */
     static int
-validate_opt_idx(int opt_idx, int opt_flags, long_u flags, char **errmsg)
+validate_opt_idx(
+       int opt_idx,
+       int opt_flags,
+       long_u flags,
+       char **errmsg,
+       set_prefix_T prefix)
 {
     // Skip all options that are not window-local (used when showing
     // an already loaded buffer in a window).
@@ -1617,9 +1622,14 @@ validate_opt_idx(int opt_idx, int opt_flags, long_u flags, char **errmsg)
        }
        // When 'modelinestrict' is on, only whitelisted options may be
        // set from a modeline.  Silently skip others.
-       if (p_mlstr && opt_idx >= 0
-               && !is_modeline_whitelisted(options[opt_idx].fullname))
-           return FAIL;
+       if (p_mlstr && opt_idx >= 0)
+       {
+           // special case: allow disabling modeline
+           if (options[opt_idx].indir == PV_ML && prefix == PREFIX_NO)
+               return OK;
+           else if (!is_modeline_whitelisted(options[opt_idx].fullname))
+               return FAIL;
+       }
 #ifdef FEAT_DIFF
        // In diff mode some options are overruled.  This avoids that
        // 'foldmethod' becomes "marker" instead of "diff" and that
@@ -2864,7 +2874,7 @@ do_set_option(
     }
 
     // Make sure the option value can be changed.
-    if (validate_opt_idx(opt_idx, opt_flags, flags, &errmsg) == FAIL)
+    if (validate_opt_idx(opt_idx, opt_flags, flags, &errmsg, prefix) == FAIL)
        goto skip;
 
     int cp_val = p_cp;
index cb489ee15fd9aa10b4d266ae986e98819de8b720..c436cbc270a73d3d215ba9bba078cd173e6ad699 100644 (file)
@@ -666,4 +666,56 @@ func Test_modeline_strict_cannot_be_set_from_modeline()
   let &modeline = modeline
 endfunc
 
+func Test_modeline_nomodeline_with_modelinestrict()
+       let modeline = &modeline
+       let modelinestrict = &modelinestrict
+       let modelines = &modelines
+       set modelinestrict modeline modelines=5
+       let ml_before = &g:modeline
+
+       call writefile(['# vim: set nomodeline:', 'line2'], 'Xnoml', 'D')
+       split Xnoml
+       call assert_equal(0, &l:modeline, 'b_p_ml should be off')
+       call assert_equal(ml_before, &g:modeline, 'global p_ml must not change')
+       bwipe!
+
+       " A fresh buffer must still inherit the unchanged global default
+       new
+       call assert_equal(ml_before, &l:modeline,
+                               \ 'new buffer should inherit unchanged global')
+       bwipe!
+
+       let &modeline = modeline
+       let &modelinestrict = modelinestrict
+       let &modelines = modelines
+endfunc
+
+func Test_modeline_nomodeline_skips_trailing_modelines()
+       let modeline = &modeline
+       let modelinestrict = &modelinestrict
+       let ts_save = &ts
+       set modeline modelinestrict ts=8
+
+       " Line 1 disables modelines; the trailing modeline must therefore
+       " never execute even though 'tabstop' is whitelisted.
+       call writefile([
+                               \ '# vim: set nomodeline :',
+                               \ 'middle line 1',
+                               \ 'middle line 2',
+                               \ 'middle line 3',
+                               \ '# vim: set ts=99 :',
+                               \ ], 'Xmodeline_disable_top', 'D')
+       split Xmodeline_disable_top
+
+       call assert_equal(0, &l:modeline,
+                               \ 'top modeline must have disabled b_p_ml')
+       call assert_equal(8, &ts,
+                               \ 'trailing modeline must not have run after nomodeline')
+
+       bwipe!
+       let &modeline = modeline
+       let &modelinestrict = modelinestrict
+       let &ts = ts_save
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index f348ffc9ef52e54ee2ec01b4c6ec245b6b4dff56..4ce61756502c4e830ed258823162bea9040b6a79 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    499,
 /**/
     498,
 /**/