How can I indent properly long code in Mathematica? Are there some best practices?
4 Answers
Here's a quick plug for some stuff in that question I linked. I have it all built into my GitHub so you can load a thing to let your cells be indentable by:
loadIndenter[] :=
(
BeginPackage["Indenter`"];
Indenter`MakeIndentable::usage = "Makes indentable";
BeginPackage["`Package`"];
Indenter`Package`$PackageName = "Indenter";
EndPackage[];
Get["https://github.com/b3m2a1/mathematica-BTools/raw/master/Packages/FrontEnd/StylesheetEdits.m"];
Get["https://github.com/b3m2a1/mathematica-BTools/raw/master/Packages/FrontEnd/IndentableCells.m"];
EndPackage[];
)
Then with that loaded just make a cell and run:
MakeIndentable@yourCellHere
And it'll be indentable. Here's a demo:
Indenting is done by selecting a piece and using Command+Shift+]
Dedenting is done with Command+Shift+[
Toggling between "\n" newlines and "\[IndentingNewlines]" is done with Command+Alt+[
Whenever you try to indent a cell any "\[IndentingNewlines]" get converted to "\n"-type newlines and I attempt to preserve the formatting if possible (that's the last thing I do in that GIF)
Indentable notebooks
That function can also make a full notebook or stylesheet indentable. If you open up a Package notebook with this:
makeNewPackageNotebook[ops : OptionsPattern[Notebook]] :=
With[
{
flops = FilterRules[{ops}, Options@Notebook]
},
FrontEndTokenExecute["NewPackage"];
SetOptions[Notebooks[][[1]], flops];
Notebooks[][[1]]
]
You can immediately make it indentable by:
MakeIndentable@
makeNewPackageNotebook[]
This'll hang for a bit while it finds and makes a stylesheet for the package.
Alternately you can scrape off a Notebook what makes it indentable and pass that in directly:
$indentingStyleSheet =
With[{nb = CreateDocument[{}, Visible -> False]},
MakeIndentable@nb;
(NotebookClose[nb]; #) &@Options[nb, StyleDefinitions]
];
newIndentablePackage[ops : OptionsPattern[Notebook]] :=
makeNewPackageNotebook[
Flatten@{ops, $indentingStyleSheet /. (StyleDefinitions ->
"Default.nb") -> (StyleDefinitions -> "Package.nb")}
]
Now any package notebook made with that function will have this indentation autoconfigured.
- 46,870
- 3
- 92
- 239
-
-
-
This is definitely something that should be considered for the Front End but with Shift+Tab and Ctrl+Shift+Tab so as to be consistent with most editors that have this feature. – Edmund Jun 08 '18 at 23:48
-
@Edmund both TextWrangler and Atom use Command-] and Command-[ so I used that scheme, but had to find things for which there already were
"MenuKey"bindings. In principal one could add one's own bindings to get these in cleaner. – b3m2a1 Jun 08 '18 at 23:51 -
-
@Edmund I'm sure any consistent scheme would be good :) Obviously making things play nicely with this indentation was tough with the box-language but I'm sure if I can do it John Fultz can do it better (there are no doubt cases mine misses that I've never seen). – b3m2a1 Jun 08 '18 at 23:53
GeneralUtilities`HoldPrettyForm
Needs["GeneralUtilities`"]
HoldPrettyForm[Row[Table[Table[Plot[Sin[i x] Cos[j x], {x, 0, Pi}],
{i, 1, 5}], {j, 1, 3}]]]
-
This is a very interesting approach. It can be applied to large code blocks and the Output cell can be promptly converted into Code cell. However, some basic customization on the output would be welcome. In particular, changing the tab based indent into a double space indent. I just did a google search for more information on HoldPrettyForm, but it seems that this page is the single page in the internet about this function! – Davi Aug 11 '21 at 18:50
-
2@Davi, you can see the definition(s) of functions used in
HoldPrettyFormusingPrintDefinitions[HoldPrettyForm]. To replace tabs with" "you can useRawBoxes@Map[# /. s_String :> StringReplace[ s, "\t" -> " "] &, #, All] &@ToBoxes@ HoldPrettyForm[ Row[Table[ Table[Plot[Sin[i x] Cos[j x], {x, 0, Pi}], {i, 1, 5}], {j, 1, 3}]]]– kglr Aug 11 '21 at 20:06 -
1... or use
ClearAll[replaceTabs]; replaceTabs = RawBoxes@*(Replace[#, s_String :> StringReplace[ s, "\t" -> " "], All] &)@*ToBoxes; replaceTabs @ HoldPrettyForm[Row[Table[Table[Plot[Sin[i x] Cos[j x], {x, 0, Pi}], {i, 1, 5}], {j, 1, 3}]]]– kglr Aug 11 '21 at 21:04 -
1Thanks, @kglr. Essentially it works. Just a small issue: when applying to any term that contains a pattern like 'x_', (e.g.,
f[x_] := x), an error is returned. I slightly extended your approach to avoid this issue. The following should work:replaceTabs = Replace[#, s_String :> StringReplace[s, "\t" -> " "], All] &; replacePatterns = ReplaceAll[#, InterpretationBox[string_String, somethingElse__] :> string] & ; RawBoxes@replaceTabs@replacePatterns@ToBoxes@HoldPrettyForm[ f[x_,y_] := <Insert your long code here> ]– Davi Aug 12 '21 at 21:29
As already said in other answers, this is very subjective, but here a tip I find very useful for coding plots: I put every command on a different line, and I use the comma separator at the beginning of the line. This is quite handy for commenting parts of the code, to enable/disable some plot options quickly (i.e. just commenting the whole line and not commenting through 2 lines).
This is an example of what I mean:
DensityPlot[(Exp[-(x^2 + y^2)], {x,-4,4}, {y,-4,4}
,PlotRange -> All
,PlotPoints -> 150
,LabelStyle -> {24, Black}
,FrameStyle -> Black
,ImageSize -> 300
]
If I want to comment the ImageSize for example, I just select the whole line and comment it:
DensityPlot[(Exp[-(x^2 + y^2)], {x,-4,4}, {y,-4,4}
,PlotRange -> All
,PlotPoints -> 150
,LabelStyle -> {24, Black}
,FrameStyle -> Black
(*,ImageSize -> 300*)
]
However, for doing the same thing having the commas at the end of the line, I would have to do this:
DensityPlot[(Exp[-(x^2 + y^2)], {x,-4,4}, {y,-4,4},
PlotRange -> All,
PlotPoints -> 150,
LabelStyle -> {24, Black},
FrameStyle -> Black(*,
ImageSize -> 300*)
]
This ofc holds just for the last option line (the other ones are easily commented in both the cases), but if you have nested options, like having epilogs etc in your code, you may have "more than one last option line" in the code, and then having the comma at the beginning of the code becomes more useful :)
- 6,057
- 13
- 28
I have a feeling many are going to find things they don't like slightly about my indentation, and this is totally a subjective question based on preference. I don't think there's a standardized best practice for coding style.
For me, similarly to other languages, have the brackets line up vertically with the start of the function name. Having commas on separate lines can make it clear to separate args for some functions. As things get more complicated tab over.
I usually use this kind of indentation in a "code" style cell rather than an "input" cell, or use a standard text editor.
Here's my basic form:
func[x_] :=
Module[
{
foo = 1,
bar = 2
},
If[cond,
Print[foo];
,
(* Else *)
Print[bar];
];
Switch[x,
_Integer,
Print[2 * val];
,
_String,
Print["2 " <> val];
,
_,
Print["Default"];
];
(* Some random code from an answer I posted to a different question *)
Export["test.gif",
ImageResize[#, 100] & /@
Table[
ImageTrim[
Import["ExampleData/coneflower.jpg"]
,
{{0, 0}, {m, m}}
]
,
{m, 100, 50, -5}
]
,
"GIF"
];
]
- 1,543
- 9
- 12
-
1"many are going to find things they don't like slightly about my indentation " I think lots of people use this style. I certainly do. – Szabolcs Jun 08 '18 at 08:09
-
@Szabolcs Even the answers which are very similar in this thread have subtle differences that some developers might feel strongly about. I just meant to say this isn't necessarily the "best practice" but at this point I'm not sure there is one, and there's likely to be other answers which are perfectly valid coming.
Nice that I picked up GeneralUtilities`HoldPrettyForm and @b3m2a1's nice indenter.
– GenericAccountName Jun 12 '18 at 20:41


Also, please remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign!
– Chris K May 17 '18 at 14:08