11
$\begingroup$

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

$\endgroup$
  • 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 May 17 '18 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 May 17 '18 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 May 17 '18 at 14:08
  • 5
    $\begingroup$ This is a HUGE missing feature in the front end, multi-line indent! How is that not a thing yet? $\endgroup$ – M.R. May 18 '18 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 Jun 8 '18 at 8:04
8
$\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$
8
$\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$
  • $\begingroup$ This is great :) $\endgroup$ – M.R. Jun 8 '18 at 18:49
  • $\begingroup$ @M.R. you might enjoy the edit, too $\endgroup$ – b3m2a1 Jun 8 '18 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 Jun 8 '18 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 Jun 8 '18 at 23:51
  • $\begingroup$ Admittedly my suggestion from using Microsoft products. $\endgroup$ – Edmund Jun 8 '18 at 23:52
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$
  • $\begingroup$ Nice formatting which is a case in point against the oneliner-movement, where people mistake Mathematica for Twitter... $\endgroup$ – gwr Jun 8 '18 at 7:59
  • 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 Jun 8 '18 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$ – GenericAccountName Jun 12 '18 at 20:41
3
$\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$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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