]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0223: Option handling for key:value suboptions is limited v9.2.0223
authorHirohito Higashi <h.east.727@gmail.com>
Sun, 22 Mar 2026 16:08:01 +0000 (16:08 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 22 Mar 2026 16:19:31 +0000 (16:19 +0000)
Problem:  Option handling for key:value suboptions is limited
Solution: Improve :set+=, :set-= and :set^= for options that use
          "key:value" pairs (Hirohito Higashi)

For comma-separated options with P_COLON (e.g., diffopt, listchars,
fillchars), :set += -= ^= now processes each comma-separated item
individually instead of treating the whole value as a single string.

For :set += and :set ^=:
- A "key:value" item where the key already exists with a different value:
  the old item is replaced.
- An exact duplicate item is left unchanged.
- A new item is appended (+=) or prepended (^=).

For :set -=:
- A "key:value" or "key:" item removes by key match regardless of value.
- A non-colon item removes by exact match.

This also handles multiple non-colon items (e.g., :set
diffopt-=filler,internal) by processing each item individually, making
the behavior order-independent.

Previously, :set += simply appended the value, causing duplicate keys to
accumulate.

fixes:  #18495
closes: #19783

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/options.txt
runtime/doc/version9.txt
src/option.c
src/optiondefs.h
src/testdir/test_listchars.vim
src/testdir/test_options.vim
src/version.c

index bddfa99211786b5bdf1d1c02101b6d5f39e224f0..d286467787447d7cb9d29c2d8659c825e9729156 100644 (file)
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.2.  Last change: 2026 Mar 16
+*options.txt*  For Vim version 9.2.  Last change: 2026 Mar 22
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -97,6 +97,17 @@ achieve special effects.  These options come in three forms:
                        If the option is a list of flags, superfluous flags
                        are removed.  When adding a flag that was already
                        present the option value doesn't change.
+                       When the option supports "key:value" items and {value}
+                       contains a "key:value" item or multiple
+                       comma-separated items, each item is processed
+                       individually:
+                       - A "key:value" item where the key already exists with
+                         a different value: the old item is removed and the
+                         new item is appended to the end.
+                       - A "key:value" item that is an exact duplicate is
+                         left unchanged.
+                       - Other items that already exist are left unchanged.
+                       - New items are appended to the end.
                        Also see |:set-args| above.
 
 :se[t] {option}^={value}                               *:set^=*
@@ -104,6 +115,11 @@ achieve special effects.  These options come in three forms:
                        the {value} to a string option.  When the option is a
                        comma-separated list, a comma is added, unless the
                        value was empty.
+                       When the option supports "key:value" items and {value}
+                       contains a "key:value" item or multiple
+                       comma-separated items, each item is processed
+                       individually.  Works like |:set+=| but new items are
+                       prepended to the beginning instead of appended.
                        Also see |:set-args| above.
 
 :se[t] {option}-={value}                               *:set-=*
@@ -116,6 +132,12 @@ achieve special effects.  These options come in three forms:
                        When the option is a list of flags, {value} must be
                        exactly as they appear in the option.  Remove flags
                        one by one to avoid problems.
+                       When the option supports "key:value" items and {value}
+                       contains a "key:value" item or multiple
+                       comma-separated items, each item is processed
+                       individually.  A "key:value" item removes the existing
+                       item with that key regardless of its value.  A "key:"
+                       item also removes by key match.
                        The individual values from a comma separated list or
                        list of flags can be inserted by typing 'wildchar'.
                        See |complete-set-option|.
index e9a949e37129ecee02fd23d95e88fb7e818a5c31..3168994851cf5c9a6d2d19e0384b73f6688eb5d3 100644 (file)
@@ -1,4 +1,4 @@
-*version9.txt* For Vim version 9.2.  Last change: 2026 Mar 19
+*version9.txt* For Vim version 9.2.  Last change: 2026 Mar 22
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -52609,6 +52609,8 @@ Other ~
 - Support for "dap" channel mode for the |debug-adapter-protocol|.
 - |status-line| can use several lines, see 'statuslineopt'.
 - New "leadtab" value for the 'listchars' setting.
+- Improved |:set+=|, |:set^=| and |:set-=| handling of comma-separated "key:value"
+  pairs individually (e.g. 'listchars', 'fillchars', 'diffopt').
 
 xxd ~
 ---
index 60af0903372040a7a5d16da0b6fbaf4e70ace7a8..45446529935eb7ce97c10ffb91069ea86c3036b9 100644 (file)
@@ -1887,6 +1887,247 @@ stropt_remove_val(
     }
 }
 
