Move input cursor to after cell group

button = Button["Move", 
   SelectionMove[EvaluationCell[], After, GeneratedCell]];

Print["one"]
Print[button]
Print["two"]
Print["three"]
Print["four"]

enter image description here

Alternatively, using a number greater than 1 in the fourth argument of SelectionMove gives the same behavior:

button = Button["Move", 
   SelectionMove[EvaluationCell[], After, CellGroup, 2]];

Print["one"]
Print[button]
Print["two"]
Print["three"]
Print["four"]

enter image description here


Instead, it is moved to after the cell containing the button, as if I had specified Cell instead of CellGroup in SelectionMove.

On October, I reported this issue to the Wolfram support: [CASE:4323226]. They confirmed the bug and suggested a workaround. Here is the response:

It does appear that SelectionMove is not behaving properly. <...>

In the meantime, here is one option for making the insertion point move to the next CellGroup. Please use the Option GeneratedCell for this. A code snippet is given below:

"something that gives the first output and puts the EvaluationCell[] in a CellGroup"
SelectionMove[EvaluationCell[], After, GeneratedCell, AutoScroll -> False]

But I immediately discovered another bug with this workaround: if evaluation cell has no GeneratedCell immediately after it, the visual insertion point in the Notebook just disappears, and SelectionMove doesn't return $Failed as it should according to the Documentation. They confirmed this bug too ([CASE:4326002]), but hadn't suggested any workaround.

As a workaround I would suggest to check the type of the next cell explicitly, using NextCell[]:

If[TrueQ@CurrentValue[NextCell[], GeneratedCell], 
 SelectionMove[EvaluationCell[], After, GeneratedCell, AutoScroll -> False],
 SelectionMove[EvaluationCell[], After, Cell, AutoScroll -> False]]

But I'm not completely sure that the workaround suggested by the support is valid in the long-term: I believe that it may reflect another bug in SelectionMove, and hence can change in the future. So I wouldn't use this workaround, and would use the well-documented and working functionality:

SelectionMove[EvaluationCell[], All, CellGroup, AutoScroll -> False];
If[Length[SelectedCells[]] > 1, 
  SelectionMove[EvaluationCell[], After, CellGroup, AutoScroll -> False], 
  SelectionMove[EvaluationCell[], After, Cell, AutoScroll -> False]];

The problem with this solution is that the user (with a very low probability) can click in the Notebook and change the selection created by the first row of the code. We can temporarily disable this using undocumented functions FrontEnd`NotebookSuspendScreenUpdates[] and FrontEnd`NotebookResumeScreenUpdates[]:

FrontEndExecute@FrontEnd`NotebookSuspendScreenUpdates[EvaluationNotebook[]];
SelectionMove[EvaluationCell[], All, CellGroup, AutoScroll -> False];
If[Length[SelectedCells[]] > 1, 
  SelectionMove[EvaluationCell[], After, CellGroup, AutoScroll -> False], 
  SelectionMove[EvaluationCell[], After, Cell, AutoScroll -> False]];
FrontEndExecute@FrontEnd`NotebookResumeScreenUpdates[EvaluationNotebook[]]

I believe that this solution is reliable despite using the undocumented functionality.

I tested these methods for a very sensitive use case:

  • Delete pre-existing output under EvaluationCell[] from CellProlog

Unfortunately cell groups api is very limited so can't say it is robust but it should work assuming the cell group structure you've described is in place.

Button["Move", 
  FrontEndExecute[{
   SelectionMove[EvaluationCell[], All, Cell];
   SelectionMove[EvaluationNotebook[], All, CellGroup];
   SelectionMove[EvaluationNotebook[], After, CellGroup]
  }], 
  Evaluator -> None
]