Make synstack() work on the character just after the end of the line.
diff --git a/src/eval.c b/src/eval.c
index a415849..2944758 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -17064,7 +17064,7 @@
     col = get_tv_number(&argvars[1]) - 1;	/* -1 on type error */
 
     if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
-	    && col >= 0 && (col == 0 || col < (long)STRLEN(ml_get(lnum)))
+	    && col >= 0 && col <= (long)STRLEN(ml_get(lnum))
 	    && rettv_list_alloc(rettv) != FAIL)
     {
 	(void)syn_get_id(curwin, lnum, (colnr_T)col, FALSE, NULL, TRUE);
diff --git a/src/ops.c b/src/ops.c
index aa1fca4..f8cde3b 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -1595,9 +1595,9 @@
 #endif
 
 /*
- * op_delete - handle a delete operation
+ * Handle a delete operation.
  *
- * return FAIL if undo failed, OK otherwise.
+ * Return FAIL if undo failed, OK otherwise.
  */
     int
 op_delete(oap)
@@ -1635,11 +1635,11 @@
 	mb_adjust_opend(oap);
 #endif
 
-/*
- * Imitate the strange Vi behaviour: If the delete spans more than one line
- * and motion_type == MCHAR and the result is a blank line, make the delete
- * linewise.  Don't do this for the change command or Visual mode.
- */
+    /*
+     * Imitate the strange Vi behaviour: If the delete spans more than one
+     * line and motion_type == MCHAR and the result is a blank line, make the
+     * delete linewise.  Don't do this for the change command or Visual mode.
+     */
     if (       oap->motion_type == MCHAR
 #ifdef FEAT_VISUAL
 	    && !oap->is_VIsual
@@ -1654,10 +1654,10 @@
 	    oap->motion_type = MLINE;
     }
 
-/*
- * Check for trying to delete (e.g. "D") in an empty line.
- * Note: For the change operator it is ok.
- */
+    /*
+     * Check for trying to delete (e.g. "D") in an empty line.
+     * Note: For the change operator it is ok.
+     */
     if (       oap->motion_type == MCHAR
 	    && oap->line_count == 1
 	    && oap->op_type == OP_DELETE
@@ -1678,11 +1678,11 @@
 	return OK;
     }
 
-/*
- * Do a yank of whatever we're about to delete.
- * If a yank register was specified, put the deleted text into that register.
- * For the black hole register '_' don't yank anything.
- */
+    /*
+     * Do a yank of whatever we're about to delete.
+     * If a yank register was specified, put the deleted text into that
+     * register.  For the black hole register '_' don't yank anything.
+     */
     if (oap->regname != '_')
     {
 	if (oap->regname != 0)
@@ -1749,9 +1749,9 @@
     }
 
 #ifdef FEAT_VISUAL
-/*
- * block mode delete
- */
+    /*
+     * block mode delete
+     */
     if (oap->block_mode)
     {
 	if (u_save((linenr_T)(oap->start.lnum - 1),
diff --git a/src/proto/undo.pro b/src/proto/undo.pro
index 40d166a..a914c49 100644
--- a/src/proto/undo.pro
+++ b/src/proto/undo.pro
@@ -1,5 +1,4 @@
 /* undo.c */
-void u_check __ARGS((int newhead_may_be_NULL));
 int u_save_cursor __ARGS((void));
 int u_save __ARGS((linenr_T top, linenr_T bot));
 int u_savesub __ARGS((linenr_T lnum));
diff --git a/src/undo.c b/src/undo.c
index 86b4d81..66a9ea0 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -233,6 +233,7 @@
 /*
  * Save the lines between "top" and "bot" for both the "u" and "U" command.
  * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
+ * Careful: may trigger autocommands that reload the buffer.
  * Returns FAIL when lines could not be saved, OK otherwise.
  */
     int
@@ -255,6 +256,8 @@
 /*
  * Save the line "lnum" (used by ":s" and "~" command).
  * The line is replaced, so the new bottom line is lnum + 1.
+ * Careful: may trigger autocommands that reload the buffer.
+ * Returns FAIL when lines could not be saved, OK otherwise.
  */
     int
 u_savesub(lnum)
@@ -269,6 +272,8 @@
 /*
  * A new line is inserted before line "lnum" (used by :s command).
  * The line is inserted, so the new bottom line is lnum + 1.
+ * Careful: may trigger autocommands that reload the buffer.
+ * Returns FAIL when lines could not be saved, OK otherwise.
  */
     int
 u_inssub(lnum)
@@ -284,6 +289,8 @@
  * Save the lines "lnum" - "lnum" + nlines (used by delete command).
  * The lines are deleted, so the new bottom line is lnum, unless the buffer
  * becomes empty.
+ * Careful: may trigger autocommands that reload the buffer.
+ * Returns FAIL when lines could not be saved, OK otherwise.
  */
     int
 u_savedel(lnum, nlines)
@@ -333,6 +340,10 @@
 
 /*
  * Common code for various ways to save text before a change.
+ * "top" is the line above the first changed line.
+ * "bot" is the line below the last changed line.
+ * Careful: may trigger autocommands that reload the buffer.
+ * Returns FAIL when lines could not be saved, OK otherwise.
  */
     static int
 u_savecommon(top, bot, newbot)
@@ -383,6 +394,13 @@
      * (e.g., obtained from a source control system).
      */
     change_warning(0);
+    if (bot > curbuf->b_ml.ml_line_count + 1)
+    {
+	/* This happens when the FileChangedRO autocommand changes the file in
+	 * a way it becomes shorter. */
+	EMSG(_("E834: Line count changed unexpectedly"));
+	return FAIL;
+    }
 #endif
 
     size = bot - top - 1;
@@ -3165,6 +3183,7 @@
  * Implementation of the "U" command.
  * Differentiation from vi: "U" can be undone with the next "U".
  * We also allow the cursor to be in another line.
+ * Careful: may trigger autocommands that reload the buffer.
  */
     void
 u_undoline()