+/*
+ * Find a comma-separated item in "src" that matches the key part of "key".
+ * The key is the part before ':'.  "keylen" is the length including ':'.
+ * Returns a pointer to the found item in "src", or NULL if not found.
+ * Sets "*itemlenp" to the length of the found item (up to ',' or NUL).
+ */
+    static char_u *
+find_key_item(char_u *src, char_u *key, int keylen, int *itemlenp)
+{
+    char_u  *p = src;
+
+    while (*p != NUL)
+    {
+       // Check if this item starts with the same key
+       if ((p == src || *(p - 1) == ',')
+               && STRNCMP(p, key, keylen) == 0)
+       {
+           // Find the end of this item
+           char_u *end = vim_strchr(p, ',');
+           if (end == NULL)
+               end = p + STRLEN(p);
+           *itemlenp = (int)(end - p);
+           return p;
+       }
+       ++p;
+    }
+    return NULL;
+}
+
+/*
+ * Remove one item of length "itemlen" at position "item" from comma-separated
+ * string "str" in-place.  Handles the comma before or after the item.
+ */
+    static void
+remove_comma_item(char_u *str, char_u *item, int itemlen)
+{
+    if (item[itemlen] == ',')
+       // Remove item and trailing comma
+       STRMOVE(item, item + itemlen + 1);
+    else if (item > str && *(item - 1) == ',')
+       // Last item: remove leading comma and item
+       STRMOVE(item - 1, item + itemlen);
+    else
+       // Only item
+       *item = NUL;
+}
+
+/*
+ * Remove all items matching "key" (with ':') from comma-separated string "str"
+ * in-place.  If "skip" is not NULL, the item at that position is kept.
+ */
+    static void
+remove_key_item(char_u *str, char_u *key, int keylen, char_u *skip)
+{
+    int            itemlen;
+    char_u  *found;
+
+    while ((found = find_key_item(str, key, keylen, &itemlen)) != NULL)
+    {
+       if (found == skip)
+       {
+           // Search for the next match after this one.
+           char_u *next = found + itemlen;
+           if (*next == ',')
+               ++next;
+           found = find_key_item(next, key, keylen, &itemlen);
+           if (found == NULL)
+               break;
+       }
+
+       remove_comma_item(str, found, itemlen);
+    }
+}
+
+/*
+ * Append a comma-separated item to the end of "str" in-place.
+ * Adds a comma before the item if "str" is not empty.
+ */
+    static void
+append_item(char_u *str, char_u *item, int item_len)
+{
+    int len = (int)STRLEN(str);
+
+    if (len > 0)
+       str[len++] = ',';
+    mch_memmove(str + len, item, (size_t)item_len);
+    str[len + item_len] = NUL;
+}
+
+/*
+ * Prepend a comma-separated item to the beginning of "str" in-place.
+ * Adds a comma after the item if "str" is not empty.
+ */
+    static void
+prepend_item(char_u *str, char_u *item, int item_len)
+{
+    int len = (int)STRLEN(str);
+    int comma = (len > 0) ? 1 : 0;
+
+    mch_memmove(str + item_len + comma, str, (size_t)len + 1);
+    mch_memmove(str, item, (size_t)item_len);
+    if (comma)
+       str[item_len] = ',';
+}
+
+/*
+ * For a P_COMMA option: process "key:value" items in "newval" individually.
+ * Each comma-separated item in "newval" is checked against "origval":
+ *
+ * For OP_ADDING/OP_PREPENDING, each item is handled as follows:
+ *   - colon item, key exists with different value: replace (remove old, add)
+ *   - colon item, exact duplicate: do nothing
+ *   - colon item, not found: add to end
+ *   - non-colon item, exists: do nothing
+ *   - non-colon item, not found: add to end
+ *
+ * For OP_REMOVING, each item is handled as follows:
+ *   - colon item: remove by key match
+ *   - non-colon item: remove by exact match
+ *
+ * The result is written to "newval".
+ * Returns true if the operation was fully handled (caller should skip the
+ * normal add/remove logic).  Returns false if newval is a single non-colon
+ * item, meaning the caller should use the existing code path.
+ */
+    static bool
+stropt_handle_keymatch(
+    char_u     *origval,
+    char_u     *newval,
+    set_op_T   op,
+    int                flags UNUSED)
+{
+    char_u  *p;
+    char_u  *item_start;
+
+    // Check if newval contains any "key:value" item or multiple
+    // comma-separated items.  If neither, let the caller use the existing
+    // code path.
+    if (vim_strchr(newval, ':') == NULL && vim_strchr(newval, ',') == NULL)
+       return false;
+
+    // Work on a copy of newval for iteration.
+    char_u *newval_copy = vim_strsave(newval);
+    if (newval_copy == NULL)
+       return false;
+
+    // Build the result in newval.  Start with a copy of origval, then
+    // modify it per-item.  newval buffer has room for origval + arg.
+    STRCPY(newval, origval);
+
+    // Process each item individually, modifying newval in-place.
+    item_start = newval_copy;
+    for (;;)
+    {
+       p = vim_strchr(item_start, ',');
+       int item_len = (p == NULL)
+                           ? (int)STRLEN(item_start) : (int)(p - item_start);
+
+       if (item_len > 0)
+       {
+           char_u *colon = vim_strchr(item_start, ':');
+           if (colon != NULL && colon < item_start + item_len)
+           {
+               int keylen = (int)(colon - item_start) + 1;
+
+               if (op == OP_ADDING || op == OP_PREPENDING)
+               {
+                   int old_itemlen;
+                   char_u *found = find_key_item(newval, item_start,
+                                                      keylen, &old_itemlen);
+                   if (found != NULL)
+                   {
+                       if (old_itemlen == item_len
+                               && STRNCMP(found, item_start,
+                                                        item_len) == 0)
+                       {
+                           // Exact duplicate: keep it in place, but
+                           // remove other items with the same key.
+                           remove_key_item(newval, item_start,
+                                                          keylen, found);
+                       }
+                       else
+                       {
+                           // Key match with different value: remove all
+                           // items with the same key, then add.
+                           remove_key_item(newval, item_start,
+                                                           keylen, NULL);
+                           if (op == OP_PREPENDING)
+                               prepend_item(newval, item_start, item_len);
+                           else
+                               append_item(newval, item_start, item_len);
+                       }
+                   }
+                   else
+                   {
+                       // New item.
+                       if (op == OP_PREPENDING)
+                           prepend_item(newval, item_start, item_len);
+                       else
+                           append_item(newval, item_start, item_len);
+                   }
+               }
+               else if (op == OP_REMOVING)
+                   remove_key_item(newval, item_start, keylen, NULL);
+           }
+           else
+           {
+               if (op == OP_ADDING || op == OP_PREPENDING)
+               {
+                   char_u *found = find_dup_item(newval, item_start,
+                                                          item_len, P_COMMA);
+                   if (found == NULL)
+                   {
+                       // New item.
+                       if (op == OP_PREPENDING)
+                           prepend_item(newval, item_start, item_len);
+                       else
+                           append_item(newval, item_start, item_len);
+                   }
+                   // else: exact duplicate — do nothing
+               }
+               else if (op == OP_REMOVING)
+               {
+                   char_u *found = find_dup_item(newval, item_start,
+                                                          item_len, P_COMMA);
+                   if (found != NULL)
+                       remove_comma_item(newval, found, item_len);
+               }
+           }
+       }
+
+       if (p == NULL)
+           break;
+       item_start = p + 1;
+    }
+
+    vim_free(newval_copy);
+
+    return true;
+}
+
 /*
  * Remove flags that appear twice in the string option value 'newval'.
  */
