3
$\begingroup$

When programming, I try and name my functions and arguments clearly and document them with usage:: so I can keep track of everything; e.g.,

functionWithALongName::usage = 
  "functionWithALongName[x] transforms x into y with options Opt1-> opt1..."

I'd like to automate this process so that the function name, functionWithALongName), the variables ,x, and the options are included in my documentation instead of having to write them all out.

I'm wondering (a) how do I reference the name of the function called and (b) if anyone has done this and would share their code so I don't have to reinvent the wheel?

$\endgroup$
6
  • $\begingroup$ You'll get part fo the way there using Definition[functionWithALongName] $\endgroup$
    – bill s
    Commented Sep 6, 2017 at 17:06
  • $\begingroup$ Take a look here: github.com/b3m2a1/mathematica-BTools $\endgroup$
    – Szabolcs
    Commented Sep 6, 2017 at 17:15
  • $\begingroup$ @Szabolcs it's actually a different package. See this. Or this answer: mathematica.stackexchange.com/questions/154743/… $\endgroup$
    – b3m2a1
    Commented Sep 6, 2017 at 17:52
  • $\begingroup$ @Szabolcs that's way more complex than I'm looking for. I'd like to be able to reference it within, say, a StringFormat function. $\endgroup$ Commented Sep 7, 2017 at 4:58
  • $\begingroup$ @bills I appreciate the comment, but I'd say that's about 1/4 where I want to go. $\endgroup$ Commented Sep 7, 2017 at 4:59

1 Answer 1

3
$\begingroup$

Here's a partial implementation, working from the DownValues and friends:

We start with a bunch of DownValues cleaning:

$usageTypeReplacements =
  {
   Integer :> int,
   Real :> float,
   String :> str,
   List :> list,
   Association :> assoc,
   Symbol :> sym
   };
$usageSymNames =
  {
   Alternatives -> alt,
   PatternTest -> test,
   Condition -> cond,
   s_Symbol :>
    RuleCondition[
     Symbol@
      ToLowerCase[StringTake[SymbolName[Unevaluated@s], UpTo[3]]],
      True
     ]
   };
symbolUsageReplacementPattern[names_, conts_] :=
  s_Symbol?(
     GeneralUtilities`HoldFunction[
      ! MatchQ[Context[#], conts] &&
       ! 
        MemberQ[$ContextPath, Context[#]] &&
       ! 
        KeyMemberQ[names, SymbolName@Unevaluated[#]]
      ]
     ) :>
   RuleCondition[
    ToExpression@
     Evaluate[$Context <>

       With[{name = SymbolName@Unevaluated[s]},
        If[StringLength@StringTrim[name, "$"] > 0,
         StringTrim[name, "$"],
         name
         ]
        ]
      ],
    True];
usagePatternReplace[
   vals_,
   reps_: {}
   ] :=
  With[{
    names = AssociationMap[Null &, {}(*Names[]*)],
    conts = 
     Alternatives @@ {"System`", "FrontEnd`", "PacletManager`", 
       "Internal`"}
    },
   Replace[
      Replace[
       #,
        {
        Verbatim[HoldPattern][a___] :> a
        },
       {2, 10}
       ],
      Join[$usageTypeReplacements, $usageSymNames],
       Depth[#]
      ] &@
    ReplaceRepeated[
     FixedPoint[
      Replace[
        #,
        {
         Verbatim[Pattern][_, e_] :>
          e,
         Verbatim[HoldPattern][Verbatim[Pattern][_, e_]] :>

          HoldPattern[e],
         Verbatim[HoldPattern][Verbatim[HoldPattern][e_]] :>

           HoldPattern[e]
         },
        1
        ] &,
      vals
      ],
     Flatten@{
       reps,
       Verbatim[PatternTest][_, ColorQ] :>
        color,
       Verbatim[PatternTest][_, ImageQ] :>
        im,
       Verbatim[Optional][name_, _] :>
        name,
       Verbatim[Pattern][_, _OptionsPattern] :>
        Sequence[],
       Verbatim[Pattern][name_, _] :>
        name,
       Verbatim[PatternTest][p_, _] :>
        p,
       Verbatim[Condition][p_, _] :>
        p,
       Verbatim[Alternatives][a_, ___][___] |
         Verbatim[Alternatives][a_, ___][___][___] |
         Verbatim[Alternatives][a_, ___][___][___][___] |
         Verbatim[Alternatives][a_, ___][___][___][___][___] |
         Verbatim[Alternatives][a_, ___][___][___][___][___][___] :>
        a,
       Verbatim[Alternatives][a_, ___] :>
        RuleCondition[
         Blank[
          Replace[Hold@a,
           {
            Hold[p : Verbatim[HoldPattern][_]] :>
             p,
            Hold[e_[___]] :> e,
            _ :> a
            }
           ]
          ],
         True
         ],
       Verbatim[Verbatim][p_][a___] :>
        p,
       Verbatim[Blank][] :>
        expr,
       Verbatim[Blank][
         t : Alternatives @@ Keys[$usageTypeReplacements]] :>

        RuleCondition[
         Replace[t,
          $usageTypeReplacements
          ],
         True
         ],
       Verbatim[Blank][t_] :>
        t,
       Verbatim[BlankSequence][] :>

        Sequence @@ ToExpression[{"expr1", "expr2"}],
       Verbatim[BlankNullSequence][] :>
        Sequence[],
       symbolUsageReplacementPattern[names, conts],
       h_[a___, Verbatim[Sequence][b___], c___] :> h[a, b, c]
       }
     ]
   ];

