The answer is a qualified yes.
Yes, we can do all of this, but it won't be as finely tuned as if we did it by hand. There are 4 things we need to handle, "usage"
messages, autocompletions, SyntaxInformation
, and "argx"
error patterns.
I've put this all in a package, so if you just want to use it, scroll to the bottom, otherwise we'll through each step individually:
( N.B. The package version has a number of little bug fixes and will be actively maintained, please use it until I can make sure all the fixes are in place and merge them here. )
Common Code
I had one function common to all the parts of this system:
getCodeValues[f_Symbol,
vs :
{Repeated[
OwnValues | DownValues | SubValues | UpValues]} : {OwnValues,
DownValues, SubValues, UpValues}
] :=
If[Intersection[Attributes@f, { ReadProtected, Locked}] === {
Locked, ReadProtected},
{},
Join @@ Map[#[f] &, vs]
];
getCodeValues~SetAttributes~HoldFirst
It'll keep popping up as we go
Usage Messages
Cleaning the patterns
The first step in generating these messages is knowing what we need from them. In general we want a cleaned form of the arguments to the function, any existing "usage"
messages, and the function name. If we have a function:
test[a_, b_, c_]:=...
we want some usage that starts:
"test[a, b, c] ..."
so that it can be used via the FE MakeTemplate system (see more of Itai's answers to get a sense for how this works)
Of course, for more complicated patterns we need to be smarter about how we clean. I won't cover all of the cases I tried to handle, but in general I tried to reduce everything to a type or to a Pattern
name so that I could generate nice Symbol
forms of the variables.
In the interest of space (as I would otherwise hit the body-length limit on this post), you can see where I did this before in my answer to How can I automatically generate usage messages?
Generating the messages
Then we clean and call ToString
on the DownValues
to generate usages:
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]
},
StringReplace["FEInfoExtractor`Private`" -> ""]@
If[! StringContainsQ[uu, meuu],
meuu <> " " <>
Which[
uu == "",
"has no usage message", !
StringStartsQ[uu,
sn | (Except[WordCharacter] .. ~~ "RowBox[{" ~~
Except[WordCharacter] ... ~~ sn)],
uu,
True,
""
],
StringCases[uu,
(StartOfLine | StartOfString) ~~ Except["\n"] ... ~~ meuu ~~
Except["\n"] ... ~~ EndOfLine,
1
][[1]]
]
],
_ -> Nothing
},
{1}
]
];
generateSymbolUsage~SetAttributes~HoldFirst;
You can then use this to build a decent "usage"
message that will fill via template
test::usage = "my test function";
test[a_] := a;
generateSymbolUsage@test
{"test[a] my test function"}
Autocompletions
To get a first sense for how this works look at this post by Szabolcs.
I've been making good use of them for my own things, and have a little system to more conveniently define autocompletion types:
Autocompletion Aliases
First I just defined a bunch of aliased forms for the possible autocompletions:
$autoCompletionFormats =
Alternatives @@ Join @@ {
Range[0, 9],
{
_String?(FileExtension[#] === "trie" &),
{
_String | (Alternatives @@ Range[0, 9]) | {__String},
(("URI" | "DependsOnArgument") -> _) ...
},
{
_String | (Alternatives @@ Range[0, 9]) | {__String},
(("URI" | "DependsOnArgument") -> _) ...,
(_String | (Alternatives @@ Range[0, 9]) | {__String})
},
{
__String
}
},
{
"codingNoteFontCom",
"ConvertersPath",
"ExternalDataCharacterEncoding",
"MenuListCellTags",
"MenuListConvertFormatTypes",
"MenuListDisplayAsFormatTypes",
"MenuListFonts",
"MenuListGlobalEvaluators",
"MenuListHelpWindows",
"MenuListNotebookEvaluators",
"MenuListNotebooksMenu",
"MenuListPackageWindows",
"MenuListPalettesMenu",
"MenuListPaletteWindows",
"MenuListPlayerWindows",
"MenuListPrintingStyleEnvironments",
"MenuListQuitEvaluators",
"MenuListScreenStyleEnvironments",
"MenuListStartEvaluators",
"MenuListStyleDefinitions",
"MenuListStyles",
"MenuListStylesheetWindows",
"MenuListTextWindows",
"MenuListWindows",
"PrintingStyleEnvironment",
"ScreenStyleEnvironment",
"Style"
}
};
$autocompletionAliases =
{
"None" | None | Normal -> 0,
"AbsoluteFileName" | AbsoluteFileName -> 2,
"FileName" | File | FileName -> 3,
"Color" | RGBColor | Hue | XYZColor -> 4,
"Package" | Package -> 7,
"Directory" | Directory -> 8,
"Interpreter" | Interpreter -> 9,
"Notebook" | Notebook -> "MenuListNotebooksMenu",
"StyleSheet" -> "MenuListStylesheetMenu",
"Palette" -> "MenuListPalettesMenu",
"Evaluator" | Evaluator -> "MenuListGlobalEvaluators",
"FontFamily" | FontFamily -> "MenuListFonts",
"CellTag" | CellTags -> "MenuListCellTags",
"FormatType" | FormatType -> "MenuListDisplayAsFormatTypes",
"ConvertFormatType" -> "MenuListConvertFormatTypes",
"DisplayAsFormatType" -> "MenuListDisplayAsFormatTypes",
"GlobalEvaluator" -> "MenuListGlobalEvaluators",
"HelpWindow" -> "MenuListHelpWindows",
"NotebookEvaluator" -> "MenuListNotebookEvaluators",
"PrintingStyleEnvironment" | PrintingStyleEnvironment ->
"PrintingStyleEnvironment",
"ScreenStyleEnvironment" | ScreenStyleEnvironment ->
ScreenStyleEnvironment,
"QuitEvaluator" -> "MenuListQuitEvaluators",
"StartEvaluator" -> "MenuListStartEvaluators",
"StyleDefinitions" | StyleDefinitions ->
"MenuListStyleDefinitions",
"CharacterEncoding" | CharacterEncoding |
ExternalDataCharacterEncoding ->
"ExternalDataCharacterEncoding",
"Style" | Style -> "Style",
"Window" -> "MenuListWindows"
};
$autocompletionTable = {
f : $autoCompletionFormats :> f,
Sequence @@ $autocompletionAliases,
s_String :> {s}
};
Then we compile these aliases down to the appropriate form:
autocompletionPreCompile[v : Except[{__Rule}, _List | _?AtomQ]] :=
Replace[
Flatten[{v}, 1],
$autocompletionTable,
{1}
];
autocompletionPreCompile[o : {__Rule}] :=
Replace[o,
(s_ -> v_) :>
(
Replace[s, _Symbol :> SymbolName[s]] ->
autocompletionPreCompile[v]
),
1
];
autocompletionPreCompile[s : Except[_List], v_] :=
autocompletionPreCompile[{s -> v}];
autocompletionPreCompile[l_, v_] :=
autocompletionPreCompile@
Flatten@{
Quiet@
Check[
Thread[l -> v],
Map[l -> # &, v]
]
};
Finally here's a quick function for actually setting these:
addAutocompletions[
pats : {(_String -> {$autoCompletionFormats ..}) ..}] :=
If[$Notebooks &&
Internal`CachedSystemInformation["FrontEnd", "VersionNumber"] >
10.0,
Scan[
FE`Evaluate[FEPrivate`AddSpecialArgCompletion[#]] &,
pats
];
pats,
$Failed
];
addAutocompletions[pat : (_String -> {$autoCompletionFormats ..})] :=
addAutocompletions[{pat}];
addAutocompletions[a__] /; (! TrueQ@$recursionProtect) :=
Block[{$recursionProtect = True},
Replace[
addAutocompletions@autocompletionPreCompile[a],
_addAutocompletions -> $Failed
]
];
If you want to test it out you can try this:
addAutocompletions[
addAutocompletions,
{
None,
Replace[Keys[$autocompletionAliases],
Verbatim[Alternatives][s_, ___] :> s,
1
]
}
];
Extracting the completions
With that in hand we move to what sorts of completions we can extract. We'll scan over the DownValues
to find one of three things:
- Colors
- Files
Alternatives
of Strings
The code then constructs the appropriate positional lists for this:
Attributes[autoCompletionsExtractSeeder] =
{
HoldFirst
};
autoCompletionsExtractSeeder[
HoldPattern[Verbatim[PatternTest][_, ColorQ]] |
(Blank | BlankSequence)[Hue | RGBColor | XYZColor | LABColor],
n_
] := Sow[n -> "Color"];
autoCompletionsExtractSeeder[
HoldPattern[Verbatim[PatternTest][_, DirectoryQ]],
n_
] := Sow[n -> "Directory"];
autoCompletionsExtractSeeder[
HoldPattern[Verbatim[PatternTest][_, FileExistsQ]] |
(Blank | BlankSequence)[File] | _File,
n_
] := Sow[n -> "FileName"];
autoCompletionsExtractSeeder[
Verbatim[Alternatives][s__String],
n_
] :=
Sow[n -> {s}];
autoCompletionsExtractSeeder[
Verbatim[Pattern][_, b_],
n_
] := autoCompletionsExtractSeeder[b, n];
autoCompletionsExtractSeeder[
Verbatim[Optional][a_, ___],
n_
] := autoCompletionsExtractSeeder[a, n];
(* actual extractor; delegates to the seeder *)
Attributes[autoCompletionsExtract] =
{
HoldFirst
};
autoCompletionsExtract[
Verbatim[HoldPattern][_[a___]]] :=
{ReleaseHold@
MapIndexed[
Function[Null, autoCompletionsExtractSeeder[#, #2[[1]]],
HoldAllComplete],
Hold[a]
]
};
autoCompletionsExtract[f_Symbol] :=
Flatten@
Reap[
autoCompletionsExtract /@
Keys@getCodeValues[f, {DownValues}]
][[2]]
(* builds list from extracted data *)
generateAutocompletions[
f : _Symbol : None,
otherAutos : {(_Integer -> _) ...} : {}
] :=
With[
{
gg =
Join[
GroupBy[
If[Unevaluated[f] =!= None,
autoCompletionsExtract[f],
{}
],
First -> Last,
Replace[{s_, ___} :> s]
],
GroupBy[
otherAutos,
First -> Last
]
]
},
With[{km = Max@Append[Keys@gg, 0]},
Table[
Lookup[gg, i, None],
{i, km}
]
]
];
SetAttributes[generateAutocompletions, HoldFirst];
We can test this as follows:
test // Clear
test[a_, col : _?ColorQ, f_String?FileExistsQ, "a" | "b" | "c"] :=
Darker@col;
generateAutocompletions@test
{None, "Color", "FileName", {"a", "b", "c"}}
SyntaxInformation
For this we have four distinct things to handle:
- ArgumentsPattern
- LocalVariables
- ColorEqualSigns
- OptionNames
I only adequately handle the first and last, so I'll only really discuss those.
ArgumentsPattern
For this, we reduce the DownValues
to a more canonical form. First a pattern reduction function:
reducePatterns[p_] :=
Replace[
p,
{
Except[
_Pattern | _Optional | _Blank | _BlankSequence |
_BlankNullSequence | _PatternSequence | _OptionsPattern |
_Repeated | _RepeatedNull | _Default | _PatternTest | \
_Condition
] -> _
},
{2}
] //. {
_Blank -> "Blank",
_BlankSequence -> "BlankSequence",
_BlankNullSequence -> "BlankNullSequence",
_OptionsPattern :> "OptionsPattern",
Verbatim[HoldPattern][
Verbatim[Pattern][a_, b_]
] | Verbatim[Pattern][a_, b_] :> b,
(PatternTest | Condition)[a_, b_] :> a,
Verbatim[Optional][a_, ___] :> "Optional"[a],
_Default -> "Default",
Verbatim[Repeated][_] -> "Repeated"[Infinity],
Verbatim[RepeatedNull][_] -> "RepeatedNull"[Infinity],
Verbatim[Repeated][_, s_] :> "Repeated"[s],
Verbatim[RepeatedNull][_, s_] :> "RepeatedNull"[s]
};
Whose output I then rebuild into more standardized lists:
reconstructPatterns[p_] :=
p //. {
"Optional"[a_] :> Optional[a],
"Default" -> _.,
"OptionsPattern" -> OptionsPattern[],
"Blank" -> _,
"BlankSequence" -> __,
"BlankNullSequence" -> ___,
"Repeated"[Infinity] -> Repeated[_],
"RepeatedNull"[Infinity] -> RepeatedNull[_],
"Repeated"[s_] :> Repeated[_, s],
"RepeatedNull"[s_] :> RepeatedNull[_, s]
} // Flatten;
This gives us an "ArgumentsPattern"
for each individual DownValues
. We then need to merge these into a maximal "ArgumentsPattern"
, starting by assigning a pattern Span
:
argPatPartLens[patList_] :=
Thread[
patList ->
Replace[
patList,
{
_Blank -> 1 ;; 1,
_BlankSequence -> 1 ;; Infinity,
_BlankNullSequence -> 0 ;; Infinity,
Verbatim[Repeated][_] -> 1 ;; Infinity,
Verbatim[RepeatedNull][_] -> 0 ;; Infinity,
Verbatim[Repeated][_, {M_}] :> 1 ;; M,
Verbatim[RepeatedNull][_, {M_}] :> 0 ;; M,
(Repeated | RepeatedNull)[_, {M_}] :> ;; M,
(Repeated | RepeatedNull)[_, {m_, M_}] :> m ;; M,
_Optional -> 0 ;; 1,
_Default -> 0 ;; 1,
_OptionsPattern -> 0 ;; Infinity,
_ -> 1 ;; 1
},
{1}
]
];
And then merge these term-by-term (i.e. MapThread
over the DownValues
) either taking the one that spans all of the others or building a new one if there is no pattern that encompasses them all:
mergeArgPats[pats_, returnNum : False | True : False] :=
Module[
{
reppedPats = argPatPartLens /@ pats,
mlen,
paddies,
werkit = True,
patMaxes,
patMins,
patChoices,
patNs,
patCho
},
mlen = Max[Length /@ reppedPats];
paddies = PadRight[#, mlen, _. -> 0 ;; 1] & /@ reppedPats;
MapThread[
Function[
patMins = MinimalBy[{##}, #[[2, 1]] &];
patMaxes = MaximalBy[{##}, #[[2, 2]] &];
patChoices = Intersection[patMins, patMaxes];
patNs = {patMins[[1, 2, 1]], patMaxes[[1, 2, 2]]};
patCho =
If[Length@patChoices > 0,
SortBy[patChoices,
Switch[#[[1]],
_OptionsPattern,
0,
_RepeatedNull | _Repeated,
1,
_Optional | _Default,
2,
_,
3
] &
][[1, 1]],
Replace[ patNs,
{
{0, 1} -> _.,
{1, Infinity} -> __,
{0, Infinity} -> ___,
{m_, n_} :> Repeated[_, {m, n}]
}
]
];
If[returnNum, patCho -> patNs, patCho]
],
paddies
]
]
Then I just apply this to the DownValues
:
generateSIArgPat[f_] :=
With[{dvs = Keys@getCodeValues[f, {DownValues}]},
mergeArgPats@
DeleteDuplicates[
reconstructPatterns /@
ReplaceAll[
reducePatterns /@ dvs,
{
(f | HoldPattern) -> List
}
]
]
];
generateSIArgPat~SetAttributes~HoldFirst
test2[a_] := a;
test2[a_, b_, c_] := b;
test2["a" | "b" | "c", asd_] := asd
test2 // generateSIArgPat
{_, _., _.}
OptionNames
This is pretty trivial. Just take the Options
and ToString
their Keys
:
generateSIOpNames[f_] :=
ToString[#, InputForm] & /@ Keys@Options[f];
generateSIOpNames~SetAttributes~HoldFirst
Total function
We'll then take two other functions which I generally don't use to build a total generator:
generateSILocVars[f_] :=
With[{att = Attributes[f],
dvs = Keys@getCodeValues[f, {DownValues}]},
Which[
MemberQ[att, HoldAll],
{1, Infinity},
MemberQ[att, HoldRest],
{2, Infinity},
MemberQ[att, HoldFirst],
{1}
]
];
generateSILocVars~SetAttributes~HoldFirst
generateSIColEq[f_] :=
With[{dvs = Keys@getCodeValues[f, {DownValues}]},
Replace[{a_, ___} :> a]@
MaximalBy[
MinMax@Flatten@Position[#, _Equal, 1] & /@ dvs,
Apply[Subtract]@*Reverse
]
];
generateSIColEq~SetAttributes~HoldFirst
Options[generateSyntaxInformation] =
{
"ArgumentsPattern" -> Automatic,
"LocalVariables" -> None,
"ColorEqualSigns" -> None,
"OptionNames" -> Automatic
};
Attributes[generateSyntaxInformation] =
{
HoldFirst
};
generateSyntaxInformation[
f_,
ops : OptionsPattern[]
] :=
{
"ArgumentsPattern" ->
Replace[OptionValue["ArgumentsPattern"],
Automatic :> generateSIArgPat[f]
],
"LocalVariables" ->
Replace[OptionValue["LocalVariables"],
Automatic :> generateSILocVars[f]
],
"ColorEqualSigns" ->
Replace[OptionValue["LocalVariables"],
Automatic :> generateSIColEq[f]
],
"OptionNames" ->
Replace[OptionValue["OptionNames"],
Automatic :> generateSIOpNames[f]
]
};
And here's what that output looks like:
generateSyntaxInformation@test2
{"ArgumentsPattern" -> {_, _., _.}, "LocalVariables" -> None,
"ColorEqualSigns" -> None, "OptionNames" -> {}}
generateSyntaxInformation@generateSyntaxInformation
{"ArgumentsPattern" -> {_, OptionsPattern[]},
"LocalVariables" -> None, "ColorEqualSigns" -> None,
"OptionNames" -> {"\"ArgumentsPattern\"", "\"LocalVariables\"",
"\"ColorEqualSigns\"", "\"OptionNames\""}}
ArgX Patterns
Here we'll need a get and set mechanism, just like for the autocompletions.
Extracting the arg count
This will use the same toolchain as we had for the "ArgumentsPattern"
. Now we just need to take the min and max argument numbers and also determine if we have a possible OptionsPattern
(if we do we want to ignore it when counting arguments).
generateArgCount[f_] :=
Module[
{
dvs = Keys@getCodeValues[f, {DownValues}],
patsNums,
patsMax,
patsMin,
patsTypes,
doNonOp = False
},
patsNums =
mergeArgPats[
DeleteDuplicates[
reconstructPatterns /@
ReplaceAll[
reducePatterns /@ dvs,
{
(f | HoldPattern) -> List
}
]
],
True
];
patsTypes = patsNums[[All, 1]];
patsMin =
Block[{noopnoop = False},
MapThread[
If[noopnoop,
0,
If[MatchQ[#, _OptionsPattern],
doNonOp = True;
noopnoop = True;
0,
#2
]
] &,
{
patsTypes,
patsNums[[All, 2, 1]]
}
]
];
patsMax =
Block[{noopnoop = False},
MapThread[
If[noopnoop,
0,
If[MatchQ[#, _OptionsPattern],
doNonOp = True;
noopnoop = True;
0,
#2
]
] &,
{
patsTypes,
patsNums[[All, 2, 2]]
}
]
];
{"MinArgs" -> Total[patsMin], "MaxArgs" -> Total[patsMax],
"OptionsPattern" -> doNonOp}
];
generateArgCount~SetAttributes~HoldFirst
Setting the arg count
Here we'll just emulate the builtins:
setArgCount[f_Symbol, minA : _Integer, maxA : _Integer,
noo : True | False] :=
f[argPatLongToNotDupe___] :=
(
1 /; (ArgumentCountQ[f,
Length@If[noo,
Replace[Hold[argPatLongToNotDupe],
Hold[argPatLongToNotDupe2___, (_Rule | _RuleDelayed | \
{(_Rule | _RuleDelayed) ..}) ...] :> Hold[argPatLongToNotDupe2]
], Hold[argPatLongToNotDupe]], minA, maxA]; False)
);
setArgCount~SetAttributes~HoldFirst
Putting it all together
Extracting all the data
Finally, we'll want a function that extracts all of these pieces:
Options[generateAutoFrontEndInfo] =
{
"SyntaxInformation" -> {},
"Autocompletions" -> {},
"UsageMessages" -> {},
"ArgCount" -> Automatic
};
generateAutoFrontEndInfo[
f_Symbol,
ops : OptionsPattern[]
] :=
{
"UsageMessages" ->
generateSymbolUsage[f,
Cases[
Flatten@List[OptionsPattern["UsageMessages"]],
_Rule | _RuleDelayed
]
],
"SyntaxInformation" ->
generateSyntaxInformation[f,
OptionValue["SyntaxInformation"]
],
"Autocompletions" ->
generateAutocompletions[f,
OptionValue["Autocompletions"]
],
"ArgCount" ->
Replace[OptionValue@"ArgCount",
Except[
KeyValuePattern[
{"MinArgs" -> _Integer, "MaxArgs" -> _Integer | Infinity,
"OptionsPattern" -> (True | False)}
]
] :> generateArgCount[f]
]
};
generateAutoFrontEndInfo~SetAttributes~HoldFirst
Setting / Dumping the data
Once we have this there are two possible things we might want to do.
- Dump to a file
- Set it on the symbol
If we do the first, we'll want a Hold
-wrapped expression. If we do the latter life is a bit simpler. Here's a function (the only one I expose in my packaged up version of this) to do that:
Options[AutoFrontEndInfo] =
{
"SyntaxInformation" -> {},
"Autocompletions" -> {},
"UsageMessages" -> {},
"SetInfo" -> False,
"GatherInfo" -> True
};
AutoFrontEndInfo[f_Symbol, o : OptionsPattern[]] :=
Module[
{
sinfBase =
If[OptionValue["GatherInfo"] =!= False,
generateAutoFrontEndInfo[f,
FilterRules[{o}, Options[generateAutoFrontEndInfo]]],
Flatten[{o, Options[AutoFrontEndInfo]}]
],
sops = FilterRules[{o}, Options[generateAutoFrontEndInfo]],
as = {},
si,
um,
ac,
argX,
set = TrueQ@OptionValue["SetInfo"]
},
si =
Replace[
Replace[Lookup[sinfBase, "SyntaxInformation"],
Except[{(_String -> _) ..}] :>
Lookup[as, "SyntaxInformation",
Lookup[Set[as, generateAutoFrontEndInfo[f, sops]],
"SyntaxInformation"]
]
],
{
(Except[_String] -> _) -> Nothing,
(k_ -> None) :> k -> {}
},
{1}
];
um =
Replace[Lookup[sinfBase, "UsageMessages"],
Except[{__String}] :>
Lookup[as, "UsageMessages",
Lookup[Set[as, generateAutoFrontEndInfo[f, sops]],
"UsageMessages"]
]
];
um = StringRiffle[um, "\n"];
ac =
Replace[Lookup[sinfBase, "Autocompletions"],
Except[_List] :>
Lookup[as, "Autocompletions",
Lookup[Set[as, generateAutoFrontEndInfo[f, sops]],
"Autocompletions"]
]
];
argX =
Association@
Replace[Lookup[sinfBase, "ArgCount"],
Except[{"MinArgs" -> _Integer,
"MaxArgs" -> _Integer | Infinity,
"OptionsPattern" -> True | False}] :>
Lookup[as, "ArgCount",
Lookup[Set[as, generateAutoFrontEndInfo[f, sops]], "ArgCount"]
]
];
If[set,
SyntaxInformation[Unevaluated@f] = si;
If[StringLength@um > 0, f::usage = um];
If[Length@ac > 0, addAutocompletions[f, ac]];
setArgCount[f, argX["MinArgs"], argX["MaxArgs"],
argX["OptionsPattern"]],
(* dump to held expression for writing to file *)
With[
{
si2 = si,
um2 = um,
acpat = autocompletionPreCompile[ac],
minA = argX["MinArgs"],
maxA = argX["MaxArgs"],
noo = argX["OptionsPattern"]
},
Hold[
SyntaxInformation[Unevaluated[f]] = si2;
If[StringLength@um2 > 0, Set[f::usage, um2]];
If[Length@acpat > 0,
If[$Notebooks &&
Internal`CachedSystemInformation["FrontEnd",
"VersionNumber"] > 10.0,
FE`Evaluate[
FEPrivate`AddSpecialArgCompletion[
ToString[Unevaluated[f]] -> acpat]
]
];
SetDelayed[
f[argPatLongToNotDupe___],
(
1 /; (ArgumentCountQ[f,
Length@If[noo,
Replace[Hold[argPatLongToNotDupe],
Hold[argPatLongToNotDupe2___, (_Rule | _RuleDelayed | \
{(_Rule | _RuleDelayed) ..}) ...] :> Hold[argPatLongToNotDupe2]
], Hold[argPatLongToNotDupe]
], minA, maxA]; False)
)
]
]
]
]
]
]
Here's an example of this at work (note that "SetInfo"
actually sets the values, otherwise it just generates a form you can put in a file or tweak by hand):
test3[a_] := a;
test3[a_, b_, c_] := b;
test3["aaaa" | "bbbb" | "cccc", asd_] := asd
AutoFrontEndInfo[test3, "SetInfo" -> True]
The usages:
Autocomplete:
Argpat coloring:
And "argx"
calls:
test3[]
test3::argb: test3 called with 0 arguments; between 1 and 3 arguments are expected.
test3[]
test3[1, 2, 3, 4]
test3::argb: test3 called with 4 arguments; between 1 and 3 arguments are expected.
test3[1, 2, 3, 4]
Package version
This is in a package on GitHub here.
You can then use this like so:
<< https://raw.githubusercontent.com/b3m2a1/mathematica-tools/master/FEInfoExtractor.wl
AutoFrontEndInfo@AutoFrontEndInfo