Skip to content

Commit

Permalink
Add pattern names in SyntaxInformation and missing usage, syntax info…
Browse files Browse the repository at this point in the history
…rmation and syntax autocompletion (#603)

## Changes

* In the Mathematica options inspector, there is an option called `"HighlightMissingArgumentsWithTemplate"`.
* If you turn it on, in case of missing arguments, instead of displaying a single red arrow, you will get a helpful hint with argument names which can serve as a super fast lightweight documentation (like you would get in, e.g., Xcode after autocompleting a function):

<img width="362.4" alt="HighlightMissingArgumentsWithTemplate" src="https://user-images.githubusercontent.com/1479325/106001860-133b6880-6076-11eb-8fd8-a8549dfe4ac6.png">

* The names of the arguments are taken from pattern names in `SyntaxInformation`.
* This PR adds names to all `SyntaxInformation` except for the built-in `RulePlot` thus enabling this feature for all *SetReplace* symbols.

* This also adds missing usage messages, syntax information, and syntax autocompletion.
* Expands argument names in usage messages to be descriptive so that using autocompleted templates are more informative.

## Comments

* `Null` is due to `OptionsPattern[]`. It can be changed to, say, `opts` if that pattern is named as `opts : OptionsPattern[]`. However, if one does that, some optional arguments will always be displayed in the template, which is, of course, unacceptable. I think we'll have to wait until this weed is fixed upstream:

<img width="324.0" alt="image" src="https://user-images.githubusercontent.com/1479325/106002768-200c8c00-6077-11eb-9d79-3a4e07caa2bc.png">

* Autocompletion is generally disabled for option names. The problem is that I'm not aware of a way to enable autocompletion for multiple arguments at a time. In order to solve this, we need to use symbols instead of strings for options (#604), and we need to figure out how to autocomplete the values for enum-like options (#605).
  • Loading branch information
maxitg authored Feb 7, 2021
1 parent aa20a11 commit 9d3d7fb
Show file tree
Hide file tree
Showing 32 changed files with 255 additions and 185 deletions.
9 changes: 8 additions & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,12 +364,19 @@ a [`List`](https://reference.wolfram.com/language/ref/List.html).

Your public symbols should also include a `usage` message, which should be created with
the [`SetUsage`](https://github.com/maxitg/SetReplace/blob/7f89c5103cae6a7c1d21b967973811fdeacfd63b/Kernel/GeneralizedGridGraph.m#L9)
function. For more information, see ``?GeneralUtilities`SetUsage``.
function. For more information, see ``?GeneralUtilities`SetUsage``. Please use full descriptive names for the arguments
in your usage message.

Further, public symbols must
include [`SyntaxInformation`](https://reference.wolfram.com/language/ref/SyntaxInformation.html),
see [an example](https://github.com/maxitg/SetReplace/blob/415f965bd228b119185c1ef05e55fe893103d69b/Kernel/WolframModel.m#L29)
for `WolframModel`.
Also please name the patterns in `SyntaxInformation`. If you do that, you can turn
`"HighlightMissingArgumentsWithTemplate" -> True` in the options inspector and get a helpful hint about the arguments in
the Front End (`Null` has to do with options, and unfortunately renaming them causes the corresponding template to be
displayed if no options are specified):

<img src="/Documentation/Images/HighlightMissingArgumentsWithTemplate.png" width="362.4">

Functions must handle invalid inputs correctly. For example, if you try to evaluate

Expand Down
3 changes: 3 additions & 0 deletions DevUtils/BuildLibSetReplace.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"Verbose" -> False
};

SyntaxInformation[BuildLibSetReplace] =
{"ArgumentsPattern" -> {OptionsPattern[]}, "OptionNames" -> Options[BuildLibSetReplace][[All, 1]]};

SetUsage @ "
BuildLibSetReplace[] builds the libSetReplace library from source, and returns an association of metadata \
on completion, or $Failed if the library could not be built.
Expand Down
5 changes: 4 additions & 1 deletion DevUtils/Console.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
PackageImport["GeneralUtilities`"]

PackageExport["ConsolePrintList"]
PackageExport["ConsoleTryEnvironment"]

SyntaxInformation[ConsolePrintList] = {"ArgumentsPattern" -> {list_}};

SetUsage @ "
ConsolePrintList[list$] will print a list of items in InputForm, one per line, with commas as appropriate.
Expand All @@ -15,7 +18,7 @@
Print["}"];
);

PackageExport["ConsoleTryEnvironment"]
SyntaxInformation[ConsoleTryEnvironment] = {"ArgumentsPattern" -> {var_, default_}};

SetUsage @ "
ConsoleTryEnvironment[var$, default$] will look up the value of the environment variable var$, but use \
Expand Down
10 changes: 7 additions & 3 deletions DevUtils/FileTreeHashes.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

PackageExport["FileTreeHashes"]

SyntaxInformation[FileTreeHashes] = {"ArgumentsPattern" -> {path_, pattern_, depth_., excludeList_.}};

FE`Evaluate[FEPrivate`AddSpecialArgCompletion["FileTreeHashes" -> {2, 0, 0, 0}]];

SetUsage @ "
FileTreeHashes['path$', pattern$, depth$, excludeList$] returns a list of pairs consisting of {'filename', hash}.
* The files found are those matching pattern$ that are within 'path$'.
Expand All @@ -13,12 +17,12 @@
* excludeList$ can contain a list of file names to exclude.
";

FileTreeHashes[path_, pattern_, depth_:1, exclude_:{}] := ModuleScope[
FileTreeHashes[path_, pattern_, depth_:1, excludeList_:{}] := ModuleScope[
path = AbsoluteFileName[path];
If[FailureQ[path] || !DirectoryQ[path], Return[$Failed]];
fileNames = FileNames[pattern, path, IgnoreCase -> True];
If[exclude =!= {},
fileNames = Discard[fileNames, StringEndsQ[#, exclude, IgnoreCase -> True] &]
If[excludeList =!= {},
fileNames = Discard[fileNames, StringEndsQ[#, excludeList, IgnoreCase -> True] &]
];
hashes = FileHash[fileNames, "SHA"];
(* strip off *depth* subdirs to get the canonical path *)
Expand Down
35 changes: 22 additions & 13 deletions DevUtils/GitLink.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,38 @@
PackageImport["PacletManager`"] (* for PacletFind, PacletInstall in versions prior to 12.1 *)

PackageExport["$GitLinkAvailableQ"]
PackageExport["GitSHAWithDirtyStar"]
PackageExport["InstallGitLink"]
PackageExport["CalculateMinorVersionNumber"]

SetUsage @ "
$GitLinkAvailableQ gets GitLink`, yields True in case of success and False otherwise.
";

(* unfortunately, owing to a bug in GitLink, GitLink *needs* to be on the $ContextPath or GitRepo objects
end up in the wrong context, since they are generated in a loopback link unqualified *)
$GitLinkAvailableQ := !FailureQ[Quiet @ Check[Needs["GitLink`"], $Failed]];

PackageExport["GitSHAWithDirtyStar"]
SyntaxInformation[GitSHAWithDirtyStar] = {"ArgumentsPattern" -> {repositoryDirectory_}};

Clear[GitSHAWithDirtyStar];
FE`Evaluate[FEPrivate`AddSpecialArgCompletion["GitSHAWithDirtyStar" -> {2}]];

SetUsage @ "
GitSHAWithDirtyStar['path$'] returns the SHA hash of the commit that is currently checked on \
for the Git repository at 'path$'. Unlike the GitSHA function, this will include a '*' character \
GitSHAWithDirtyStar['repositoryDirectory$'] returns the SHA hash of the commit that is currently checked on \
for the Git repository at 'repositoryDirectory$'. Unlike the GitSHA function, this will include a '*' character \
if the current working tree is dirty.
";

GitSHAWithDirtyStar[repoDir_] /; TrueQ[$GitLinkAvailableQ] := ModuleScope[
repo = GitLink`GitOpen[repoDir];
GitSHAWithDirtyStar[repositoryDirectory_] /; TrueQ[$GitLinkAvailableQ] := ModuleScope[
repo = GitLink`GitOpen[repositoryDirectory];
sha = GitLink`GitSHA[repo, repo["HEAD"]];
cleanQ = AllTrue[# === {} &] @ GitLink`GitStatus[repo];
If[cleanQ, sha, sha <> "*"]
];

GitSHAWithDirtyStar[_] /; FalseQ[$GitLinkAvailableQ] := Missing["NotAvailable"];

PackageExport["InstallGitLink"]
SyntaxInformation[InstallGitLink] = {"ArgumentsPattern" -> {}};

SetUsage @ "
InstallGitLink[] will attempt to install GitLink on the current system (if necessary).
Expand All @@ -39,17 +46,19 @@
PacletInstall["https://www.wolframcloud.com/obj/maxp1/GitLink-2019.11.26.01.paclet"];
];

PackageExport["CalculateMinorVersionNumber"]
SyntaxInformation[CalculateMinorVersionNumber] = {"ArgumentsPattern" -> {repositoryDirectory_, masterBranch_}};

FE`Evaluate[FEPrivate`AddSpecialArgCompletion["GitSHAWithDirtyStar" -> {2, {"master", "main"}}]];

SetUsage @ "
CalculateMinorVersionNumber[repositoryDirectory$, masterBranch$] will calculate a minor version \
derived from the number of commits between the last checkpoint and the 'master' branch, \
which can be overriden with the 'MasterBranch' option. The checkpoint is defined in scripts/version.wl.
derived from the number of commits between the last checkpoint and the masterBranch$. \
The checkpoint is defined in scripts/version.wl.
";

CalculateMinorVersionNumber[repoDir_, masterBranch_] := ModuleScope[
versionInformation = Import[FileNameJoin[{repoDir, "scripts", "version.wl"}]];
gitRepo = GitLink`GitOpen[repoDir];
CalculateMinorVersionNumber[repositoryDirectory_, masterBranch_] := ModuleScope[
versionInformation = Import[FileNameJoin[{repositoryDirectory, "scripts", "version.wl"}]];
gitRepo = GitLink`GitOpen[repositoryDirectory];
If[$internalBuildQ, GitLink`GitFetch[gitRepo, "origin"]];
minorVersionNumber = Max[0, Length[GitLink`GitRange[
gitRepo,
Expand Down
3 changes: 3 additions & 0 deletions DevUtils/PackSetReplace.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
PackSetReplace::nogitlink = "GitLink is not installed, so the built paclet version cannot be correctly \
calculated. Proceed with caution, and consider installing GitLink by running InstallGitLink[].";

SyntaxInformation[PackSetReplace] =
{"ArgumentsPattern" -> {OptionsPattern[]}, "OptionNames" -> Options[PackSetReplace][[All, 1]]};

SetUsage @ "
PackSetReplace[] creates a PacletObject containing the local source and last built library.
* Note that PackSetReplace[] does *not* call BuildLibSetReplace[], unlike the command line scripts.
Expand Down
45 changes: 25 additions & 20 deletions DevUtils/Rasterization.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

PackageImport["GeneralUtilities`"]

PackageExport["RasterizeExpressionAndExportToMarkdown"]
PackageExport["RasterizeCellsAndExportToMarkdown"]
PackageExport["RasterizePreviousInputOutputAndExportToMarkdown"]

SetRelatedSymbolGroup[
RasterizeExpressionAndExportToMarkdown,
RasterizeCellsAndExportToMarkdown,
RasterizePreviousInputOutputAndExportToMarkdown
];

$usageSuffix = "
* 'path$' should be a path, relative to the repository root, with a CamelCased filename ending in '.png'.
* If 'path$' consists of only a file name, the subdirectory 'Documentation/Images' will be used.
* 'relativePath$' should be a path, relative to the repository root, with a CamelCased filename ending in '.png'.
* If 'relativePath$' consists of only a file name, the subdirectory 'Documentation/Images' will be used.
* The resulting markdown will contain an absolute path relative to the root of the repository. It will \
correctly render on e.g. GitHub but may not render in e.g. VSCode.
* The option 'MaxWidth' controls the desired maximum width of the resulting raster. If possible, the expression \
Expand All @@ -26,36 +30,36 @@
"DryRun" -> False
};

PackageExport["RasterizeExpressionAndExportToMarkdown"]

SetUsage @ Evaluate["
RasterizeExpressionAndExportToMarkdown['path$', expr$] will rasterize expr$, write the result to 'path$', and
return an HTML <img> tag that can be pasted directly into a markdown file.
RasterizeExpressionAndExportToMarkdown['relativePath$', expr$] will rasterize expr$, write the result to \
'relativePath$', and return an HTML <img> tag that can be pasted directly into a markdown file.
* The resulting image WILL NOT have an attached Out[]= label." <> $usageSuffix];

SyntaxInformation[RasterizeExpressionAndExportToMarkdown] = {"ArgumentsPattern" -> {_, _, OptionsPattern[]}};

Options[RasterizeExpressionAndExportToMarkdown] = $exportOptions;

SyntaxInformation[RasterizeExpressionAndExportToMarkdown] = {
"ArgumentsPattern" -> {relativePath_, expr_, OptionsPattern[]},
"OptionNames" -> Options[RasterizeExpressionAndExportToMarkdown][[All, 1]]};

RasterizeExpressionAndExportToMarkdown[relativePath_, expr_, opts:OptionsPattern[]] := CatchFailureAsMessage @ Scope[
UnpackOptions[maxWidth];
image = rasterize[maxWidth, expr];
exportImageToMarkdown[relativePath, image, FilterOptions @ opts]
];

PackageExport["RasterizeCellsAndExportToMarkdown"]

SetUsage @ Evaluate["
RasterizeCellsAndExportToMarkdown['path$', cells$] will rasterize a cell or set of cells, write the result to 'path$', \
and return an HTML <img> tag that can be pasted directly into a markdown file.
RasterizeCellsAndExportToMarkdown['relativePath$', cells$] will rasterize a cell or set of cells, write the result to \
'relativePath$', and return an HTML <img> tag that can be pasted directly into a markdown file.
* Cells can be Cell[$$] expressions or CellObject[$$] expressions (which will be read with NotebookRead).
* The resulting image WILL include cell labels (In[]:=, Out[]=, etc)." <> $usageSuffix
];

SyntaxInformation[RasterizeCellsAndExportToMarkdown] = {"ArgumentsPattern" -> {_, OptionsPattern[]}};

Options[RasterizeCellsAndExportToMarkdown] = $exportOptions;

SyntaxInformation[RasterizeCellsAndExportToMarkdown] = {
"ArgumentsPattern" -> {relativePath_, cells_, OptionsPattern[]},
"OptionNames" -> Options[RasterizeCellsAndExportToMarkdown][[All, 1]]};

$cellP = HoldPattern[Cell[_, __]] | HoldPattern[CellObject[_]];

RasterizeCellsAndExportToMarkdown[relativePath_, cells_, opts:OptionsPattern[]] := CatchFailureAsMessage @ Scope[
Expand All @@ -67,12 +71,11 @@
exportImageToMarkdown[relativePath, image, FilterOptions @ opts]
];

PackageExport["RasterizePreviousInputOutputAndExportToMarkdown"]

SetUsage @ Evaluate["
RasterizePreviousInputOutputAndExportToMarkdown['path$'] will read the previous input and output cell from the \
current notebook, rasterize the output, write the result to 'path$', and return a markdown code block containing \
the input and an HTML <img> tag containing the rasterized output, that can be pasted directly into a markdown file.
RasterizePreviousInputOutputAndExportToMarkdown['relativePath$'] will read the previous input and output cell from the \
current notebook, rasterize the output, write the result to 'relativePath$', and return a markdown code block \
containing the input and an HTML <img> tag containing the rasterized output, that can be pasted directly into a \
markdown file.
* If the input cell does not contain purely textual boxes, it cannot be faithfully represented as text, and so \
it will be included in the rasterized image instead.
* The option 'RasterizeInput' -> True will force the input to be rasterized, and will not create a markdown \
Expand All @@ -81,7 +84,9 @@

Options[RasterizePreviousInputOutputAndExportToMarkdown] = Append[$exportOptions, "RasterizeInput" -> False];

SyntaxInformation[RasterizePreviousInputOutputAndExportToMarkdown] = {"ArgumentsPattern" -> {_, OptionsPattern[]}};
SyntaxInformation[RasterizePreviousInputOutputAndExportToMarkdown] = {
"ArgumentsPattern" -> {relativePath_, OptionsPattern[]},
"OptionNames" -> Options[RasterizePreviousInputOutputAndExportToMarkdown][[All, 1]]};

(* this detects whether formatting boxes have been embedded into the input string via so-called
"Linear Syntax" (which is an insane thing that shouldn't exist) *)
Expand Down
14 changes: 14 additions & 0 deletions DevUtils/init.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@
PackageExport["$DevUtilsRoot"]
PackageExport["$DevUtilsTemporaryDirectory"]

SetUsage @ "
$SetReplaceRoot is the directory from which SetReplace was loaded.
";

$SetReplaceRoot = FileNameDrop[$InputFileName, -2];

SetUsage @ "
$DevUtilsRoot is the directory from which the DevUtils package was loaded.
";

$DevUtilsRoot = FileNameDrop[$InputFileName, -1];

SetUsage @ "
$DevUtilsTemporaryDirectory is the temprary directory that DevUtils uses.
";

$DevUtilsTemporaryDirectory := EnsureDirectory @ FileNameJoin[{$TemporaryDirectory, "SetReplace"}];
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 5 additions & 6 deletions Kernel/AcyclicGraphTake.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,20 @@

(* Documentation *)
SetUsage @ "
AcyclicGraphTake[gr$, vrts$] gives the intersection in graph gr$ of the in-component of the first vertex in vrts$ \
with the out-component of the second vertex in vrts$.
AcyclicGraphTake[graph$, vertexList$] gives the intersection in graph$ of the in-component of the first vertex in \
vertexList$ with the out-component of the second vertex in vertexList$.
";

(* SyntaxInformation *)
SyntaxInformation[AcyclicGraphTake] =
{"ArgumentsPattern" -> {_, _}};
SyntaxInformation[AcyclicGraphTake] = {"ArgumentsPattern" -> {graph_, vertexList_}};

(* Argument count *)
AcyclicGraphTake[args___] := 0 /;
!Developer`CheckArgumentCount[AcyclicGraphTake[args], 2, 2] && False;

(* main *)
expr : AcyclicGraphTake[graph_, vertices_] := ModuleScope[
res = Catch[acyclicGraphTake[HoldForm @ expr, graph, vertices]];
expr : AcyclicGraphTake[graph_, vertexList_] := ModuleScope[
res = Catch[acyclicGraphTake[HoldForm @ expr, graph, vertexList]];
res /; res =!= $Failed
];

Expand Down
37 changes: 20 additions & 17 deletions Kernel/GeneralizedGridGraph.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@
(* Documentation *)

SetUsage @ "
GeneralizedGridGraph[{n$1, n$2, $$, n$k}] gives the k-dimensional grid graph with n$1 \[Times] n$2 \[Times] $$ n$k \
vertices.
GeneralizedGridGraph[{$$, n$k -> 'Circular', $$}] makes the grid wrap around in k-th dimension.
GeneralizedGridGraph[{$$, n$k -> 'Directed', $$}] makes the edges directed in k-th dimension.
GeneralizedGridGraph[{$$, n$k -> {'Circular', 'Directed'}, $$}] makes the grid both circular and directed.
GeneralizedGridGraph[{size$1, size$2, $$, size$k}] gives the k$-dimensional grid graph with \
size$1 \[Times] size$2 \[Times] $$ size$k vertices.
GeneralizedGridGraph[{$$, size$k -> 'Circular', $$}] makes the grid wrap around in k$-th dimension.
GeneralizedGridGraph[{$$, size$k -> 'Directed', $$}] makes the edges directed in k$-th dimension.
GeneralizedGridGraph[{$$, size$k -> {'Circular', 'Directed'}, $$}] makes the grid both circular and directed.
";

Options[GeneralizedGridGraph] = Join[Options[Graph], {"VertexNamingFunction" -> Automatic}];

$vertexNamingFunctions = {Automatic (* IndexGraph *), "Coordinates"};

SyntaxInformation[GeneralizedGridGraph] =
{"ArgumentsPattern" -> {_, OptionsPattern[]}, "OptionNames" -> Options[GeneralizedGridGraph][[All, 1]]};
{"ArgumentsPattern" -> {dimensionSpecs_, OptionsPattern[]}, "OptionNames" -> Options[GeneralizedGridGraph][[All, 1]]};

FE`Evaluate[FEPrivate`AddSpecialArgCompletion["GeneralizedGridGraph" -> {{"Circular", "Directed"}}]];

GeneralizedGridGraph::dimsNotList = "Dimensions specification `` should be a list.";

Expand All @@ -40,10 +42,11 @@
generalizedGridGraph[args_, opts___] /;
!supportedOptionQ[GeneralizedGridGraph, "VertexNamingFunction", $vertexNamingFunctions, {opts}] := Throw[$Failed];

generalizedGridGraph[dimSpecs_List, opts___] := generalizedGridGraphExplicit[toExplicitDimSpec /@ dimSpecs, opts];
generalizedGridGraph[dimensionSpecs_List, opts___] :=
generalizedGridGraphExplicit[toExplicitDimSpec /@ dimensionSpecs, opts];

generalizedGridGraph[dimSpecs : Except[_List], opts___] := (
Message[GeneralizedGridGraph::dimsNotList, dimSpecs];
generalizedGridGraph[dimensionSpecs : Except[_List], opts___] := (
Message[GeneralizedGridGraph::dimsNotList, dimensionSpecs];
Throw[$Failed];
);

Expand All @@ -66,21 +69,21 @@
Throw[$Failed];
);

generalizedGridGraphExplicit[dimSpecs_, opts___] := ModuleScope[
generalizedGridGraphExplicit[dimensionSpecs_, opts___] := ModuleScope[
{edgeStyle, vertexNamingFunction} = OptionValue[GeneralizedGridGraph, {opts}, {EdgeStyle, "VertexNamingFunction"}];
edges = singleDimensionEdges[dimSpecs, #] & /@ Range[Length[dimSpecs]];
edges = singleDimensionEdges[dimensionSpecs, #] & /@ Range[Length[dimensionSpecs]];
directionalEdgeStyle = EdgeStyle -> If[
ListQ[edgeStyle] && Length[edgeStyle] == Length[dimSpecs] && AllTrue[edgeStyle, Head[#] =!= Rule &],
ListQ[edgeStyle] && Length[edgeStyle] == Length[dimensionSpecs] && AllTrue[edgeStyle, Head[#] =!= Rule &],
Catenate @ MapThread[Function[{dirEdges, style}, # -> style & /@ dirEdges], {edges, edgeStyle}]
,
Nothing
];
If[GraphQ[#], #, Throw[$Failed]] & @ Graph[
renameVertices[vertexNamingFunction] @ Graph[
(* Reversal is needed to be consistent with "GridEmbedding" *)
If[!ListQ[#], {}, #] & @ Flatten[Outer[v @@ Reverse[{##}] &, ##] & @@ Reverse[Range /@ dimSpecs[[All, 1]]]],
If[!ListQ[#], {}, #] & @ Flatten[Outer[v @@ Reverse[{##}] &, ##] & @@ Reverse[Range /@ dimensionSpecs[[All, 1]]]],
Catenate[edges],
GraphLayout -> graphLayout[dimSpecs],
GraphLayout -> graphLayout[dimensionSpecs],
directionalEdgeStyle],
If[directionalEdgeStyle[[2]] === Nothing, {opts}, FilterRules[{opts}, Except[EdgeStyle]]]]
];
Expand All @@ -93,9 +96,9 @@

graphLayout[_] := "SpringElectricalEmbedding";

singleDimensionEdges[dimSpecs_, k_] := Catenate[
singleThreadEdges[dimSpecs[[k]], #] & /@
Flatten[Outer[v, ##] & @@ ReplacePart[Range /@ dimSpecs[[All, 1]], k -> {threadDim}]]];
singleDimensionEdges[dimensionSpecs_, k_] := Catenate[
singleThreadEdges[dimensionSpecs[[k]], #] & /@
Flatten[Outer[v, ##] & @@ ReplacePart[Range /@ dimensionSpecs[[All, 1]], k -> {threadDim}]]];

singleThreadEdges[{n_, wrapSpec_, dirSpec_}, thread_] :=
Replace[dirSpec, {$$directed -> DirectedEdge, $$undirected -> UndirectedEdge}] @@@
Expand Down
Loading

0 comments on commit 9d3d7fb

Please sign in to comment.