9
$\begingroup$

Overview:
I have written a few mathematica packages, always using the method of writing my code in a mathematica notebook and letting mathematica autogenerate the .m package file. Currently I do not have the mathematica notebook frontend available (I am using Wolfram Engine only) and I want to use and edit those .m packages I generated a while ago. Unfortunately the .m files generated by mathematica are a huge mess because all tabs for formatting seem to have been removed when autogenerating the package from the mathematica notebook. So whilst the notebook looked fine for me the mathematica package is close to "unreadable".

Here is an example:
Screenshot from the code in the mathematica notebook: getMeanColorsHelper in mathematica notebook

Same code in the autogenerated package:

getMeanColorsHelper[image_?ImageQ,n_?IntegerQ,method_:"KMeans",distanceFunction_:EuclideanDistance]:= Module[{colorCluster ,i,meanColors,order,rules,nChannels,imageGray,meanColorsGray},
meanColors = Table[0,{i,n}];
nChannels = ImageChannels[image];
If[nChannels>1,meanColorsGray = Table[0,{i,n}]];
If[nChannels>1,imageGray = ColorConvert[image,"Grayscale"]];
colorCluster = ClusteringComponents[image,n,Method->method,DistanceFunction->distanceFunction,RandomSeeding->RandomInteger[{1,10000}]];
For[i=1,i<=n,i++,
meanColors[[i]] = Flatten[Pick[ImageData[image],colorCluster,i],1]//Mean;
If[nChannels>1,meanColorsGray[[i]]=Flatten[Pick[ImageData[imageGray],colorCluster,i],1]//Mean];
];
order = If[nChannels>1,Ordering[meanColorsGray],Ordering[meanColors]];
rules = MapIndexed[#1->First@#2&,order];
meanColors = meanColors[[#]]&/@order;
If[nChannels==1,meanColors=List[#]&/@meanColors;];
Return[meanColors];
];

So I have two questions:
1) Does anybody know about a way to automatically format my .m package files to make them readable without manually inserting all the tabs again? (maybe some code linter, ...)
(note: I do not want to format the mathematica notebook file but the .m file, which can be read by a standard text editor)
2) Is there a way to make mathematica autogenerate packages with proper indentation, so I could avoid these problems in the future (should I have access to the mathematica frontend again)?

$\endgroup$
3
  • $\begingroup$ Does this answer your question? Programmatic formatting for Mathematica code - possible? $\endgroup$ May 1 '20 at 12:23
  • $\begingroup$ I am not sure whether that is really a duplicate because I think that Leonid's packages runs only in the notebook environment... $\endgroup$ May 1 '20 at 12:25
  • 2
    $\begingroup$ @HenrikSchumacher: Yes, it is my understanding that it is only formatting code cells of a mathematica notebook. I want to format the package file, i.e. the code I can read in a standard text editor without the mathematica frontend. $\endgroup$
    – Wizard
    May 1 '20 at 12:31
7
$\begingroup$

2) Is there a way to make mathematica autogenerate packages with proper indentation, so I could avoid these problems in the future (should I have access to the mathematica frontend again)?

I do not think that Mathematica has any built-in capability to produce decently formatted plain-text code. This is something that bothers me a lot, so I've been looking for ways ... I would recommend not writing packages in notebooks, or at last not using Input cells. Code let you manage the indenting yourself.

1) Does anybody know about a way to automatically format my .m package files to make them readable without manually inserting all the tabs again?

The best way is probably to use a separate formatting program. I highly recommend the Wolfram Language plugin for IntelliJ. You seem to be looking for a way to conveniently edit old code, not just to reformat it. That is all the more reason to use a good IDE.

The installation instructions are here.

Auto-formatting your code as-is, without any manual intervention whatsoever, produces the following:

getMeanColorsHelper[image_?ImageQ, n_?IntegerQ, method_ : "KMeans", distanceFunction_ : EuclideanDistance] := Module[{colorCluster , i, meanColors, order, rules, nChannels, imageGray, meanColorsGray},
  meanColors = Table[0, {i, n}];
  nChannels = ImageChannels[image];
  If[nChannels > 1, meanColorsGray = Table[0, {i, n}]];
  If[nChannels > 1, imageGray = ColorConvert[image, "Grayscale"]];
  colorCluster = ClusteringComponents[image, n, Method -> method, DistanceFunction -> distanceFunction, RandomSeeding -> RandomInteger[{1, 10000}]];
  For[i = 1, i <= n, i++,
    meanColors[[i]] = Flatten[Pick[ImageData[image], colorCluster, i], 1] // Mean;
    If[nChannels > 1, meanColorsGray[[i]] = Flatten[Pick[ImageData[imageGray], colorCluster, i], 1] // Mean];
  ];
  order = If[nChannels > 1, Ordering[meanColorsGray], Ordering[meanColors]];
  rules = MapIndexed[#1 -> First@#2&, order];
  meanColors = meanColors[[#]]& /@ order;
  If[nChannels == 1, meanColors = List[#]& /@ meanColors;];
  Return[meanColors];
];

Just use Code -> Reformat Code in the menu, or better: look up the shortcut in the menu and use it.

A few newlines here and there, to taste, produces this:

getMeanColorsHelper[image_?ImageQ, n_?IntegerQ, method_ : "KMeans", distanceFunction_ : EuclideanDistance] :=
    Module[{colorCluster , i, meanColors, order, rules, nChannels, imageGray, meanColorsGray},
      meanColors = Table[0, {i, n}];
      nChannels = ImageChannels[image];
      If[nChannels > 1, 
        meanColorsGray = Table[0, {i, n}]
      ];
      If[nChannels > 1, 
        imageGray = ColorConvert[image, "Grayscale"]
      ];
      colorCluster = 
          ClusteringComponents[image, n, 
            Method -> method, 
            DistanceFunction -> distanceFunction, 
            RandomSeeding -> RandomInteger[{1, 10000}]
          ];
      For[i = 1, i <= n, i++,
        meanColors[[i]] = Flatten[Pick[ImageData[image], colorCluster, i], 1] // Mean;
        If[nChannels > 1, 
          meanColorsGray[[i]] = Flatten[Pick[ImageData[imageGray], colorCluster, i], 1] // Mean
        ];
      ];
      order = If[nChannels > 1, Ordering[meanColorsGray], Ordering[meanColors]];
      rules = MapIndexed[#1 -> First@#2&, order];
      meanColors = meanColors[[#]]& /@ order;
      If[nChannels == 1, 
        meanColors = List[#]& /@ meanColors;
      ];
      Return[meanColors];
    ];

Every time I type a newline, the broken line is automatically indented correctly. You do not need to re-run the auto-formatter.

maybe some code linter

A linter is not for formatting. It is for finding likely mistakes in your code. The WL plugin has this functionality too.


Here's a screenshot, also showing off local variable highlighting and dynamic usage message popups.

enter image description here

$\endgroup$
3
  • $\begingroup$ Note: A public version compatible with IntelliJ 2020.1 should be out within days. Right now, you can use the beta version for compatibility with 2020.1. $\endgroup$
    – Szabolcs
    May 1 '20 at 16:10
  • $\begingroup$ Thanks for the info. I was aware of the IntelliJ plugin, but did not know about the "reformat code" functionality. $\endgroup$
    – Wizard
    May 7 '20 at 13:17
  • $\begingroup$ As I am just needing this for a few packages and am currently not doing any mathematica package development, I was hoping for a non commercial solution to the problem, which is why a had not accepted the answer yet. Not to say that the plugin is not worth its money. I am actually really thankful for the work halirutan has done for this community! $\endgroup$
    – Wizard
    Jun 4 '20 at 10:43

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.