@@ -2002,34 +2243,43 @@ stropt_get_newval(
                goto done;
        }
 
-       // locate newval[] in origval[] when removing it and when adding to
-       // avoid duplicates
-       int len = 0;
-       if (op == OP_REMOVING || (flags & P_NODUP))
+       // For P_COMMA|P_COLON options with "key:value" items: process
+       // each item individually by matching on the key part.  If
+       // handled, skip the normal add/remove logic below.
+       if ((flags & P_COMMA) && (flags & P_COLON) && op != OP_NONE
+               && stropt_handle_keymatch(origval, newval, op, flags))
+           ;  // fully handled
+       else
        {
-           len = (int)STRLEN(newval);
-           s = find_dup_item(origval, newval, len, flags);
-
-           // do not add if already there
-           if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL)
+           // locate newval[] in origval[] when removing it and when
+           // adding to avoid duplicates
+           int len = 0;
+           if (op == OP_REMOVING || (flags & P_NODUP))
            {
-               op = OP_NONE;
-               STRCPY(newval, origval);
+               len = (int)STRLEN(newval);
+               s = find_dup_item(origval, newval, len, flags);
+
+               // do not add if already there
+               if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL)
+               {
+                   op = OP_NONE;
+                   STRCPY(newval, origval);
+               }
+
+               // if no duplicate, move pointer to end of original value
+               if (s == NULL)
+                   s = origval + (int)STRLEN(origval);
            }
 
