I dug up my old custom key events answer to get this to work, but here's the real deal, a key event to move your cell up by one which also has protection against being at the top of the notebook.
First to allow dynamic key event binding
Get["https://raw.githubusercontent.com/b3m2a1/mathematica-tools/master/\
CustomKeyEvents.wl"];
Then
AddKeyEvent[
"u",
KernelExecute[
Block[
{
curCell,
prev,
contents
},
{curCell, contents, prev} =
FrontEndExecute[{
FrontEnd`NotebookSuspendScreenUpdates[FrontEnd`SelectedNotebook[]],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], All, Cell],
FrontEnd`SelectedObject[FrontEnd`SelectedNotebook[]],
FrontEnd`NotebookRead[FrontEnd`SelectedNotebook[]],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], Previous, Cell],
FrontEnd`SelectedObject[FrontEnd`SelectedNotebook[]],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], Before, Cell]
}
][[{3, 4, 6}]];
If[MatchQ[curCell, _CellObject] && MatchQ[prev, _CellObject],
FrontEndExecute@{
FrontEnd`NotebookWrite[
FrontEnd`SelectedNotebook[],
contents,
None
],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], Previous, Cell],
FrontEnd`NotebookDelete[curCell],
FrontEnd`NotebookResumeScreenUpdates[FrontEnd`SelectedNotebook[]]
},
FrontEndExecute@
FrontEnd`NotebookResumeScreenUpdates[FrontEnd`SelectedNotebook[]]
]
]
],
"Modifiers" -> {"Command"}
];
Just type Command+u and it will move the cell, even reselecting the cell afterwards to make it easy to keep copying up.
Couldn't help myself, so here's going down, too
AddKeyEvent["d",
KernelExecute[
Block[
{curCell, next, contents},
{curCell, contents, next} =
FrontEndExecute[{
FrontEnd`NotebookSuspendScreenUpdates[FrontEnd`SelectedNotebook[]],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], All, Cell],
FrontEnd`SelectedObject[FrontEnd`SelectedNotebook[]],
FrontEnd`NotebookRead[FrontEnd`SelectedNotebook[]],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], Next, Cell],
FrontEnd`SelectedObject[FrontEnd`SelectedNotebook[]],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], After, Cell]
}][[{3, 4, 6}]];
If[MatchQ[curCell, _CellObject] && MatchQ[next, _CellObject],
FrontEndExecute@{
FrontEnd`NotebookWrite[FrontEnd`SelectedNotebook[], contents, None],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], Previous, Cell],
FrontEnd`NotebookDelete[curCell],
FrontEnd`NotebookResumeScreenUpdates[FrontEnd`SelectedNotebook[]]
},
FrontEndExecute@
FrontEnd`NotebookResumeScreenUpdates[FrontEnd`SelectedNotebook[]]]
]
],
"Modifiers" -> {"Command"}
];
Button
M.R. wanted an example, so digging deep into the memory banks here's what I have. I can't promise it's optimal but it's only 2 calls to the front end and the process goes by without the user ever being able to see anything happen
Button[
"Click Me to Move the Current Cell",
Block[
{
curCell,
prev,
contents
},
{curCell, contents} =
FrontEndExecute[{
FrontEnd`NotebookSuspendScreenUpdates[FrontEnd`SelectedNotebook[]],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], All, Cell],
FrontEnd`SelectedObject[FrontEnd`SelectedNotebook[]],
FrontEnd`NotebookRead[FrontEnd`SelectedNotebook[]],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], Previous, Cell],
FrontEnd`SelectionMove[FrontEnd`SelectedNotebook[], Before, Cell]
}
][[{3, 4}]];
FrontEndExecute@{
FrontEnd`NotebookWrite[
FrontEnd`SelectedNotebook[],
contents,
Cell
],
FrontEnd`NotebookDelete[curCell],
FrontEnd`NotebookResumeScreenUpdates[FrontEnd`SelectedNotebook[]]
}
]
]
I don't think there's a faster way to get the selection than selection move, annoyingly. If I could use FrontEnd`PreviousSiblingObject
inside the call or chain some of this stuff better I think I could do this in 1 call.
I also feel like I have a vague memory of copying a cell without needing to read out its contents first? But my memory could just be faulty.
It's up to you to figure out how to work this into the .tr
file, or you can work with the Menu
system (I know I had some library for doing dynamic key rebinding by sticking stuff in menus a number of years back)
As for the indent / dedent problem, I had some solution for this I'd been using since 2017 which worked well for me, but who knows if the script I stuck on GitHub can still be loaded without issues (see this)
FrontEnd`Private
orFE`
contexts, given that the front end itself doesn't have a mechanism for rearranging the children of aFrontEndObject
(unless things have changed in the past 4 years). This means you'll want to a) freeze notebook updates (easy) b) copy the current cell above the previous one (shouldn't be terrible with FrontEnd packets) c) delete the current cell assuming the previous worked and d) no matter what unfreeze the notebook updates. Not easy but not terrible. $\endgroup$"NotebookSuspendScreenUpdates"
, and"NotebookResumeScreenUpdates"
$\endgroup$FrontEndObject
out from the FE packet (they return) before deleting, but again I think there's no way to ensure no kernel involvement $\endgroup$