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$
  • $\begingroup$ You'll get part fo the way there using Definition[functionWithALongName] $\endgroup$ – bill s Sep 6 '17 at 17:06
  • $\begingroup$ Take a look here: github.com/b3m2a1/mathematica-BTools $\endgroup$ – Szabolcs Sep 6 '17 at 17:15
  • $\begingroup$ @Szabolcs it's actually a different package. See this. Or this answer: mathematica.stackexchange.com/questions/154743/… $\endgroup$ – b3m2a1 Sep 6 '17 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$ – mikemtnbikes Sep 7 '17 at 4:58
  • $\begingroup$ @bills I appreciate the comment, but I'd say that's about 1/4 where I want to go. $\endgroup$ – mikemtnbikes Sep 7 '17 at 4:59
2
$\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.

| improve this answer | |
$\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.