-           // if no duplicate, move pointer to end of original value
-           if (s == NULL)
-               s = origval + (int)STRLEN(origval);
+           // concatenate the two strings; add a ',' if needed
+           if (op == OP_ADDING || op == OP_PREPENDING)
+               stropt_concat_with_comma(origval, newval, op, flags);
+           else if (op == OP_REMOVING)
+               // Remove newval[] from origval[]. (Note: "len" has been
+               // set above and is used here).
+               stropt_remove_val(origval, newval, flags, s, len);
        }
 
-       // concatenate the two strings; add a ',' if needed
-       if (op == OP_ADDING || op == OP_PREPENDING)
-           stropt_concat_with_comma(origval, newval, op, flags);
-       else if (op == OP_REMOVING)
-           // Remove newval[] from origval[]. (Note: "len" has been set above
-           // and is used here).
-           stropt_remove_val(origval, newval, flags, s, len);
-
        if (flags & P_FLAGLIST)
            // Remove flags that appear twice.
            stropt_remove_dupflags(newval, flags);
index 40733fdff53e33eedf2a0c359a9abf9df57536f5..a40c4a77f40ef0ec71ce18531b7516296ada99c4 100644 (file)
@@ -1011,7 +1011,7 @@ static struct vimoption options[] =
                            did_set_filetype_or_syntax, NULL,
                            {(char_u *)"", (char_u *)0L}
                            SCTX_INIT},