Then we take a function that uses this to build out a little template for the DownValues:

generateSymbolUsage[f_, 
   defaultMessages : {(_Rule | _RuleDelayed) ...} : {}] :=
  With[
   {
    uml =
     Replace[defaultMessages,
      {
       (h : Rule | RuleDelayed)[Verbatim[HoldPattern][pat___], m_] :>
        h[HoldPattern[Verbatim[HoldPattern][pat]], m],
       (h : Rule | RuleDelayed)[pat___, m_] :>
        h[Verbatim[HoldPattern][pat], m],
       _ -> Nothing
       },
      {1}
      ]
    },
   Replace[
    DeleteDuplicates@usagePatternReplace[Keys@getCodeValues[f]],
    {
     Verbatim[HoldPattern][s_[a___]] :>
      With[
       {
        uu =
         StringTrim@
          Replace[HoldPattern[s[a]] /. uml,

           Except[_String] :>

            Replace[s::usage, Except[_String] -> ""]
           ],
        sn = ToString[Unevaluated@s],
        meuu = ToString[Unevaluated[s[a]], InputForm]
        },
       (* change this chunk to customize the template *)
       If[! StringContainsQ[uu, meuu],
        meuu <> " " <>


          If[! StringStartsQ[uu, 
             sn | (Except[WordCharacter] .. ~~ "RowBox[{" ~~ sn)],
           uu,
           ""
           ] // StringTrim,
        StringCases[uu, 
          (StartOfLine | StartOfString) ~~ Except["\n"] ... ~~ meuu ~~
            Except["\n"] ... ~~ EndOfLine,
          1
          ][[1]]
        ]
       ],
     _ -> Nothing
     },
    {1}
    ]
   ];
generateSymbolUsage~SetAttributes~HoldFirst;

generateSymbolUsage /@ {generateSymbolUsage, 
  autoCompletionsExtractSeeder}

{{"generateSymbolUsage[f, def]"}, \
{"autoCompletionsExtractSeeder[test, n]", 
  "autoCompletionsExtractSeeder[alt, n]", 
  "autoCompletionsExtractSeeder[pat, n]"}}

This doesn't fill out the rest of the template, but we can tweak it like so:

generateSymbolUsage2[f_, 
   defaultMessages : {(_Rule | _RuleDelayed) ...} : {}] :=
  With[
   {
    uml =
     Replace[defaultMessages,
      {
       (h : Rule | RuleDelayed)[Verbatim[HoldPattern][pat___], m_] :>
        h[HoldPattern[Verbatim[HoldPattern][pat]], m],
       (h : Rule | RuleDelayed)[pat___, m_] :>
        h[Verbatim[HoldPattern][pat], m],
       _ -> Nothing
       },
      {1}
      ]
    },
   Replace[
    DeleteDuplicates@usagePatternReplace[Keys@getCodeValues[f]],
    {
     Verbatim[HoldPattern][s_[a___]] :>
      With[
       {
        aerger =
         {ReleaseHold@
           Map[
            Function[
             Null,
             ToString[#, InputForm],
             HoldFirst
             ],
            Hold[a]
            ]},
        opa = ToString[#, InputForm] & /@ Options[s],
        meuu = ToString[Unevaluated[s[a]], InputForm]
        },
       TemplateApply[
        meuu <> 
         " takes `nopenopenope`argument`argplur` \
`argggggggggggg``hasopa``oppppppppp`",
        <|

         "nopenopenope" -> If[Length@aerger == 0, "no ", ""],
         "argplur" -> If[Length@aerger == 1, "", "s"],
         "argggggggggggg" ->
          Switch[Length@aerger,
           0 | 1,
           StringJoin@aerger,
           2,
           StringRiffle[aerger, " and "],
           _,
           StringJoin@
            Insert[
             Riffle[aerger, ", "],
             "and " ,
             -2
             ]
           ],
         "hasopa" -> If[Length@opa > 0, " and options ", ""],
         "oppppppppp" ->
          Switch[Length@opa,
           0 | 1,
           StringJoin@opa,
           2,
           StringRiffle[opa, " and "],
           _,
           StringJoin@
            Insert[
             Riffle[opa, ", "],
             "and " ,
             -2
             ]
           ]
         |>
        ]
       ],
     _ -> Nothing
     },
    {1}
    ]
   ];
generateSymbolUsage2~SetAttributes~HoldFirst;

and then we're like 80% of the way there:

StringRiffle[generateSymbolUsage2@#, "\n"] & /@ {generateSymbolUsage2,
   AutoSyntax}

{"generateSymbolUsage2[f, def] takes arguments f and def", \
"AutoSyntax[f] takes argument f and options \"SyntaxInformation\" -> \
{}, \"Autocompletions\" -> {}, \"UsageMessages\" -> {}, \"SetSyntax\" \
-> False, and \"GatherInfo\" -> True"}

This is undoubtedly not my most robust piece of code ever, though.

$\endgroup$
1
  • $\begingroup$ I belief this answer is incomplete and requires the function getCodeValues that can be found in your other answer mathematica.stackexchange.com/a/164008/45020. Note that it is used here but never defined. That answer might well contain a more complete answer in general so I think a link to that answer might be useful here. $\endgroup$
    – Kvothe
    Commented Apr 13, 2023 at 15:01

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.