The following may not fully solve your problem, but seems to logically belong here and is too long for a comment.
I do use tabs in my code formatter. This question prompted me to write a palette for the formatter, which was long overdue (it surely can be improved). The palette should work with both "Input"-style and "Program"-style cells. The palette relies on the CodeFormatter`
package, so you will have to put it somewhere where Mathematica can find it, e.g. into $UserBaseDirectory/Applications
. Anyways, here is the code:
Clear[extractText];
extractText[] :=
First[FrontEndExecute[
FrontEnd`ExportPacket[
NotebookSelection[SelectedNotebook[]],
"InputText"]]];
Clear[getCellType];
getCellType[boxes_] :=
With[{prcells =
Cases[boxes, Cell[___, "Program", ___], {0, Infinity}]},
"Program" /; prcells =!= {}];
getCellType[boxes_] := "Boxes";
Clear[extractBoxedCode];
extractBoxedCode[boxes_] :=
If[# === {}, {}, First@#] &@
Cases[boxes,
Cell[BoxData[{b__}], ___] :> RowBox[{b}],
{0, Infinity}]
Clear[stringCodeToBoxes];
stringCodeToBoxes[code_String] :=
ToExpression[#, InputForm, MakeBoxes] &@
StringReplace[#, "\n" -> ""] &@code;
ClearAll[writeCell];
writeCell[cellcont_, type_] :=
NotebookWrite[SelectedNotebook[], Cell[cellcont, type]];
ClearAll[throwError];
throwError[] := (
Message[formattingPalette::frmtfail];
Throw[$Failed, formattingPalette]
);
ClearAll[catchError];
SetAttributes[catchError, HoldAll];
catchError[code_] := Catch[code, formattingPalette];
ClearAll[makeFormatButton];
SetAttributes[makeFormatButton, HoldAll];
makeFormatButton[backup_Symbol] :=
Button["Format",
catchError@
Module[{read, celltype, boxedCode, formatted},
backup = read = NotebookRead[SelectedNotebook[]];
celltype = getCellType[read];
boxedCode =
If[celltype === "Boxes",
extractBoxedCode[read],
(* else *)
stringCodeToBoxes[extractText[]]
];
If[boxedCode === {}, throwError[]];
formatted = Catch[CodeFormatter`FullCodeFormat[boxedCode], _];
If[MatchQ[formatted , { $Failed, _}], throwError[]];
writeCell[BoxData[formatted], "Input"];
If[celltype === "Program",
(* Create a "Program"-style cell with the same formatting *)
SelectionMove[SelectedNotebook[], Previous, Cell];
writeCell["\<" <> extractText[] <> "\>", "Program"];
]
]];
ClearAll[formattingPalette];
formattingPalette::frmtfail = "Formatting failed";
formattingPalette[] :=
Module[{},
If[Needs["CodeFormatter`"] === $Failed,
Return[$Failed]
];
CreatePalette[
Module[{backup},
{
makeFormatButton[backup],
Button["Undo format",
NotebookWrite[SelectedNotebook[], backup]
]
}
]]];
To use it, you have to first select the code cell with the code you wish to format, then press the Format
button. This will create a cell with formatted code in place of the original cell. If something goes wrong and / or you don't like the way the formatter formatted your code, select the cell with the resulting code again, and press the "Undo format" button on the palette - it should replace the cell's contents back to its original one.
The above code, of course, will eventually be placed in a package, perhaps becoming a part of the formatter. But, it hopefully can be used right now in its current form. Note that the formatter currently can not handle a lot of boxes, but if I start getting bug reports with particular pieces of code which it finds problematic, I will try to improve it and fix these bugs.
Just to test it, here is a block of code on which this does work decently:
ClearAll[selectJIT];
selectJIT[pred_, listType_] :=
selectJIT[pred, Verbatim[listType]] =
Block[{lst},
With[{decl = {Prepend[listType, lst]}},
Compile @@
Hold[decl, Select[lst, pred], CompilationTarget -> "C",
RuntimeOptions -> "Speed"]]];
Here is how the formatted text ("Program" style) cell looks:
Format -> Style
menu item to check). $\endgroup$