14
$\begingroup$

How can I indent properly long code in Mathematica? Are there some best practices?

$\endgroup$
9
  • 4
    $\begingroup$ Use code style cells instead of input style cells and indent manually, as you would in a plain text editor. Or just use a plain text editor. Alternatively, do not write long code (which is not always possible, but when it is, it also makes things more maintainable, so it's good practice). $\endgroup$
    – Szabolcs
    Commented May 17, 2018 at 11:23
  • 5
    $\begingroup$ By "do not write long code" I meant: break down your code into pieces that are short enough to be easy to understand. Each can live in its own input cell. $\endgroup$
    – Szabolcs
    Commented May 17, 2018 at 11:24
  • $\begingroup$ Welcome to Mathematica.SE! I suggest the following: 1) As you receive help, try to give it too, by answering questions in your area of expertise. 2) Take the tour and check the faqs! 3) When you see good questions and answers, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge. Also, please remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign! $\endgroup$
    – Chris K
    Commented May 17, 2018 at 14:08
  • 7
    $\begingroup$ This is a HUGE missing feature in the front end, multi-line indent! How is that not a thing yet? $\endgroup$
    – M.R.
    Commented May 18, 2018 at 15:54
  • 1
    $\begingroup$ @M.R. and b3m2a1 and others, stylesheets used in DevTools` allow multiline indentation on Tab hit but I didn't advertise it too much because I only tested it in .m/code cells and sometimes selection is not quite right afterwards, indentation is correct though. $\endgroup$
    – Kuba
    Commented Jun 8, 2018 at 8:04

4 Answers 4

10
$\begingroup$

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:

enter image description here

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.

$\endgroup$
6
  • $\begingroup$ This is great :) $\endgroup$
    – M.R.
    Commented Jun 8, 2018 at 18:49
  • $\begingroup$ @M.R. you might enjoy the edit, too $\endgroup$
    – b3m2a1
    Commented Jun 8, 2018 at 19:46
  • $\begingroup$ 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. $\endgroup$
    – Edmund
    Commented Jun 8, 2018 at 23:48
  • $\begingroup$ @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. $\endgroup$
    – b3m2a1
    Commented Jun 8, 2018 at 23:51
  • $\begingroup$ Admittedly my suggestion from using Microsoft products. $\endgroup$
    – Edmund
    Commented Jun 8, 2018 at 23:52
9
$\begingroup$

GeneralUtilities`HoldPrettyForm

Needs["GeneralUtilities`"]
HoldPrettyForm[Row[Table[Table[Plot[Sin[i x] Cos[j x], {x, 0, Pi}], 
  {i, 1, 5}], {j, 1, 3}]]]

enter image description here

$\endgroup$
4
  • $\begingroup$ 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! $\endgroup$
    – Davi
    Commented Aug 11, 2021 at 18:50
  • 2
    $\begingroup$ @Davi, you can see the definition(s) of functions used in HoldPrettyForm using PrintDefinitions[HoldPrettyForm]. To replace tabs with " " you can use RawBoxes@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}]]] $\endgroup$
    – kglr
    Commented Aug 11, 2021 at 20:06
  • 1
    $\begingroup$ ... 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}]]] $\endgroup$
    – kglr
    Commented Aug 11, 2021 at 21:04
  • 1
    $\begingroup$ Thanks, @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> ] $\endgroup$
    – Davi
    Commented Aug 12, 2021 at 21:29
4
$\begingroup$

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 :)

$\endgroup$
3
$\begingroup$

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"
    ];
]
$\endgroup$
2
  • 1
    $\begingroup$ "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. $\endgroup$
    – Szabolcs
    Commented Jun 8, 2018 at 8:09
  • $\begingroup$ @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. $\endgroup$ Commented Jun 12, 2018 at 20:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.