-    {"fillchars",   "fcs",  P_STRING|P_VI_DEF|P_RALL|P_ONECOMMA|P_NODUP,
+    {"fillchars",   "fcs",  P_STRING|P_VI_DEF|P_RALL|P_ONECOMMA|P_NODUP|P_COLON,
                            (char_u *)&p_fcs, PV_FCS, did_set_chars_option, expand_set_chars_option,
                            {(char_u *)"vert:|,fold:-,eob:~,lastline:@",
                                                                  (char_u *)0L}
@@ -1672,7 +1672,7 @@ static struct vimoption options[] =
     {"list",       NULL,   P_BOOL|P_VI_DEF|P_RWIN,
                            (char_u *)VAR_WIN, PV_LIST, NULL, NULL,
                            {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
-    {"listchars",   "lcs",  P_STRING|P_VI_DEF|P_RALL|P_ONECOMMA|P_NODUP,
+    {"listchars",   "lcs",  P_STRING|P_VI_DEF|P_RALL|P_ONECOMMA|P_NODUP|P_COLON,
                            (char_u *)&p_lcs, PV_LCS, did_set_chars_option, expand_set_chars_option,
                            {(char_u *)"eol:$", (char_u *)0L} SCTX_INIT},
     {"loadplugins", "lpl",  P_BOOL|P_VI_DEF,
index fd0e146b23b18c2e06af7e682787dca5d022aecf..741b20d9b4dbc34b49b456497bf3782fc469a62b 100644 (file)
@@ -250,7 +250,7 @@ func Test_listchars()
              \ '.-+*0++0>>>>$',
              \ '$'
              \ ]
-  call assert_equal('eol:$,nbsp:S,leadmultispace:.-+*,space:+,trail:>,eol:$', &listchars)
+  call assert_equal('eol:$,nbsp:S,leadmultispace:.-+*,space:+,trail:>', &listchars)
   call Check_listchars(expected, 7)
   call Check_listchars(expected, 6, -1, 1)
   call Check_listchars(expected, 6, -1, 2)
@@ -338,11 +338,21 @@ func Test_listchars()
              \ 'XyYX0Xy0XyYX$',
              \ '$'
              \ ]
+  call assert_equal('eol:$,space:x,multispace:XyY', &listchars)
+  call Check_listchars(expected, 7)
+  call Check_listchars(expected, 6, -1, 6)
+  call assert_equal(expected, split(execute("%list"), "\n"))
+
+  " when using :let, multiple 'multispace:' fields can exist
+  " and the last occurrence of 'multispace:' is used
+  let &listchars = 'eol:$,multispace:yYzZ,space:x,multispace:XyY'
   call assert_equal('eol:$,multispace:yYzZ,space:x,multispace:XyY', &listchars)
   call Check_listchars(expected, 7)
   call Check_listchars(expected, 6, -1, 6)
   call assert_equal(expected, split(execute("%list"), "\n"))
 
+  " restore to single multispace: for subsequent tests
+  set listchars=eol:$,space:x,multispace:XyY
   set listchars+=lead:>,trail:<
 
   let expected = [
@@ -359,8 +369,7 @@ func Test_listchars()
   call assert_equal(expected, split(execute("%list"), "\n"))
 
   " removing 'multispace:'
-  set listchars-=multispace:XyY
-  set listchars-=multispace:yYzZ
+  set listchars-=multispace:
 
   let expected = [
              \ '>>>>ffff<<<<$',
index 8097a3f04a40390f93cc3320cbaf427583049947..d33c062528534039305e815d2af19e89ab15a135 100644 (file)
@@ -2951,4 +2951,145 @@ func Test_showcmd()
   let &cp = _cp
 endfunc
 
+" Test that :set+= and :set-= handle "key:value" items in comma-separated
+" options by matching on the key part.
+func Test_comma_option_key_value()
+  " += replaces existing item with same key
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt+=algorithm:histogram
+  call assert_equal('internal,filler,algorithm:histogram', &diffopt)
+
+  " += with exact duplicate does nothing
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt+=algorithm:patience
+  call assert_equal('internal,filler,algorithm:patience', &diffopt)
+
+  " += with multiple items, each processed individually
+  set diffopt=algorithm:patience,filler
+  set diffopt+=algorithm:histogram,filler
+  call assert_equal('filler,algorithm:histogram', &diffopt)
+
+  " += with non-colon item appends normally
+  set diffopt=internal,filler
+  set diffopt+=iwhite
+  call assert_equal('internal,filler,iwhite', &diffopt)
+
+  " += repeated updates
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt+=algorithm:histogram
+  set diffopt+=algorithm:minimal
+  set diffopt+=algorithm:myers
+  call assert_equal('internal,filler,algorithm:myers', &diffopt)
+
+  " += all exact duplicates does nothing
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt+=algorithm:patience,filler
+  call assert_equal('internal,filler,algorithm:patience', &diffopt)
+
+  " -= with "key:" removes item regardless of value
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt-=algorithm:
+  call assert_equal('internal,filler', &diffopt)
+
+  " -= with "key:value" also matches by key
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt-=algorithm:histogram
+  call assert_equal('internal,filler', &diffopt)
+
+  " -= without colon does not match "key:value" items
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt-=algorithm
+  call assert_equal('internal,filler,algorithm:patience', &diffopt)
+
+  " -= with multiple non-colon items (order independent)
+  set diffopt=internal,filler,closeoff
+  set diffopt-=filler,internal
+  call assert_equal('closeoff', &diffopt)
+
+  " -= with multiple non-colon items (same order as in option)
+  set diffopt=internal,filler,closeoff
+  set diffopt-=internal,filler
+  call assert_equal('closeoff', &diffopt)
+
+  " -= with multiple items: non-colon and colon mixed
+  set diffopt&
+  set diffopt-=indent-heuristic,inline:char
+  call assert_equal('internal,filler,closeoff', &diffopt)
+
+  " -= with multiple items: colon and non-colon mixed (reverse order)
+  set diffopt&
+  set diffopt-=inline:char,indent-heuristic
+  call assert_equal('internal,filler,closeoff', &diffopt)
+
+  " += with multiple non-colon items
+  set diffopt=internal,filler
+  set diffopt+=closeoff,iwhite
+  call assert_equal('internal,filler,closeoff,iwhite', &diffopt)
+
+  " += with multiple non-colon items, some already exist
+  set diffopt=internal,filler,closeoff
+  set diffopt+=filler,iwhite
+  call assert_equal('internal,filler,closeoff,iwhite', &diffopt)
+
+  " -= with multiple items including key match
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt-=algorithm:,filler
+  call assert_equal('internal', &diffopt)
+
+  " -= key match when item is at the beginning
+  set diffopt=algorithm:patience,internal,filler
+  set diffopt-=algorithm:
+  call assert_equal('internal,filler', &diffopt)
+
+  " -= key match when item is at the end
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt-=algorithm:
+  call assert_equal('internal,filler', &diffopt)
+
+  " -= key match when item is the only item
+  set diffopt=algorithm:patience
+  set diffopt-=algorithm:
+  call assert_equal('', &diffopt)
+
+  " ^= prepends new item
+  set diffopt=internal,filler
+  set diffopt^=algorithm:histogram
+  call assert_equal('algorithm:histogram,internal,filler', &diffopt)
+
+  " ^= replaces item and prepends
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt^=algorithm:histogram
+  call assert_equal('algorithm:histogram,internal,filler', &diffopt)
+
+  " ^= with exact duplicate does nothing
+  set diffopt=internal,filler,algorithm:patience
+  set diffopt^=algorithm:patience
+  call assert_equal('internal,filler,algorithm:patience', &diffopt)
+
+  set diffopt&
+
+  " Multiple items with the same key (set via :let)
+  " += with different value removes all items with the same key
+  let &lcs = 'eol:$,multispace:yY,space:x,multispace:XY'
+  set lcs+=multispace:AB
+  call assert_equal('eol:$,space:x,multispace:AB', &lcs)
+
+  " += with exact duplicate keeps it and removes others with the same key
+  let &lcs = 'eol:$,multispace:XY,space:x,multispace:XY'
+  set lcs+=multispace:XY
+  call assert_equal('eol:$,multispace:XY,space:x', &lcs)
+
+  " -= removes all items with the same key
+  let &lcs = 'eol:$,multispace:yY,space:x,multispace:XY'
+  set lcs-=multispace:
+  call assert_equal('eol:$,space:x', &lcs)
+
+  " ^= with different value removes all items and prepends
+  let &lcs = 'eol:$,multispace:yY,space:x,multispace:XY'
+  set lcs^=multispace:AB
+  call assert_equal('multispace:AB,eol:$,space:x', &lcs)
+
+  set lcs&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index ca65857756534f5c1a70adb29ad2210b8381dbef..50918467f6367934cc21ba3154e944dce45e4f98 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    223,
 /**/
     222,
 /**/