├── .gitignore ├── ForScience ├── ChemUtils.wl ├── ChemUtils │ ├── AdjacencyToBonds.wl │ ├── GromosAtomInterpreter.wl │ ├── GromosImport.wl │ ├── GromosMoleculeOrientation.wl │ ├── GromosPositionToMoleculeList.wl │ ├── Molecule.wl │ ├── MoleculePlot3D.wl │ └── ToBond.wl ├── ForScience.wl ├── GuideForScience.wl ├── PacletInfo.m ├── PacletUtils.wl ├── PacletUtils │ ├── Abstract.wl │ ├── AbstractDoc.wl │ ├── BuildAction.wl │ ├── BuildActionDoc.wl │ ├── BuildPaclet.wl │ ├── BuildPacletDoc.wl │ ├── BuildTimeEvaluate.wl │ ├── BuildTimeEvaluateDoc.wl │ ├── CleanExampleDirectory.wl │ ├── CleanExampleDirectoryDoc.wl │ ├── CompatibilityChecker.wl │ ├── CompatibilityCheckerDoc.wl │ ├── DeclareMetadataHandler.wl │ ├── DeclareSectionAccessor.wl │ ├── Details.wl │ ├── DetailsDoc.wl │ ├── DocID.wl │ ├── DocUtils.wl │ ├── DocumentationBuilder.wl │ ├── DocumentationBuilderDoc.wl │ ├── DocumentationCache.wl │ ├── DocumentationFooter.wl │ ├── DocumentationHeader.wl │ ├── DocumentationHeaderDoc.wl │ ├── DocumentationOptions.wl │ ├── DocumentationOptionsDoc.wl │ ├── DocumentationStyling.wl │ ├── DocumentationType.wl │ ├── ExampleInput.wl │ ├── ExampleInputDoc.wl │ ├── Examples.wl │ ├── ExamplesDoc.wl │ ├── FSHeader.wl │ ├── FSHeaderDoc.wl │ ├── FormatUsage.wl │ ├── FormatUsageCase.wl │ ├── FormatUsageCaseDoc.wl │ ├── FormatUsageDoc.wl │ ├── GuideCreatingDocumentationPages.wl │ ├── GuideDoc.wl │ ├── GuideDocumenter.wl │ ├── GuidePacletUtils.wl │ ├── GuideSections.wl │ ├── GuideSectionsDoc.wl │ ├── Guides.wl │ ├── GuidesDoc.wl │ ├── IndexDocumentation.wl │ ├── MakeUsageString.wl │ ├── MakeUsageStringDoc.wl │ ├── OverviewEntries.wl │ ├── OverviewEntriesDoc.wl │ ├── PacletSelfCheck.wl │ ├── ParseFormatting.wl │ ├── ParseFormattingDoc.wl │ ├── ProcessFile.wl │ ├── ProcessFileDoc.wl │ ├── SeeAlso.wl │ ├── SeeAlsoDoc.wl │ ├── SymbolDocumenter.wl │ ├── TutorialCreatingSymbolPages.wl │ ├── TutorialDoc.wl │ ├── TutorialDocumenter.wl │ ├── TutorialOverviewDoc.wl │ ├── TutorialOverviewDocumenter.wl │ ├── TutorialSections.wl │ ├── TutorialSectionsDoc.wl │ ├── Tutorials.wl │ ├── TutorialsDoc.wl │ ├── UnloadPacletDocumentation.wl │ ├── Usage.wl │ ├── UsageCompiler.wl │ ├── UsageCompilerDoc.wl │ ├── UsageDoc.wl │ ├── UsageSection.wl │ ├── VariableLeakTracer.wl │ └── VariableLeakTracerDoc.wl ├── PlotUtils.wl ├── PlotUtils │ ├── ColorFunctions.wl │ ├── ColorFunctionsDoc.wl │ ├── CombinePlots.wl │ ├── CombinePlotsDoc.wl │ ├── CustomTicks.wl │ ├── CustomTicksDoc.wl │ ├── ForSciencePlotTheme.wl │ ├── ForSciencePlotThemeDoc.wl │ ├── GraphicsInformation.wl │ ├── GraphicsInformationDoc.wl │ ├── GuidePlotUtils.wl │ ├── InternalUtils.wl │ ├── PlotGrid.wl │ ├── PlotGridDoc.wl │ ├── VectorMarker.wl │ └── VectorMarkerDoc.wl ├── Util.wl └── Util │ ├── AddKey.wl │ ├── ApplyStructure.wl │ ├── ApplyToWrapped.wl │ ├── AssignmentWrapper.wl │ ├── AssociationFoldList.wl │ ├── AutoSlot.wl │ ├── CachedImport.wl │ ├── CondDef.wl │ ├── ContextualRule.wl │ ├── DefTo.wl │ ├── DelayedExport.wl │ ├── DropMissing.wl │ ├── FancyTrace.wl │ ├── FirstHead.wl │ ├── FixedShort.wl │ ├── FunctionError.wl │ ├── ImportDataset.wl │ ├── ImportDatasetDoc.wl │ ├── IndexedFunction.wl │ ├── InvCondDef.wl │ ├── KeyGroupBy.wl │ ├── Let.wl │ ├── ListLookup.wl │ ├── MergeRules.wl │ ├── NaturallyOrdered.wl │ ├── PartLookup.wl │ ├── PrettyTime.wl │ ├── PrettyUnit.wl │ ├── ProcFunction.wl │ ├── ProgressReport.wl │ ├── PublishRelease.wl │ ├── PullUp.wl │ ├── SPrintF.wl │ ├── SimpleTOC.wl │ ├── SkipCommentsStream.wl │ ├── SkipMissing.wl │ ├── TableToTexForm.wl │ ├── ToAssociationRule.wl │ ├── ToFunction.wl │ ├── UpdateForScience.wl │ └── WindowedMap.wl ├── LICENSE ├── README.md └── build.nb /.gitignore: -------------------------------------------------------------------------------- 1 | *.paclet 2 | build/ 3 | cache/ 4 | token 5 | -------------------------------------------------------------------------------- /ForScience/ChemUtils.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Block[{Notation`AutoLoadNotationPalette=False}, 4 | BeginPackage["ForScience`ChemUtils`",{If[$FrontEnd=!=Null,"Notation`",Nothing],"ForScience`Util`","ForScience`PacletUtils`"}] 5 | ] 6 | 7 | 8 | If[$VersionNumber<12, 9 | <<`GromosMoleculeOrientation`; 10 | <<`ToBond`; 11 | <<`AdjacencyToBonds`; 12 | <<`Molecule`; 13 | <<`MoleculePlot3D`; 14 | <<`GromosAtomInterpreter`; 15 | <<`GromosPositionToMoleculeList`; 16 | <<`GromosImport`; 17 | ](*TODO: add new GROMOS 2 Molecule functions to have ChemUtils in MMA \[GreaterEqual] 12*) 18 | 19 | 20 | EndPackage[] 21 | -------------------------------------------------------------------------------- /ForScience/ChemUtils/AdjacencyToBonds.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | AdjacencyToBonds::usage=FormatUsage@"AdjacencyToBonds[mat] converts an adjancency matrix to a list of '''Bond''' specifications. Entries can be wrapped in arbitrary wrappers."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | AdjacencyToBonds[mat_]:=ApplyToWrapped[Bond[##],mat[[##]],_Integer]&@@@ 10 | Select[Apply@Less]@Position[Normal@mat,Except[0],{2},Heads->False] 11 | SyntaxInformation[AdjacencyToBonds]={"ArgumentsPattern"->{_}}; 12 | 13 | End[] 14 | -------------------------------------------------------------------------------- /ForScience/ChemUtils/GromosAtomInterpreter.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | GromosAtomInterpreter::usage=FormatUsage@"GromosAtomInterpreter[atom] interprets ```atom``` from GROMOS files to a MMA atom type."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | GromosAtomInterpreter[AtomName_]:= 10 | If[StringLength[AtomName]>1, 11 | If[FailureQ[Interpreter["Element"][StringTake[AtomName,{2}]]], 12 | StringTake[AtomName,{1}]<>ToLowerCase@StringTake[AtomName,{2}], 13 | StringTake[AtomName,1]], 14 | AtomName] 15 | SyntaxInformation[GromosAtomInterpreter]={"ArgumentsPattern"->{_}}; 16 | 17 | 18 | End[] 19 | -------------------------------------------------------------------------------- /ForScience/ChemUtils/GromosImport.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | Options[GromosImport]={"PositionParser"->Automatic,"Blocks"->All}; 7 | Options[ParseGromosBlock]=Options[GromosImport]; 8 | Options[iParseGromosBlock]=Options[GromosImport]; 9 | 10 | (*ParseGromosBlock[o:OptionsPattern[]][t_,str_,"END"]:=t->iParseGromosBlock[t,str,o]*) 11 | ParseGromosBlock[o:OptionsPattern[]][t_,str_,"END"]:=With[ 12 | {blocks=OptionValue@"Blocks"}, 13 | If[blocks===All||MemberQ[blocks,t], 14 | t->iParseGromosBlock[t,str,o], 15 | Nothing 16 | ] 17 | ] 18 | iParseGromosBlock["TITLE",title_,o:OptionsPattern[]]:=title 19 | iParseGromosBlock["POSITION"|"VELOCITY"|"SHAKEFAILPOSITION"|"SHAKEFAILPREVPOSITION",str_,o:OptionsPattern[]]:=Module[{hold}, 20 | With[ 21 | {res=AssociationThread[{"CG","CGName","Atom","No","x","y","z"}->#]&/@ 22 | ReadList[StringToStream@str,{Number,Word,Word,Number,Real,Real,Real}] 23 | }, 24 | Switch[OptionValue["PositionParser"], 25 | Automatic, 26 | res, 27 | "ByChargeGroup", 28 | GroupBy[res,StringTemplate["`CG``CGName`"]] 29 | ] 30 | ] 31 | ] 32 | iParseGromosBlock["PHYSICALCONSTANTS",phys_,o:OptionsPattern[]]:=AssociationThread[{"FPEPSI","HBAR","SPDL","BOLTZ"},ReadList[StringToStream@phys,Number,RecordLists->True]] 33 | iParseGromosBlock["SOLUTEATOM",str_,o:OptionsPattern[]]:=Module[{stream,holdRead,holdAtom,holdAll,adjList,totNumber}, 34 | adjList={}; 35 | holdAll={}; 36 | stream=StringToStream[str]; 37 | totNumber=ToExpression@ReadLine[stream]; 38 | For[n=1,n<=totNumber,n++, 39 | holdRead=StringSplit[ReadLine[stream]]; 40 | holdAtom=AssociationThread[{"ATNM","MRES","PANM","IAC","MASS","CG","CGC","INE"},ToExpression[holdRead[[1;;8]]]]; 41 | If[holdAtom[["INE"]]>=1, 42 | For[m=0,mReadLine[stream]]; 47 | (*TODO write INE14 parsing*) 48 | AppendTo[holdAll,holdAtom]; 49 | ]; 50 | <|"Atoms"->holdAll,"adjList"->adjList|> 51 | ] 52 | iParseGromosBlock["ATOMTYPENAME"|"RESNAME",str_,o:OptionsPattern[]]:=Module[{totNumber,stream,holdAll}, 53 | holdAll={}; 54 | stream=StringToStream[str]; 55 | totNumber=ToExpression@ReadLine[stream]; 56 | For[i=1,i<=totNumber,i++, 57 | AppendTo[holdAll,ToExpression@ReadLine[stream]]; 58 | ]; 59 | holdAll 60 | ] 61 | iParseGromosBlock["SOLVENTATOM",str_,o:OptionsPattern[]]:=Module[{totNumber,stream,holdAll}, 62 | holdAll={}; 63 | stream=StringToStream[str]; 64 | totNumber=ToExpression@ReadLine[stream]; 65 | For[i=1,i<=totNumber,i++, 66 | AppendTo[holdAll,ToExpression@ReadLine[stream]]; 67 | ]; 68 | holdAll 69 | ] 70 | iParseGromosBlock[_,str_,opt:OptionsPattern[]]:=ToExpression@ReadList[StringToStream@str,Number,RecordLists->True] 71 | 72 | GromosImport[file_,opts:OptionsPattern[]]:=Module[ 73 | { 74 | s=OpenRead[file,Method->"SkipComments"], 75 | ret 76 | }, 77 | ret=ParseGromosBlock[opts]@@@ReadList[s,{"String","Record","String"},RecordSeparators->{"END"}]; 78 | Close@s; 79 | ret 80 | ] 81 | 82 | ImportExport`RegisterImport[ 83 | "GROMOS", 84 | { 85 | GromosImport 86 | }, 87 | { 88 | "Association"->(Association@#&), 89 | "Dataset"->(Dataset@Association@#&) 90 | }, 91 | "DefaultElement"->"Association", 92 | "Options"->Keys@Options@GromosImport 93 | ] 94 | 95 | 96 | End[] 97 | -------------------------------------------------------------------------------- /ForScience/ChemUtils/GromosMoleculeOrientation.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | GromosMoleculeOrientation::usage=FormatUsage@"Returns the dot product with a ```axis```. The option ```ref``` specifies the atoms through which the orientation is defined."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | iGromosMoleculeOrientation[data_,ref1_,ref2_,axis_,coords_]:=Module[{hold1,hold2}, 10 | hold1={0,0,0}; 11 | hold2={0,0,0}; 12 | If[ref1===Automatic, 13 | If[ref2===Automatic, 14 | hold1=Values@data[[1,{"x","y","z"}]]; 15 | hold2=Values@data[[2,{"x","y","z"}]];, 16 | Map[ 17 | If[#Atom==ref1,hold1={#x,#y,#z}]; 18 | If[#Atom!=ref1&&hold2=={0,0,0},hold2={#x,#y,#z}]; 19 | &,data] 20 | ];, 21 | If[ref2===Automatic, 22 | Map[ 23 | If[(#Atom!=ref2)&&(hold1=={0,0,0}),hold1={#x,#y,#z}]; 24 | If[#Atom==ref2,hold2={#x,#y,#z}]; 25 | &,data], 26 | Map[ 27 | If[#Atom==ref1,hold1={#x,#y,#z}]; 28 | If[#Atom==ref2,hold2={#x,#y,#z}]; 29 | &,data]; 30 | ]; 31 | ]; 32 | If[coords, 33 | Flatten[{hold1,((hold1-hold2).axis)/(Norm[hold1-hold2]*Norm[axis])}], 34 | ((hold1-hold2).axis)/(Norm[hold1-hold2]*Norm[axis]) 35 | ] 36 | ] 37 | 38 | Options[GromosMoleculeOrientation]={"axis"->{0,0,1},"ref"->{Automatic,Automatic},"coords"->False}; 39 | GromosMoleculeOrientation[data_,OptionsPattern[]]:=Module[{hold1,hold2}, 40 | Map[ 41 | iGromosMoleculeOrientation[#,OptionValue["ref"][[1]],OptionValue["ref"][[2]],OptionValue["axis"],OptionValue["coords"]] 42 | &,data] 43 | ] 44 | SyntaxInformation[GromosMoleculeOrientation]={"ArgumentsPattern"->{_,OptionsPattern[]}}; 45 | 46 | 47 | End[] 48 | -------------------------------------------------------------------------------- /ForScience/ChemUtils/GromosPositionToMoleculeList.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | GromosPositionToMoleculeList::usage=FormatUsage@"GromosPositionToMoleculeList[data] gives the '''Molecule''' representation of the GROMOS POSITION Block ```data```."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | GromosPositionToMoleculeList[data_]:=Map[GromosAtomInterpreter[#Atom]->{#x*1000,#y*1000,#z*1000}&,data] 10 | SyntaxInformation[GromosPositionToMoleculeList]={"ArgumentsPattern"->{_}}; 11 | 12 | 13 | End[] 14 | -------------------------------------------------------------------------------- /ForScience/ChemUtils/MoleculePlot3D.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | MoleculePlot3D::usage=FormatUsage@"MoleculePlot3D[atoms,bonds] plots the molecule specified by ```atoms``` and ```bonds``` given. 4 | MoleculePlot3D[graphics] plots ```graphics```, where '''Molecule[\[Ellipsis]]''' objects can be used as primitives. Options given are taken as defaults for all molecules."; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | MoleculePlot3D[atoms:{__Rule},bonds:(_?ArrayQ|None),o:OptionsPattern[]]:=MoleculePlot3D[Molecule[atoms,bonds],o] 11 | MoleculePlot3D[atoms:{__Rule},o:OptionsPattern[]]:=MoleculePlot3D[atoms,None,o] 12 | MoleculePlot3D[g_,o:OptionsPattern[]]:= 13 | Graphics3D[ 14 | g/.HoldPattern@Molecule[spec__]:>Normal@Molecule[spec,FilterRules[{o},Options[Molecule]]], 15 | FilterRules[{o},Options[Graphics3D]], 16 | Lighting->"Neutral", 17 | Boxed->False 18 | ] 19 | Options[MoleculePlot3D]=Join[Options[Graphics3D],Options[Molecule]]; 20 | SyntaxInformation[ToBond]:={"ArgumentsPattern"->{_,_.,OptionsPattern[]}}; 21 | 22 | 23 | End[] 24 | -------------------------------------------------------------------------------- /ForScience/ChemUtils/ToBond.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Bond::usage=FormatUsage@"Bond[a,b][t] represents a chemical bond between ```a``` and ```b```, where ```t``` is the type of bond (1,2,3 for single, double and triple)."; 4 | ToBond::usage=FormatUsage@"ToBond[\[Ellipsis]] handles conversion of various formats to '''Bond''' specifications. See '''Definition@ToBond''' for supported formats."; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | Attributes[Bond]={Orderless}; 11 | SyntaxInformation[Bond]:={"ArgumentsPattern"->{_,_,_.}}; 12 | 13 | If[$FrontEnd=!=Null, 14 | Notation[ParsedBoxWrapper[RowBox[{"a_", "<->", "b_"}]]\[DoubleLongLeftArrow]ParsedBoxWrapper[RowBox[{RowBox[{"Bond", "[", RowBox[{"a_", ",", "b_"}], "]"}], "[", "1", "]"}]]]; 15 | Notation[ParsedBoxWrapper[RowBox[{"a_", "\[DoubleLongLeftRightArrow]", "b_"}]]\[DoubleLongLeftArrow]ParsedBoxWrapper[RowBox[{RowBox[{"Bond", "[", RowBox[{"a_", ",", "b_"}], "]"}], "[", "2", "]"}]]]; 16 | Notation[ParsedBoxWrapper[RowBox[{"a_", "\[Congruent]", "b_"}]]\[DoubleLongLeftArrow]ParsedBoxWrapper[RowBox[{RowBox[{"Bond", "[", RowBox[{"a_", ",", "b_"}], "]"}], "[", "3", "]"}]]]; 17 | AddInputAlias["sb"->ParsedBoxWrapper["<->"]]; 18 | AddInputAlias["db"->ParsedBoxWrapper["\[DoubleLongLeftRightArrow]"]]; 19 | AddInputAlias["tb"->ParsedBoxWrapper["\[Congruent]"]]; 20 | ] 21 | ToBond[a_<->b_]:=Bond[a,b][1] 22 | ToBond[a_\[DoubleLongLeftRightArrow]b_]:=Bond[a,b][2] 23 | ToBond[a_\[Congruent]b_]:=Bond[a,b][3] 24 | ToBond[Bond[a_,b_,t_:1]]:=Bond[a,b][t] 25 | ToBond[b:Bond[_,_][_]]:=b 26 | ToBond[{{a_,b_},t_}]:=Bond[a,b][t] 27 | ToBond[{a_<->b_,t_}]:=Bond[a,b][t] 28 | Attributes[ToBond]={Listable}; 29 | SyntaxInformation[ToBond]:={"ArgumentsPattern"->{_}}; 30 | 31 | 32 | End[] 33 | -------------------------------------------------------------------------------- /ForScience/GuideForScience.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | $GuideForScience=Guide["ForScience"]; 4 | 5 | 6 | DocumentationHeader[$GuideForScience]=FSGuideHeader; 7 | 8 | 9 | Abstract[$GuideForScience]="The ForScience paclet contains various utility functions that are split into several subpaclets, depending on their use case. General utility functions are in the Util subpaclet. These functions help for general tasks, such as importing and processing data. Several other functions are included that are intended to fill gaps in the functionality of built-in functions. The functions of the PlotUtils subpaclet help with the creation of advanced plots and figures, and the ChemUtils subpaclet contains functions related to chemistry, such as new import formats and plotting functions. Finally, the PacletUtils subpaclet contains functions related to paclet development and deployment, including build and documentation tools."; 10 | 11 | 12 | GuideSections[$GuideForScience]={ 13 | { 14 | SectionTitle["PlotUtils"], 15 | {ForSciencePlotTheme,Text["customizable plot theme"]}, 16 | {VectorMarker,CustomTicks,Text["customizable plot markers and ticks"]}, 17 | {CombinePlots,PlotGrid,Text["advanced merging and stacking of plots"]}, 18 | Hold[Jet,Parula,Fire,Text["additional color schemes"]] 19 | }, 20 | { 21 | SectionTitle["PacletUtils"], 22 | {BuildPaclet,Text["handle building and packing of paclets"]}, 23 | {DocumentationBuilder,DocumentationHeader,Text["build high quality documentation pages"]}, 24 | {Usage,Text["create nicely formatted usage messages"]} 25 | } 26 | }; 27 | 28 | 29 | Guides[$GuideForScience]={$GuidePlotUtils,$GuidePacletUtils}; 30 | -------------------------------------------------------------------------------- /ForScience/PacletInfo.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Paclet[ 4 | Name -> "ForScience", 5 | Description -> "Contains various utility functions and styling to make it easier to use MMA for scientific plots", 6 | Creator -> "Lukas Lang & Marc Lehner", 7 | URL -> "https://github.com/MMA-ForScience/ForScience", 8 | Version -> "0.88.45", 9 | MathematicaVersion -> "11.1+", 10 | Extensions -> { 11 | { "Documentation", 12 | Language -> "English", 13 | "MainPage" -> "ReferencePages/Guides/ForScience" 14 | }, 15 | { "Kernel", Context -> { 16 | "ForScience`", 17 | "ForScience`PacletUtils`", 18 | "ForScience`Util`", 19 | "ForScience`PlotUtils`", 20 | "ForScience`ChemUtils`" 21 | }} 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /ForScience/PacletUtils.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | BeginPackage["ForScience`PacletUtils`",{"ForScience`","PacletManager`","JLink`","DocumentationSearch`"}] 4 | 5 | 6 | (*usage formatting utilities, need to make public before defining, as they're already used in the usage definition*) 7 | <<`FormatUsageCase`; 8 | <<`ParseFormatting`; 9 | <<`MakeUsageString`; 10 | <<`FormatUsage`; 11 | <<`Usage`; 12 | 13 | <<`ProcessFile`; 14 | <<`BuildPaclet`; 15 | <<`BuildAction`; 16 | <<`DocumentationType`; 17 | <<`DocumentationCache`; 18 | <<`DocumentationStyling`; 19 | <<`DocID`; 20 | <<`DocUtils`; 21 | <<`DocumentationOptions`; 22 | <<`DocumentationHeader`; 23 | <<`DocumentationFooter`; 24 | <<`IndexDocumentation`; 25 | <<`DocumentationBuilder`; 26 | <<`GuideDocumenter`; 27 | <<`TutorialDocumenter`; 28 | <<`TutorialOverviewDocumenter`; 29 | <<`SymbolDocumenter`; 30 | <<`UsageSection`; 31 | <<`DeclareMetadataHandler`; 32 | <<`DeclareSectionAccessor`; 33 | <<`ExampleInput`; 34 | <<`Abstract`; 35 | <<`GuideSections`; 36 | <<`TutorialSections`; 37 | <<`OverviewEntries`; 38 | <<`Details`; 39 | <<`Examples`; 40 | <<`SeeAlso`; 41 | <<`Tutorials`; 42 | <<`Guides`; 43 | <<`FSHeader`; 44 | <<`CleanExampleDirectory`; 45 | <<`CompatibilityChecker`; 46 | <<`VariableLeakTracer`; 47 | <<`UsageCompiler`; 48 | <<`BuildTimeEvaluate`; 49 | <<`UnloadPacletDocumentation`; 50 | 51 | 52 | BuildAction[ 53 | (* guide & tutorial symbols. Need to declare them here, as they're used inside Begin[BuildAction] ... End[] *) 54 | $GuidePacletUtils; 55 | $GuideCreatingDocPages; 56 | $TutorialCreatingSymbolPages; 57 | ] 58 | 59 | 60 | <<`FormatUsageCaseDoc`; 61 | <<`ParseFormattingDoc`; 62 | <<`MakeUsageStringDoc`; 63 | <<`FormatUsageDoc`; 64 | <<`UsageDoc`; 65 | 66 | <<`ProcessFileDoc`; 67 | <<`BuildPacletDoc`; 68 | <<`BuildActionDoc`; 69 | <<`DocumentationBuilderDoc`; 70 | <<`DocumentationHeaderDoc`; 71 | <<`DocumentationOptionsDoc`; 72 | <<`GuideDoc`; 73 | <<`TutorialDoc`; 74 | <<`TutorialSectionsDoc`; 75 | <<`TutorialOverviewDoc`; 76 | <<`OverviewEntriesDoc`; 77 | <<`AbstractDoc`; 78 | <<`GuideSectionsDoc`; 79 | <<`DetailsDoc`; 80 | <<`ExamplesDoc`; 81 | <<`ExampleInputDoc`; 82 | <<`SeeAlsoDoc`; 83 | <<`TutorialsDoc`; 84 | <<`GuidesDoc`; 85 | <<`FSHeaderDoc`; 86 | <<`CleanExampleDirectoryDoc`; 87 | <<`CompatibilityCheckerDoc`; 88 | <<`VariableLeakTracerDoc`; 89 | <<`UsageCompilerDoc`; 90 | <<`BuildTimeEvaluateDoc`; 91 | 92 | 93 | <<`PacletSelfCheck`; 94 | 95 | 96 | BuildAction[ 97 | <<`GuidePacletUtils`; 98 | <<`GuideCreatingDocumentationPages`; 99 | 100 | <<`TutorialCreatingSymbolPages`; 101 | ] 102 | 103 | 104 | EndPackage[] 105 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/Abstract.wl: -------------------------------------------------------------------------------- 1 | (*::Package::*) 2 | 3 | Abstract; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | Abstract::invalidFormat="Abstract of `` cannot be set to ``. A string is expected."; 10 | 11 | 12 | DeclareMetadataHandler[Abstract,"invalidFormat",_,_String,""] 13 | 14 | 15 | MakeGuideAbstract[gd_,nb_,OptionsPattern[]]:=If[Abstract[gd]=!="", 16 | NotebookWrite[nb,Cell[ParseToDocEntry[Abstract[gd],"LinkOptions"->BaseStyle->"InlineFunctionSans"],"GuideAbstract"]] 17 | ] 18 | 19 | 20 | MakeOverviewAbstract[gd_,nb_,OptionsPattern[]]:=If[Abstract[gd]=!="", 21 | NotebookWrite[nb,Cell[ParseToDocEntry@Abstract[gd],"TutorialAbstract"]] 22 | ] 23 | 24 | 25 | AppendTo[$DocumentationSections["Guide"],MakeGuideAbstract]; 26 | AppendTo[$DependencyCollectors["Guide"],Abstract]; 27 | 28 | 29 | 30 | AppendTo[$DocumentationSections["Overview"],MakeOverviewAbstract]; 31 | AppendTo[$DependencyCollectors["Overview"],Abstract]; 32 | 33 | 34 | End[] 35 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/AbstractDoc.wl: -------------------------------------------------------------------------------- 1 | (*::Package::*) 2 | 3 | Usage[Abstract]="[*[*Abstract[guide]*]'''=```abstract```'''*] sets the abstract for the guide page ```guide``` to ```abstract```. 4 | [*[*Abstract[overview]*]'''=```abstract```'''*] sets the abstract for the overview page ```overview``` to ```abstract```."; 5 | 6 | 7 | Begin[BuildAction] 8 | 9 | 10 | DocumentationHeader[Abstract]=FSHeader["0.66.0","0.70.0"]; 11 | 12 | 13 | Details[Abstract]={ 14 | "[*Abstract*] is one of the metadata symbols used by [*DocumentationBuilder*] for guide pages (see [*Guide*]) and tutorial overviews (see [*TutorialOverview*]). Others include [*GuideSections*], [*Tutorials*] and [*Guides*] (for guides) and [*OverviewEntries*] (for tutorial overviews).", 15 | "[*Abstract[\[Ellipsis]]*] should be set to a string.", 16 | "The string ```abstract``` in [*Abstract[\[Ellipsis]]*]'''=```abstract```''' can contain formatting specifications supported by [*FormatUsage*].", 17 | "The text of [*Abstract[\[Ellipsis]]*] is also used to populate the summary seen in the search results page." 18 | }; 19 | 20 | 21 | Examples[Abstract,"Basic examples"]={ 22 | { 23 | "Load the ForScience package:", 24 | ExampleInput[Needs["ForScience`PacletUtils`"]], 25 | "Declare a new symbol and tag it as guide:", 26 | ExampleInput[guide=Guide["My test guide"];,InitializationCell->True], 27 | "Set [*DocumentationHeader*] to enable [*DocumentationBuilder*] to build the guide page:", 28 | ExampleInput[DocumentationHeader[guide]={"DOCUMENTATION EXAMPLE GUIDE",Orange};,InitializationCell->True], 29 | "Add an abstract to the guide and build the documenation page:", 30 | ExampleInput[ 31 | Abstract[guide]="This is an abstract.";, 32 | DocumentationBuilder[guide] 33 | ], 34 | ExampleInput[NotebookClose[%];,Visible->False] 35 | }, 36 | { 37 | "Use formatting:", 38 | ExampleInput[ 39 | Abstract[guide]="This is an abstract with ```formatted``` text. This is a linked symbol: [*List*]. This is a link to another guide: <*Guide/Associations*>.";, 40 | DocumentationBuilder[guide] 41 | ], 42 | ExampleInput[NotebookClose[%];,Visible->False] 43 | } 44 | }; 45 | 46 | 47 | SeeAlso[Abstract]={Guide,TutorialOverview,DocumentationBuilder,GuideSections,Tutorials,Guides}; 48 | 49 | 50 | Guides[Abstract]={$GuideCreatingDocPages}; 51 | 52 | 53 | End[] 54 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/BuildAction.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | BuildAction; 4 | CleanBuildActions; 5 | $EnableBuildActions; 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | 11 | $EnableBuildActions=False; 12 | 13 | 14 | $BuildActionContext="ForScience`BuildAction`"; 15 | 16 | 17 | Attributes[BuildAction]={HoldAll}; 18 | 19 | 20 | SyntaxInformation[BuildAction]={"ArgumentsPattern"->{__}}; 21 | 22 | 23 | BuildAction[acts__]/;$BuildActive||$EnableBuildActions:=CompoundExpression[acts] 24 | BuildAction[acts__]:=Null 25 | Begin[BuildAction]^:=Begin[$BuildActionContext] 26 | 27 | 28 | CleanBuildActions[expr_]:=With[ 29 | {ba=Symbol["BuildAction"]}, 30 | Module[ 31 | {BALvl=-1,lvl=0}, 32 | Switch[#, 33 | HoldComplete@_ba, 34 | Nothing, 35 | HoldComplete@Begin[_], 36 | ++lvl; 37 | If[MatchQ[#,HoldComplete@Begin[ba]], 38 | BALvl=Max[lvl,BALvl] 39 | ]; 40 | If[BALvl>0,Nothing,#], 41 | HoldComplete@End[], 42 | --lvl; 43 | If[BALvl==lvl+1, 44 | BALvl=-1; 45 | Nothing, 46 | If[BALvl>0,Nothing,#] 47 | ], 48 | _, 49 | If[BALvl>0,Nothing,#] 50 | ]&/@expr 51 | ] 52 | ] 53 | 54 | End[] 55 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/BuildActionDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[BuildAction]="BuildAction[act_1,\[Ellipsis]] behaves like [*CompoundExpression*] if [*$BuildActive*] or [*$EnableBuildActions*] is [*True*], otherwise returns [*Null*]. 4 | [*Begin[[*BuildAction*]]*] switches to a special context."; 5 | Usage[CleanBuildActions]="CleanBuildActions[{expr_1,\[Ellipsis]}] removes any top-level [*BuildAction[\[Ellipsis]]*] expressions when used in [*ProcessFile*]."; 6 | Usage[$EnableBuildActions]="$EnableBuildActions can be set to [*True*] to manually enable the evaluation of expressions wrapped in [*BuildAction*]."; 7 | 8 | 9 | Begin[BuildAction] 10 | 11 | 12 | DocumentationHeader[BuildAction]=FSHeader["0.49.0","0.87.31"]; 13 | 14 | 15 | Details[BuildAction]={ 16 | "When [*$BuildActive*] or [*$EnableBuildActions*] is [*True*], [*BuildAction[expr_1,\[Ellipsis]]*] is equivalent to ```expr```_1;\[Ellipsis].", 17 | "When [*$BuildActive*] and [*EnableBuildActions*] are both [*False*], [*BuildAction[\[Ellipsis]]*] does nothing.", 18 | "Expressions wrapped in [*BuildAction*] are effectively only evaluated during [*BuildPaclet[\[Ellipsis]]*], or when [*$EnableBuildActions*] is manually set to [*True*].", 19 | "[*Begin*][[*BuildAction*]] switches to a special context to prevent other contexts from being polluted.", 20 | "[*Begin*][[*BuildAction*]] and [*End[]*] are typically wrapped around metadata assignments that are not needed once the package is deployed. These include [*Details[sym]*],[*Examples[sym]*] and [*SeeAlso[sym]*].", 21 | "[*BuildAction[\[Ellipsis]]*] can be removed during paclet build by using [*CleanBuildActions*] as file post-processor for [*BuildPaclet*].", 22 | "Expressions between [*Begin*][[*BuildAction*]]/[*End[]*] can be removed likewise be removed using [*CleanBuildActions*]." 23 | }; 24 | 25 | 26 | SeeAlso[BuildAction]=Hold[$BuildActive,$EnableBuildActions,CleanBuildActions,BuildPaclet,Details,Examples,SeeAlso]; 27 | 28 | 29 | DocumentationHeader[CleanBuildActions]=FSHeader["0.49.0","0.87.31"]; 30 | 31 | 32 | Details[CleanBuildActions]={ 33 | "[*CleanBuildActions*] is a file processor for [*ProcessFile*] and [*BuildPaclet*].", 34 | "[*CleanBuildActions*] removes any top-level [*BuildAction[\[Ellipsis]]*] expressions.", 35 | "Expressions between [*Begin*][[*BuildAction*]]/[*End[]*] are also removed by [*CleanBuildActions*].", 36 | "[*CleanBuildActions*] is typically used as file post-processor for [*BuildPaclet*] to strip unnecessary code before deployment." 37 | }; 38 | 39 | 40 | SeeAlso[CleanBuildActions]={BuildAction,BuildPaclet,ProcessFile}; 41 | 42 | 43 | DocumentationHeader[$EnableBuildActions]=FSHeader["0.65.0"]; 44 | 45 | 46 | Details[$EnableBuildActions]={ 47 | "[*$EnableBuildActions*] can be used to force [*BuildAction[\[Ellipsis]]*] expressions to be evaluated.", 48 | "By default, [*$EnableBuildActions*] is [*False*].", 49 | "[*$EnableBuildActions*] is never automatically set to [*True*]." 50 | }; 51 | 52 | 53 | SeeAlso[$EnableBuildActions]=Hold[BuildAction,BuildPaclet,$BuildActive]; 54 | 55 | 56 | End[] 57 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/BuildPaclet.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | BuildPaclet; 4 | $BuildActive; 5 | $BuiltPaclet; 6 | $BuildCacheDirectory; 7 | 8 | 9 | Begin["`Private`"] 10 | 11 | 12 | If[!TrueQ[$BuildActive], 13 | $BuildActive=False; 14 | $BuiltPaclet=""; 15 | $BuildCacheDirectory=""; 16 | ] 17 | 18 | 19 | Options[BuildPaclet]={"BuildDirectory"->"build/","CacheDirectory"->"cache/",ProgressIndicator->True}; 20 | 21 | 22 | SyntaxInformation[BuildPaclet]={"ArgumentsPattern"->{_,_.,OptionsPattern[]}}; 23 | 24 | 25 | BuildPaclet::noDir="The specified path `` is not a directory"; 26 | BuildPaclet::cleanFailed="Could not delete build directory ``."; 27 | BuildPaclet::initFailed="Build initialization failed."; 28 | 29 | 30 | procList={Except[_List]...}; 31 | procLists={procList,procList}; 32 | flatOpts=OptionsPattern[]?(Not@*ListQ); 33 | 34 | 35 | $PrivateContext=$Context; 36 | GetMarker; 37 | 38 | 39 | BuildPaclet[dir_,o:flatOpts]:=BuildPaclet[dir,{},o] 40 | BuildPaclet[dir_,postProcs:procList,gProcs:(procList|procLists):{},o:flatOpts]:= 41 | BuildPaclet[dir,{{},postProcs},gProcs,o] 42 | BuildPaclet[dir_,procs:procLists,gPostProcs:procList:{},o:flatOpts]:= 43 | BuildPaclet[dir,procs,{{},gPostProcs},o] 44 | BuildPaclet[dir_,procs:{preProcs:procList,postProcs:procList},gProcs:{gPreProcs:procList,gPostProcs:procList},o:flatOpts]:= 45 | Module[ 46 | { 47 | buildDir=OptionValue["BuildDirectory"], 48 | cacheDir=OptionValue["CacheDirectory"], 49 | absoluteCacheDir, 50 | oldBuildActive=$BuildActive, 51 | oldBuiltPaclet=$BuiltPaclet, 52 | oldCacheDirectory=$BuildCacheDirectory 53 | }, 54 | If[!DirectoryQ[dir],Message[BuildPaclet::noDir,dir];Return@$Failed]; 55 | If[ 56 | FileExistsQ@FileNameDrop[#,0]&&!DirectoryQ@#, 57 | Message[BuildPaclet::noDir,#]; 58 | Return@$Failed 59 | ]&/@{buildDir,cacheDir}; 60 | If[DirectoryQ@buildDir&&Quiet@DeleteDirectory[buildDir,DeleteContents->True]===$Failed, 61 | Message[BuildPaclet::cleanFailed,buildDir]; 62 | Return@$Failed 63 | ]; 64 | Check[ 65 | CopyDirectory[dir,buildDir]; 66 | If[!FileExistsQ@cacheDir, 67 | CreateDirectory[cacheDir] 68 | ]; 69 | absoluteCacheDir=AbsoluteFileName@cacheDir; 70 | SetDirectory[buildDir], 71 | Message[BuildPaclet::initFailed]; 72 | Return@$Failed 73 | ]; 74 | $BuildActive=True; 75 | Begin[$PrivateContext]; 76 | $BuiltPaclet=KeyMap[ToString,Association@@Get["PacletInfo.m"]]["Name"]; 77 | End[]; 78 | $BuildCacheDirectory=absoluteCacheDir; 79 | If[OptionValue@ProgressIndicator,PrintTemporary@"Running pre-processors..."]; 80 | #[]&/@gPreProcs; 81 | Module[ 82 | {trackGet=True,getTag,loadedFiles,curFile,prog=0}, 83 | (* make sure local version is found, see https://mathematica.stackexchange.com/a/66118/36508*) 84 | PacletDirectoryAdd["."]; 85 | loadedFiles=First@Last@Reap[ 86 | Internal`InheritedBlock[ 87 | {Get}, 88 | Unprotect@Get; 89 | Get[file_]:=With[ 90 | {s=Stack[]}, 91 | ( 92 | If[StringStartsQ[#,Directory[]], 93 | Block[ 94 | {trackGet=False}, 95 | ProcessFile[preProcs]@Sow[#,getTag] 96 | ] 97 | ]&@(curFile=FindFile@file); 98 | ++prog; 99 | First@GetMarker[Get@file] 100 | )/;trackGet&&Length@s<3||s[[-3]]=!=GetMarker 101 | ]; 102 | If[OptionValue@ProgressIndicator, 103 | PrintTemporary@"Loading paclet files..."; 104 | Monitor[ 105 | Get[dir<>"`"], 106 | Row@{ProgressIndicator[prog,Indeterminate]," ",curFile} 107 | ], 108 | Get[dir<>"`"] 109 | ]; 110 | ], 111 | getTag 112 | ]; 113 | PacletDirectoryRemove["."]; 114 | loadedFiles=Select[StringStartsQ[Directory[]]]@loadedFiles; 115 | If[OptionValue@ProgressIndicator,PrintTemporary@"Processing files..."]; 116 | If[OptionValue@ProgressIndicator,Apply@Monitor,First]@Hold[ 117 | prog=0; 118 | (curFile=#;++prog;ProcessFile[postProcs]@#)&/@loadedFiles, 119 | Row@{ProgressIndicator[prog,{0,Length@loadedFiles}]," ",curFile} 120 | ]; 121 | If[OptionValue@ProgressIndicator,PrintTemporary@"Running post-processors..."]; 122 | #[]&/@gPostProcs; 123 | ResetDirectory[]; 124 | $BuildActive=oldBuildActive; 125 | $BuiltPaclet=oldBuiltPaclet; 126 | $BuildCacheDirectory=oldCacheDirectory; 127 | PackPaclet[buildDir] 128 | ] 129 | ] 130 | 131 | 132 | End[] 133 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/BuildPacletDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[BuildPaclet]="BuildPaclet[dir] copies the content of ```dir``` to the build directory specified by the '''\"BuildDirectory\"''', loads it and packs the paclet. 4 | BuildPaclet[dir,{postProc_1,\[Ellipsis]}] applies all ```postProc_i``` to any files loaded during paclet load using [*ProcessFile*] 5 | BuildPaclet[dir,{{preProc_1,\[Ellipsis]},{postProc_1,\[Ellipsis]}}] applies any ```preProc_i``` before the file is actually loaded. 6 | BuildPaclet[dir,{\[Ellipsis]},{globalPostProc_1,\[Ellipsis]}] evaluates all ```globalPostProc_i``` after the paclet has been loaded from the build directory. 7 | BuildPaclet[dir,{\[Ellipsis]},{{globalPreProc_1,\[Ellipsis]},{globalPostProc_1,\[Ellipsis]}}] evaluates all ```globalPreProc_i``` before the paclet is loaded from the build directory."; 8 | Usage[$BuildActive]="$BuildActive is '''True''' whenever a paclet is currently being built (see [*BuildPaclet*])."; 9 | Usage[$BuiltPaclet]="$BuiltPaclet is set to the name of the actively built paclet. If no paclet is being built, this is '''\"\"'''."; 10 | Usage[$BuildCacheDirectory]="$BuildCacheDirectory is set to the absolute path to the cache directory specified for [*BuildPaclet*] during build. If no paclet is being built, this is '''\"\"'''." 11 | 12 | 13 | Begin[BuildAction] 14 | 15 | 16 | DocumentationHeader[BuildPaclet]=FSHeader["0.48.0","0.83.10"]; 17 | 18 | 19 | Details[BuildPaclet]={ 20 | "[*BuildPaclet*] is the main function of the paclet build framework.", 21 | "[*BuildPaclet*] returns the path to the packed .paclet file.", 22 | "[*BuildPaclet*] copies the files from the paclet directory ```dir``` to a temporary build directory before packing them.", 23 | "During [*BuildPaclet[dir,\[Ellipsis]]*], the paclet is loaded directly from the directory, even if it is already installed.", 24 | "To guarantee proper loading, the paclet should load all subcomponents using [*Get*].", 25 | "[*BuildPaclet*] accepts the following options:", 26 | TableForm@{ 27 | {"\"BuildDirectory\"","\"build/\"","The working directory of [*BuildPaclet*]"}, 28 | {"\"CacheDirectory\"","\"cache/\"","The directory for caching things between builds"} 29 | }, 30 | "File pre/post-processors are effectively handled using [*ProcessFile*].", 31 | "File pre-processors are applied to each file in ```dir``` before it is loaded during paclet load. External files are left untouched.", 32 | "File post-processors are applied at the end once all files are loaded.", 33 | "Predefined file processors include [*UsageCompiler,CompatibilityChecker,CleanBuildActions*] and [*VariableLeakTracer*].", 34 | "During evaluation of [*BuildPaclet[\[Ellipsis]]*], the following global variables are set:", 35 | TableForm@{ 36 | {"[*$BuildActive*]",True}, 37 | {"[*$BuiltPaclet*]","Name of the paclet being built"}, 38 | {"[*$BuildCacheDirectory*]","Absolute path of the specified cache directory"} 39 | }, 40 | "The working directory is the specified build directory when calling any of the processors.", 41 | "Global pre-processors are called before anything is loaded, but after basic validation checks have been performed.", 42 | "Global post-processors are called after everything is loaded and after the file post-processors have been applied.", 43 | "Global processors get called with no arguments.", 44 | "[*DocumentationBuilder*] is designed as a global post-processor for [*BuildPaclet*]." 45 | }; 46 | 47 | 48 | SeeAlso[BuildPaclet]=Hold[$BuiltPaclet,$BuildActive,$BuildCacheDirectory,DocumentationBuilder,ProcessFile,CompatibilityChecker,UsageCompiler]; 49 | 50 | 51 | DocumentationHeader[$BuildActive]=FSHeader["0.48.0"]; 52 | 53 | 54 | Details[$BuildActive]={ 55 | "[*$BuildActive*] is [*True*] during evaluation of [*BuildPaclet[\[Ellipsis]]*] and [*False*] otherwise.", 56 | "[*$BuildActive*] controls the evaluation of [*BuildAction[\[Ellipsis]]*] expressions." 57 | }; 58 | 59 | 60 | SeeAlso[$BuildActive]=Hold[BuildAction,BuildPaclet,$BuiltPaclet,$BuildCacheDirectory]; 61 | 62 | 63 | DocumentationHeader[$BuiltPaclet]=FSHeader["0.55.1"]; 64 | 65 | 66 | Details[$BuiltPaclet]={ 67 | "[*$BuiltPaclet*] is set during the evaluation of [*BuildPaclet[\[Ellipsis]]*] and \"\" otherwise.", 68 | "[*$BuiltPaclet*] is set to the paclet name as specified in the \"PacletInfo.m\" file." 69 | }; 70 | 71 | 72 | SeeAlso[$BuiltPaclet]=Hold[BuildPaclet,$BuildActive,$BuildCacheDirectory]; 73 | 74 | 75 | DocumentationHeader[$BuildCacheDirectory]=FSHeader["0.83.10"]; 76 | 77 | 78 | Details[$BuildCacheDirectory]={ 79 | "[*$BuildCacheDirectory*] is set during the evaluation of [*BuildPaclet[\[Ellipsis]]*] and \"\" otherwise.", 80 | "[*$BuildCacheDirectory*] is set to the absolute path of the cache directory specified for [*BuildPaclet*] via the \"CacheDirectory\" option." 81 | }; 82 | 83 | 84 | SeeAlso[$BuildCacheDirectory]=Hold[BuildPaclet,$BuildActive,$BuiltPaclet] 85 | 86 | 87 | End[] 88 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/BuildTimeEvaluate.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | BuildTimeEvaluate; 4 | BuildTimeEvaluator; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | BuildTimeResult[_]:=Missing["NotAvailable"] 11 | 12 | 13 | Attributes[BuildTimeEvaluate]={HoldAll}; 14 | 15 | 16 | SyntaxInformation[BuildTimeEvaluate]={"ArgumentsPattern"->{_,_.,OptionsPattern[]}}; 17 | 18 | 19 | Options[BuildTimeEvaluate]={"CacheResults"->True,"CacheID"->0}; 20 | 21 | 22 | BuildTimeEvaluate[id_String,expr_,OptionsPattern[]]:= 23 | BuildTimeResult[id]= 24 | If[$BuildActive&&OptionValue["CacheResults"], 25 | Module[ 26 | { 27 | cacheDir=FileNameJoin@{$BuildCacheDirectory,"BuildTimeExpressions"}, 28 | file, 29 | ev, 30 | cacheData 31 | }, 32 | file=FileNameJoin@{cacheDir,id<>ToString@OptionValue["CacheID"]<>".mx"}; 33 | If[FileExistsQ@file, 34 | Import@file, 35 | ev=expr; 36 | If[!FileExistsQ@cacheDir, 37 | CreateDirectory[cacheDir] 38 | ]; 39 | Export[file,ev]; 40 | ev 41 | ] 42 | ], 43 | expr 44 | ] 45 | 46 | 47 | BuildTimeEvaluate[id_String,OptionsPattern[]]:= 48 | BuildTimeResult[id] 49 | 50 | 51 | BuildTimeEvaluator[exprs_,OptionsPattern[]]:= 52 | With[ 53 | {bte=Symbol["BuildTimeEvaluate"]}, 54 | exprs/.bte[id_String,_:PatternSequence[],OptionsPattern[]]:> 55 | With[ 56 | { 57 | res=BuildTimeResult[id] 58 | }, 59 | res/;True 60 | ] 61 | ] 62 | 63 | 64 | End[] 65 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/BuildTimeEvaluateDoc.wl: -------------------------------------------------------------------------------- 1 | 2 | (* ::Package:: *) 3 | 4 | Usage[BuildTimeEvaluate]="BuildTimeEvaluate[id,expr] assigns ```expr``` to [*BuildTimeEvaluate[id]*] and returns ```expr```. 5 | BuildTimeEvaluate[id] will be replaced by the expression assigned to ```id```."; 6 | Usage[BuildTimeEvaluator]="BuildTimeEvaluator is a file processor that replaces any [*BuildTimeEvaluate*] expressions by the appropriate expressions."; 7 | 8 | 9 | Begin[BuildAction] 10 | 11 | 12 | DocumentationHeader[BuildTimeEvaluate]=FSHeader["0.84.0","0.85.1"]; 13 | 14 | 15 | Details[BuildTimeEvaluate]={ 16 | "[*BuildTimeEvaluate[id,expr]*] assigns the result of evaluating ```expr``` to ```id```.", 17 | "[*BuildTimeEvaluate[id,expr]*] evaluates to ```expr```.", 18 | "[*BuildTimeEvaluate[id]*] evaluates to the expression assigned to ```id``` by previous [*BuildTimeEvaluate*] calls.", 19 | "[*BuildTimeEvaluate[id]*] can be used to insert the result of a computation into held expressions.", 20 | "If no expression has been assigned to ```id```, [*BuildTimeEvaluate[id]*] evaluates to [*Missing*][\"NotAvailable\"].", 21 | "Expressions wrapped in [*BuildTimeEvaluate*] can be pre-evaluated during paclet build by [*BuildTimeEvaluator*].", 22 | "[*BuildTimeEvaluate*] is typically wrapped around expensive code where only the result is important.", 23 | "[*BuildTimeEvaluate*] accepts the following options:", 24 | TableForm@{ 25 | {"\"CacheResults\"",True,"Whether to cache the result of evaluating ```expr```"}, 26 | {"\"CacheID\"",0,"An identifier to use for cache lookups"} 27 | }, 28 | "Changing the value of \"CacheID\" can force ```expr``` to be reevaluated (assuming that value has not been used before).", 29 | "[*BuildTimeEvaluate*] uses the cache directory specified by [*$BuildCacheDirectory*] to cache the results of the expressions to be evaluated.", 30 | "[*BuildTimeEvaluate*] only uses the cache during paclet build, i.e. when [*$BuildActive*] is [*True*]." 31 | }; 32 | 33 | 34 | SeeAlso[BuildTimeEvaluate]=Hold[BuildTimeEvaluator,BuildPaclet,BuildAction,$BuildCacheDirectory]; 35 | 36 | 37 | DocumentationHeader[BuildTimeEvaluator]=FSHeader["0.84.0","0.85.1"]; 38 | 39 | 40 | Details[BuildTimeEvaluator]={ 41 | "[*BuildTimeEvaluator*] is a file processor for [*ProcessFile*] and [*BuildPaclet*].", 42 | "[*BuildTimeEvaluator*] replaces any expressions of the form [*BuildTimeEvaluate[id]*] and [*BuildTimeEvaluate[id,expr]*] by the expression assigned to ```id```.", 43 | "[*BuildTimeEvaluate[id,expr]*] can be used to assign the result of evaluating ```expr``` to ```id```.", 44 | "[*BuildTimeEvaluator*] is typically used as file post-processor for [*BuildPaclet*] to pre-execute expensive setup code." 45 | }; 46 | 47 | 48 | SeeAlso[BuildTimeEvaluator]=Hold[BuildTimeEvaluate,BuildPaclet,ProcessFile,$BuildCacheDirectory]; 49 | 50 | 51 | End[] 52 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/CleanExampleDirectory.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | CleanExampleDirectory; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | CleanExampleDirectory=ExampleInput[ 10 | With[ 11 | {dir=Directory[]}, 12 | ResetDirectory[]; 13 | DeleteDirectory[dir,DeleteContents->True]; 14 | ];, 15 | Visible->False 16 | ]; 17 | 18 | 19 | End[] 20 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/CleanExampleDirectoryDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[CleanExampleDirectory]="CleanExampleDirectory is an [*ExampleInput*] that deletes the current directory and resets the working directory."; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[CleanExampleDirectory]=FSHeader["0.63.0"]; 10 | 11 | 12 | Details[CleanExampleDirectory]={ 13 | "[*CleanExampleDirectory*] deletes the current working directory and resets the working directory back to the previous value.", 14 | "[*CleanExampleDirectory*] does not generate anything in the documentation notebook. See the [*Visible*] option of [*ExampleInput*].", 15 | "[*CleanExampleDirectory*] is intended to be used after the last input cell of a symbol's examples, to clean up and restore the working directory." 16 | }; 17 | 18 | 19 | SeeAlso[CleanExampleDirectory]={DocumentationBuilder,Examples,ExampleInput}; 20 | 21 | 22 | End[] 23 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/CompatibilityChecker.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | CompatibilityChecker; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | SyntaxInformation[CompatibilityChecker]={"ArgumentsPattern"->{_,OptionsPattern[]}}; 10 | 11 | 12 | Options[CompatibilityChecker]={"WarnForModification"->False}; 13 | 14 | 15 | Attributes[SymbolVersion]={HoldFirst}; 16 | 17 | 18 | SymbolVersion[s_Symbol]:=SymbolVersion[s]= 19 | WolframLanguageData[ 20 | SafeSymbolName@s, 21 | "VersionIntroduced" 22 | ]/._Missing->0 23 | Attributes[SymbolVersion]={HoldFirst}; 24 | 25 | 26 | Attributes[SymbolModVersion]={HoldFirst}; 27 | 28 | 29 | SymbolModVersion[s_Symbol]:=SymbolModVersion[s]= 30 | WolframLanguageData[ 31 | SafeSymbolName@s, 32 | "VersionLastModified" 33 | ]/._Missing->SymbolVersion[s] 34 | Attributes[SymbolModVersion]={HoldFirst}; 35 | 36 | 37 | CompatibilityChecker::tooNew="Symbol `` was only introduced in version ``."; 38 | CompatibilityChecker::tooNewMod="Symbol `` was modified in version ``."; 39 | CompatibilityChecker[ver_,OptionsPattern[]][exprs_]:=( 40 | Cases[ 41 | exprs, 42 | s:Except[HoldPattern@Symbol[___],_Symbol]:> 43 | With[ 44 | {sVer=If[OptionValue["WarnForModification"], 45 | SymbolModVersion[s], 46 | SymbolVersion[s] 47 | ]}, 48 | If[sVer>ver, 49 | If[OptionValue["WarnForModification"], 50 | Message[CompatibilityChecker::tooNewMod,HoldForm@s,sVer], 51 | Message[CompatibilityChecker::tooNew,HoldForm@s,sVer] 52 | ] 53 | ] 54 | ], 55 | {3,Infinity}, 56 | Heads->True 57 | ]; 58 | exprs 59 | ) 60 | 61 | 62 | End[] 63 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/CompatibilityCheckerDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[CompatibilityChecker]="CompatibilityChecker[ver] is a file processor that checks for each symbol when it was introduced/modified and produces a message if it is newer than ```ver```."; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[CompatibilityChecker]=FSHeader["0.51.0","0.61.11"]; 10 | 11 | 12 | Details[CompatibilityChecker]={ 13 | "[*CompatibilityChecker[\[Ellipsis]]*] is a file processor to be used by [*ProcessFile*] and [*BuildPaclet*].", 14 | "[*CompatibilityChecker*] uses [*WolframLanguageData*] to determine the version a symbol was introduced/last modified.", 15 | "[*CompatibilityChecker*] accepts the following options:", 16 | TableForm@{ 17 | {"\"WarnForModification\"",False,"Whether to check for the version of last modification instead"} 18 | }, 19 | "[*CompatibilityChecker*] issues a CompatibilityChecker::tooNew/CompatibilityChecker::tooNewMod message for each offending symbol use.", 20 | "[*CompatibilityChecker*] does not modify the contents of the processed file." 21 | }; 22 | 23 | 24 | Examples[CompatibilityChecker,"Basic examples"]={ 25 | { 26 | "Load the ForScience package:", 27 | ExampleInput[Needs["ForScience`PacletUtils`"]], 28 | "Create a test file:", 29 | ExampleInput[ 30 | SetDirectory@CreateDirectory[];, 31 | "Export[\"test.wl\", 32 | \" 33 | Print[1]; 34 | Plot[x,{x,0,1}]; 35 | Echo[2]; 36 | \", 37 | \"String\" 38 | ];", 39 | InitializationCell->True 40 | ], 41 | "Check for version 9 compatibility:", 42 | ExampleInput[ProcessFile["test.wl",{CompatibilityChecker[9]}]] 43 | }, 44 | { 45 | "Check for version 11 compatibility:", 46 | ExampleInput[ProcessFile["test.wl",{CompatibilityChecker[11]}]] 47 | }, 48 | { 49 | "Check for symbols that were modified after version 11:", 50 | ExampleInput[ProcessFile["test.wl",{CompatibilityChecker[11,"WarnForModification"->True]}]] 51 | } 52 | }; 53 | 54 | 55 | Examples[CompatibilityChecker,"Possible issues"]={ 56 | { 57 | "Repeated messages of the same type are suppressed by default:", 58 | ExampleInput[ 59 | "Export[\"tooMany.wl\", 60 | \" 61 | newSymbols={Echo,EchoFunction,SequenceReplace,Nothing,Unevaluated}; 62 | \", 63 | \"String\" 64 | ];", 65 | ProcessFile["tooMany.wl",{CompatibilityChecker[9]}] 66 | ], 67 | "Turn off General::stop to display all messages:", 68 | ExampleInput[ 69 | Off[General::stop], 70 | ProcessFile["tooMany.wl",{CompatibilityChecker[9]}], 71 | On[General::stop] 72 | ] 73 | }, 74 | { 75 | "Unknown symbols are ignored:", 76 | ExampleInput[ 77 | "Export[\"typos.wl\", 78 | \" 79 | unknownSymbols={EcoFunction,Noting,listPlot}; 80 | \", 81 | \"String\" 82 | ];", 83 | ProcessFile["typos.wl",{CompatibilityChecker[1]}] 84 | ], 85 | CleanExampleDirectory 86 | } 87 | }; 88 | 89 | 90 | SeeAlso[CompatibilityChecker]={BuildPaclet,ProcessFile,UsageCompiler,VariableLeakTracer}; 91 | 92 | 93 | End[] 94 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/DeclareMetadataHandler.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | DeclareMetadataHandler[handler_,msg_,symPat_,pat_,def_]:=( 7 | handler/:HoldPattern[handler[sym:symPat]=data:pat]:=(handler[sym]^=data); 8 | HoldPattern[handler[sym:symPat]=data_]^:=(Message[MessageName[handler,msg],HoldForm@sym,data];data); 9 | handler[symPat]:=def 10 | ) 11 | 12 | 13 | End[] -------------------------------------------------------------------------------- /ForScience/PacletUtils/DeclareSectionAccessor.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | DeclareSectionAccessor[acc_,{invFormMsg_,noMixItemMsg_,noMixSubMsg_,needSubMsg_,invKeyMsg_},symPat_,keyPat_,itemPat_]:=( 7 | CheckSectionAssoc[acc][ass_Association]:=With[ 8 | {errKeys=Select[Keys@ass,Not@*MatchQ[keyPat]]}, 9 | Message[MessageName[acc,invKeyMsg],#]&/@errKeys; 10 | If[Length@errKeys>0, 11 | False, 12 | AllTrue[ass,CheckSectionAssoc[acc]] 13 | ] 14 | ]; 15 | CheckSectionAssoc[acc][itemPat]:=True; 16 | CheckSectionAssoc[acc][item_]:=(Message[MessageName[acc,invFormMsg],item];False); 17 | 18 | 19 | acc/:HoldPattern[acc[sym:symPat,cats:(keyPat..)]=newItem:itemPat|_Association?(CheckSectionAssoc[acc])]:= 20 | Catch[ 21 | Module[ 22 | { 23 | path={cats}, 24 | item=acc[sym], 25 | subItem 26 | }, 27 | subItem=item; 28 | Do[ 29 | subItem=Lookup[ 30 | subItem, 31 | path[[i]], 32 | item=Insert[item,path[[i]]-><||>,Append[Key/@path[[;;i-1]],-1]];<||> 33 | ]; 34 | If[!AssociationQ@subItem, 35 | Message[MessageName[acc,noMixSubMsg],path[[i+1]],HoldForm@sym,path[[;;i]]]; 36 | Throw[Null] 37 | ], 38 | {i,Length@path-1} 39 | ]; 40 | If[!AssociationQ@newItem&&AssociationQ@subItem@Last@path, 41 | Message[MessageName[acc,noMixItemMsg],HoldForm@sym,path]; 42 | Throw[Null] 43 | ]; 44 | If[AssociationQ@newItem&&!AssociationQ@subItem@Last@path, 45 | Message[MessageName[acc,noMixSubMsg],Last@path,HoldForm@sym,path]; 46 | Throw[Null] 47 | ]; 48 | acc[sym]^=Insert[item,Last@path->newItem,If[!MissingQ@subItem@Last@path,Key/@path,Append[Key/@Most@path,-1]]]; 49 | newItem 50 | ] 51 | ]; 52 | acc[sym:symPat,cats:(keyPat..)]:=Quiet@Check[Extract[acc[sym],Key/@{cats}],{}]; 53 | HoldPattern[acc[symPat,__]=newItem_]^:=(Message[MessageName[acc,invFormMsg],newItem];newItem); 54 | acc/:HoldPattern[acc[sym:symPat]=item_Association?(CheckSectionAssoc[acc])]:=(acc[sym]^=item); 55 | acc/:HoldPattern[acc[sym:symPat]=_]:=Message[MessageName[acc,needSubMsg],HoldForm@sym]; 56 | acc/:HoldPattern[acc[sym:symPat,cats:(keyPat..)]=.]:=(acc[sym,##]=Quiet@KeyDrop[acc[sym,##],Last@{cats}])&@@Most@{cats}; 57 | acc/:HoldPattern[acc[sym:symPat]=.]:=acc[sym]=<||>; 58 | acc[symPat]:=<||>; 59 | acc[symPat,__]:={}; 60 | ) 61 | 62 | 63 | End[] 64 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/DocumentationCache.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | AppendTo[$DocumentationTypeData,$DependencyCollectors->{}]; 7 | 8 | 9 | Attributes[CacheFile]={HoldFirst}; 10 | 11 | 12 | CacheFile[sym_]:=StringReplace[Context@sym<>DocumentationTitle[sym],"`"->"_"] 13 | 14 | 15 | Attributes[CreateCacheID]={HoldFirst}; 16 | 17 | 18 | CreateCacheID[sym_,type_]:=AssociationMap[#@Unevaluated@sym&,Sort@$DependencyCollectors[type]] 19 | 20 | 21 | GetCacheDir[Automatic]:= 22 | FileNameJoin@{ 23 | If[$BuildActive, 24 | $BuildCacheDirectory, 25 | OptionValue[BuildPaclet,{},"CacheDirectory"] 26 | ], 27 | "Documentation" 28 | } 29 | GetCacheDir[dir_]:= 30 | FileNameJoin@{ 31 | Directory[], 32 | dir, 33 | "Documentation" 34 | } 35 | 36 | 37 | DocumentationCachePut::noDir="The specified cache directory `` is not a directory."; 38 | DocumentationCachePut::noUpdate="Could not update documentation cache entry for ``."; 39 | 40 | 41 | Options[DocumentationCachePut]={"CacheDirectory"->Automatic}; 42 | 43 | 44 | Attributes[DocumentationCachePut]={HoldFirst}; 45 | 46 | 47 | DocumentationCachePut[sym_,type_,doc_,links_,OptionsPattern[]]:=With[ 48 | {cacheDir=FileNameJoin@{GetCacheDir@OptionValue["CacheDirectory"],type}}, 49 | With[ 50 | {cacheFile=FileNameJoin@{cacheDir,CacheFile[sym]}}, 51 | If[!DirectoryQ@cacheDir, 52 | If[FileExistsQ@FileNameDrop[cacheDir,0],Message[DocumentationCachePut::noDir,cacheDir];Return@Null]; 53 | CreateDirectory[cacheDir]; 54 | ] 55 | If[!FailureQ@CopyFile[doc,cacheFile<>".nb",OverwriteTarget->True], 56 | Export[cacheFile<>".mx",<|"Dependencies"->CreateCacheID[sym,type],Hyperlink->links|>], 57 | Message[DocumentationCachePut::noUpdate,HoldForm@sym] 58 | ]; 59 | ] 60 | ] 61 | 62 | 63 | Options[DocumentationCacheGet]={"CacheDirectory"->Automatic}; 64 | 65 | 66 | Attributes[DocumentationCacheGet]={HoldFirst}; 67 | 68 | 69 | DocumentationCacheGet[sym_,type_,OptionsPattern[]]:=With[ 70 | {cacheFile=FileNameJoin@{GetCacheDir@OptionValue["CacheDirectory"],type,CacheFile[sym]}}, 71 | If[!FileExistsQ[cacheFile<>".mx"],Return@Null]; 72 | With[ 73 | { 74 | curID=CreateCacheID[sym,type], 75 | cacheData=Import[cacheFile<>".mx"] 76 | }, 77 | If[cacheData["Dependencies"]=!=curID,Return@Null]; 78 | If[AnyTrue[cacheData[Hyperlink],DocumentedQ], 79 | If[ 80 | !FailureQ@Export[ 81 | cacheFile<>".nb", 82 | Import[cacheFile<>".nb"]/. 83 | TagBox[ 84 | _, 85 | Hyperlink->{id_,linkFunc_}|id_/;DocumentedQ[id], 86 | OptionsPattern[] 87 | ]:>With[ 88 | {res=First[{linkFunc,DocumentationLink}]@id}, 89 | res/;True 90 | ] 91 | ], 92 | Export[ 93 | cacheFile<>".mx", 94 | MapAt[Select[Not@*DocumentedQ],Key[Hyperlink]]@cacheData 95 | ] 96 | ] 97 | ]; 98 | cacheFile<>".nb" 99 | ] 100 | ] 101 | 102 | 103 | End[] 104 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/DocumentationFooter.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | Attributes[MakeFooter]={HoldFirst}; 7 | 8 | 9 | MakeFooter[sym_,_]:=With[ 10 | { 11 | splitter=StringSplit[#,RegularExpression["((?Cell[ver,"HistoryVersion"], 20 | 1 21 | ], 22 | "History" 23 | ], 24 | Cell[" ","FooterCell"] 25 | ] 26 | ] 27 | 28 | 29 | End[] 30 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/DocumentationHeader.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | $ForScienceColor; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | $ForScienceColor=Darker@Green; 10 | 11 | 12 | AppendTo[$DependencyCollectors[_],DocumentationHeader] 13 | 14 | 15 | SyntaxInformation[DocumentationHeader]={"ArgumentsPattern"->{_}}; 16 | 17 | 18 | If[!MatchQ[$DocumentedObjects,_Hold], 19 | $DocumentedObjects=Hold[]; 20 | ] 21 | 22 | 23 | Attributes[DocumentationHeader]={HoldFirst}; 24 | 25 | 26 | DocumentationHeader::invalid="Can't set documentation header data of `` to ``. Data should be of the form {str,col,introduction|None}."; 27 | 28 | 29 | DocumentationHeader/: 30 | HoldPattern[DocumentationHeader[sym_]={hd_String,col_?ColorQ,ft:_String|None:None}]:= 31 | ( 32 | $DocumentedObjects=Union[$DocumentedObjects,Hold[sym]]; 33 | DocumentationHeader[sym]^={hd,col,ft} 34 | ) 35 | HoldPattern[DocumentationHeader[sym_]=.]^:= 36 | ( 37 | $DocumentedObjects=DeleteCases[$DocumentedObjects,sym]; 38 | sym/:DocumentationHeader[sym]=. 39 | ) 40 | HoldPattern[DocumentationHeader[sym_]=header_]^:= 41 | (Message[DocumentationHeader::invalid,HoldForm@sym,header];header) 42 | DocumentationHeader[_]:={} 43 | 44 | 45 | AppendTo[$DocumentationTypeData,$HeaderEntries->{}]; 46 | 47 | 48 | AppendTo[$DocumentationStyles[_], 49 | VersionAwareTemplateBox["PacletName", 50 | Cell[#,"PacletNameCell"]&, 51 | Evaluate@GridBox[ 52 | {{ 53 | ItemBox[ 54 | Cell[ 55 | BoxData@RowBox@{ 56 | SpacerBox@8, 57 | Cell[#,"PacletNameCell",TextAlignment->Center], 58 | SpacerBox@8 59 | }, 60 | TextAlignment->Center 61 | ], 62 | Background->#2, 63 | ItemSize->Full 64 | ], 65 | "" 66 | }}, 67 | GridBoxAlignment->{"Rows"->{{Center}}}, 68 | GridBoxItemSize->{"Columns"->{Full,Scaled[0.02]},"Rows"->{{2.5}}} 69 | ]& 70 | ] 71 | ]; 72 | AppendTo[$DocumentationStyles[_], 73 | VersionAwareTemplateBox["HeaderMenuArrow", 74 | StyleBox["\[FilledDownTriangle]", "AnchorBarArrow"]&, 75 | GraphicsBox[ 76 | { 77 | GrayLevel[2/3], 78 | Thickness[0.13], 79 | LineBox@{{-1.8,0.5},{0,0},{1.8,0.5}} 80 | }, 81 | AspectRatio->1, 82 | ImageSize->20, 83 | PlotRange->{{-3,4},{-1,1}} 84 | ]& 85 | ] 86 | ]; 87 | 88 | 89 | Attributes[MakeHeader]={HoldFirst}; 90 | 91 | 92 | MakeHeader[sym_,type_]:= 93 | With[ 94 | { 95 | title=DocumentationHeader[sym][[1]], 96 | col=DocumentationHeader[sym][[2]], 97 | entries=#@sym&/@$HeaderEntries[type] 98 | }, 99 | Cell[ 100 | BoxData@GridBox@{{ 101 | TemplateBox[{title,col},"PacletName"], 102 | Cell[ 103 | TextData@{ 104 | Riffle[ 105 | entries, 106 | "\[ThickSpace]\[ThickSpace]\[ThickSpace]\[ThickSpace]\[ThickSpace]\[ThickSpace]" 107 | ] 108 | }, 109 | "AnchorBar" 110 | ] 111 | }}, 112 | "AnchorBarGrid" 113 | ] 114 | ] 115 | 116 | 117 | HeaderDropdownLink/:(_:>HeaderDropdownLink[ref_]):=RawDocumentationLink[ref]/.{ 118 | {id_,_Missing}->(id@Label:>TagBox[ref,Hyperlink->{id,HeaderDropdownLink}]), 119 | {id_,uri_}->(id@Label:>Documentation`HelpLookup[uri]) 120 | } 121 | 122 | 123 | MakeHeaderDropdown[title_,style_,refs_]:=If[Length@refs>0, 124 | Cell[ 125 | BoxData@TagBox[ 126 | ActionMenuBox[ 127 | FrameBox[Cell[TextData[{title," ",Cell@BoxData@TemplateBox[{},"HeaderMenuArrow"]}]],StripOnInput->False], 128 | #:>HeaderDropdownLink[#]&/@refs, 129 | Appearance->None, 130 | MenuAppearance->Automatic, 131 | MenuStyle->style 132 | ], 133 | MouseAppearanceTag["LinkHand"] 134 | ], 135 | LineSpacing->{1.4,0} 136 | ], 137 | Nothing 138 | ] 139 | 140 | 141 | End[] 142 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/DocumentationHeaderDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[DocumentationHeader]="[*[*DocumentationHeader[sym]*]'''={```header```,```color```,```footer```}'''*] sets the header text, header color and footer message for the documentation pages built by [*DocumentationBuilder*]."; 4 | Usage[$ForScienceColor]="$ForScienceColor is the documentation header color used by the ForScience package."; 5 | 6 | 7 | Begin[BuildAction] 8 | 9 | 10 | DocumentationHeader[DocumentationHeader]=FSHeader["0.55.0","0.70.2"]; 11 | 12 | 13 | Details[DocumentationHeader]={ 14 | "[*DocumentationHeader*] is the metadata handler for the basic data needed by [*DocumentationBuilder*] to build a documentation page.", 15 | "[*DocumentationBuilder*] can build a documentation page if and only if the corresponding [*DocumentationHeader*] is set.", 16 | "[*DocumentationHeader[sym]*] needs to be set to a list with the following three entries:", 17 | TableForm@{ 18 | {"```header```","The header text, describing the type of documentation page"}, 19 | {"```color```","The color of the header bar"}, 20 | {"```footer```","The content of the footer of the documentation page, containing introduction and modification dates"} 21 | }, 22 | "In the footer text, version numbers of the form #.#.# automatically formatted as such.", 23 | "The footer text can be set to [*None*] or left away if no footer should be included.", 24 | "[*DocumentationHeader[sym]*] can be removed with [*DocumentationHeader[sym]*]'''=.'''" 25 | }; 26 | 27 | 28 | Examples[DocumentationHeader,"Basic examples"]={ 29 | { 30 | "Load the ForScience package:", 31 | ExampleInput[Needs["ForScience`PacletUtils`"]], 32 | "Create an empty documentation page with the specified coloring and text:", 33 | ExampleInput[ 34 | DocumentationHeader[foo]={"MY FANCY DOCUMENTATION",Magenta,"This is was introduced in 0.0.0"};, 35 | DocumentationBuilder[foo] 36 | ], 37 | ExampleInput[NotebookClose[%];,Visible->False] 38 | }, 39 | { 40 | "Do not include a footer:", 41 | ExampleInput[ 42 | DocumentationHeader[foo]={"MY FANCY DOCUMENTATION",Green};, 43 | DocumentationBuilder[foo] 44 | ], 45 | ExampleInput[NotebookClose[%];DocumentationHeader[foo]=.,Visible->False] 46 | } 47 | }; 48 | 49 | 50 | Examples[DocumentationHeader,"Properties & Relations"]={ 51 | { 52 | "Documentation pages can only be generated for symbols with [*DocumentationHeader*] set:", 53 | ExampleInput[Clear@bar,Visible->False], 54 | ExampleInput[ 55 | Usage[bar]="bar[a] is doing absolutely nothing.";, 56 | DocumentationBuilder[bar] 57 | ], 58 | "Set [*DocumentationHeader[bar]*] to enable building the page:", 59 | ExampleInput[ 60 | DocumentationHeader[bar]={"GREAT SYMBOL",Black,"Now it works."};, 61 | DocumentationBuilder[bar] 62 | ], 63 | ExampleInput[NotebookClose[%];DocumentationHeader[bar]=.,Visible->False] 64 | } 65 | }; 66 | 67 | 68 | SeeAlso[DocumentationHeader]=Hold[DocumentationBuilder,$ForScienceColor,Usage,Details,Examples,SeeAlso]; 69 | 70 | 71 | Guides[DocumentationHeader]={$GuideCreatingDocPages}; 72 | 73 | 74 | Tutorials[DocumentationHeader]={$TutorialCreatingSymbolPages}; 75 | 76 | 77 | DocumentationHeader[$ForScienceColor]=FSHeader["0.55.0"]; 78 | 79 | 80 | SeeAlso[$ForScienceColor]={DocumentationHeader,DocumentationBuilder}; 81 | 82 | 83 | Guides[$ForScienceColor]={$GuideCreatingDocPages}; 84 | 85 | 86 | End[] 87 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/DocumentationOptions.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | DocumentationOptions; 4 | SetDocumentationOptions; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | optionPattern=(_Rule|_RuleDelayed)...; 11 | 12 | 13 | Attributes[DocumentationOptions]={HoldFirst}; 14 | 15 | 16 | DocumentationOptions::invFrm="`` is not a list of options."; 17 | DocumentationOptions::invOpt="Unknown option `` for documentation page part ``."; 18 | 19 | 20 | FilterInvalidOptions[part_,opts_]:=With[ 21 | {validOpts=Keys@DocumentationOptions[part]}, 22 | Replace[ 23 | opts, 24 | _[o_,_]/;!MemberQ[validOpts,o]:>( 25 | Message[DocumentationOptions::invOpt,o,part]; 26 | Nothing 27 | ), 28 | 1 29 | ] 30 | ] 31 | 32 | 33 | DocumentationOptions[part_[sym_Symbol]]:=DocumentationOptions[sym,part] 34 | HoldPattern[DocumentationOptions[part_[sym_Symbol]]=opts_]^:=(DocumentationOptions[sym,part]=opts) 35 | With[ 36 | {optionPattern={optionPattern}}, 37 | DocumentationOptions/:HoldPattern[DocumentationOptions[sym_Symbol,part_]=opts:optionPattern]:=( 38 | sym/:DocumentationOptions[sym,part]=Sort@DeleteCases[ 39 | DeleteDuplicatesBy[First]@FilterInvalidOptions[part,opts], 40 | _[_,Default] 41 | ] 42 | ); 43 | DocumentationOptions/:HoldPattern[DocumentationOptions[part_]=opts:optionPattern]:=( 44 | DocumentationOptions[part]^=Sort@DeleteDuplicatesBy[First]@opts 45 | ) 46 | ] 47 | HoldPattern[DocumentationOptions[_,_|PatternSequence[]]=opts_]^:=( 48 | Message[DocumentationOptions::invFrm,opts]; 49 | opts 50 | ) 51 | DocumentationOptions[_]:={} 52 | DocumentationOptions[_,_]:={} 53 | 54 | 55 | Attributes[SetDocumentationOptions]={HoldFirst}; 56 | 57 | 58 | SetDocumentationOptions[part_[sym_Symbol],opts:optionPattern]:=SetDocumentationOptions[sym,part,opts] 59 | SetDocumentationOptions[part_,opts:optionPattern]:=( 60 | DocumentationOptions[part]=FilterInvalidOptions[part,{opts}]~Join~DocumentationOptions[part] 61 | ) 62 | SetDocumentationOptions[sym_Symbol,part_,opts:optionPattern]:=( 63 | DocumentationOptions[sym,part]={opts}~Join~DeleteDuplicatesBy[First][ 64 | Cases[{opts},_[_,Default]]~Join~DocumentationOptions[sym,part] 65 | ] 66 | ) 67 | 68 | 69 | Attributes[DocumentationOptionValue]={HoldFirst}; 70 | 71 | 72 | DocumentationOptionValue[part_[sym_Symbol],opts_List]:=DocumentationOptionValue[part[sym],#]&/@opts 73 | DocumentationOptionValue[part_[sym_Symbol],opt_]:=If[ 74 | !MemberQ[Keys@DocumentationOptions[part],opt], 75 | Message[DocumentationOptions::invOpt,opt,part];opt, 76 | opt/.Join[DocumentationOptions[sym,part],DocumentationOptions[part]] 77 | ] 78 | 79 | 80 | FullDocumentationOptionValues[part_][sym_]:=AssociationMap[ 81 | DocumentationOptionValue[part[sym],#]&, 82 | Keys@DocumentationOptions[part] 83 | ] 84 | 85 | 86 | End[] 87 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/DocumentationStyling.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | AppendTo[$DependencyCollectors[_],$Pre111CompatStyles&] 7 | 8 | 9 | If[!MatchQ[$Pre111CompatStyles,True|False], 10 | $Pre111CompatStyles=False; 11 | ] 12 | 13 | 14 | StyleSwitch[ver_]:=FEPrivate`Less[FEPrivate`$VersionNumber,ver] 15 | StyleSwitch[ver_,old_,new_]:=FEPrivate`If[StyleSwitch[ver],old,new] 16 | 17 | 18 | StyleMultiSwitch[new_]:=new 19 | StyleMultiSwitch[old_,ver_,rest__]:=FEPrivate`If[StyleSwitch[ver],old,StyleMultiSwitch[rest]] 20 | 21 | 22 | VersionAwareTemplateBox[ver_Real:11.1,style_,preDf_,postDf_,o:OptionsPattern[]]:= 23 | VersionAwareTemplateBox[ver,StyleData[style],preDf,postDf,o] 24 | VersionAwareTemplateBox[ver_Real:11.1,style_StyleData,preDf_,postDf_,o:OptionsPattern[]]:= 25 | Cell[style, 26 | TemplateBoxOptions->{ 27 | DisplayFunction->Switch[ver, 28 | 11.1, 29 | Pre111StyleSwitch, 30 | 12.0, 31 | Pre120StyleSwitch, 32 | _, 33 | StyleSwitch[ver,##]& 34 | ][preDf,postDf] 35 | }, 36 | o 37 | ] 38 | 39 | 40 | CreateStyleDefinitions[type_,custom_]:= 41 | Notebook[ 42 | { 43 | Cell[StyleData[StyleDefinitions->FrontEnd`FileName[{"Wolfram"},"Reference.nb",CharacterEncoding->"UTF-8"]]], 44 | Sequence@@Replace[ 45 | GatherBy[ 46 | Join[ 47 | custom, 48 | $DocumentationStyles[type]/.{ 49 | Pre120StyleSwitch[old_,new_]:>StyleSwitch[12.0,old,new], 50 | Pre120StyleSwitch[]:>StyleSwitch[12.0], 51 | Pre111StyleSwitch[old_,new_]/;$Pre111CompatStyles:> 52 | StyleSwitch[11.1,old,new], 53 | Pre111StyleSwitch[]/;$Pre111CompatStyles:> 54 | StyleSwitch[11.1], 55 | Pre111StyleSwitch[_,new_]:>new, 56 | Pre111StyleSwitch[]->False 57 | } 58 | ], 59 | #[[1,1]]& 60 | ], 61 | { 62 | {style_Cell}:>style, 63 | styles:{Cell[id_,___],__Cell}:>Cell[ 64 | id, 65 | Sequence@@DeleteDuplicatesBy[First]@Flatten[Options/@styles] 66 | ], 67 | _->Nothing 68 | }, 69 | 1 70 | ] 71 | } 72 | ] 73 | 74 | 75 | AppendTo[$DocumentationTypeData,$DocumentationStyles->{ 76 | Cell[StyleData["Spacer1"], 77 | TemplateBoxOptions->{ 78 | DisplayFunction->( 79 | StyleBox[ 80 | GraphicsBox[ 81 | {}, 82 | ImageSize->{#,0}, 83 | BaselinePosition->(Scaled[0]->Baseline) 84 | ], 85 | CacheGraphics->False 86 | ]& 87 | ), 88 | InterpretationFunction->( 89 | InterpretationBox[ 90 | "", 91 | Spacer[#] 92 | ]& 93 | ), 94 | Tooltip->None 95 | } 96 | ], 97 | Cell[StyleData["Spacer2"], 98 | TemplateBoxOptions->{ 99 | DisplayFunction->( 100 | StyleBox[ 101 | GraphicsBox[ 102 | {}, 103 | ImageSize->{#,#2}, 104 | BaselinePosition->(Scaled[0]->Baseline) 105 | ], 106 | CacheGraphics->False 107 | ]& 108 | ), 109 | InterpretationFunction->( 110 | InterpretationBox[ 111 | "", 112 | Spacer[{#,#2}] 113 | ]& 114 | ), 115 | Tooltip->None 116 | } 117 | ], 118 | Cell[StyleData["RefLinkPlain",StyleDefinitions->StyleData["RefLink"]]], 119 | Cell[StyleData["OrangeLink"], 120 | TemplateBoxOptions->{ 121 | DisplayFunction:>( 122 | TagBox[ 123 | ButtonBox[ 124 | StyleBox[#,FontColor->Dynamic@If[CurrentValue["MouseOver"],RGBColor[0.854902,0.396078,0.145098],Inherited]], 125 | ButtonData->#2 126 | ], 127 | MouseAppearanceTag["LinkHand"] 128 | ]& 129 | ) 130 | }, 131 | ButtonBoxOptions->{BaseStyle->{"Link","GuideFunctionsSubsection"}} 132 | ] 133 | }]; 134 | 135 | 136 | End[] 137 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/DocumentationType.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | DocumentationTitle; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | $DocumentationTypeData=<||>; 10 | $DocumentationTypes=<||>; 11 | 12 | 13 | $DocumentationTypeData/:HoldPattern@AppendTo[$DocumentationTypeData,data_->def_]:=( 14 | data[_]:=$DocumentationTypeData[data]; 15 | data/:AppendTo[data[Verbatim[Blank][]],item_]:=( 16 | AppendTo[data[#],item]&/@Keys@$DocumentationTypes; 17 | AppendTo[$DocumentationTypeData[data],item]; 18 | ); 19 | $DocumentationTypeData[data]=def; 20 | ) 21 | 22 | 23 | $DocumentationTypes/:HoldPattern@AppendTo[$DocumentationTypes,spec:(type_->_)]:=( 24 | KeyValueMap[(#[type]=#2)&,$DocumentationTypeData]; 25 | $DocumentationTypes=Append[$DocumentationTypes,spec] 26 | ) 27 | 28 | 29 | Attributes[DocumentationOfTypeQ]={HoldFirst}; 30 | DocumentationOfTypeQ; 31 | Attributes[DocumentationTitle]={HoldFirst}; 32 | 33 | 34 | End[] 35 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/Examples.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | 4 | Examples; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | Attributes[Examples]={HoldFirst}; 11 | 12 | 13 | Examples::invalidFormat="`` is not a valid example format. Examples sections must be set to lists of lists or an associaton."; 14 | Examples::noMixingEx="Cannot add an example to `` under ``, as subcategories are already registered."; 15 | Examples::noMixingSub="Cannot add example subcategory '``' to `` under ``, as examples are already registered at this level."; 16 | Examples::needSubCat="Cannot add an example to ``, need to specify at least one subcategory."; 17 | Examples::noStringKey="Example section key `` must be string."; 18 | 19 | 20 | DeclareSectionAccessor[Examples,{"invalidFormat","noMixingEx","noMixingSub","needSubCat","noStringKey"},_,_String,{_List...}] 21 | 22 | 23 | ExampleHeader[title_,num_]:={title,"\[NonBreakingSpace]\[NonBreakingSpace]",Cell[StringTemplate["(``)"]@num,"ExampleCount"]} 24 | 25 | 26 | ExampleCount[ex_Association]:=Total[ExampleCount/@ex] 27 | ExampleCount[ex_List]:=Length@ex 28 | 29 | 30 | Attributes[ExamplesSection]={HoldFirst}; 31 | 32 | 33 | DocumentationOptions[Examples]={Open->Automatic}; 34 | 35 | 36 | $ExampleLevels={"PrimaryExamplesSection","ExampleSection","ExampleSubsection"}; 37 | 38 | 39 | Attributes[ExamplesSection]={HoldFirst}; 40 | 41 | 42 | ExamplesSection[sym_,sec_Association,nb_,lev_,path_]:= 43 | MapIndexed[ 44 | With[ 45 | { 46 | type=$ExampleLevels[[Min[lev,Length@$ExampleLevels]]], 47 | newPath=Append[path,First@#2] 48 | }, 49 | CreateDocumentationOpener[ 50 | nb, 51 | ExampleHeader[First@#,ExampleCount[Last@#]], 52 | type, 53 | lev==1, 54 | Append[ 55 | If[lev==1, 56 | Cell["","SectionFooterSpacer"], 57 | Nothing 58 | ] 59 | ]@ 60 | ExamplesSection[sym,Last@#,nb,lev+1,newPath], 61 | DocumentationOptionValue[Examples[sym],Open]/.{ 62 | All->True, 63 | None->False, 64 | Automatic:>MatchQ[newPath,{1..}] 65 | } 66 | ] 67 | ]& 68 | ]@Normal@sec 69 | ExamplesSection[_,sec_List,_,_,_]:= 70 | Join@@Riffle[ 71 | Map[ 72 | Switch[#, 73 | _String, 74 | Cell[ParseToDocEntry@#,"ExampleText"], 75 | Labeled[_String,_], 76 | Cell[ParseToDocEntry@First@#,"ExampleText",CellID->GenerateCellID@Last@#], 77 | _ExampleInput, 78 | ExampleInputToCell[#], 79 | _, 80 | SpecToCell[#,"ExampleText"] 81 | ]&, 82 | sec, 83 | {2} 84 | ], 85 | {{Cell[BoxData[InterpretationBox[Cell["\t","ExampleDelimiter"],$Line=0;]],"ExampleDelimiter"]}}, 86 | {2,-2,2} 87 | ] 88 | ExamplesSection[sym_Symbol,nb_]:=ExamplesSection[sym,<|"Examples"->Examples[sym]|>,nb,1,{1}] 89 | 90 | 91 | AppendTo[$DocumentationStyles["Symbol"], 92 | Cell[StyleData["ExampleSectionDelimiter",StyleDefinitions->StyleData["PageDelimiter"]], 93 | CellMargins->Pre120StyleSwitch[-2,{{24,14},{12,12}}], 94 | CellOpen->Pre120StyleSwitch[False,True] 95 | ] 96 | ]; 97 | 98 | 99 | Options[MakeExampleSection]={Examples->True}; 100 | 101 | 102 | Attributes[MakeExampleSection]={HoldFirst}; 103 | 104 | 105 | MakeExampleSection[sym_,nb_,OptionsPattern[]]:=If[OptionValue@Examples&&Length@Examples@sym>0, 106 | NotebookWrite[nb,Cell["","ExampleSectionDelimiter"]]; 107 | EvaluateAndWrite[nb,ExamplesSection[sym,nb],Join[ 108 | (* this enables evaluation of the $Line=0 lines hidden in the example delimiters *) 109 | {Cell[StyleData["ExampleDelimiter"],Evaluatable->True,CellContext->Notebook]}, 110 | Cell[StyleData[#], 111 | Evaluatable->True, 112 | CellContext->Notebook, 113 | TemplateBoxOptions->{InterpretationFunction->($Line=0;&)} 114 | ]&/@$ExampleLevels 115 | ]]; 116 | ] 117 | 118 | 119 | AppendTo[$DocumentationSections["Symbol"],MakeExampleSection]; 120 | AppendTo[$DependencyCollectors["Symbol"],Examples]; 121 | AppendTo[$DependencyCollectors["Symbol"],FullDocumentationOptionValues@Examples]; 122 | 123 | 124 | End[] 125 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/FSHeader.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | FSHeader; 4 | FSGuideHeader; 5 | FSTutorialHeader; 6 | FSOverviewHeader; 7 | 8 | 9 | Begin["`Private`"] 10 | 11 | 12 | FSHeader[verStr_]:={"FOR-SCIENCE SYMBOL",$ForScienceColor,StringTemplate["Introduced in ``"][verStr]} 13 | FSHeader[verStr_,modVerStr_]:={"FOR-SCIENCE SYMBOL",$ForScienceColor,StringTemplate["Introduced in `` | Updated in ``"][verStr,modVerStr]} 14 | 15 | 16 | FSGuideHeader={"FOR-SCIENCE GUIDE",Lighter@$ForScienceColor}; 17 | 18 | 19 | FSTutorialHeader={"FOR-SCIENCE TUTORIAL",Darker@$ForScienceColor}; 20 | 21 | 22 | FSOverviewHeader={"FOR-SCIENCE OVERVIEW",Darker@$ForScienceColor}; 23 | 24 | 25 | End[] 26 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/FSHeaderDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[FSHeader]="FSHeader[verStr] returns the common ForScience documentation header with the specified introduction version. 4 | FSHeader[verStr,modVerStr] returns the documentation header with both introduction and modification version."; 5 | 6 | 7 | Usage[FSGuideHeader]="FSGuideHeader is the common documentation header to ForScience guide pages."; 8 | 9 | 10 | Usage[FSTutorialHeader]="FSTutorialHeader is the common documentation header to ForScience tutorial pages."; 11 | 12 | 13 | Usage[FSOverviewHeader]="FSOverviewHeader is the common documentation header to ForScience tutorial overview pages."; 14 | 15 | 16 | Begin[BuildAction] 17 | 18 | 19 | DocumentationHeader[FSHeader]=FSHeader["0.62.0"]; 20 | 21 | 22 | SeeAlso[FSHeader]=Hold[DocumentationHeader,FSGuideHeader,FSTutorialHeader,FSOverviewHeader]; 23 | 24 | 25 | DocumentationHeader[FSGuideHeader]=FSHeader["0.66.0"]; 26 | 27 | 28 | SeeAlso[FSGuideHeader]=Hold[DocumentationHeader,FSHeader,FSTutorialHeader,FSOverviewHeader]; 29 | 30 | 31 | DocumentationHeader[FSTutorialHeader]=FSHeader["0.68.0"]; 32 | 33 | 34 | SeeAlso[FSTutorialHeader]=Hold[DocumentationHeader,FSHeader,FSGuideHeader,FSOverviewHeader]; 35 | 36 | 37 | DocumentationHeader[FSOverviewHeader]=FSHeader["0.70.0"]; 38 | 39 | 40 | SeeAlso[FSOverviewHeader]=Hold[DocumentationHeader,FSHeader,FSGuideHeader,FSTutorialHeader]; 41 | 42 | 43 | End[] 44 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/FormatUsage.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | FormatUsage; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | FormatUsage[str_]:=MakeUsageString@Map[ParseFormatting@FormatUsageCase[#,StartOfLine->True]&]@StringSplit[str,"\n"] 10 | SyntaxInformation[Unevaluated@FormatUsage]={"ArgumentsPattern"->{_}}; 11 | 12 | 13 | End[] 14 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/FormatUsageCase.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | (*usage formatting utilities, need to make public before defining, as they're already used in the usage definition*) 4 | FormatUsageCase; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | SyntaxInformation[FormatUsageCase]={"ArgumentsPattern"->{_,OptionsPattern[]}}; 11 | 12 | 13 | Options[FormatUsageCase]={StartOfLine->False}; 14 | 15 | 16 | FormatUsageCase[str_,OptionsPattern[]]:=StringReplace[ 17 | str, 18 | ( 19 | (func:(WordCharacter|"$"|"`")../;!StringContainsQ[func,"``"])~~ 20 | args:("["~~Except["["|"]"]...~~"]")...:> 21 | "[*"<>func<>StringReplace[args,arg:WordCharacter..:>"```"<>arg<>"```"]<>"*]" 22 | )/.(rhs_:>lhs_):>{ 23 | If[OptionValue[StartOfLine],StartOfLine~~rhs~~w:WhitespaceCharacter:>lhs<>w,Nothing], 24 | "[*"~~rhs~~"*]":>lhs 25 | } 26 | ] 27 | 28 | 29 | End[] 30 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/FormatUsageCaseDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[FormatUsageCase]="FormatUsageCase[str] prepares all function calls wrapped in \\[* and \\*] to be formatted by [*ParseFormatting*]."; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[FormatUsageCase]=FSHeader["0.0.1","0.60.4"]; 10 | 11 | 12 | Details[FormatUsageCase]={ 13 | "[*FormatUsageCase*] is called as part of [*FormatUsage*].", 14 | "[*FormatUsageCase*] accepts the following options:", 15 | TableForm@{ 16 | {StartOfLine,False,"whether to automatically detect function calls at the beginning of lines"} 17 | }, 18 | "[*FormatUsageCase*] works on function calls of the form [*func[arg_1,\[Ellipsis]][\[Ellipsis]]\[Ellipsis]*].", 19 | "[*FormatUsageCase*] formats all function arguments as \"Times Italic\".", 20 | "Under normal circumstances, [*FormatUsageCase*] should not need to be used directly." 21 | }; 22 | 23 | 24 | Examples[FormatUsageCase,"Basic examples"]={ 25 | { 26 | "Load the ForScience package:", 27 | ExampleInput[Needs["ForScience`PacletUtils`"]], 28 | "Use [*FormatUsageCase*] to automatically apply formatting specifiers to arguments of function calls:", 29 | ExampleInput[str=FormatUsageCase["This is a function call: [*f[a,b]*]"]], 30 | "Format it:", 31 | ExampleInput[MakeUsageString@ParseFormatting@str] 32 | }, 33 | { 34 | "Format more complex function calls:", 35 | ExampleInput[ 36 | str=FormatUsageCase["Sub calls are supported: [*f[arg_1,\[LeftAssociation]b\[Rule]c\[RightAssociation]][arg_2,\[Ellipsis]]*]"], 37 | MakeUsageString@ParseFormatting@str 38 | ] 39 | } 40 | }; 41 | 42 | 43 | Examples[FormatUsageCase,"Options","StartOfLine"]={ 44 | { 45 | "Setting [*StartOfLine->True*] enables automatic detection of function calls at the beginning of lines:", 46 | ExampleInput[ 47 | str=FormatUsageCase["f[a,b] is automatically formatted",StartOfLine->True], 48 | MakeUsageString@ParseFormatting@str 49 | ] 50 | } 51 | }; 52 | 53 | 54 | Examples[FormatUsageCase,"Properties & Relations"]={ 55 | { 56 | "[*FormatUsageCase*] is applied as part of [*FormatUsage*]:", 57 | ExampleInput[FormatUsage["f[a,b] and [*g[c,d]*] are properly formatted."]] 58 | } 59 | }; 60 | 61 | 62 | Examples[FormatUsageCase,"Possible issues"]={ 63 | { 64 | "Every argument is formatted, no matter if it is a symbol or a placeholder:", 65 | ExampleInput[ 66 | str=FormatUsageCase["[*FormatUsageCase[str,StartOfLine\[Rule]True]*]"], 67 | MakeUsageString@ParseFormatting@str 68 | ] 69 | }, 70 | { 71 | "Nested function calls are not supported:", 72 | ExampleInput[ 73 | str=FormatUsageCase["[*f[a,g[b]]*]"], 74 | MakeUsageString@ParseFormatting@str 75 | ] 76 | } 77 | }; 78 | 79 | 80 | SeeAlso[FormatUsageCase]={ParseFormatting,FormatUsage,Usage,MakeUsageString}; 81 | 82 | 83 | Guides[FormatUsageCase]={$GuideCreatingDocPages}; 84 | 85 | 86 | End[] 87 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/FormatUsageDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[FormatUsage]="FormatUsage[str] combines the functionalities of [*FormatUsageCase*], [*ParseFormatting*] and [*MakeUsageString*]."; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[FormatUsage]=FSHeader["0.0.1","0.88.19"]; 10 | 11 | 12 | Details[FormatUsage]={ 13 | "[*FormatUsage[str]*] takes a string containing format specifications and returns a formatted string.", 14 | "Supported format specifications are:", 15 | TableForm@{ 16 | {"``\\````str`````\\`","Formats ```str``` as \"TI\" (times italic), e.g. ```str```"}, 17 | {"\\'''```str```\\'''","Formats ```str``` as \"MR\" (mono regular), e.g. '''str'''"}, 18 | {"\\***```str```\\***","Prevents display of special characters in ```str```"}, 19 | {"\\///```str```\\///","Formats ```str``` italic, e.g. ///str///"}, 20 | {"```a```\\_```b```","Formats as subscript, e.g. a_b"}, 21 | {"```a```\\^```b```","Formats as superscript, e.g. a^b"}, 22 | {"\\{*```str```\\*}","Treats ```str``` as a single token, e.g. for subscripting"}, 23 | {"\\[*```str```\\*]","Effectively applies [*FormatUsageCase*] to ```str``` and tags it for hyperlinking (for documentation pages)"}, 24 | {"\\<*```str```\\*>","Tags ```str``` for hyperlinking (for documentation pages)"}, 25 | {"\\\\```char```","Escapes ```char```, preventing further interpretation"} 26 | }, 27 | "[*FormatUsage*] effectively calls [*FormatUsageCase*] with [*StartOfLine->True*].", 28 | "Format specifications can be arbitrarily nested.", 29 | "The formatted strings returned by [*FormatUsage*] are usable as usage messages for symbols.", 30 | "In \\<*```spec```\\*>, ```spec``` can either be the exact title of a documentation page or ```type```//```title```, where type is any documentation page type, such as Symbol,Format,Workflow,...", 31 | "In \\<*```spec```\\*>, ```spec``` can also be a web link, e.g. \\<*http://wolfram.com\\*>. Note that the protocol (i.e. http:// or https://) needs to be present.", 32 | "The label of a link specified by \\<*```spec```\\*> can be specified by prepending ```label```::, i.e. ```label```::```title``` or ```label```::```type```//```title```." 33 | }; 34 | 35 | 36 | Examples[FormatUsage,"Basic examples"]={ 37 | { 38 | "Load the ForScience package:", 39 | ExampleInput[Needs["ForScience`PacletUtils`"]], 40 | "Format a simple string:", 41 | ExampleInput[FormatUsage["This is a string with some ```formatting``` '''applied'''."]] 42 | }, 43 | { 44 | "Test the different formatting styles:", 45 | ExampleInput[FormatUsage["FormatUsage can format ```Times Italic```, '''Mono Regular''' and subscripted_{*with spaces!*}."]] 46 | }, 47 | { 48 | "More formatting:", 49 | ExampleInput[FormatUsage["///This text is italic///^{*and this is a superscript*}."]] 50 | }, 51 | { 52 | "Create a string to be used as usage message and assign it:", 53 | ExampleInput[myFunc::usage=FormatUsage["myFunc[x_1,x_2,\[Ellipsis]] does nothing. 54 | myFunc[{lhs\[RuleDelayed]rhs}] does something."];], 55 | "Check it (hover over the symbol and click on the dropdown to verify it is formatted properly):", 56 | ExampleInput["?myFunc"] 57 | }, 58 | { 59 | "Nest styles arbitrarily:", 60 | ExampleInput[FormatUsage["```bar_{*ab,'''cd'''*}```"]] 61 | }, 62 | { 63 | "Escape characters that would otherwise be interpreted as format specifiers:", 64 | ExampleInput[FormatUsage["\\```bar\\_\\{*ab,\\'''cd\\'''\\*}\\```"]] 65 | }, 66 | { 67 | "Show the control sequences for special characters instead of the characters themselves:", 68 | ExampleInput[FormatUsage["The character \[Alpha] can be entered as ***\[Alpha]***."]] 69 | } 70 | }; 71 | 72 | 73 | Examples[FormatUsage,"Properties & Relations"]={ 74 | { 75 | "[*Usage*] automatically formats the string using [*FormatUsage*] and assigns it as usage message:", 76 | ExampleInput[Usage[foo]="foo[x,y] is a placeholder.";], 77 | "Look at the usage message:", 78 | ExampleInput["?foo"] 79 | } 80 | }; 81 | 82 | 83 | Examples[FormatUsage,"Possible issues"]={ 84 | { 85 | "The front-end does not reload the usage messages in the dropdown once they are set:", 86 | ExampleInput[ 87 | foo::usage=FormatUsage["foo[x] does something."], 88 | foo::usage=FormatUsage["foo[y] now does something else."] 89 | ] 90 | } 91 | }; 92 | 93 | 94 | SeeAlso[FormatUsage]={Usage,FormatUsageCase,ParseFormatting,MakeUsageString}; 95 | 96 | 97 | Guides[FormatUsage]={$GuideCreatingDocPages}; 98 | 99 | 100 | End[] 101 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/GuideCreatingDocumentationPages.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | $GuideCreatingDocPages=Guide["Creating Documentation Pages"]; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[$GuideCreatingDocPages]=FSGuideHeader; 10 | 11 | 12 | Abstract[$GuideCreatingDocPages]="The ForScience PacletUtils subpaclet contains powerful documentation creation utilities, enabling a fully Wolfram Language based workflow."; 13 | 14 | 15 | GuideSections[$GuideCreatingDocPages]={ 16 | { 17 | {DocumentationBuilder,Text["build individual pages or everything that is documented"]}, 18 | {DocumentationHeader,Text["control basic documentation page appearance"]} 19 | }, 20 | { 21 | SectionTitle["Usage Messages"], 22 | {Usage,FormatUsage,Text["create and specify formatted usage messages"]}, 23 | {ParseFormatting,MakeUsageString,FormatUsageCase,Text["gain more granular control over the formatting process"]} 24 | }, 25 | { 26 | SectionTitle["Symbol Reference Pages"], 27 | {Usage,Text["specify usage cases"]}, 28 | {Details,Text["specify contents of \"Details and Options\""]}, 29 | {Examples,ExampleInput,Text["create interactive examples"]}, 30 | {SeeAlso,Text["refer to other symbols"]}, 31 | {Tutorials,Guides,Text["refer to related tutorials and guides"]} 32 | }, 33 | { 34 | SectionTitle["Documentation Guides"], 35 | {Guide,GuideQ,Text["create a new guide"]}, 36 | {Abstract,Text["specify a summary"]}, 37 | {GuideSections,SectionTitle,Text["specify sections with different functions"]}, 38 | {Tutorials,Guides,Text["refer to related tutorials and guides"]} 39 | }, 40 | { 41 | SectionTitle["Documentation Tutorials"], 42 | {Guide,GuideQ,Text["create a new tutorial"]}, 43 | {TutorialSections,Text["specify (sub)sections with text, examples, \[Ellipsis]"]}, 44 | {Tutorials,Guides,Text["refer to related tutorials and guides"]} 45 | }, 46 | { 47 | SectionTitle["Tutorial overviews"], 48 | {TutorialOverview,TutorialOverviewQ,Text["create a new tutorial"]}, 49 | {Abstract,Text["specify a summary"]}, 50 | {OverviewEntries,Text["specify the structure and linked tutorials of the overview"]} 51 | } 52 | }; 53 | 54 | 55 | Guides[$GuideCreatingDocPages]={$GuidePacletUtils}; 56 | 57 | 58 | Tutorials[$GuideCreatingDocPages]={$TutorialCreatingSymbolPages}; 59 | 60 | 61 | End[] 62 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/GuideDocumenter.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Guide; 4 | GuideQ; 5 | 6 | 7 | Abstract; 8 | 9 | 10 | Begin["`Private`"] 11 | 12 | 13 | Attributes[GuideQ]={HoldFirst}; 14 | GuideQ[_]=False; 15 | 16 | 17 | HoldPattern[gd_=Guide[title_]]^:=( 18 | DocumentationTitle[gd]^=title; 19 | GuideQ[gd]^=True; 20 | gd 21 | ) 22 | 23 | 24 | AppendTo[$DocumentationTypes,"Guide"->"Guides"]; 25 | 26 | 27 | DocumentationOfTypeQ[sym_,"Guide"]:=GuideQ@sym 28 | 29 | 30 | DocumentationSummary[gd_,"Guide"]:=StripFormatting@ParseToDocEntry@Abstract@gd 31 | 32 | 33 | MakeDocumentationContent[gd_,"Guide",nb_,opts:OptionsPattern[]]:=( 34 | NotebookWrite[nb,Cell[DocumentationTitle[gd],"GuideTitle"]]; 35 | #[gd,nb,FilterRules[{opts},Options@#]]&/@$DocumentationSections["Guide"] 36 | ) 37 | 38 | 39 | End[] 40 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/GuidePacletUtils.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | $GuidePacletUtils=Guide["PacletUtils"]; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[$GuidePacletUtils]=FSGuideHeader; 10 | 11 | 12 | Abstract[$GuidePacletUtils]="The ForScience PacletUtils subpaclet contains various function to simplify paclet development. [*BuildPaclet*] enables building/packing of paclets with pre/post processing, also of individual files. Another core part of the PacletUtils is the automated documentation building toolbox. It enables the creation of documentation pages with the exact same look and feel as the original documentation. The built documentation pages are also automatically packed together with the paclet and indexed, to make deployment as easy as possible."; 13 | 14 | 15 | GuideSections[$GuidePacletUtils]={ 16 | { 17 | SectionTitle["Paclet Building and Deployment"], 18 | {BuildPaclet,Text["manage paclet building"]}, 19 | {BuildAction,Text["only execute code during paclet build"]}, 20 | {UsageCompiler,DocumentationBuilder,"\[Ellipsis]",Text["perform pre/post processing during paclet build"]}, 21 | {ProcessFile,Text["perform robust expression based file processing"]}, 22 | Hold[$BuildActive,$BuiltPaclet,$ProcessedFile,$EnableBuildActions] 23 | }, 24 | { 25 | SectionTitle["Creating Documentation Pages"], 26 | {DocumentationBuilder,DocumentationHeader,Text["build documentation pages"]}, 27 | {Usage,Details,Examples,"\[Ellipsis]",Text["create symbol reference pages"]}, 28 | {Guide,Abstract,GuideSections,"\[Ellipsis]",Text["create guide pages"]}, 29 | {Tutorial,TutorialSections,"\[Ellipsis]",Text["create tutorial pages"]} 30 | }, 31 | { 32 | {Usage,FormatUsage,ParseFormatting,"\[Ellipsis]",Text["create nicely formatted usage messages"]} 33 | } 34 | }; 35 | 36 | 37 | (Guides@#=DeleteDuplicates[Append[Guides@#,$GuidePacletUtils]])&[ 38 | Unevaluated@@ToExpression[#,StandardForm,Hold] 39 | ]&/@ 40 | Names["ForScience`PacletUtils`*"] 41 | 42 | 43 | Guides[$GuidePacletUtils]={$GuideForScience,$GuideCreatingDocPages}; 44 | 45 | 46 | End[] 47 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/GuideSections.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | GuideSections; 4 | SectionTitle; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | GuideSections::invalidFormat="GuideSections of `` cannot be set to ``. A list of lists is expected."; 11 | 12 | 13 | DeclareMetadataHandler[GuideSections,"invalidFormat",_,{_List...},{}] 14 | 15 | 16 | Options[SectionTitle]={Hyperlink->Automatic}; 17 | 18 | 19 | MakeGuideSection[{SectionTitle[title_,tOpts:OptionsPattern[]],rest___}]:= 20 | With[ 21 | { 22 | spec=Hold[Hyperlink]/.Join[{tOpts},Options[SectionTitle]] 23 | }, 24 | With[ 25 | { 26 | link=If[ 27 | MatchQ[ 28 | spec, 29 | Hold[Except[False,True|Automatic|_String|_Symbol]] 30 | ], 31 | spec/.{True|Automatic->title}/.Hold[s_]:>Last@RawDocumentationLink[s], 32 | Missing[] 33 | ] 34 | }, 35 | Cell@CellGroupData@Prepend[ 36 | Cell[ 37 | link/. 38 | { 39 | _Missing->title, 40 | uri_:>BoxData@TemplateBox[ 41 | {title<>" \[RightGuillemet]",uri}, 42 | "OrangeLink", 43 | BaseStyle->"GuideFunctionsSubsection" 44 | ] 45 | }, 46 | "GuideFunctionsSubsection" 47 | ] 48 | ]@MakeGuideSectionContent[{rest},link] 49 | ] 50 | ] 51 | MakeGuideSection[sec_List]:=MakeGuideSectionContent[sec] 52 | 53 | 54 | MakeGuideSectionContent[sec_,link_:Missing[]]:=Switch[#, 55 | (Hold|List)[__,_Text], 56 | Cell[ 57 | TextData@Join[ 58 | MakeGuideSectionLine[Most@#,", ",link], 59 | {" ",StyleBox["\[LongDash]","GuideEmDash"]," ",ParseToDocEntry@First@Last@#} 60 | ], 61 | "GuideText" 62 | ], 63 | (Hold|List)[__], 64 | Cell[ 65 | TextData@MakeGuideSectionLine[ 66 | #, 67 | Unevaluated@Sequence["\[NonBreakingSpace]",StyleBox["\[MediumSpace]\[FilledVerySmallSquare]\[MediumSpace]","InlineSeparator"]," "], 68 | link 69 | ], 70 | "InlineGuideFunctionListing" 71 | ], 72 | _, 73 | SpecToCell[#,"ExampleText"] 74 | ]&/@sec 75 | 76 | 77 | MakeGuideSectionLine[elements_,sep_,link_]:=Riffle[ 78 | Cell[BoxData@#,"InlineFunctionSans"]&/@List@@Replace[ 79 | elements, 80 | { 81 | "\[Ellipsis]":>If[ 82 | MissingQ@link, 83 | Cell["...","InlineFunctionSans"], 84 | TemplateBox[{"...",link},"RefLinkPlain",BaseStyle->{"InlineFunctionSans"}] 85 | ], 86 | Hyperlink["\[Ellipsis]",ref_String]:>DocumentationLink[Evaluate["\[Ellipsis]::"<>ref],BaseStyle->"InlineFunctionSans"], 87 | Hyperlink["\[Ellipsis]",ref_Symbol]:>DocumentationLink[DocID[ref,"","\[Ellipsis]"],BaseStyle->"InlineFunctionSans"], 88 | ref:(_String|_Symbol):>DocumentationLink[ref,BaseStyle->"InlineFunctionSans"] 89 | }, 90 | 1 91 | ], 92 | Unevaluated@sep 93 | ] 94 | 95 | 96 | AppendTo[$DocumentationStyles["Guide"], 97 | Cell[StyleData["GuideMainSectionHeader",StyleDefinitions->StyleData["SeeAlsoSection"]], 98 | CellMargins->Pre111StyleSwitch[0,-2], 99 | CellElementSpacings->{ 100 | "CellMinHeight"->Pre111StyleSwitch[Inherited,0], 101 | "ClosedCellHeight"->Pre111StyleSwitch[Inherited,0], 102 | "ClosedGroupTopMargin"->Pre111StyleSwitch[60,4] 103 | }, 104 | CellOpen->Pre111StyleSwitch[] 105 | ] 106 | ]; 107 | AppendTo[$DocumentationStyles["Guide"], 108 | Cell[StyleData["GuideSectionDelimiter",StyleDefinitions->StyleData["GuideDelimiter"]], 109 | CellMargins->{{26,24},{Pre111StyleSwitch[-1,4],10}}, 110 | CellFrameMargins->{{0,0},{Pre111StyleSwitch[0,2],10}} 111 | ] 112 | ]; 113 | AppendTo[$DocumentationStyles["Guide"], 114 | Cell[StyleData["GuideMainDelimiter",StyleDefinitions->StyleData["GuideDelimiter"]], 115 | CellMargins->Pre111StyleSwitch[-2,{{26,24},{4,10}}], 116 | CellElementSpacings->{ 117 | "CellMinHeight"->Pre111StyleSwitch[0,1], 118 | "ClosedCellHeight"->Pre111StyleSwitch[0,Inherited] 119 | }, 120 | CellOpen->Pre111StyleSwitch[False,True] 121 | ] 122 | ]; 123 | 124 | 125 | MakeGuideSections[gd_,nb_,OptionsPattern[]]:=If[GuideSections[gd]=!={}, 126 | If[$Pre111CompatStyles, 127 | NotebookWrite[ 128 | nb, 129 | Cell[ 130 | TextData@{ 131 | Cell@BoxData@TemplateBox[{},"SectionOpenerArrow"], 132 | Cell@BoxData@SpacerBox[1], 133 | "Reference" 134 | }, 135 | "GuideMainSectionHeader","GuideMainSectionHeader" 136 | ] 137 | ]; 138 | NotebookWrite[ 139 | nb, 140 | Cell["","SectionHeaderSpacer"] 141 | ] 142 | ]; 143 | NotebookWrite[ 144 | nb, 145 | Cell["\t","GuideMainDelimiter"] 146 | ]; 147 | NotebookWrite[ 148 | nb, 149 | # 150 | ]&/@Riffle[ 151 | MakeGuideSection[#]&/@GuideSections[gd], 152 | Cell["\t","GuideSectionDelimiter"] 153 | ]; 154 | NotebookWrite[ 155 | nb, 156 | Cell["","SectionFooterSpacer"] 157 | ] 158 | ] 159 | 160 | 161 | AppendTo[$DocumentationSections["Guide"],MakeGuideSections]; 162 | AppendTo[$DependencyCollectors["Guide"],GuideSections]; 163 | 164 | 165 | End[] 166 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/Guides.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Guides; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | Attributes[Guides]={HoldFirst}; 10 | 11 | 12 | Guides::invalidInput="Guides information of `` cannot be set to ``. A list of guide titles is expected."; 13 | Guides::invalidSymbol="Symbol `` is not tagged as guide and cannot be added to the guides section."; 14 | 15 | 16 | DeclareMetadataHandler[Guides,"invalidInput",_,{(_String|_Symbol)...},{}] 17 | 18 | 19 | Attributes[MakeGuidesSection]={HoldFirst}; 20 | 21 | 22 | MakeGuidesSection[sym_,nb_,OptionsPattern[]]:=With[ 23 | {valid=DeleteCases[_Symbol?(Not@*GuideQ)]@Guides[sym]}, 24 | If[Length@valid>0, 25 | NotebookWrite[nb, 26 | LinkSection["Related Guides","MoreAboutSection","RelatedGuide.png",True, 27 | RowBox@{"\[FilledVerySmallSquare]",Cell[ 28 | BoxData@DocumentationLink[#,"Guide","LinkStyle"->"RefLinkPlain",BaseStyle->{"MoreAbout"}], 29 | "MoreAbout" 30 | ]}&/@valid 31 | ] 32 | ] 33 | ] 34 | ] 35 | 36 | 37 | Attributes[MakeGuidesHeader]={HoldFirst}; 38 | 39 | 40 | MakeGuidesHeader[sym_]:=MakeHeaderDropdown[ 41 | "Related Guides", 42 | "MoreAbout", 43 | Replace[ 44 | Guides[sym], 45 | { 46 | s_Symbol?(Not@*GuideQ):>( 47 | Message[Guides::invalidSymbol,HoldForm@s];Nothing 48 | ), 49 | g_:>DocID[g,"Guide"] 50 | }, 51 | 1 52 | ] 53 | ] 54 | 55 | 56 | AppendTo[$DocumentationSections["Symbol"],MakeGuidesSection]; 57 | AppendTo[$HeaderEntries["Symbol"],MakeGuidesHeader]; 58 | AppendTo[$DependencyCollectors["Symbol"],Guides]; 59 | 60 | 61 | AppendTo[$DocumentationSections["Guide"], MakeGuidesSection]; 62 | AppendTo[$HeaderEntries["Guide"],MakeGuidesHeader]; 63 | AppendTo[$DependencyCollectors["Guide"],Guides]; 64 | 65 | 66 | AppendTo[$DocumentationSections["Tutorial"], MakeGuidesSection]; 67 | AppendTo[$HeaderEntries["Tutorial"],MakeGuidesHeader]; 68 | AppendTo[$DependencyCollectors["Tutorial"],Guides]; 69 | 70 | 71 | End[] 72 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/GuidesDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[Guides]="[*[*Guides[sym]*]'''={```guide```_1,\[Ellipsis]}'''*] sets the guides to appear in the \"Related Guides\" section of the documentation page built by [*DocumentationBuilder*]. 4 | [*[*Guides[guide]*]'''={```guide```_1,\[Ellipsis]}'''*] sets the related guides for ```guide```. 5 | [*[*Guides[tut]*]'''={```guide```_1,\[Ellipsis]}'''*] sets the related guides for ```tut```."; 6 | 7 | 8 | Begin[BuildAction] 9 | 10 | 11 | DocumentationHeader[Guides]=FSHeader["0.64.0","0.66.0"]; 12 | 13 | 14 | Details[Guides]={ 15 | "[*Guides*] is one of the metadata symbols used by [*DocumentationBuilder*]. Others include [*Usage*], [*Details*], [*Examples*], [*SeeAlso*] and [*Tutorials*].", 16 | "In [*Guides[\[Ellipsis]]*]'''={```guide```_1,\[Ellipsis]}''', every ```guide_i``` should be one of the following:", 17 | TableForm@{ 18 | {"\"```title```\"","Exact title of a guide"}, 19 | {"```guide```","A symbol tagged as [*Guide*]"} 20 | }, 21 | "Guides listed in [*Guides[\[Ellipsis]]*] appear at the bottom of the documentation page in the \"Related Guides\" section and in the header in the \"Related Guides\" dropdown.", 22 | "[*Guides*] is the exact analogue of [*Tutorials*] for documentation guides." 23 | }; 24 | 25 | 26 | Examples[Guides,"Basic examples"]={ 27 | { 28 | "Load the ForScience package:", 29 | ExampleInput[Needs["ForScience`PacletUtils`"]], 30 | "Set up a symbol to attach the examples to:", 31 | ExampleInput[Examples[test]=.;,Visible->False], 32 | ExampleInput[ 33 | DocumentationHeader[test]={"DOCUMENTATION EXAMPLE",Darker@Orange,"Introduced in the documentation"};, 34 | InitializationCell->True 35 | ], 36 | "Add some related guides:", 37 | ExampleInput[ 38 | Guides[test]={"List Manipulation","Associations"};, 39 | DocumentationBuilder[test] 40 | ], 41 | ExampleInput[NotebookClose[%];,Visible->False] 42 | }, 43 | { 44 | "Refer to a guide through its symbol:", 45 | ExampleInput[ 46 | gd=Guide["Test guide"];, 47 | Guides[test]={gd};, 48 | DocumentationBuilder[test] 49 | ], 50 | ExampleInput[NotebookClose[%];,Visible->False] 51 | } 52 | }; 53 | 54 | 55 | Examples[Guides,"Properties & Relations"]={ 56 | { 57 | "[*Guides*] can be used to set related guides of guide pages:", 58 | ExampleInput[ 59 | gd=Guide["Test guide"];, 60 | DocumentationHeader[gd]={"TEST GUIDE",Red,"Never introduced"};, 61 | Guides[gd]={"Associations"};, 62 | DocumentationBuilder[gd] 63 | ], 64 | ExampleInput[NotebookClose[%];,Visible->False] 65 | } 66 | }; 67 | 68 | 69 | SeeAlso[Guides]={DocumentationBuilder,Guide,Tutorials,SeeAlso,Usage,Details,Examples}; 70 | 71 | 72 | Guides[Guides]={$GuideCreatingDocPages}; 73 | 74 | 75 | End[] 76 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/IndexDocumentation.wl: -------------------------------------------------------------------------------- 1 | Begin["`Private`"] 2 | 3 | 4 | DownValues[NewDocumentationNotebookIndexer]=DownValues[NewDocumentationNotebookIndexer]/. 5 | HoldPattern@AddToClassPath[p_]:>AddToClassPath[p,Prepend->True]; 6 | 7 | 8 | Options[IndexDocumentation]=Join[{ProgressIndicator->True},Options[DocumentationCachePut]]; 9 | 10 | 11 | IndexDocumentation[dir_,useCached_,OptionsPattern[]]:=With[ 12 | { 13 | cacheDirectory=FileNameJoin@{GetCacheDir@OptionValue["CacheDirectory"],"indexes"}, 14 | indexDirectory=FileNameJoin@{Directory[],dir,"Index"}, 15 | spellDirectory=FileNameJoin@{Directory[],dir,"SpellIndex"}, 16 | searchDirectory=FileNameJoin@{Directory[],dir,DocumentationSearch`Private`$indexName} 17 | }, 18 | With[ 19 | { 20 | cachedIndex=FileNameJoin@{cacheDirectory,"Index"}, 21 | cachedSpell=FileNameJoin@{cacheDirectory,"SpellIndex"}, 22 | cachedSearch=FileNameJoin@{cacheDirectory,DocumentationSearch`Private`$indexName} 23 | }, 24 | With[ 25 | { 26 | cachedDirs={cachedIndex,cachedSpell,cachedSearch}, 27 | dirs={indexDirectory,spellDirectory,searchDirectory} 28 | }, 29 | If[OptionValue[ProgressIndicator],PrintTemporary@"Creating documentation index..."]; 30 | If[ 31 | !useCached|| 32 | !AllTrue[cachedDirs,DirectoryQ]|| 33 | Check[ 34 | MapThread[CopyDirectory,{cachedDirs,dirs}]; 35 | False, 36 | Quiet[DeleteDirectory[#,DeleteContents->True]&/@Join[dirs,cachedDirs]]; 37 | True 38 | ], 39 | With[ 40 | { 41 | oldJVM=JLink`Package`getDefaultJVM[], 42 | oldCP=JavaClassPath[] 43 | }, 44 | Internal`WithLocalSettings[ 45 | (* use separate JVM for indexer, as old lucene library breaks help center *) 46 | InstallJava[ForceLaunch->True,Default->True], 47 | With[ 48 | {indexer=NewDocumentationNotebookIndexer@indexDirectory}, 49 | AddDocumentationNotebook[indexer,#]&/@FileNames["*.nb",dir,\[Infinity]]; 50 | CloseDocumentationNotebookIndexer@indexer; 51 | CreateSpellIndex[indexDirectory,spellDirectory]; 52 | ], 53 | UninstallJava[]; 54 | (*reset classpath to ensure new lucene is found on new JVMs*) 55 | JLink`Package`$currentClassPath=oldCP; 56 | JLink`Package`setDefaultJVM[oldJVM]; 57 | ]; 58 | CreateDirectory@searchDirectory; 59 | First[(*get the JavaObject of the newly created search index to close it*) 60 | TextSearch`PackageScope`createHandle[ 61 | Quiet@CreateDocumentationIndex[ 62 | dir, 63 | searchDirectory, 64 | DocumentationSearch`Private`$indexVersion 65 | ] 66 | ] 67 | ]@close[]; 68 | ]; 69 | Quiet@CreateDirectory[cacheDirectory]; 70 | Quiet[DeleteDirectory[#,DeleteContents->True]&/@cachedDirs]; 71 | MapThread[CopyDirectory,{dirs,cachedDirs}]; 72 | ] 73 | ] 74 | ] 75 | ] 76 | 77 | 78 | End[] -------------------------------------------------------------------------------- /ForScience/PacletUtils/MakeUsageString.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | MakeUsageString; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | MakeUsageString[boxes_]:=MakeUsageString[{boxes}] 10 | 11 | 12 | MakeUsageString[boxes_List]:=StringRiffle[ 13 | If[StringStartsQ[#,"\!"],#,"\!\(\)"<>#]&/@( 14 | Replace[ 15 | boxes, 16 | { 17 | TagBox[b_,"[**]"]:>StyleBox[b,"MR"], 18 | TagBox[RowBox@l:{__String},"<**>"]:>DocID[Evaluate@StringJoin@l][Label], 19 | TagBox[b_,_]:>b 20 | }, 21 | All 22 | ]//Replace[ 23 | #, 24 | s_String:>StringReplace[s,","~~EndOfString->", "], 25 | {3} 26 | ]&//Replace[ 27 | #, 28 | s_String?(StringContainsQ["\""]):> 29 | "\""<>StringReplace[s,"\""->"\\\""]<>"\"", 30 | {4,Infinity} 31 | ]&//Replace[ 32 | #, 33 | RowBox[l_]|box_:>StringJoin@Replace[#&[l,{box}],b:Except[_String]:> 34 | "\!\(\*"<>ToString[b,InputForm]<>"\)",1], 35 | 1 36 | ]& 37 | ), 38 | "\n" 39 | ] 40 | 41 | 42 | End[] 43 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/MakeUsageStringDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[MakeUsageString]="MakeUsageString[boxes] converts the box expression returned by [*ParseFormatting*] to a string that can be used as usage message. 4 | MakeUsageString[{boxes_1,\[Ellipsis]}] creates a multiline string, with line ```i``` corresponding to ```boxes_i```."; 5 | 6 | 7 | Begin[BuildAction] 8 | 9 | 10 | DocumentationHeader[MakeUsageString]=FSHeader["0.50.0","0.60.9"]; 11 | 12 | 13 | Details[MakeUsageString]={ 14 | "[*MakeUsageString*] takes a box expressions as those returned by [*ParseFormatting*] and returns a formatted string.", 15 | "[*MakeUsageString*] is called as the last step of [*FormatUsage*].", 16 | "Under normal circumstances, [*MakeUsageString*] should not need to be used directly." 17 | }; 18 | 19 | 20 | SeeAlso[MakeUsageString]={Usage,ParseFormatting,FormatUsage}; 21 | 22 | 23 | Guides[MakeUsageString]={$GuideCreatingDocPages}; 24 | 25 | 26 | End[] 27 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/OverviewEntries.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | 4 | OverviewEntries; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | OverviewEntries::invalidFormat="`` is not a valid entry format. Overview entries must be set to Automatic or a tutorial reference."; 11 | OverviewEntries::noMixingEx="Cannot add an overview entry to `` under ``, as subsections are already registered."; 12 | OverviewEntries::noMixingSub="Cannot add tutorial subcategory '``' to `` under ``, as entries are already added at this level."; 13 | OverviewEntries::needSubCat="Cannot add entry directly to ``, need to specify at least one level."; 14 | OverviewEntries::invalidKey="Overview entry key `` must be a string or tutorial symbol."; 15 | 16 | 17 | DeclareSectionAccessor[OverviewEntries,{"invalidFormat","noMixingEx","noMixingSub","needSubCat","invalidKey"},_,_String|_Symbol|Hyperlink[_],Automatic|_String|_Symbol] 18 | 19 | 20 | AppendTo[$DocumentationStyles["Overview"], 21 | Cell[StyleData["TOCChapterLink"], 22 | TemplateBoxOptions->{ 23 | DisplayFunction:>( 24 | TagBox[ 25 | ButtonBox[ 26 | StyleBox[#,FontColor->Dynamic@If[CurrentValue["MouseOver"],RGBColor[0.854902,0.396078,0.145098],Inherited]], 27 | ButtonData->#2, 28 | BaseStyle->Pre111StyleSwitch[{"Link"},{"Link","GuideFunctionsSubsection"}] 29 | ], 30 | MouseAppearanceTag["LinkHand"] 31 | ]& 32 | ) 33 | } 34 | ] 35 | ]; 36 | 37 | 38 | $TOCEntryLevels={"TOCChapter","TOCSection","TOCSubsection","TOCSubsubsection","TOCSubsubsubsection"}; 39 | 40 | 41 | MakeOverviewTOCEntries[sec_,lev_]:=KeyValueMap[ 42 | With[ 43 | {type=$TOCEntryLevels[[Min[lev,Length@$TOCEntryLevels]]]}, 44 | Cell@CellGroupData@Flatten@{ 45 | Cell[ 46 | #/.{ 47 | Hyperlink[spec_]:>BoxData@DocumentationLink[ 48 | spec, 49 | "LinkStyle"->If[lev==1,"TOCChapterLink","RefLinkPlain"], 50 | BaseStyle->{type} 51 | ], 52 | Hyperlink[lbl_,url_]:>BoxData@TemplateBox[ 53 | {lbl,url}, 54 | If[lev==1,"TOCChapterLink","RefLinkPlain"], 55 | BaseStyle->{type} 56 | ] 57 | }, 58 | type 59 | ], 60 | MakeOverviewTOCEntries[#2,lev+1] 61 | } 62 | ]& 63 | ]@sec 64 | 65 | 66 | OverviewEntries::invSym = "`` is not a symbol tagged as tutorial. No content will be added to the overview."; 67 | 68 | 69 | ExtractNotebookStructure[name_,Automatic]:=ExtractNotebookStructure[name,name] 70 | ExtractNotebookStructure[name_,Flat]:=Last@ExtractNotebookStructure[name,name] 71 | ExtractNotebookStructure[name_Symbol,ref_]:=ExtractNotebookStructure[DocumentationTitle[name],ref] 72 | ExtractNotebookStructure[name_,ref_String]:=With[ 73 | {uri=Last@RawDocumentationLink[ref]}, 74 | If[MissingQ@uri, 75 | name-><||>, 76 | Hyperlink[name,uri]-> 77 | ExtractCellStructure[Import@Documentation`ResolveLink@uri,uri] 78 | ] 79 | ] 80 | ExtractNotebookStructure[name_,ref_Symbol?TutorialQ]:=With[ 81 | {uri=Last@RawDocumentationLink[ref]}, 82 | If[MissingQ@uri, 83 | name-><||>, 84 | Hyperlink[name,uri]-> 85 | ExtractTutorialSectionStructure[TutorialSections@ref,uri] 86 | ] 87 | ] 88 | ExtractNotebookStructure[name_,ref_Symbol]:=(Message[OverviewEntries::invSym,ref];name-><||>) 89 | 90 | 91 | ExtractTutorialSectionStructure[sec_Association,uri_]:=<| 92 | KeyValueMap[ 93 | Hyperlink[ 94 | StringTrim[#,":"], 95 | StringTemplate["``#``"][uri,GenerateCellID@#] 96 | ]->ExtractTutorialSectionStructure[#2,uri]& 97 | ]@KeyDrop[sec,None] 98 | |> 99 | ExtractTutorialSectionStructure[_,_]:=<||> 100 | 101 | 102 | ExtractCellStructure[cells_,uri_]:=<| 103 | First[ 104 | Last@Reap[ 105 | cells/. 106 | CellGroupData[{ 107 | c:Cell[tit_,s:Alternatives@@$SectionLevels,___], 108 | inner___ 109 | },___]:>Sow[ 110 | Hyperlink[ 111 | StringTrim[tit,":"], 112 | StringTemplate["``#``"][uri,CellID/.Options[c,CellID]] 113 | ]->ExtractCellStructure[{inner},uri] 114 | ] 115 | ], 116 | {} 117 | ] 118 | |> 119 | 120 | 121 | InsertNotebookStructures[key_->sub_Association]:=key->AssociationMap[InsertNotebookStructures,sub] 122 | InsertNotebookStructures[Hyperlink[key_]|key_->ref_]:=ExtractNotebookStructure[key,ref] 123 | 124 | 125 | BuildTOCStructure[overview_]:=AssociationMap[ 126 | InsertNotebookStructures, 127 | OverviewEntries@overview 128 | ] 129 | 130 | 131 | MakeOverviewTOC[overview_,nb_,OptionsPattern[]]:=If[OverviewEntries[overview]=!=<||>, 132 | NotebookWrite[ 133 | nb, 134 | MakeOverviewTOCEntries[ 135 | BuildTOCStructure@overview, 136 | 1 137 | ] 138 | ] 139 | ] 140 | 141 | 142 | AppendTo[$DocumentationSections["Overview"],MakeOverviewTOC]; 143 | AppendTo[$DependencyCollectors["Overview"],BuildTOCStructure]; 144 | 145 | 146 | End[] 147 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/PacletSelfCheck.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | PacletSelfCheck; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | testBuildList={ 10 | (* Form: list of {functionWhichShouldBeTested[],expectedOutput,Message} *) 11 | (* Test for ChemUtils *) 12 | {HoldComplete@ToString@FullForm[MoleculePlot3D@Molecule[{"C"->{0,0,0}}]],"Graphics3D[List[EdgeForm[None], CapForm[None], AbsoluteThickness[3], Directive[], List[List[RGBColor[0.4`, 0.4`, 0.4`], Sphere[List[0, 0, 0], 170.`]]], List[]], List[], Rule[Lighting, \"Neutral\"], Rule[Boxed, False]]","ChemUtils"}, 13 | (* Test for PlotUtils*) 14 | {HoldComplete@ToString[FullForm@ListPlot[{1,2},PlotTheme->"ForScience"][[2,17]]],"Rule[FrameTicksStyle, Directive[GrayLevel[0], Rule[FontSize, 18.`], Rule[FontFamily, \"Times\"]]]","PlotUtils"} 15 | (* Test for Utils TODO: write Utils tests*) 16 | }; 17 | 18 | 19 | PacletSelfCheck[]:=If[ReleaseHold[#[[1]]]==#[[2]],Print[ToString[#[[3]]]<>"\t\t\tDone"];True,Print[ToString[#[[3]]]<>"\t\t\tError"];ReleaseHold[#[[1]]]]&/@testBuildList 20 | 21 | 22 | End[] 23 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/ParseFormatting.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | (*usage formatting utilities, need to make public before defining, as they're already used in the usage definition*) 4 | ParseFormatting; 5 | 6 | 7 | Begin["`Private`ParseFormatting`"] 8 | 9 | 10 | ti; 11 | mr; 12 | sc; 13 | it; 14 | go; 15 | gc; 16 | co; 17 | cc; 18 | lo; 19 | lc; 20 | sb; 21 | sp; 22 | 23 | 24 | ToRowBox[{el_}]:=el 25 | ToRowBox[l_]:=RowBox@l 26 | 27 | 28 | $closingSequences=<|ti->"```",mr->"'''",sc->"***",it->"///",gc->"*}",cc->"*]",lc->"*>"|>; 29 | 30 | 31 | ParseToToken[str_,i_,simplify_:True][t_]:=( 32 | If[simplify,ToRowBox,RowBox]@Flatten@Reap[ 33 | Module[{curToken}, 34 | While[True, 35 | If[i>Length@str,Throw[$closingSequences[t],EndOfFile]]; 36 | curToken=str[[i]]; 37 | ++i; 38 | If[curToken===t,Break[]]; 39 | Sow@ParseToken[str,i][curToken] 40 | ]; 41 | ] 42 | ][[2]] 43 | ) 44 | Attributes[ParseToToken]={HoldRest}; 45 | 46 | 47 | ParseToken[str_,i_][ti]:=StyleBox[ParseToToken[str,i][ti],"TI"] 48 | ParseToken[str_,i_][mr]:=StyleBox[ParseToToken[str,i][mr],"MR"] 49 | ParseToken[str_,i_][sc]:=StyleBox[ParseToToken[str,i][sc],ShowSpecialCharacters->False] 50 | ParseToken[str_,i_][it]:=StyleBox[ParseToToken[str,i][it],FontSlant->Italic] 51 | ParseToken[str_,i_][go]:=ParseToToken[str,i,False][gc] 52 | ParseToken[str_,i_][co]:=TagBox[ParseToToken[str,i][cc],"[**]"] 53 | ParseToken[str_,i_][lo]:=TagBox[ParseToToken[str,i][lc],"<**>"] 54 | ParseToken[str_,i_][t:gc|cc|lc]:=Throw[$closingSequences[t],"Unmatched"] 55 | ParseToken[str_,i_][t_]:=t 56 | Attributes[ParseToken]={HoldRest}; 57 | 58 | 59 | ParseFormatting::endReached="End reached while looking for `` during parsing of \"``\"."; 60 | ParseFormatting::unmatched="Unmatched closing group sequence `` found while parsing \"``\"."; 61 | ParseFormatting[str_]:=Module[ 62 | {i=1,pStr}, 63 | pStr=StringReplace[ 64 | { 65 | "\\"~~c_:>c, 66 | "```"->" ```ti ", 67 | "'''"->" ```mr ", 68 | "***"->" ```sc ", 69 | "///"->" ```it ", 70 | "{*"->" ```go ", 71 | "*}"->" ```gc ", 72 | "[*"->" ```co ", 73 | "*]"->" ```cc ", 74 | "<*"->" ```lo ", 75 | "*>"->" ```lc ", 76 | ", "->" ```cm ", 77 | " "->" ```ws ", 78 | "_"->" ```sb ", 79 | "^"->" ```sp ", 80 | "\""->"```qt" 81 | } 82 | ]@str; 83 | pStr=StringReplace[pStr,"\\"->"\\\\"]; 84 | pStr=First@MathLink`CallFrontEnd[ 85 | FrontEnd`UndocumentedTestFEParserPacket[pStr,True] 86 | ]; 87 | pStr=pStr/.s_String:>StringReplace[s,{"```qt"->"\"","\\\\"->"\\"}]; 88 | pStr=Append[EndOfLine]@Replace[ 89 | Flatten@Replace[{First@pStr},RowBox@x_:>x,\[Infinity]], 90 | { 91 | "```ti"->ti, 92 | "```mr"->mr, 93 | "```sc"->sc, 94 | "```it"->it, 95 | "```go"->go, 96 | "```gc"->gc, 97 | "```co"->co, 98 | "```cc"->cc, 99 | "```lo"->lo, 100 | "```lc"->lc, 101 | "```ws"->" ", 102 | "```sb"->sb, 103 | "```sp"->sp 104 | }, 105 | 1 106 | ]; 107 | Catch[ 108 | FixedPoint[ 109 | Replace[#,{pre___,s:Longest@Repeated[_String,{2,\[Infinity]}],post___}:> 110 | {pre,StringReplace["```cm"->", "]@StringJoin@s,post},1]&, 111 | First[ 112 | {ParseToToken[pStr, i][EndOfLine]}//. 113 | {pre___,a:Except[sb|sp]:"",scr:sb|sp,b:Except[sb|sp]:"",post___}:> 114 | {pre,If[b==="",a,If[scr===sb,SubscriptBox,SuperscriptBox][a,b]],post} 115 | ] 116 | ]/."```cm"->",", 117 | EndOfFile|"Unmatched", 118 | ( 119 | Replace[ 120 | {##}, 121 | { 122 | {seq_,EndOfFile}:>Message[ParseFormatting::endReached,seq,str], 123 | {seq_,"Unmatched"}:>Message[ParseFormatting::unmatched,seq,str] 124 | } 125 | ]; 126 | str 127 | )& 128 | ] 129 | ] 130 | SyntaxInformation[ParseFormatting]={"ArgumentsPattern"->{_}}; 131 | 132 | 133 | End[] 134 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/ParseFormattingDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[ParseFormatting]="ParseFormatting[str] returns a box expression formatted according to the format specification of ```str```."; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[ParseFormatting]=FSHeader["0.50.0","0.87.18"]; 10 | 11 | 12 | Details[ParseFormatting]={ 13 | "[*ParseFormatting*] returns a box expression.", 14 | "The returned box expression can be converted to a string using [*MakeUsageString*].", 15 | "For supported format specifications, see [*FormatUsage*].", 16 | "[*ParseFormatting*] is called as part of [*FormatUsage*].", 17 | "Under normal circumstances, [*ParseFormatting*] should not need to be used directly." 18 | }; 19 | 20 | 21 | SeeAlso[ParseFormatting]={Usage,MakeUsageString,FormatUsage}; 22 | 23 | 24 | Guides[ParseFormatting]={$GuideCreatingDocPages}; 25 | 26 | 27 | End[] 28 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/ProcessFile.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | ProcessFile; 4 | $ProcessedFile; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | SyntaxInformation[ProcessFile]={"ArgumentsPattern"->{_.,{__}}}; 11 | 12 | 13 | $ProcessedFile=""; 14 | 15 | 16 | ProcessFile::msgs="Messages were generated during processing of '``'."; 17 | 18 | 19 | $ProcessFileContext="ProcessFile`"; 20 | 21 | 22 | ProcessFile[_,{}]:=Null 23 | ProcessFile[{in_,in_},{}]:=Null 24 | ProcessFile[{in_,out_},{}]:=CopyFile[in,out,OverwriteTarget->True] 25 | ProcessFile[processors_][file_]:=ProcessFile[file,processors] 26 | ProcessFile[file_,processors_List]:=ProcessFile[{file,file},processors] 27 | ProcessFile[{in_,out_},processors_List]:=Block[ 28 | (*set context to ensure proper context prefixes for symbols. 29 | Adapted from https://mathematica.stackexchange.com/a/124670/36508*) 30 | { 31 | $ProcessedFile=AbsoluteFileName@in, 32 | $Context=$ProcessFileContext, 33 | $ContextPath={$ProcessFileContext,"System`"} 34 | }, 35 | Check[ 36 | Quiet[ 37 | Module[ 38 | { 39 | init=Import[in,{"Package","HeldExpressions"}], 40 | res 41 | }, 42 | res=(RightComposition@@processors)@init; 43 | If[init=!=res||in=!=out, 44 | Export[ 45 | out, 46 | StringReplace[ 47 | ExportString[ 48 | res, 49 | {"Package","HeldExpressions"}, 50 | PageWidth->Infinity 51 | ], 52 | (* 53 | Collect all symbols in subcontexts of $ProcessFileContext (these were specified as `subcontext`symbol) 54 | Use the resulting list to post-process the string content of the file to ensure the symbols are specified using relative contexts again 55 | *) 56 | DeleteDuplicates@Cases[ 57 | res, 58 | s:Except[HoldPattern@Symbol[___],_Symbol]/; 59 | StringStartsQ[$ProcessFileContext~~_]@Context@s:> 60 | Hold@s, 61 | All 62 | ]/.Hold[s_]:>With[ 63 | { 64 | name=ToString@Unevaluated@s 65 | }, 66 | name->StringDrop[name,StringLength@$ProcessFileContext-1] 67 | ] 68 | ], 69 | "String" 70 | ] 71 | ] 72 | ] 73 | , 74 | {General::shdw} 75 | ], 76 | Message[ProcessFile::msgs,in] 77 | ]; 78 | Quiet@Remove["ProcessFile`*"]; 79 | Quiet@Remove["ProcessFile`*`*"]; 80 | ] 81 | 82 | 83 | End[] 84 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/SeeAlso.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | SeeAlso; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | Attributes[SeeAlso]={HoldFirst}; 10 | 11 | 12 | SeeAlso::invalidInput="SeeAlso information of `` cannot be set to ``. A list/held expression of symbols is expected."; 13 | 14 | 15 | DeclareMetadataHandler[SeeAlso,"invalidInput",_,(List|Hold)[_Symbol...],{}] 16 | 17 | 18 | AppendTo[$DocumentationStyles["Symbol"], 19 | Cell[StyleData["SeeAlsoItem",StyleDefinitions->"InlineFormula"], 20 | FontFamily->Pre111StyleSwitch["Verdana","Source Sans Pro"] 21 | ] 22 | ]; 23 | 24 | 25 | Attributes[MakeSeeAlsoSection]={HoldFirst}; 26 | 27 | 28 | MakeSeeAlsoSection[sym_,nb_,OptionsPattern[]]:=If[Length@SeeAlso@sym>0, 29 | NotebookWrite[nb, 30 | LinkSection["See Also","SeeAlsoSection","RelatedFunction.png",False, 31 | { 32 | Cell[ 33 | TextData@Riffle[ 34 | CodeCell@DocumentationLink[#,BaseStyle->{"SeeAlsoItem"}]&/@List@@Replace[SeeAlso[sym],s_:>DocID[s,"Symbol"],1], 35 | Unevaluated@Sequence["\[NonBreakingSpace]",StyleBox["\[MediumSpace]\[FilledVerySmallSquare]\[MediumSpace]","InlineSeparator"]," "] 36 | ], 37 | "SeeAlso" 38 | ] 39 | } 40 | ] 41 | ]; 42 | ] 43 | 44 | 45 | Attributes[MakeSeeAlsoHeader]={HoldFirst}; 46 | 47 | 48 | MakeSeeAlsoHeader[sym_]:=MakeHeaderDropdown["See Also","SeeAlso",List@@Replace[SeeAlso[sym],s_:>DocID[s,"Symbol"],1]] 49 | 50 | 51 | AppendTo[$DocumentationSections["Symbol"],MakeSeeAlsoSection]; 52 | AppendTo[$HeaderEntries["Symbol"],MakeSeeAlsoHeader]; 53 | AppendTo[$DependencyCollectors["Symbol"],SeeAlso]; 54 | 55 | 56 | End[] 57 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/SeeAlsoDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[SeeAlso]="[*[*SeeAlso[sym]*]'''={```sym_1```,\[Ellipsis]}'''*] sets the symbols to appear in the see also section of the documentation page built by [*DocumentationBuilder*]. 4 | [*[*SeeAlso[sym]*]'''='''[*Hold[sym_1,\[Ellipsis]]*]*] can be used if some of the symbols have ownvalues."; 5 | 6 | 7 | Begin[BuildAction] 8 | 9 | 10 | DocumentationHeader[SeeAlso]=FSHeader["0.59.0","0.63.12"]; 11 | 12 | 13 | Details[SeeAlso]={ 14 | "[*SeeAlso*] is one of the metadata symbols used by [*DocumentationBuilder*]. Others include [*Usage*], [*Details*], [*Examples*], [*Guides*] and [*Tutorials*].", 15 | "[*SeeAlso[sym]*] can either be set to a list of symbols or [*Hold[sym_1,\[Ellipsis]]*]. The latter should be used if some of the symbols have ownvalues.", 16 | "Symbols listed in [*SeeAlso[sym]*] appear at the bottom of the documentation page in the \"See Also\" section and in the header in the \"See Also\" dropdown." 17 | }; 18 | 19 | 20 | Examples[SeeAlso,"Basic examples"]={ 21 | { 22 | "Load the ForScience package:", 23 | ExampleInput[Needs["ForScience`PacletUtils`"]], 24 | "Set up a symbol to attach the examples to:", 25 | ExampleInput[Examples[test]=.;,Visible->False], 26 | ExampleInput[ 27 | DocumentationHeader[test]={"DOCUMENTATION EXAMPLE",Red,"Introduced in the documentation"};, 28 | InitializationCell->True 29 | ], 30 | "Add some related symbols:", 31 | ExampleInput[ 32 | SeeAlso[test]={List,foo,SeeAlso}, 33 | DocumentationBuilder[test] 34 | ], 35 | ExampleInput[NotebookClose[%];,Visible->False] 36 | }, 37 | { 38 | "Link to symbols with ownvalues:", 39 | ExampleInput[ 40 | SeeAlso[test]=Hold[$BuildActive,SeeAlso,List], 41 | DocumentationBuilder[test] 42 | ], 43 | ExampleInput[NotebookClose[%];,Visible->False] 44 | } 45 | }; 46 | 47 | 48 | Examples[SeeAlso,"Possible issues"]={ 49 | { 50 | "Symbols with ownvalues are evaluated inside the list:", 51 | ExampleInput[ 52 | SeeAlso[test]={$MachinePrecision,SeeAlso,List} 53 | ], 54 | "Use [*Hold[\[Ellipsis]]*] to prevent evaluation:", 55 | ExampleInput[ 56 | SeeAlso[test]=Hold[$MachinePrecision,SeeAlso,List], 57 | DocumentationBuilder[test] 58 | ], 59 | ExampleInput[NotebookClose[%];,Visible->False] 60 | } 61 | }; 62 | 63 | 64 | SeeAlso[SeeAlso]={DocumentationBuilder,Guides,Tutorials,Usage,Details,Examples}; 65 | 66 | 67 | Guides[SeeAlso]={$GuideCreatingDocPages}; 68 | 69 | 70 | Tutorials[SeeAlso]={$TutorialCreatingSymbolPages}; 71 | 72 | 73 | End[] 74 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/SymbolDocumenter.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | AppendTo[$DocumentationTypes,"Symbol"->"Symbols"]; 7 | 8 | 9 | DocumentationOfTypeQ[sym_,"Symbol"]:=True 10 | 11 | 12 | DocumentationTitle[sym_]:=SafeSymbolName@sym 13 | 14 | 15 | DocumentationSummary[sym_,"Symbol"]:=StringRiffle[StripFormatting/@UsageBoxes[sym]," "] 16 | 17 | 18 | AppendTo[$DocumentationStyles["Symbol"], 19 | Cell[StyleData["ContextNameCell"], 20 | Editable->False, 21 | ShowCellBracket->False, 22 | CellMargins->{{24, 22}, {0, 0}}, 23 | FontFamily->Pre111StyleSwitch["Arial","Source Sans Pro"], 24 | FontSize->18, 25 | FontWeight->"Bold", 26 | FontColor->GrayLevel[44/85] 27 | ] 28 | ]; 29 | 30 | 31 | MakeDocumentationContent[sym_,"Symbol",nb_,opts:OptionsPattern[]]:=( 32 | NotebookWrite[nb,Cell[Context@sym,"ContextNameCell"]]; 33 | NotebookWrite[nb,Cell[SafeSymbolName@sym,"ObjectName"]]; 34 | #[sym,nb,FilterRules[{opts},Options@#]]&/@$DocumentationSections["Symbol"] 35 | ) 36 | 37 | 38 | End[] 39 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/TutorialDocumenter.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Tutorial; 4 | TutorialQ; 5 | 6 | 7 | TutorialSections; 8 | 9 | 10 | Begin["`Private`"] 11 | 12 | 13 | Attributes[TutorialQ]={HoldFirst}; 14 | TutorialQ[_]=False; 15 | 16 | 17 | HoldPattern[tut_=Tutorial[title_]]^:=( 18 | DocumentationTitle[tut]^=title; 19 | TutorialQ[tut]^=True; 20 | tut 21 | ) 22 | 23 | 24 | AppendTo[$DocumentationTypes,"Tutorial"->"Tutorials"]; 25 | 26 | 27 | DocumentationOfTypeQ[sym_,"Tutorial"]:=TutorialQ@sym 28 | 29 | 30 | DocumentationSummary[tut_,"Tutorial"]:=StripFormatting@ParseToDocEntry@First@First[DeleteCases[{}]@KeySortBy[{StringQ}]@TutorialSections@tut,{""}] 31 | 32 | 33 | MakeDocumentationContent[tut_,"Tutorial",nb_,opts:OptionsPattern[]]:=( 34 | NotebookWrite[nb,Cell[DocumentationTitle[tut],"Title"]]; 35 | #[tut,nb,FilterRules[{opts},Options@#]]&/@$DocumentationSections["Tutorial"] 36 | ) 37 | 38 | 39 | End[] 40 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/TutorialOverviewDocumenter.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | TutorialOverview; 4 | TutorialOverviewQ; 5 | 6 | 7 | Abstract; 8 | 9 | 10 | Begin["`Private`"] 11 | 12 | 13 | Attributes[TutorialOverviewQ]={HoldFirst}; 14 | TutorialOverviewQ[_]=False; 15 | 16 | 17 | HoldPattern[overview_=TutorialOverview[title_]]^:=( 18 | DocumentationTitle[overview]^=title; 19 | TutorialOverviewQ[overview]^=True; 20 | overview 21 | ) 22 | 23 | 24 | AppendTo[$DocumentationTypes,"Overview"->"Tutorials"]; 25 | 26 | 27 | DocumentationOfTypeQ[sym_,"Overview"]:=TutorialOverviewQ@sym 28 | 29 | 30 | DocumentationSummary[overview_,"Overview"]:=StripFormatting@ParseToDocEntry@Abstract@overview 31 | 32 | 33 | MakeDocumentationContent[overview_,"Overview",nb_,opts:OptionsPattern[]]:=( 34 | NotebookWrite[nb,Cell[DocumentationTitle[overview],"TOCDocumentTitle"]]; 35 | #[overview,nb,FilterRules[{opts},Options@#]]&/@$DocumentationSections["Overview"] 36 | ) 37 | 38 | 39 | End[] 40 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/Tutorials.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Tutorials; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | Attributes[Tutorials]={HoldFirst}; 10 | 11 | 12 | Tutorials::invalidInput="Tutorials information of `` cannot be set to ``. A list of tutorial/overview titles is expected."; 13 | Tutorials::invalidSymbol="Symbol `` is not tagged as tutorial/overview and cannot be added to the tutorials section."; 14 | 15 | 16 | DeclareMetadataHandler[Tutorials,"invalidInput",_,{(_String|_Symbol)...},{}] 17 | 18 | 19 | Attributes[MakeTutorialsSection]={HoldFirst}; 20 | 21 | 22 | MakeTutorialsSection[sym_,nb_,OptionsPattern[]]:=With[ 23 | {valid=DeleteCases[_Symbol?(!TutorialQ@#&&!TutorialOverviewQ@#&)]@Tutorials[sym]}, 24 | If[Length@valid>0, 25 | NotebookWrite[nb, 26 | LinkSection["Tutorials","TutorialsSection","RelatedTutorial.png",True, 27 | RowBox@{"\[FilledVerySmallSquare]",Cell[ 28 | BoxData@DocumentationLink[ 29 | #, 30 | Which[StringQ@#,"Tutorial OR Overview",TutorialQ@#,"Tutorial",True,"Overview"], 31 | "LinkStyle"->"RefLinkPlain", 32 | BaseStyle->{"Tutorials"} 33 | ], 34 | "Tutorials" 35 | ]}&/@valid 36 | ] 37 | ] 38 | ] 39 | ] 40 | 41 | 42 | Attributes[MakeTutorialsHeader]={HoldFirst}; 43 | 44 | 45 | MakeTutorialsHeader[sym_]:=MakeHeaderDropdown[ 46 | "Tutorials", 47 | "Tutorials", 48 | Replace[ 49 | Tutorials[sym], 50 | { 51 | t_Symbol?TutorialQ:>DocID[t,"Tutorial"], 52 | o_Symbol?TutorialOverviewQ:>DocID[o,"Overview"], 53 | s_Symbol:>( 54 | Message[Tutorials::invalidSymbol,HoldForm@s];Nothing 55 | ) 56 | }, 57 | 1 58 | ] 59 | ] 60 | 61 | 62 | AppendTo[$DocumentationSections["Symbol"],MakeTutorialsSection]; 63 | AppendTo[$HeaderEntries["Symbol"],MakeTutorialsHeader]; 64 | AppendTo[$DependencyCollectors["Symbol"],Tutorials]; 65 | 66 | 67 | AppendTo[$DocumentationSections["Guide"], MakeTutorialsSection]; 68 | AppendTo[$HeaderEntries["Guide"],MakeTutorialsHeader]; 69 | AppendTo[$DependencyCollectors["Guide"],Tutorials]; 70 | 71 | 72 | AppendTo[$DocumentationSections["Tutorial"], MakeTutorialsSection]; 73 | AppendTo[$HeaderEntries["Tutorial"],MakeTutorialsHeader]; 74 | AppendTo[$DependencyCollectors["Tutorial"],Tutorials]; 75 | 76 | 77 | End[] 78 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/TutorialsDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[Tutorials]="[*[*Tutorials[sym]*]'''={```tutorial```_1,\[Ellipsis]}'''*] sets the tutorials to appear in the \"Tutorials\" section of the documentation page built by [*DocumentationBuilder*]. 4 | [*[*Tutorials[guide]*]'''={```tutorial```_1,\[Ellipsis]}'''*] sets the related tutorials for ```guide```. 5 | [*[*Tutorials[tut]*]'''={```tutorial```_1,\[Ellipsis]}'''*] sets the related tutorials for ```tut```."; 6 | 7 | Begin[BuildAction] 8 | 9 | 10 | DocumentationHeader[Tutorials]=FSHeader["0.64.0","0.68.0"]; 11 | 12 | 13 | Details[Tutorials]={ 14 | "[*Tutorials*] is one of the metadata symbols used by [*DocumentationBuilder*]. Others include [*Usage*], [*Details*], [*Examples*], [*SeeAlso*] and [*Guides*].", 15 | "In [*Tutorials[\[Ellipsis]]*]'''={```tutorial```_1,\[Ellipsis]}''', every ```tutorial_i``` should be one of the following:", 16 | TableForm@{ 17 | {"\"```title```\"","Exact title of a tutorial"}, 18 | {"```tutorial```","A symbol tagged as [*Tutorial*]"}, 19 | {"```overview```","A symbol tagged as [*TutorialOverview*]"} 20 | }, 21 | "Tutorials listed in [*Tutorials[\[Ellipsis]]*] appear at the bottom of the documentation page in the \"Tutorials\" section and in the header in the \"Tutorials\" dropdown.", 22 | "[*Tutorials*] is the exact analogue of [*Guides*] for documentation tutorials." 23 | }; 24 | 25 | 26 | Examples[Tutorials,"Basic examples"]={ 27 | { 28 | "Load the ForScience package:", 29 | ExampleInput[Needs["ForScience`PacletUtils`"]], 30 | "Set up a symbol to attach the examples to:", 31 | ExampleInput[Examples[test]=.;,Visible->False], 32 | ExampleInput[ 33 | DocumentationHeader[test]={"DOCUMENTATION EXAMPLE",Darker@Gray,"Introduced in the documentation"};, 34 | InitializationCell->True 35 | ], 36 | "Add some tutorials:", 37 | ExampleInput[ 38 | Tutorials[test]={"Vectors and Matrices","Lists as Sets"};, 39 | DocumentationBuilder[test] 40 | ], 41 | ExampleInput[NotebookClose[%];,Visible->False] 42 | } 43 | }; 44 | 45 | 46 | Examples[Tutorials,"Properties & Relations"]={ 47 | { 48 | "[*Tutorials*] can be used to set related tutorials of guide pages:", 49 | ExampleInput[ 50 | gd=Guide["Test guide"];, 51 | DocumentationHeader[gd]={"TEST GUIDE",Blue,"Footer text"};, 52 | Tutorials[gd]={"Lists as Sets"};, 53 | DocumentationBuilder[gd] 54 | ], 55 | ExampleInput[NotebookClose[%];,Visible->False] 56 | } 57 | }; 58 | 59 | 60 | SeeAlso[Tutorials]={DocumentationBuilder,Tutorial,TutorialOverview,Guides,SeeAlso,Usage,Details,Examples}; 61 | 62 | 63 | Guides[Tutorials]={$GuideCreatingDocPages}; 64 | 65 | 66 | End[] 67 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/UnloadPacletDocumentation.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | UnloadPacletDocumentation::usage=FormatUsage@"UnloadPacletDocumentation[paclet] attempts to gracefully unload the documentation of ```paclet``` in order to remove any locks on the directory."; 4 | LoadPacletDocumentation::usage=FormatUsage@"LoadPacletDocumentation[paclet] attempts to load the documentation of a newly installed paclet."; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | Begin["PacletManager`Documentation`Private`"] 10 | If[!TrueQ@ForScience`PacletUtils`Private`$PacletDocSearchEvaluationLeakFixed&&($VersionNumber<11.2), 11 | (* fix the error message occuring when starting the documentation center only after DocumentationSearch has been loaded *) 12 | ForScience`PacletUtils`Private`$PacletDocSearchEvaluationLeakFixed=True; 13 | DownValues[loadDocSearchPackage]=DownValues[loadDocSearchPackage]/. 14 | HoldPattern[(_=$searchLanguage)]:>( 15 | (* this code originally uses Evaluate[Symbol[...]]=... which fails if the symbol already has a value *) 16 | ToExpression["DocumentationSearch`$SearchLanguage",InputForm,Hold]/.Hold[s_]:>(s=$searchLanguage) 17 | ) 18 | ] 19 | End[] 20 | 21 | 22 | (* maps the given function over all registered instances of TextSearchIndex *) 23 | ApplyToTextSearchIndexInstances[func_]:=JavaBlock[ 24 | (* first, get the Class object for the TextSearchIndex class *) 25 | LoadJavaClass["com.wolfram.jlink.JLinkClassLoader"]; 26 | indexClass=JLinkClassLoader`classFromName["com.wolfram.textsearch.TextSearchIndex"]; 27 | (* get java.lang.reflect.Field references to the private fields we need and make them accessible *) 28 | instances=indexClass@getDeclaredField["instances"]; 29 | directories=indexClass@getDeclaredField["directories"]; 30 | paths=indexClass@getDeclaredField["paths"]; 31 | java`lang`reflect`Field`setAccessible[{instances,directories,paths},True]; 32 | Map[ 33 | (* we supply the function with the object and the value of the two private files 'directories' and 'paths' *) 34 | func[#,directories@get[#],paths@get[#]]&, 35 | (* get the instance map (Map>) and get the dereferenced values *) 36 | #@get[]&/@JavaObjectToExpression@instances@get[Null]@values[] 37 | ] 38 | ] 39 | 40 | 41 | UnloadPacletDocumentation::notFound="Paclet `` not found"; 42 | 43 | 44 | UnloadPacletDocumentation[paclet_]:= 45 | With[ 46 | {basePath=FindFile[paclet<>"`"]}, 47 | If[FailureQ@basePath, 48 | Message[UnloadPacletDocumentation::notFound,paclet], 49 | With[ 50 | {path=DirectoryName@basePath}, 51 | If[$VersionNumber<11.2, 52 | (* in 11.1, we can simply call CloseDocumentationIndex on all (spell)indexes that lie in the paclets directory *) 53 | CloseDocumentationIndex/@Select[ 54 | Join[DocumentationIndexes[],DocumentationSpellIndexes[]], 55 | StringStartsQ[path] 56 | ], 57 | (* in 11.2/11.3, we have to carefully remove the paths from the TextSearchIndex instances via JLink and reflection *) 58 | ApplyToTextSearchIndexInstances[(* map over all instances *) 59 | Function[ 60 | {index,directories,paths}, 61 | Map[(* go through all directories add to the TextSearchIndex *) 62 | If[(* if a directory lies within the paclet directory ... *) 63 | StringStartsQ[#@getDirectory[]@toAbsolutePath[]@toString[],path], 64 | (* remove it both from directories (List<...>) and paths (Set) *) 65 | directories@remove[#]; 66 | paths@remove[#@getDirectory[]]; 67 | ]&, 68 | JavaObjectToExpression@directories 69 | ]; 70 | (* call index.finalizeWrites() - this closes the .indexReader to release the lock *) 71 | (* it also resets both .indexReader and .indexSearcher to null, so that they will be recreated upon the next call to .search *) 72 | index@finalizeWrites[]; 73 | (* if the list of directories is now empty, close the index and release the object *) 74 | (* this happens in 11.2 for the TextSearchIndex instance that creates the TextSearchIndex directory upon updating of the old index *) 75 | If[directories@size[]==0, 76 | (* close the instance. This also removes it from the .instances map on the java side *) 77 | index@close[]; 78 | (* release the object in order to invalidate the entry cached by Mathematica. This ensures that a new instance is created when updating the index the next time *) 79 | ReleaseJavaObject@index; 80 | ]; 81 | ] 82 | ] 83 | ]; 84 | ] 85 | ] 86 | ] 87 | 88 | 89 | LoadPacletDocumentation[paclet_]:=Which[11.2<=$VersionNumber,(*this is only necessary in 11.2+, 11.1 properly handles paclet installation *) 90 | Get["DocumentationSearch`"] (* this seems to be sufficient to get the documentation back up and running *) 91 | ] 92 | 93 | 94 | End[] 95 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/Usage.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | Attributes[UsageBoxes]={HoldFirst}; 10 | 11 | 12 | UsageBoxes[_]={}; 13 | 14 | 15 | Attributes[Usage]={HoldFirst}; 16 | 17 | 18 | Usage::noString="Cannot set usage of `` to ``. Only strings are allowed."; 19 | 20 | 21 | Usage/:HoldPattern[Usage[sym_]=usage_String]:= 22 | sym::usage=MakeUsageString[ 23 | UsageBoxes[sym]=( 24 | ParseFormatting/@StringSplit[ 25 | FormatUsageCase[Usage[sym]^=usage,StartOfLine->True], 26 | "\n" 27 | ] 28 | ) 29 | ] 30 | HoldPattern[Usage[sym_]=usage_]^:=(Message[Usage::noString,HoldForm@sym,usage];usage) 31 | Usage[_]:="" 32 | 33 | 34 | End[] 35 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/UsageCompiler.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | UsageCompiler; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | UsageCompiler[exprs_]:=With[ 10 | {fu=Symbol["FormatUsage"],usage=Symbol["Usage"]}, 11 | exprs/.{ 12 | HoldPattern[fu[u_String]]:> 13 | With[ 14 | {ev=FormatUsage@u}, 15 | ev/;True 16 | ], 17 | HoldPattern[usage[sym_Symbol]=u_String]:> 18 | With[ 19 | {ev=FormatUsage@u}, 20 | (sym::usage=ev)/;True 21 | ] 22 | } 23 | ] 24 | 25 | 26 | End[] 27 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/UsageCompilerDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[UsageCompiler]="UsageCompiler is a file processor that compiles any [*FormatUsage[usage]*] found."; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[UsageCompiler]=FSHeader["0.52.0","0.59.19"]; 10 | 11 | 12 | Details[UsageCompiler]={ 13 | "[*UsageCompiler*] is a file processor to be used with [*BuildPaclet*] (as post-processor) and [*ProcessFile*].", 14 | "[*UsageCompiler*] compiles all expressions of the form [*FormatUsage[usage]*] and [*Usage[sym]*]'''=```usage```'''.", 15 | "For expressions of the form [*FormatUsage[usage]*] the resulting expression is fully equivalent.", 16 | "Expressions of the form [*Usage[sym]*]'''=```usage```''' are replaced by '''```sym```::usage=```formatted```''', losing the metadata necessary for [*DocumentationBuilder*]. Since [*UsageCompiler*] is intended to be used as part of [*BuildPaclet*], this is not an issue, as the metadata are not necessary after building.", 17 | "Applying [*UsageCompiler*] to a file can significantly decrease loading time, depending on the complexity of the strings to be formatted." 18 | }; 19 | 20 | 21 | Examples[UsageCompiler,"Basic examples"]={ 22 | { 23 | "Load the ForScience package:", 24 | ExampleInput[Needs["ForScience`PacletUtils`"]], 25 | "Create a test file with some usage messages inside:", 26 | ExampleInput[ 27 | SetDirectory@CreateDirectory[];, 28 | "Export[\"test.wl\", 29 | \" 30 | foo::usage=FormatUsage@\\\"foo[a,b] is a function.\\\"; 31 | Usage[bar]=\\\"bar[\[LeftAssociation]a\[Rule]b\[RightAssociation],c_i] is another function.\\\"; 32 | \", 33 | \"String\" 34 | ];", 35 | InitializationCell->True 36 | ], 37 | "Process the file using [*UsageCompiler*]:", 38 | ExampleInput[ProcessFile["test.wl",{UsageCompiler}];], 39 | "The file now directly contains the formatted usage strings:", 40 | ExampleInput[Import["test.wl","String"]] 41 | } 42 | }; 43 | 44 | 45 | Examples[UsageCompiler,"Possible issues"]={ 46 | { 47 | "For expressions of the form [*Usage[sym]*]'''=```usage```''', [*Usage*] metadata are lost:", 48 | ExampleInput[ClearAll["bar"],Visible->False], 49 | ExampleInput[ 50 | Import["test.wl"], 51 | "?bar", 52 | "InputForm[Usage[bar]]" 53 | ], 54 | CleanExampleDirectory 55 | } 56 | }; 57 | 58 | 59 | SeeAlso[UsageCompiler]={BuildPaclet,ProcessFile,Usage,FormatUsage,CompatibilityChecker,VariableLeakTracer}; 60 | 61 | 62 | End[] 63 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/UsageDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[Usage]="[*Usage[```sym```]'''=```usage```'''*] sets the usage message of ```sym``` to [*FormatUsage[sym]*]. If set, a usage section is generated by [*DocumentBuilder*]."; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[Usage]=FSHeader["0.56.0"]; 10 | 11 | 12 | Details[Usage]={ 13 | "[*Usage[sym]*]'''=```usage```''' effectively assigns [*FormatUsage[usage]*] as usage message to ```sym```.", 14 | "The format specifcations supported are the same as those for [*FormatUsage*].", 15 | "[*Usage*] is one of the metadata symbols used by [*DocumentationBuilder*]. Others include [*Details*], [*Examples*], [*SeeAlso*], [*Tutorials*] and [*Guides*].", 16 | "Assigning a value to [*Usage[sym]*] causes usage examples to be generated by [*DocumentationBuilder[sym]*].", 17 | "In [*Usage[sym]*]'''=```usage```''', the definition is attached to ```sym``` as an upvalue." 18 | }; 19 | 20 | 21 | Examples[Usage,"Basic examples"]={ 22 | { 23 | "Load the ForScience package:", 24 | ExampleInput[Needs["ForScience`PacletUtils`"]], 25 | "Assign a formatted usage message to a symbol:", 26 | ExampleInput[Usage[foo]="foo[a,b] does something cool."], 27 | "Display the help for the symbol:", 28 | ExampleInput["?foo"], 29 | "Create a documentation page for the symbol:", 30 | ExampleInput[ 31 | DocumentationHeader[foo]={"TEST",Black,""}, 32 | nb=DocumentationBuilder[foo] 33 | ], 34 | "Close the notebook again:", 35 | ExampleInput[NotebookClose[nb]] 36 | } 37 | }; 38 | 39 | 40 | Examples[Usage,"Properties & Relations"]={ 41 | { 42 | "For assigning usage messages, [*Usage[sym]*]'''=```usage```''' is equivalent to [*sym::usage=[*FormatUsage[usage]*]*]:", 43 | ExampleInput[ 44 | bar::usage=FormatUsage["bar[a,b] is great!"], 45 | Usage[foobar]="foobar[c,d] is even better!", 46 | "?bar", 47 | "?foobar" 48 | ], 49 | "Generated documentation pages get a usage section only when [*Usage[sym]*] is assigned:", 50 | ExampleInput[ 51 | DocumentationHeader[bar]={"TEST",Black,""}, 52 | DocumentationHeader[foobar]={"TEST",Black,""}, 53 | nb=DocumentationBuilder[bar], 54 | nb2=DocumentationBuilder[foobar] 55 | ], 56 | "Close the notebook again:", 57 | ExampleInput[ 58 | NotebookClose[nb], 59 | NotebookClose[nb2] 60 | ] 61 | } 62 | }; 63 | 64 | 65 | SeeAlso[Usage]={FormatUsage,ParseFormatting,DocumentationBuilder,Details,Examples,SeeAlso,Tutorials,Guides}; 66 | 67 | 68 | Guides[Usage]={$GuideCreatingDocPages}; 69 | 70 | 71 | Tutorials[Usage]={$TutorialCreatingSymbolPages}; 72 | 73 | 74 | End[] 75 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/UsageSection.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | Options[MakeUsageSection]={Usage->True}; 7 | 8 | 9 | Attributes[MakeUsageSection]={HoldFirst}; 10 | 11 | 12 | MakeUsageSection[sym_,nb_,OptionsPattern[]]:=If[OptionValue@Usage&&UsageBoxes@sym=!={}, 13 | NotebookWrite[nb, 14 | Cell[ 15 | BoxData@GridBox[ 16 | { 17 | "", 18 | Cell[ 19 | Insert["\[LineSeparator]",{1,2}]@BoxesToDocEntry[#] 20 | ] 21 | }&/@UsageBoxes[sym] 22 | ], 23 | "Usage", 24 | GridBoxOptions->{ 25 | GridBoxBackground->{ 26 | "Columns"->{{None}}, 27 | "ColumnsIndexed"->{}, 28 | "Rows"->{None,None,{None}}, 29 | "RowsIndexed"->{} 30 | } 31 | } 32 | ] 33 | ]; 34 | ] 35 | 36 | AppendTo[$DocumentationSections["Symbol"],MakeUsageSection]; 37 | AppendTo[$DependencyCollectors["Symbol"],Usage]; 38 | 39 | 40 | End[] 41 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/VariableLeakTracer.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | VariableLeakTracer; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | VariableLeakTracer::leaked = "Variable `` leaked in ``!"; 10 | 11 | 12 | VariableLeakTracer[vars__]:=( 13 | lastFile=""; 14 | tracedVariables={vars}; 15 | Quiet@Remove@vars; 16 | iVariableLeakTracer[exprs_]:=( 17 | If[Length@Names@#>0, 18 | tracedVariables=DeleteCases[#,tracedVariables]; 19 | Message[VariableLeakTracer::leaked,First@Names@#,lastFile] 20 | ]&/@tracedVariables; 21 | lastFile=$ProcessedFile; 22 | exprs 23 | ); 24 | iVariableLeakTracer 25 | ) 26 | 27 | 28 | End[] 29 | -------------------------------------------------------------------------------- /ForScience/PacletUtils/VariableLeakTracerDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[VariableLeakTracer]="VariableLeakTracer[\"var_1\",\[Ellipsis]] is a [*BuildPaclet*] file preprocessor that issues a message whenever any of the ```var_i``` has be defined."; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[VariableLeakTracer]=FSHeader["0.61.0"]; 10 | 11 | 12 | Details[VariableLeakTracer]={ 13 | "[*VariableLeakTracer[\[Ellipsis]]*] is a file preprocessor to be used by [*BuildPaclet*].", 14 | "[*VariableLeakTracer*] can be used to determine the file where a given variable is defined.", 15 | "The ```var_i``` in [*VariableLeakTracer[var_1,\[Ellipsis]]*] are expected to be strings as those used by [*Names*].", 16 | "Typically, the variable masks passed to [*VariableLeakTracer*] specify the symbol context to ensure that the correct file is determined.", 17 | "[*VariableLeakTracer[\[Ellipsis]]*] effectively calls [*Names*] to check whether a symbol has been defined.", 18 | "Calling [*VariableLeakTracer[var_1,\[Ellipsis]]*] sets the list of variables to be tracked to {```var```_1,\[Ellipsis]}. All instances of [*VariableLeakTracer[\[Ellipsis]]*] share a single list.", 19 | "As [*VariableLeakTracer*] is a file pre-processor, the messages generated by [*ProcessFile*] show the file after the offending one.", 20 | "Leaks in the last loaded file are not detected.", 21 | "[*VariableLeakTracer*] issues a VariableLeakTracer::leaked message for each leaked symbol.", 22 | "[*VariableLeakTracer*] does not modify the contents of the processed file." 23 | }; 24 | 25 | 26 | Examples[VariableLeakTracer,"Basic examples"]={ 27 | { 28 | "Load the ForScience package:", 29 | ExampleInput[Needs["ForScience`PacletUtils`"]], 30 | "Create a example paclet:", 31 | ExampleInput[ 32 | SetDirectory@CreateDirectory[];, 33 | CreateDirectory["paclet"];, 34 | "Export[\"paclet/PacletInfo.m\", 35 | \"Paclet[ 36 | Name->\\\"Test\\\", 37 | Version->\\\"0.0\\\", 38 | Extensions->{ 39 | {\\\"Kernel\\\",Context->{ 40 | \\\"paclet`\\\", 41 | \\\"paclet`sub1`\\\", 42 | \\\"paclet`sub2`\\\" 43 | }} 44 | } 45 | ]\", 46 | \"String\" 47 | ];", 48 | "Export[\"paclet/paclet.wl\", 49 | \" 50 | BeginPackage[\\\"paclet`\\\"]; 51 | aMain=1; 52 | bMain=2; 53 | <True 62 | ], 63 | "Find where ```bMain``` is defined:", 64 | ExampleInput[BuildPaclet["paclet",{{VariableLeakTracer["paclet`bMain"]},{}}];] 65 | }, 66 | { 67 | "Use wildcards:", 68 | ExampleInput[BuildPaclet["paclet",{{VariableLeakTracer["paclet`a*1"]},{}}];] 69 | } 70 | }; 71 | 72 | Examples[VariableLeakTracer,"Possible issues"]={ 73 | { 74 | "When using wildcards, only the first leaked symbol is found:", 75 | ExampleInput[BuildPaclet["paclet",{{VariableLeakTracer["paclet`*Sub1"]},{}}];] 76 | }, 77 | { 78 | "Variables in the last file are not detected:", 79 | ExampleInput[BuildPaclet["paclet",{{VariableLeakTracer["paclet`bSub2"]},{}}];], 80 | ExampleInput[ 81 | NotebookWrite[ 82 | #, 83 | NotebookRead[#]/. 84 | s_String:>StringReplace[ 85 | s, 86 | StringExpression@@Riffle[FileNameSplit[Directory[]],"\\"..]->"." 87 | ] 88 | ]&/@Cells[CellStyle->"MSG"];, 89 | Visible->False 90 | ], 91 | CleanExampleDirectory 92 | } 93 | }; 94 | 95 | 96 | SeeAlso[VariableLeakTracer]={BuildPaclet,CompatibilityChecker,UsageCompiler}; 97 | 98 | 99 | End[] 100 | -------------------------------------------------------------------------------- /ForScience/PlotUtils.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | BeginPackage["ForScience`PlotUtils`",{"ForScience`","ForScience`PacletUtils`","ForScience`Util`"}] 4 | 5 | 6 | <<`InternalUtils`; 7 | <<`ColorFunctions`; 8 | <<`VectorMarker`; 9 | <<`ForSciencePlotTheme`; 10 | <<`CustomTicks`; 11 | <<`GraphicsInformation`; 12 | <<`CombinePlots`; 13 | <<`PlotGrid`; 14 | 15 | 16 | BuildAction[ 17 | (* guide & tutorial symbols. Need to declare them here, as they're used inside Begin[BuildAction] ... End[] *) 18 | $GuidePlotUtils; 19 | ] 20 | 21 | 22 | <<`ColorFunctionsDoc`; 23 | <<`VectorMarkerDoc`; 24 | <<`ForSciencePlotThemeDoc`; 25 | <<`CustomTicksDoc`; 26 | <<`CombinePlotsDoc`; 27 | <<`GraphicsInformationDoc`; 28 | <<`PlotGridDoc`; 29 | 30 | 31 | BuildAction[ 32 | <<`GuidePlotUtils`; 33 | ] 34 | 35 | 36 | EndPackage[] 37 | -------------------------------------------------------------------------------- /ForScience/PlotUtils/ColorFunctions.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Jet; 4 | Parula; 5 | Fire; 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | 11 | $JetData={ 12 | {"Jet","Jet color gradient",{}}, 13 | {"Gradients"}, 14 | 1, 15 | {0,1}, 16 | { 17 | {0,RGBColor[0,0,9/16]}, 18 | {1/9,Blue}, 19 | {23/63,Cyan}, 20 | {13/21,Yellow}, 21 | {47/63,Orange}, 22 | {55/63,Red}, 23 | {1,RGBColor[1/2,0,0]} 24 | }, 25 | "" 26 | }; 27 | 28 | 29 | $ParulaData={ 30 | {"Parula","Parula color gradient",{}}, 31 | {"Gradients"}, 32 | 1, 33 | {0,1}, 34 | RGBColor@@@{ 35 | {0.2422,0.1504,0.6603}, 36 | {0.2504,0.1650,0.7076}, 37 | {0.2578,0.1818,0.7511}, 38 | {0.2647,0.1978,0.7952}, 39 | {0.2706,0.2147,0.8364}, 40 | {0.2751,0.2342,0.8710}, 41 | {0.2783,0.2559,0.8991}, 42 | {0.2803,0.2782,0.9221}, 43 | {0.2813,0.3006,0.9414}, 44 | {0.2810,0.3228,0.9579}, 45 | {0.2795,0.3447,0.9717}, 46 | {0.2760,0.3667,0.9829}, 47 | {0.2699,0.3892,0.9906}, 48 | {0.2602,0.4123,0.9952}, 49 | {0.2440,0.4358,0.9988}, 50 | {0.2206,0.4603,0.9973}, 51 | {0.1963,0.4847,0.9892}, 52 | {0.1834,0.5074,0.9798}, 53 | {0.1786,0.5289,0.9682}, 54 | {0.1764,0.5499,0.9520}, 55 | {0.1687,0.5703,0.9359}, 56 | {0.1540,0.5902,0.9218}, 57 | {0.1460,0.6091,0.9079}, 58 | {0.1380,0.6276,0.8973}, 59 | {0.1248,0.6459,0.8883}, 60 | {0.1113,0.6635,0.8763}, 61 | {0.0952,0.6798,0.8598}, 62 | {0.0689,0.6948,0.8394}, 63 | {0.0297,0.7082,0.8163}, 64 | {0.0036,0.7203,0.7917}, 65 | {0.0067,0.7312,0.7660}, 66 | {0.0433,0.7411,0.7394}, 67 | {0.0964,0.7500,0.7120}, 68 | {0.1408,0.7584,0.6842}, 69 | {0.1717,0.7670,0.6554}, 70 | {0.1938,0.7758,0.6251}, 71 | {0.2161,0.7843,0.5923}, 72 | {0.2470,0.7918,0.5567}, 73 | {0.2906,0.7973,0.5188}, 74 | {0.3406,0.8008,0.4789}, 75 | {0.3909,0.8029,0.4354}, 76 | {0.4456,0.8024,0.3909}, 77 | {0.5044,0.7993,0.3480}, 78 | {0.5616,0.7942,0.3045}, 79 | {0.6174,0.7876,0.2612}, 80 | {0.6720,0.7793,0.2227}, 81 | {0.7242,0.7698,0.1910}, 82 | {0.7738,0.7598,0.1646}, 83 | {0.8203,0.7498,0.1535}, 84 | {0.8634,0.7406,0.1596}, 85 | {0.9035,0.7330,0.1774}, 86 | {0.9393,0.7288,0.2100}, 87 | {0.9728,0.7298,0.2394}, 88 | {0.9956,0.7434,0.2371}, 89 | {0.9970,0.7659,0.2199}, 90 | {0.9952,0.7893,0.2028}, 91 | {0.9892,0.8136,0.1885}, 92 | {0.9786,0.8386,0.1766}, 93 | {0.9676,0.8639,0.1643}, 94 | {0.9610,0.8890,0.1537}, 95 | {0.9597,0.9135,0.1423}, 96 | {0.9628,0.9373,0.1265}, 97 | {0.9691,0.9606,0.1064}, 98 | {0.9769,0.9839,0.0805} 99 | } 100 | }; 101 | 102 | 103 | $FireData={ 104 | {"Fire","Fire color gradient",{}}, 105 | {"Gradients"}, 106 | 1, 107 | {0,1}, 108 | RGBColor@@@({ 109 | {0,0,0}, 110 | {0,0,61}, 111 | {1,0,96}, 112 | {25,0,130}, 113 | {49,0,165}, 114 | {73,0,192}, 115 | {98,0,220}, 116 | {122,0,227}, 117 | {146,0,210}, 118 | {162,0,181}, 119 | {173,0,151}, 120 | {184,0,122}, 121 | {195,0,93}, 122 | {207,14,64}, 123 | {217,35,35}, 124 | {229,57,5}, 125 | {240,79,0}, 126 | {252,101,0}, 127 | {255,117,0}, 128 | {255,133,0}, 129 | {255,147,0}, 130 | {255,161,0}, 131 | {255,175,0}, 132 | {255,190,0}, 133 | {255,205,0}, 134 | {255,219,0}, 135 | {255,234,0}, 136 | {255,248,35}, 137 | {255,255,98}, 138 | {255,255,160}, 139 | {255,255,223}, 140 | {255,255,255} 141 | }/255) 142 | }; 143 | 144 | 145 | If[!TrueQ@$ColorFunctionsRegistered, 146 | $ColorFunctionsRegistered=True; 147 | (* ensure that the ColorData framework is fully loaded. 148 | This is done by first forcing ColorDataFunction[] to be typeset 149 | (loading ColorData leaves a BoxFormAutoload call in the typesetting of ColorDataFunction which reverts the injection below) 150 | Then `colorSchemes and `colorSchemeNames are evaluated *) 151 | MakeBoxes@ColorDataFunction[]; 152 | DataPaclets`ColorDataDump`colorSchemes; 153 | DataPaclets`ColorDataDump`colorSchemeNames; 154 | ( 155 | AppendTo[ 156 | DataPaclets`ColorDataDump`colorSchemes, 157 | # 158 | ]; 159 | AppendTo[ 160 | DataPaclets`ColorDataDump`colorSchemeNames, 161 | #[[1,1]] 162 | ]; 163 | )&/@{ 164 | $JetData, 165 | $ParulaData, 166 | $FireData 167 | } 168 | ] 169 | 170 | 171 | Jet=ColorData["Jet"] 172 | Parula=ColorData["Parula"] 173 | Fire=ColorData["Fire"] 174 | 175 | 176 | End[] 177 | -------------------------------------------------------------------------------- /ForScience/PlotUtils/ColorFunctionsDoc.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[Jet]="Jet is a color scheme based on the MATLAB color scheme of the same name."; 4 | Usage[Parula]="Parula is a color scheme based on the MATLAB color scheme of the same name."; 5 | Usage[Fire]="Fire is a color scheme based on the ImageJ color scheme of the same name."; 6 | 7 | 8 | Begin[BuildAction] 9 | 10 | 11 | DocumentationHeader[Jet]=FSHeader["0.0.1","0.83.8"]; 12 | 13 | 14 | Details[Jet]={ 15 | "[*Jet*] is an adaptation of the MATLAB color function with the same name.", 16 | "[*Jet*] is a [*ColorDataFunction*] gradient.", 17 | "[*Jet*] is equivalent to [*ColorData*][\"Jet\"]." 18 | }; 19 | 20 | 21 | Examples[Jet,"Basic examples"]={ 22 | { 23 | "Load the ForScience package:", 24 | ExampleInput[Needs["ForScience`PlotUtils`"]], 25 | "[*Jet*] is a [*ColorDataFunction*]:", 26 | ExampleInput[Jet] 27 | }, 28 | { 29 | "The color scheme can also be accessed via [*ColorData*]:", 30 | ExampleInput[ColorData["Jet"]] 31 | }, 32 | { 33 | "Use the color scheme as [*ColorFunction*]:", 34 | ExampleInput[ContourPlot[Sin[x]Sin[y],{x,-\[Pi],\[Pi]},{y,-\[Pi],\[Pi]},ColorFunction->Jet]] 35 | }, 36 | { 37 | "The color scheme can also be specified as string:", 38 | ExampleInput[ContourPlot[Sin[x]Sin[y],{x,-\[Pi],\[Pi]},{y,-\[Pi],\[Pi]},ColorFunction->"Jet"]] 39 | } 40 | }; 41 | 42 | 43 | SeeAlso[Jet]=Hold[ColorData,Parula,Fire,ColorFunction,ColorDataFunction]; 44 | 45 | 46 | DocumentationHeader[Parula]=FSHeader["0.85.0"]; 47 | 48 | 49 | Details[Parula]={ 50 | "[*Parula*] is an adaptation of the MATLAB color function with the same name.", 51 | "[*Parula*] is a [*ColorDataFunction*] gradient.", 52 | "[*Parula*] is equivalent to [*ColorData*][\"Parula\"]." 53 | }; 54 | 55 | 56 | Examples[Parula,"Basic examples"]={ 57 | { 58 | "Load the ForScience package:", 59 | ExampleInput[Needs["ForScience`PlotUtils`"]], 60 | "[*Parula*] is a [*ColorDataFunction*]:", 61 | ExampleInput[Parula] 62 | }, 63 | { 64 | "The color scheme can also be accessed via [*ColorData*]:", 65 | ExampleInput[ColorData["Parula"]] 66 | }, 67 | { 68 | "Use the color scheme as [*ColorFunction*]:", 69 | ExampleInput[ContourPlot[Sin[x+y]Sin[x-y],{x,-\[Pi],\[Pi]},{y,-\[Pi],\[Pi]},ColorFunction->Parula]] 70 | }, 71 | { 72 | "The color scheme can also be specified as string:", 73 | ExampleInput[ContourPlot[Sin[x+y]Sin[x-y],{x,-\[Pi],\[Pi]},{y,-\[Pi],\[Pi]},ColorFunction->"Parula"]] 74 | } 75 | }; 76 | 77 | 78 | SeeAlso[Parula]=Hold[ColorData,Jet,Fire,ColorFunction,ColorDataFunction]; 79 | 80 | 81 | DocumentationHeader[Fire]=FSHeader["0.88.0"]; 82 | 83 | 84 | Details[Fire]={ 85 | "[*Fire*] is an adaptation of the ImageJ color function with the same name.", 86 | "[*Fire*] is a [*ColorDataFunction*] gradient.", 87 | "[*Fire*] is equivalent to [*ColorData*][\"Fire\"]." 88 | }; 89 | 90 | 91 | Examples[Fire,"Basic examples"]={ 92 | { 93 | "Load the ForScience package:", 94 | ExampleInput[Needs["ForScience`PlotUtils`"]], 95 | "[*Fire*] is a [*ColorDataFunction*]:", 96 | ExampleInput[Fire] 97 | }, 98 | { 99 | "The color scheme can also be accessed via [*ColorData*]:", 100 | ExampleInput[ColorData["Fire"]] 101 | }, 102 | { 103 | "Use the color scheme as [*ColorFunction*]:", 104 | ExampleInput[ContourPlot[Sin[x+y]Sin[x-y],{x,-\[Pi],\[Pi]},{y,-\[Pi],\[Pi]},ColorFunction->Fire]] 105 | }, 106 | { 107 | "The color scheme can also be specified as string:", 108 | ExampleInput[ContourPlot[Sin[x+y]Sin[x-y],{x,-\[Pi],\[Pi]},{y,-\[Pi],\[Pi]},ColorFunction->"Fire"]] 109 | } 110 | }; 111 | 112 | 113 | SeeAlso[Parula]=Hold[ColorData,Jet,Parula,ColorFunction,ColorDataFunction]; 114 | 115 | 116 | End[] 117 | -------------------------------------------------------------------------------- /ForScience/PlotUtils/CustomTicks.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | CustomTicks; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | (* Scaled[...] will be replaced by iScaled[...] to prevent replacement by Graphics typesetting *) 10 | MakeBoxes[iScaled,frm_]^:=MakeBoxes[Scaled,frm] 11 | 12 | 13 | ProcessScalingFunctions[Scaled[s_]]:= 14 | {#/s&,#*s&} 15 | ProcessScalingFunctions[spec_]:= 16 | Visualization`Utilities`ScalingDump`scaleFn/@ 17 | Visualization`Utilities`ScalingDump`scalingPair@spec 18 | 19 | 20 | NormalizeTickSpec[tick:{_,_,{_,_},_}]:= 21 | tick 22 | NormalizeTickSpec[{x_,lbl_,len_,sty_}]:= 23 | {x,lbl,{len,len}/2,sty} 24 | NormalizeTickSpec[{x_,lbl_,len_:0.00625}]:= 25 | NormalizeTickSpec@{x,lbl,len,{}} 26 | NormalizeTickSpec[x_]:= 27 | NormalizeTickSpec@{x,x} 28 | 29 | 30 | ProcessTickLength[def_,Automatic]:= 31 | def 32 | ProcessTickLength[_,None]:= 33 | {0,0} 34 | ProcessTickLength[_,l_/;l<0]:= 35 | {0,-l} 36 | ProcessTickLength[_,l_/;l>=0]:= 37 | {l,0} 38 | ProcessTickLength[def_,Scaled@s_/;s<0]:= 39 | -s Reverse@def 40 | ProcessTickLength[def_,Scaled@s_]:= 41 | s def 42 | ProcessTickLength[def_,len:{_,_}]:= 43 | MapThread[ 44 | Replace[ 45 | #, 46 | { 47 | Automatic->First@#2, 48 | None->0, 49 | Scaled@s_:>Abs[s]#2[[Sign@s]] 50 | } 51 | ]&, 52 | { 53 | len, 54 | {def,Reverse@def} 55 | } 56 | ] 57 | 58 | 59 | ProcessTickSpec[OptionsPattern[CustomTicks]][{x_,lbl_,len_,sty_}]:= 60 | Let[ 61 | { 62 | minorQ=Head@lbl===Spacer, 63 | lblStyle=OptionValue@LabelStyle, 64 | tckStyle=Replace[OptionValue@TicksStyle,{maj_,min_}:>If[minorQ,min,maj]], 65 | tckLength=Replace[OptionValue@"TicksLength",{maj_,min_}:>If[minorQ,min,maj]], 66 | lblFunc=OptionValue["LabelFunction"]@* 67 | If[MatchQ[tckStyle,None|Automatic],#&,Style[#,tckStyle]&] 68 | }, 69 | { 70 | x, 71 | Which[ 72 | minorQ, 73 | lbl, 74 | lblStyle===Automatic, 75 | lblFunc@lbl, 76 | lblStyle===None, 77 | Spacer@{0,0}, 78 | True, 79 | lblFunc@Style[lbl,lblStyle] 80 | ], 81 | If[tckStyle===None,{0,0},ProcessTickLength[len,tckLength]], 82 | If[MatchQ[tckStyle,None|Automatic],sty,Flatten@{sty,tckStyle}] 83 | } 84 | ] 85 | 86 | 87 | Options[CustomTicks]={ 88 | ScalingFunctions->Automatic, 89 | LabelStyle->Automatic, 90 | "LabelFunction"->Identity, 91 | TicksStyle->Automatic, 92 | "TicksLength"->Automatic, 93 | Precision->4 94 | }; 95 | 96 | 97 | prot=Unprotect@Charting`ScaledTicks; 98 | Charting`ScaledTicks[{"TicksFunction",CustomTicks[opts:OptionsPattern[]]},sc_,"Nice"][_,_,_]:= 99 | CustomTicks[ 100 | ScalingFunctions->Replace[ 101 | OptionValue[CustomTicks,{opts},ScalingFunctions], 102 | Automatic->sc 103 | ], 104 | opts 105 | ] 106 | Protect/@prot; 107 | 108 | 109 | CustomTicks::invTicks="Could not generate ticks using supplied options ``"; 110 | 111 | 112 | CustomTicks[opts:OptionsPattern[]]/;MemberQ[{opts},Scaled,All,Heads->True]:= 113 | Unevaluated@CustomTicks[opts]/.Scaled->iScaled 114 | CustomTicks[opts:OptionsPattern[]][limits__]:= 115 | With[ 116 | { 117 | pOpts={opts}/.iScaled->Scaled, 118 | scaleFuncs=ProcessScalingFunctions[OptionValue@ScalingFunctions/.iScaled->Scaled], 119 | rLimits=Round[{limits},10.^(Round@Log10[-Subtract[limits]]-OptionValue@Precision)] 120 | }, 121 | ProcessTickSpec[pOpts]/@ 122 | Replace[ 123 | Replace[ 124 | NormalizeTickSpec/@ 125 | Charting`ScaledTicks[scaleFuncs]@@rLimits, 126 | Except@_List:>(Message[CustomTicks::invTicks,pOpts];{}) 127 | ], 128 | { 129 | {_?(Not@*Between[{limits}]),_Spacer,__}:> 130 | Nothing, 131 | {x_,lbl:Except@_Spacer,rest__}:>{ 132 | Clip[ 133 | x, 134 | Sort@{limits} 135 | ], 136 | ToString[lbl,TraditionalForm], 137 | rest 138 | } 139 | }, 140 | 1 141 | ] 142 | ] 143 | 144 | 145 | End[] 146 | -------------------------------------------------------------------------------- /ForScience/PlotUtils/GraphicsInformation.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | (* Adapted from Carl Woll's answer here: https://mathematica.stackexchange.com/a/138907/36508 *) 4 | 5 | GraphicsInformation; 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | 11 | ToNotebook[gr_]:= 12 | Notebook[ 13 | { 14 | Cell[BoxData@ToBoxes@InstrumentGraphics[ExtractGraphics/@gr],"Output",ImageSizeMultipliers->{1,1}] 15 | }, 16 | WindowSize->CurrentValue[EvaluationNotebook[],WindowSize] 17 | ] 18 | 19 | 20 | SowRange[k_,dir_]:=(Sow[dir->{##},k];None)& 21 | 22 | 23 | InstrumentGraphics[gr:{__Graphics}]:= 24 | MapIndexed[ 25 | Show[ 26 | #1, 27 | GridLines->{SowRange[#2,"x"],SowRange[#2,"y"]}, 28 | Epilog->{ 29 | Annotation[Rectangle[Scaled@{0,0},Scaled@{1,1}],#2,"pr"], 30 | Annotation[Rectangle[ImageScaled@{0,0},ImageScaled@{1,1}],#2,"is"] 31 | } 32 | ]&, 33 | gr 34 | ] 35 | 36 | 37 | GraphicsObj=_Graphics|_Legended; 38 | 39 | 40 | $NullMarker; 41 | 42 | 43 | GraphicsInformation[gr:{GraphicsObj..}]:= 44 | Query[Transpose][ 45 | <| 46 | ImagePadding->Abs[#is-#pr], 47 | ImageSize->Abs[Subtract@@@#is], 48 | "PlotRangeSize"->Abs[Subtract@@@#pr], 49 | PlotRange->{#x,#y} 50 | |>&/@ 51 | Values@KeySort@<| 52 | Last@Reap[ 53 | Sow[#[[2]]->#2,#[[1]]]&@@@Cases[ 54 | "Regions"/.FrontEndExecute@ExportPacket[ToNotebook[gr],"BoundingBox",Verbose->True], 55 | {{_,"is"|"pr"},_} 56 | ], 57 | _, 58 | #-><|#2|>& 59 | ] 60 | |> 61 | ] 62 | GraphicsInformation[gr:GraphicsObj|Null]:= 63 | First/@GraphicsInformation[{gr}] 64 | GraphicsInformation[gr_List]:= 65 | With[ 66 | { 67 | flat=DeleteCases[Null]@Flatten@gr, 68 | struct=gr/.{Null->$NullMarker[],List->List,Except[_List]->0} 69 | }, 70 | ( 71 | ApplyStructure[#,struct,List|$NullMarker]/. 72 | $NullMarker[]->Null 73 | )&/@ 74 | GraphicsInformation[flat]/; 75 | MatchQ[flat,{GraphicsObj..}] 76 | ] 77 | 78 | 79 | End[] 80 | -------------------------------------------------------------------------------- /ForScience/PlotUtils/GuidePlotUtils.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | $GuidePlotUtils=Guide["PlotUtils"]; 4 | 5 | 6 | Begin[BuildAction] 7 | 8 | 9 | DocumentationHeader[$GuidePlotUtils]=FSGuideHeader; 10 | 11 | 12 | Abstract[$GuidePlotUtils]="The ForScience PlotUtils subpaclet contains various function to help with the creation of nice plots. The <*ForScience plot theme::ForSciencePlotTheme*> provides a customizable way to create consistently styled plots. The functions [*CombinePlots*] and [*PlotGrid*] can be used to combine plots, enabling the creation figures with less wasted space. All functions of the PlotUtils subpaclet are designed to be maximally compatible with the built-in plot creation and manipulation functionality."; 13 | 14 | 15 | GuideSections[$GuidePlotUtils]={ 16 | { 17 | SectionTitle["Plot styling"], 18 | {ForSciencePlotTheme,Text["customizable, consistently styled plots"]}, 19 | {VectorMarker,Text["high precision, highly customizable plot markers"]}, 20 | {CustomTicks,Text["highly customizable ticks"]}, 21 | Hold[Jet,Parula,Fire,Text["additional color functions"]] 22 | }, 23 | { 24 | SectionTitle["Combining plots"], 25 | {CombinePlots,Text["create plots with secondary axes"]}, 26 | {PlotGrid,Text["create plot grids with shared axes"]} 27 | }, 28 | { 29 | SectionTitle["Inspecting plots"], 30 | {GraphicsInformation,Text["get accurate values for key dimensions of graphics expressions"]} 31 | } 32 | }; 33 | 34 | 35 | (Guides@#=DeleteDuplicates[Append[Guides@#,$GuidePlotUtils]])&[ 36 | Unevaluated@@ToExpression[#,StandardForm,Hold] 37 | ]&/@ 38 | Names["ForScience`PlotUtils`*"] 39 | 40 | 41 | Guides[$GuidePlotUtils]={$GuideForScience}; 42 | 43 | 44 | End[] 45 | -------------------------------------------------------------------------------- /ForScience/Util.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Block[{Notation`AutoLoadNotationPalette=False}, 4 | BeginPackage["ForScience`Util`",{If[$FrontEnd=!=Null,"Notation`",Nothing],"PacletManager`","ForScience`PacletUtils`"}] 5 | ] 6 | 7 | 8 | <<`AssignmentWrapper`; 9 | <<`MergeRules`; 10 | <<`Let`; 11 | <<`FunctionError`; 12 | <<`ProcFunction`; 13 | <<`IndexedFunction`; 14 | <<`AutoSlot`; 15 | <<`ToFunction`; 16 | <<`TableToTexForm`; 17 | <<`FixedShort`; 18 | <<`FancyTrace`; 19 | <<`WindowedMap`; 20 | <<`KeyGroupBy`; 21 | <<`AssociationFoldList`; 22 | <<`SPrintF`; 23 | <<`PrettyUnit`; 24 | <<`PrettyTime`; 25 | <<`DefTo`; 26 | <<`CondDef`; 27 | <<`InvCondDef`; 28 | <<`FirstHead`; 29 | <<`ProgressReport`; 30 | <<`AddKey`; 31 | <<`NaturallyOrdered`; 32 | <<`CachedImport`; 33 | <<`ImportDataset`; 34 | <<`ToAssociationRule`; 35 | <<`UpdateForScience`; 36 | <<`PublishRelease`; 37 | <<`PullUp`; 38 | <<`DelayedExport`; 39 | <<`SkipMissing`; 40 | <<`DropMissing`; 41 | <<`ApplyToWrapped`; 42 | <<`ContextualRule`; 43 | <<`SkipCommentsStream`; 44 | <<`PartLookup`; 45 | <<`SimpleTOC`; 46 | <<`ApplyStructure`; 47 | <<`ListLookup`; 48 | 49 | 50 | <<`ImportDatasetDoc`; 51 | 52 | 53 | EndPackage[] 54 | -------------------------------------------------------------------------------- /ForScience/Util/AddKey.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | AddKey::usage=FormatUsage@"AddKey[key,f] is an operator that appends the specified key where the value is obtained by applying ```f``` to the argument. 4 | AddKey[{key_1,\[Ellipsis]},{f_1,\[Ellipsis]}] works similar, but operates on all pairs '''{```key_i```,```f_i```}'''. 5 | AddKey[key_1\[Rule]f_1,key_2\[Rule]f_2,\[Ellipsis]] works on the pairs '''{```key_i```,```f_i```}'''."; 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | 11 | AddKey[r__Rule]:=AddKey@@((List@@@{r})\[Transpose]) 12 | AddKey[key_,f_]:=#~Append~(key->f@#)& 13 | AddKey[keys_List,fs_List]:=RightComposition@@MapThread[AddKey,{keys,fs}] 14 | SyntaxInformation[AddKey]={"ArgumentsPattern"->{__}}; 15 | 16 | 17 | End[] 18 | -------------------------------------------------------------------------------- /ForScience/Util/ApplyStructure.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[ApplyStructure]="ApplyStructure[list,tmpl] reshapes ```list``` such that it has the same structure as ```tmpl```. 4 | ApplyStructure[list,tmpl,head] assumes any expression with a head matching ```head``` to be part of the structure of ```tmpl```. 5 | ApplyStructure[tmpl] is the operator form."; 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | 11 | Attributes[ThreadOver]={HoldAll}; 12 | 13 | 14 | (f:ThreadOver[_,_,_,pat_])[struct_]:= 15 | f/@struct/;MatchQ[struct,pat[___]] 16 | ThreadOver[i_,_,src_,_][_]:= 17 | src[[++i]]/;i0, 36 | Message[ApplyStructure::tooFew,miss,list,tmpl], 37 | iBlank}; 11 | 12 | 13 | SyntaxInformation[ApplyToWrapped]={"ArgumentsPattern"->{_,_,_,_.,OptionsPattern[]}}; 14 | 15 | 16 | ApplyToWrapped::noMatch="Expression `` is not a wrapped expression matching ``."; 17 | 18 | 19 | ApplyToWrapped[func_,expr_,target_,extract_:None,Longest[OptionsPattern[]]]:=ReleaseHold[ 20 | Hold@IApplyToWrapped[expr,target,extract,{}]//. 21 | DownValues@IApplyToWrapped/. 22 | { 23 | IApplyToWrapped[e_,c_]:>With[ 24 | { 25 | r=Which[ 26 | extract===None, 27 | func@e, 28 | OptionValue@Method===Blank, 29 | func[e,c], 30 | True, 31 | With[ 32 | { 33 | cf=Unevaluated@c/.w_[Verbatim@_,rest___]:>(w[#,rest]&) 34 | }, 35 | func[e,cf] 36 | ] 37 | ] 38 | }, 39 | r/;True 40 | ], 41 | _Hold?(MemberQ[#,HoldPattern@IApplyToWrapped[_,_,_,_],{0,\[Infinity]}]&):>Hold[Message[ApplyToWrapped::noMatch,HoldForm@expr,target];expr] 42 | } 43 | ] 44 | 45 | 46 | Attributes[IApplyToWrapped]={HoldFirst}; 47 | 48 | 49 | IApplyToWrapped[expr_,target_,extract_,coll_]/;MatchQ[Unevaluated@expr,target]:=IApplyToWrapped[expr,coll] 50 | IApplyToWrapped[expr:w_[wrapped_,args___],target_,extract:Except[None],{coll___}]/;MatchQ[Unevaluated@expr,extract]:=IApplyToWrapped[ 51 | wrapped, 52 | target, 53 | extract, 54 | {coll,w[_,args]} 55 | ] 56 | IApplyToWrapped[w_[wrapped_,args___],target_,extract_,coll_]:=w[ 57 | IApplyToWrapped[ 58 | wrapped, 59 | target, 60 | extract, 61 | coll 62 | ], 63 | args 64 | ] 65 | 66 | 67 | End[] 68 | -------------------------------------------------------------------------------- /ForScience/Util/AssignmentWrapper.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | AssignmentWrapper::usage=FormatUsage@"'''//_=''' works like '''//''', but the ```rhs``` is wrapped around any '''Set'''/'''SetDelayed''' on the ```lhs```. E.g. '''foo=bar//_=FullForm''' is equivalent to '''FullForm[foo=bar]'''."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | Notation[ParsedBoxWrapper[RowBox[{"expr_", SubscriptBox["//", "="], "wrap_"}]] \[DoubleLongRightArrow] ParsedBoxWrapper[RowBox[{"AssignmentWrapper", "[", RowBox[{"expr_", ",", "wrap_"}], "]"}]]] 10 | AssignmentWrapper/:h_[lhs_,AssignmentWrapper[rhs_,wrap_]]:=If[h===Set||h===SetDelayed,wrap[h[lhs,rhs]],h[lhs,wrap[rhs]]] 11 | Attributes[AssignmentWrapper]={HoldAllComplete}; 12 | SyntaxInformation[AssignmentWrapper]={"ArgumentsPattern"->{_,_}}; 13 | 14 | 15 | End[] 16 | -------------------------------------------------------------------------------- /ForScience/Util/AssociationFoldList.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | AssociationFoldList::usage=FormatUsage@"AssociationFoldList[f,assoc] works like '''FoldList''', but preserves the association keys."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | AssociationFoldList[f_,list_]:=AssociationThread[Keys@list,FoldList[f,Values@list]] 10 | SyntaxInformation[AssociationFoldList]={"ArgumentsPattern"->{_,_}}; 11 | 12 | 13 | End[] 14 | -------------------------------------------------------------------------------- /ForScience/Util/AutoSlot.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | `Private`ProcessingAutoSlot=True;(*disable AutoSlot related parsing while setting usage messages. Needed when loading this multiple times*) 4 | \[Bullet]::usage=FormatUsage@"\[Bullet] works analogously to '''#''', but doesn't require an eclosing '''&'''. Slots are only filled on the topmost level. E.g. '''f[\[Bullet], g[\[Bullet]]][3]'''\[RightArrow]'''f[3,g[\[Bullet]]]'''. Can also use ```\[Bullet]n``` and ```\[Bullet]name```, analogous to '''#'''. See also '''\[Bullet]\[Bullet]'''. Enter \[Bullet] as ***<*\[Bullet]::\\\\[Bullet]*>*** or '''ALT+7'''."; 5 | \[Bullet]\[Bullet]::usage=FormatUsage@"\[Bullet]\[Bullet] works the same as \[Bullet], but is analogue to ##. Can also use ```\[Bullet]\[Bullet]n```, analogous to ```##```. Enter \[Bullet] as ***<*\[Bullet]::\\\\[Bullet]*>*** or '''ALT+7'''."; 6 | AutoSlot::usage=\[Bullet]::usage; 7 | AutoSlotSequence::usage=\[Bullet]\[Bullet]::usage; 8 | `Private`ProcessingAutoSlot=False; 9 | 10 | 11 | Begin["`Private`"] 12 | 13 | 14 | ProcessingAutoSlot=True; 15 | slotMatcher=StringMatchQ["\[Bullet]"~~___]; 16 | ( 17 | #/:expr:_[___,#[___],___]/;!ProcessingAutoSlot:=Block[ 18 | {ProcessingAutoSlot=True}, 19 | Replace[AutoFunction[expr],{AutoSlot[i___]:>IAutoSlot[i],AutoSlotSequence[i___]:>IAutoSlotSequence[i]},{2}] 20 | ]; 21 | )&/@{AutoSlot,AutoSlotSequence}; 22 | MakeBoxes[(IAutoSlot|AutoSlot)[i_String|i:_Integer?NonNegative:1],fmt_]/;!ProcessingAutoSlot:=With[{sym=Symbol["\[Bullet]"<>ToString@i]},MakeBoxes[sym,fmt]] 23 | MakeBoxes[(IAutoSlotSequence|AutoSlotSequence)[i:_Integer?Positive:1],fmt_]/;!ProcessingAutoSlot:=With[{sym=Symbol["\[Bullet]\[Bullet]"<>ToString@i]},MakeBoxes[sym,fmt]] 24 | MakeBoxes[IAutoSlot[i___],fmt_]/;!ProcessingAutoSlot:=MakeBoxes[AutoSlot[i],fmt] 25 | MakeBoxes[IAutoSlotSequence[i___],fmt_]/;!ProcessingAutoSlot:=MakeBoxes[AutoSlotSequence[i],fmt] 26 | MakeBoxes[AutoFunction[func_],fmt_]:=MakeBoxes[func,fmt] 27 | MakeExpression[RowBox[{"?", t_String?slotMatcher}], fmt_?(!ProcessingAutoSlot&)(*make check here instead of ordinary condition as that one causes an error*)]:= 28 | MakeExpression[RowBox[{"?", If[StringMatchQ[t,"\[Bullet]\[Bullet]"~~___],"AutoSlotSequence","AutoSlot"]}], fmt] 29 | MakeExpression[arg_RowBox?(MemberQ[#,_String?slotMatcher,Infinity]&),fmt_?(!ProcessingAutoSlot&)(*make check here instead of ordinary condition as that one causes an error*)]:=Block[ 30 | {ProcessingAutoSlot=True}, 31 | MakeExpression[ 32 | arg/.a_String:>First[ 33 | StringCases[a,t:("\[Bullet]\[Bullet]"|"\[Bullet]")~~i___:>ToBoxes@If[t=="\[Bullet]",AutoSlot,AutoSlotSequence]@If[StringMatchQ[i,__?DigitQ],ToExpression@i,i/.""->1]], 34 | a 35 | ], 36 | fmt 37 | ] 38 | ] 39 | ProcessingAutoSlot=False; 40 | Attributes[AutoFunction]={HoldFirst}; 41 | funcData[AutoFunction]={{IAutoSlot[i__:1]:>i,IAutoSlotSequence[i__:1]:>i},{2},{AutoSlot,AutoSlotSequence}}; 42 | func:AutoFunction[_][___]:=ProcFunction[func] 43 | SyntaxInformation[AutoSlot]={"ArgumentsPattern"->{_.}}; 44 | SyntaxInformation[AutoSlotSequence]={"ArgumentsPattern"->{_.}}; 45 | 46 | 47 | End[] 48 | -------------------------------------------------------------------------------- /ForScience/Util/CachedImport.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | CachedImport::usage=FormatUsage@"CachedImport[file] imports the specified file using [*Import*], but caches the result to speed up future imports. See {*$ImportCache*} for clearing the cache. 4 | CachedImport[file,type,opt_1,\[Ellipsis]] imports as the specified file type. 5 | CachedImport[file,{type,elem},\[Ellipsis]] imports the specified element. 6 | CachedImport[file,{type,opt_1,\[Ellipsis]}] is equivalent to [*CachedImport[file,type,opt_1,\[Ellipsis]]*]. 7 | CachedImport[file,importer] uses the specified importer to import the file."; 8 | $ImportCache::usage=FormatUsage@"This symbol that handles the cache for [*CachedImport*]. Use '''Clear[$ImportCache]''' to clear all cached values. Cache entries are invalidated if the corresponding file is modified."; 9 | 10 | 11 | Begin["`Private`CachedImport`"] 12 | 13 | 14 | EnsureUnmodified[file_,path_,importer_,ts_,sz_]:=If[ 15 | Quiet[FileDate[path]===ts&&FileByteCount[path]===sz], 16 | Hold@ImportTask[$ImportCache,file,path,importer], 17 | Hold@ImportTask[Import,UpdateCache,file,path,importer] 18 | ] 19 | 20 | 21 | ImportTask[Import,wrapper_,file_,path_,importer_]:=With[ 22 | {result=importer@file}, 23 | Hold@wrapper[file,path,importer,result] 24 | ] 25 | 26 | 27 | ImportTask[wrapper_,file_,path_,importer_]:=Hold[ 28 | wrapper[file,path,importer] 29 | ] 30 | 31 | 32 | MakeBoxes[task:ImportTask[new:Import|PatternSequence[],_,file_,path_,importer_],fmt_]^:=BoxForm`ArrangeSummaryBox[ 33 | ImportTask, 34 | Unevaluated@task, 35 | BoxForm`GenericIcon[InputStream], 36 | { 37 | BoxForm`SummaryItem[{"Type: ",If[Length@{new}>0,"Import","Cache lookup"]}], 38 | BoxForm`SummaryItem[{"Filename: ",FileNameTake@file}] 39 | }, 40 | { 41 | BoxForm`SummaryItem[{"Path: ",path}], 42 | BoxForm`SummaryItem[{"Importer: ",importer}] 43 | }, 44 | fmt 45 | ] 46 | 47 | 48 | UpdateCache[file_,path_,importer_,result_]:=( 49 | Quiet[ 50 | CacheLookup[file,path,importer]=.; 51 | $ImportCache[file,path,importer]=.; 52 | ]; 53 | If[!FailureQ@result, 54 | With[ 55 | {ts=FileDate@path,sz=FileByteCount@path}, 56 | CacheLookup[file,path,importer]:=EnsureUnmodified[file,path,importer,ts,sz] 57 | ]; 58 | $ImportCache[file,path,importer]:=result 59 | ]; 60 | result 61 | ) 62 | 63 | 64 | clearing=False; 65 | SetupImportCache[]:=( 66 | Clear[$ImportCache]/;!clearing^:=Block[ 67 | {clearing=True}, 68 | Clear@CacheLookup; 69 | Clear@$ImportCache; 70 | SetupImportCache[]; 71 | ]; 72 | SetSharedFunction[$ImportCache]^:=SetSharedFunction[$ImportCache,CacheLookup]; 73 | UnsetShared[$ImportCache]^:=UnsetShared[$ImportCache,CacheLookup]; 74 | CacheLookup[file_,path_,importer_]:=Hold@ImportTask[Import,UpdateCache,file,path,importer] 75 | ) 76 | 77 | 78 | SetupImportCache[]; 79 | 80 | 81 | If[$KernelID==0, 82 | DistributeDefinitions[ImportTask]; 83 | ] 84 | 85 | 86 | Options[CachedImport]={"CacheImports"->True,"DeferImports"->False}; 87 | 88 | 89 | CachedImport[file_,{type_,o:OptionsPattern[]},oo:OptionsPattern[]]:= 90 | CachedImport[file,Import[#,type,o]&,oo] 91 | CachedImport[file_,type:(_List|_String),o:OptionsPattern[]]:=With[ 92 | {ciOpts=FilterRules[{o},Options[CachedImport]]}, 93 | CachedImport[file,Import[#,type,Complement[{o},ciOpts]]&,ciOpts] 94 | ] 95 | CachedImport[file_,importer:Except[_Rule]:Import,OptionsPattern[]]:=With[ 96 | {path=Quiet@AbsoluteFileName@file}, 97 | If[OptionValue["DeferImports"],#,ReleaseHold@ReleaseHold@#]&[ 98 | If[OptionValue["CacheImports"]&&path=!=$Failed, 99 | CacheLookup[file,path,importer], 100 | Hold@ImportTask[Import,#4&,file,path,importer] 101 | ] 102 | ] 103 | ] 104 | 105 | 106 | End[] 107 | -------------------------------------------------------------------------------- /ForScience/Util/CondDef.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | CondDef::usage=FormatUsage@"CondDef[cond][arg_1,\[Ellipsis]] is the conditional version of '''DefTo'''. Returns ```arg_1``` only if ```cond``` is not empty, otherwise returns an empty sequence." 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | CondDef[__][v_,___]:=v 10 | CondDef[][__]:=Sequence[] 11 | SyntaxInformation[CondDef]={"ArgumentsPattern"->{__}}; 12 | 13 | 14 | End[] 15 | -------------------------------------------------------------------------------- /ForScience/Util/ContextualRule.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | ContextualRule::usage=FormatUsage@"ContextualRule[lhs\[RuleDelayed]rhs,hPat] returns a rule where ```lhs``` is replaced by ```rhs``` only within an expression with head matching ```hPat```."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | ContextualRule[lhs_:>rhs_,hPat_]:=(h:hPat)[pre___,lhs,post___]:>h[pre,rhs,post] 10 | SyntaxInformation[ContextualRule]={"ArgumentsPattern"->{_,_}}; 11 | 12 | 13 | End[] 14 | -------------------------------------------------------------------------------- /ForScience/Util/DefTo.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | DefTo::usage=FormatUsage@"DefTo[arg_1,arg_2,\[Ellipsis]] returns ```arg_1```. Useful in complex patterns to assign defaults to empty matches."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | SyntaxInformation[DefTo]={"ArgumentsPattern"->{_,__}}; 10 | 11 | 12 | DefTo[v_,___]:=v 13 | DefTo[]:=Sequence[] 14 | 15 | 16 | End[] 17 | -------------------------------------------------------------------------------- /ForScience/Util/DelayedExport.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | DelayedExport::usage=FormatUsage@"DelayedExport[file,expr] creates a preview of what expr would look like if exported to the specified file. Exporting is actually done only once the button is pressed. Note: PDF importing has a bug that ignores clipping regions. If the preview has some overflowing lines, check the actual PDF. Note 2: Some formats can not be reimported. In those cases, the preview will be the original expression. Set '''PerformanceGoal''' to '''\"Speed\"''' to always show original expression."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | DelayedExport[file_,expr_,OptionsPattern[]]:=DynamicModule[ 10 | {curExpr=expr}, 11 | Column@{ 12 | Row@ 13 | { 14 | Button[ 15 | StringForm["Save to ``",file], 16 | Export[file,expr] 17 | ], 18 | Button[ 19 | "Refresh", 20 | curExpr=expr 21 | ] 22 | }, 23 | Dynamic[ 24 | If[OptionValue[PerformanceGoal]=="Quality"&& 25 | MemberQ[$ImportFormats,ToUpperCase@FileExtension@file], 26 | Check[ 27 | Quiet@Column@{"Preview:",ImportString[ExportString[curExpr,#],#]&@FileExtension@file}, 28 | # 29 | ], 30 | # 31 | ]&@Unevaluated@Column@{"Expression to export:",curExpr}, 32 | TrackedSymbols:>{curExpr} 33 | ] 34 | } 35 | ] 36 | SyntaxInformation[DelayedExport]={"ArgumentsPattern"->{_,_,OptionsPattern[]}}; 37 | Attributes[DelayedExport]={HoldRest}; 38 | Options[DelayedExport]={PerformanceGoal->"Quality"}; 39 | 40 | 41 | End[] 42 | -------------------------------------------------------------------------------- /ForScience/Util/DropMissing.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | DropMissing::usage=FormatUsage@"DropMissing[l,part] drops all elements where ```part``` has head '''Missing'''. 4 | '''DropMissing'''[```l```,'''Query'''[```q```]] drops all elements where the result of ```q``` has head '''Missing'''. 5 | DropMissing[spec] is the operator form."; 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | 11 | DropMissing[q_]:=Select[Not@*MissingQ@*(Query[q])] 12 | DropMissing[l_,q_]:=DropMissing[q]@l 13 | SyntaxInformation[DropMissing]={"ArgumentsPattern"->{_.,_}}; 14 | 15 | 16 | End[] 17 | -------------------------------------------------------------------------------- /ForScience/Util/FancyTrace.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | FancyTrace::usage=FormatUsage@"FancyTrace[expr] produces an interactive version of the Trace output."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | FancyTraceStyle[i_,o:OptionsPattern[FancyTrace]]:=Style[i,o,FontFamily->"Consolas",Bold] 10 | FancyTraceShort[i_,fac_,o:OptionsPattern[FancyTrace]]:=Tooltip[Short[i,fac OptionValue["ShortWidth"]],Panel@FancyTraceStyle[i,o],TooltipStyle->{CellFrame->None,Background->White}] 11 | FancyTraceArrowStyle[a_,o:OptionsPattern[FancyTrace]]:=Style[a,OptionValue["ArrowColor"],FontSize->OptionValue["ArrowScale"]OptionValue[FontSize]] 12 | FancyTracePanel[i_,o:OptionsPattern[FancyTrace]]:=Panel[i,Background->OptionValue["PanelBackground"],ContentPadding->False] 13 | FancyTraceColumn[l_,o:OptionsPattern[FancyTrace]]:=Column[ 14 | Riffle[ 15 | IFancyTrace[#,"PanelBackground"->Darker[OptionValue["PanelBackground"],OptionValue["DarkeningFactor"]],o]&/@l, 16 | If[OptionValue["DownArrows"],FancyTraceArrowStyle["\[DoubleDownArrow]",o],Nothing] 17 | ], 18 | Alignment->OptionValue["ColumnAlignment"] 19 | ] 20 | Options[FancyTrace]=Options[Style]~Join~{"ArrowColor"->Darker@Red,"ArrowScale"->1.5,"ShortWidth"->0.15,"TraceFilter"->Sequence[],"TraceOptions"->{},"DarkeningFactor"->0.1,"PanelBackground"->White,"DownArrows"->False,"ColumnAlignment"->Left}; 21 | FancyTrace[expr_,o:OptionsPattern[]]:=Framed@IFancyTrace[Trace[expr,Evaluate@OptionValue["TraceFilter"],Evaluate[Sequence@@OptionValue["TraceOptions"]]]/.s:(Slot|SlotSequence):>Defer[s],o] 22 | SetAttributes[FancyTrace,HoldFirst] 23 | SyntaxInformation[FancyTrace]={"ArgumentsPattern"->{_,OptionsPattern[]}}; 24 | 25 | IFancyTrace[l_List,o:OptionsPattern[FancyTrace]]:= 26 | DynamicModule[ 27 | {expanded=False}, 28 | EventHandler[ 29 | FancyTracePanel[ 30 | Dynamic@If[ 31 | expanded, 32 | FancyTraceColumn[l,o], 33 | FancyTraceStyle[Row@{ 34 | FancyTraceShort[First@l,1,o], 35 | If[ 36 | Length@l<3, 37 | FancyTraceArrowStyle[" \[DoubleRightArrow] ",o], 38 | Tooltip[FancyTraceArrowStyle[" \[DoubleRightArrow] \[CenterEllipsis] \[DoubleRightArrow] ",o],FancyTraceColumn[l[[2;;-2]],o],TooltipStyle->{CellFrame->None,Background->OptionValue["PanelBackground"]}] 39 | ], 40 | FancyTraceShort[Last@l,1,o] 41 | }, 42 | o 43 | ] 44 | ], 45 | o 46 | ], 47 | {"MouseClicked":>(expanded=!expanded)}, 48 | PassEventsUp->False 49 | ] 50 | ] 51 | IFancyTrace[i_,o:OptionsPattern[FancyTrace]]:=FancyTracePanel[FancyTraceStyle[FancyTraceShort[i,2,o],o],o] 52 | IFancyTrace[{},o:OptionsPattern[FancyTrace]]:=Panel[Background->OptionValue["PanelBackground"]] 53 | 54 | 55 | End[] 56 | -------------------------------------------------------------------------------- /ForScience/Util/FirstHead.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | FirstHead::usage=FormatUsage@"FirstHead[expr] extracts the first head of ```expr```, that is e.g. '''h''' in [*h[a]*] or [*h[a,b][c][d,e]*]."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | FirstHead[h_[___]]:=FirstHead[Unevaluated@h] 10 | FirstHead[h_]:=h 11 | SyntaxInformation[FirstHead]={"ArgumentsPattern"->{_}}; 12 | 13 | 14 | End[] 15 | -------------------------------------------------------------------------------- /ForScience/Util/FixedShort.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | FixedShort::usage=FormatUsage@"FixedShort[expr,w,pw] displays ```expr``` like [*Short[expr,w]*], but relative to the pagewidth ```pw```. ```w``` defaults to 1."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | (*adapted from https://mathematica.stackexchange.com/a/164228/36508*) 10 | FixedShort/:MakeBoxes[FixedShort[expr_,w_:1,pw_],StandardForm]:=With[ 11 | {oldWidth=Options[$Output,PageWidth]}, 12 | Internal`WithLocalSettings[ 13 | SetOptions[$Output,PageWidth->pw], 14 | MakeBoxes[Short[expr,w],StandardForm], 15 | SetOptions[$Output,oldWidth] 16 | ] 17 | ] 18 | SyntaxInformation[FixedShort]={"ArgumentsPattern"->{_,_.,_}}; 19 | 20 | 21 | End[] 22 | -------------------------------------------------------------------------------- /ForScience/Util/FunctionError.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | FunctionError;(*make error symbols public*) 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | FunctionError::missingArg="`` in `` cannot be filled from ``."; 10 | FunctionError::noAssoc="`` is expected to have an Association as the first argument."; 11 | FunctionError::missingKey="Named slot `` in `` cannot be filled from ``."; 12 | FunctionError::invalidSlot="`` (in ``) should contain a non-negative integer or string."; 13 | FunctionError::invalidSlotSeq="`` (in ``) should contain a positive integer."; 14 | FunctionError::slotArgCount="`` called with `` arguments; 0 or 1 expected."; 15 | 16 | 17 | End[] 18 | -------------------------------------------------------------------------------- /ForScience/Util/IndexedFunction.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | IndexedFunction::usage=FormatUsage@"IndexedFunction[expr,id] works like [*Function[expr]*], but only considers Slots/SlotSequences subscripted with ```id``` (e.g. '''#_1''' or '''##3_f'''. Can also be entered using a subscripted '''&''' (e.g. '''&_1''', this can be entered using \[AliasIndicator]cf\[AliasIndicator])."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | If[$FrontEnd=!=Null, 10 | Notation[ParsedBoxWrapper[SubscriptBox[RowBox[{"func_", "&"}], "id_"]] \[DoubleLongLeftRightArrow]ParsedBoxWrapper[RowBox[{"IndexedFunction", "[", RowBox[{"func_", ",", "id_"}], "]"}]]]; 11 | AddInputAlias["cf"->ParsedBoxWrapper[SubscriptBox["&", "\[Placeholder]"]]]; 12 | ] 13 | funcData[IndexedFunction,id_]:={{Subscript[Slot[i__:1], id]:>i,Subscript[SlotSequence[i__:1], id]:>i},{Subscript[#, id]&@*Slot,Subscript[#, id]&@*SlotSequence}}; 14 | func:IndexedFunction[_,_][___]:=ProcFunction[func] 15 | Attributes[IndexedFunction]={HoldFirst}; 16 | SyntaxInformation[IndexedFunction]={"ArgumentsPattern"->{_,_}}; 17 | 18 | 19 | End[] 20 | -------------------------------------------------------------------------------- /ForScience/Util/InvCondDef.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | InvCondDef::usage=FormatUsage@"InvCondDef[cond][arg_1,\[Ellipsis]] is the inverse of '''CondDef'''. Returns ```arg_1``` only if ```cond``` is empty, otherwise returns an empty sequence."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | InvCondDef[][v_,___]:=v 10 | InvCondDef[__][__]:=Sequence[] 11 | SyntaxInformation[InvCondDef]={"ArgumentsPattern"->{__}}; 12 | 13 | 14 | End[] 15 | -------------------------------------------------------------------------------- /ForScience/Util/KeyGroupBy.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | KeyGroupBy::usage=FormatUsage@"KeyGroupBy[expr,f] works like '''GroupBy''', but operates on keys. 4 | KeyGroupBy[f] is the operator form."; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | KeyGroupBy[f_][expr_]:=Association/@GroupBy[Normal@expr,f@*Keys] 11 | KeyGroupBy[expr_,f_]:=KeyGroupBy[f][expr] 12 | SyntaxInformation[KeyGroupBy]={"ArgumentsPattern"->{_,_.}}; 13 | 14 | 15 | End[] 16 | -------------------------------------------------------------------------------- /ForScience/Util/Let.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Let::usage=FormatUsage@"Let[{var_1=expr_1,\[Ellipsis]},expr] works exactly like '''With''', but allows variable definitions to refer to previous ones."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | (*From https://mathematica.stackexchange.com/a/10451/36508*) 10 | SetAttributes[Let,HoldAll]; 11 | SyntaxInformation[Let]={"ArgumentsPattern"->{_,_}(*,"LocalVariables"\[Rule]{"Solve",{1}}*)}; 12 | Let/:(assign:SetDelayed|RuleDelayed)[lhs_,rhs:HoldPattern[Let[{__},_]]]:=Block[ 13 | {With}, 14 | Attributes[With]={HoldAll}; 15 | assign[lhs,Evaluate[rhs]] 16 | ]; 17 | Let[{},expr_]:=expr; 18 | Let[{head_},expr_]:=With[{head},expr]; 19 | Let[{head_,tail__},expr_]:=Block[{With},Attributes[With]={HoldAll}; 20 | With[{head},Evaluate[Let[{tail},expr]]]]; 21 | 22 | 23 | End[] 24 | -------------------------------------------------------------------------------- /ForScience/Util/ListLookup.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Usage[ListLookup]="ListLookup[expr,pos,def] returns the part of ```expr``` at the position specified by ```pos``` if it exists, and ```def``` otherwise. 4 | ListLookup[expr,{pos_1,\[Ellipsis]},def] tries to extract a list of parts from ```expr```. 5 | ListLookup[expr,pos,def,h] wraps the parts with head ```h``` before evaluation. 6 | ListLookup[pos,def] is the operator form."; 7 | 8 | 9 | Begin["`Private`"] 10 | 11 | 12 | SyntaxInformation[AddKey]={"ArgumentsPattern"->{_,_,_.,_.}}; 13 | 14 | 15 | ListLookup[expr_,pos_,def_,h_:Identity]:=h@@Quiet[Check[Extract[expr,pos,HoldComplete],{def}]] 16 | ListLookup[expr_,pos:{___List},def_,h_:Identity]:=ListLookup[expr,#,def,h]&/@pos 17 | ListLookup[pos_,def_][expr_]:=ListLookup[expr,pos,def] 18 | 19 | 20 | End[] 21 | -------------------------------------------------------------------------------- /ForScience/Util/MergeRules.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | MergeRules::usage=FormatUsage@"MergeRules[rule_1,\[Ellipsis]] combines all rules into a single rule that matches anything any of the rules match and returns the corresponding replacement. Useful e.g. for '''Cases'''."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | MergeRules[rules:(Rule|RuleDelayed)[_,_]..]:=With[ 10 | {ruleList={rules}}, 11 | With[ 12 | {ruleNames=Unique["rule"]&/@ruleList}, 13 | With[ 14 | { 15 | wRules=Hold@@(List/@ruleNames), 16 | patterns=ruleList[[All,1]], 17 | replacements=Extract[ruleList,{All,2},Hold] 18 | }, 19 | Alternatives@@MapThread[Pattern,{ruleNames,patterns}]:> 20 | replacements[[1,First@FirstPosition[wRules,{__}]]] 21 | ] 22 | ] 23 | ] 24 | SyntaxInformation[MergeRules]={"ArgumentsPattern"->{__}}; 25 | 26 | 27 | End[] 28 | -------------------------------------------------------------------------------- /ForScience/Util/NaturallyOrdered.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | 4 | NaturallyOrdered::usage=FormatUsage@"NaturallyOrdered[str] is sorted according to natural string ordering by [*Sort*] and similar functions. 5 | NaturallyOrdered[expr] is sorted sorted using natural ordering for strings, and the default ordering for everything else."; 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | 11 | ToLinkedList[list_]:=Fold[{#2,#}&,{},Reverse@list] 12 | 13 | 14 | SyntaxInformation[NaturallyOrdered]={"ArgumentsPattern"->{_}}; 15 | 16 | 17 | Attributes[NaturallyOrdered]={HoldFirst}; 18 | 19 | 20 | NaturallyOrdered[expr_]:=With[ 21 | {evaluated=expr}, 22 | Hold[evaluated]/.s_String:>With[ 23 | { 24 | res=ToLinkedList@StringSplit[s,d:DigitCharacter..:>FromDigits@d] 25 | }, 26 | res/;True 27 | ]/. 28 | Hold[pExpr_]:> 29 | NaturallyOrdered[pExpr,evaluated] 30 | ] 31 | 32 | 33 | MakeBoxes[expr:NaturallyOrdered[_,orig_],fmt_]^:=With[ 34 | { 35 | boxes=t=MakeBoxes[NaturallyOrdered[orig],fmt] 36 | }, 37 | InterpretationBox[boxes,expr] 38 | ] 39 | 40 | 41 | Normal[NaturallyOrdered[_,str_]]^:=str 42 | Normal[expr_,NaturallyOrdered]^:=expr/.NaturallyOrdered[_,str_]:>str 43 | 44 | 45 | End[] 46 | -------------------------------------------------------------------------------- /ForScience/Util/PartLookup.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | PartLookup::usage=FormatUsage@"PartLookup[expr,pos,def] works like [*Lookup*], but on arbitrary expressions. ```pos``` can be a number or a [*Extract*] specification."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | SyntaxInformation[PartLookup]={"ArgumentsPattern"->{_,_,_}}; 10 | 11 | 12 | PartLookup[l_,pos_,def_]:=PartLookup[l,{pos},def] 13 | PartLookup[l_,pos_List,def_]:=ReleaseHold@Quiet@Check[ 14 | Extract[ 15 | l, 16 | pos, 17 | HoldComplete 18 | ], 19 | Hold@def 20 | ] 21 | 22 | 23 | End[] 24 | -------------------------------------------------------------------------------- /ForScience/Util/PrettyTime.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | PrettyTime::usage=FormatUsage@"PrettyTime[time] is a special for of '''PrettyUnit''' for the most common time units."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | $PrettyTimeUnits={"ms","s","min","h"}; 10 | PrettyTime[time_]:=PrettyUnit[time,$PrettyTimeUnits] 11 | SyntaxInformation[PrettyTime]={"ArgumentsPattern"->{_}}; 12 | 13 | 14 | End[] 15 | -------------------------------------------------------------------------------- /ForScience/Util/PrettyUnit.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | PrettyUnit::usage=FormatUsage@"PrettyUnit[qty,{unit_1,unit_2,\[Ellipsis]}] tries to convert ```qty``` to that unit that produces the \"nicest\" result."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | PrettyUnit[qty_,units_List]:=SelectFirst[#,QuantityMagnitude@#>1&,Last@#]&@Sort[UnitConvert[qty,#]&/@units] 10 | SyntaxInformation[PrettyUnit]={"ArgumentsPattern"->{_,_}}; 11 | 12 | 13 | End[] 14 | -------------------------------------------------------------------------------- /ForScience/Util/ProcFunction.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | funcData[__]=None; 7 | 8 | ProcFunction[(func:fType_[funcExpr_,fData___])[args___]]:=ProcFunction[funcExpr,{args},func,Sequence@@funcData[fType,fData]] 9 | ProcFunction[funcExpr_,args:{argSeq___},func_,{sltPat_:>sltIdx_,sltSeqPat_:>sltSeqIdx_},levelspec_:All,{sltHead_,sltSeqHead_}]:=With[ 10 | { 11 | hExpr=Hold@funcExpr, 12 | funcForm=HoldForm@func 13 | }, 14 | ReleaseHold[ 15 | Replace[ 16 | Replace[ 17 | hExpr, 18 | { 19 | s:sltPat:>With[ 20 | {arg=Which[ 21 | Length@{sltIdx}>1, 22 | Message[FunctionError::slotArgCount,sltHead,Length@{sltIdx}];s, 23 | StringQ@sltIdx, 24 | If[ 25 | AssociationQ@First@args, 26 | Lookup[First@args,sltIdx,Message[FunctionError::missingKey,sltIdx,func,First@args];s], 27 | Message[FunctionError::noAssoc,funcForm];s 28 | ], 29 | !IntegerQ@sltIdx||sltIdx<0, 30 | Message[FunctionError::invalidSlot,s,funcForm];s, 31 | sltIdx==0, 32 | func, 33 | sltIdx<=Length@args, 34 | args[[sltIdx]], 35 | True, 36 | Message[FunctionError::missingArg,s,funcForm, 37 | HoldForm[func@argSeq]];s 38 | ]}, 39 | arg/;True 40 | ], 41 | s:sltSeqPat:>With[ 42 | {arg=Which[ 43 | Length@{sltSeqIdx}>1, 44 | Message[FunctionError::slotArgCount,sltSeqHead,Length@{sltSeqIdx}];s, 45 | !IntegerQ@sltSeqIdx||sltSeqIdx<=0, 46 | Message[FunctionError::invalidSlotSeq,s,funcForm];s, 47 | sltIdx<=Length@args+1, 48 | pfArgSeq@@args[[sltIdx;;]], 49 | True, 50 | Message[FunctionError::missingArg,s,funcForm,HoldForm[func@argSeq]];s 51 | ]}, 52 | arg/;True 53 | ] 54 | }, 55 | levelspec, 56 | Heads->True 57 | ]//. 58 | h_[pre___,pfArgSeq[seq___],post___]:>h[pre,seq,post], 59 | Hold[]->Hold@Sequence[] 60 | ] 61 | ] 62 | ]; 63 | Attributes[ProcFunction]={HoldFirst}; 64 | 65 | 66 | End[] 67 | -------------------------------------------------------------------------------- /ForScience/Util/PublishRelease.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | PublishRelease::usage=FormatUsage@"PublishRelease[opts] creates a new GitHub release for a paclet file in the current folder. Requires access token with "<>"public_repo access."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | PublishRelease::remoteNewer="The online version of the source code (`2`) is newer than the version of the local paclet file (`1`). Ensure that the latest version is built."; 10 | PublishRelease::localNewer="The version of the local paclet file (`1`) is newer than the online version of the source code (`2`). Ensure that the latest changes have been pushed."; 11 | PublishRelease::checkFailed="Could not connect to GitHub."; 12 | PublishRelease::createFailed="Could not create new release draft. Failed with message: ``"; 13 | PublishRelease::uploadFailed="Could not upload paclet file. Failed with message: ``"; 14 | PublishRelease::publishFailed="Could not publish release. Failed with message: ``"; 15 | PublishRelease[OptionsPattern[]]:=Let[ 16 | { 17 | repo=OptionValue@"Repository", 18 | branch=OptionValue@"Branch", 19 | pacletFile=First@FileNames@"*.paclet", 20 | packageName=OptionValue@"PackageName"/.Automatic->First@StringCases[pacletFile,RegularExpression["(.*)-[^-]*"]->"$1"], 21 | localVerStr=StringTake[pacletFile,{12,-8}], 22 | localVer=ToExpression/@StringSplit[localVerStr,"."], 23 | remoteVerStr=Lookup[ 24 | Association@@Import[ 25 | SPrintF["https://raw.githubusercontent.com/``/``/``/PacletInfo.m",repo,branch,packageName] 26 | ], 27 | Key@Version, 28 | Message[PublishRelease::checkFailed];Abort[]; 29 | ], 30 | remoteVer=ToExpression/@StringSplit[remoteVerStr,"."], 31 | headers="Headers"->{"Authorization"->"token "<>OptionValue@"Token"} 32 | }, 33 | Switch[Order[localVer,remoteVer], 34 | 1,Message[PublishRelease::remoteNewer,localVerStr,remoteVerStr], 35 | -1,Message[PublishRelease::localNewer,localVerStr,remoteVerStr], 36 | 0,DynamicModule[ 37 | {prerelease=True}, 38 | Row@{ 39 | Button[SPrintF["Publish `` version `` on GitHub",packageName,localVerStr], 40 | Module[ 41 | {createResponse,uploadUrl,uploadFileRespone,publishResponse}, 42 | Echo["Creating release draft..."]; 43 | createResponse=Import[ 44 | HTTPRequest[ 45 | SPrintF["https://api.github.com/repos/``/releases",repo], 46 | <| 47 | "Body"->ExportString[<| 48 | "tag_name"->"v"<>localVerStr, 49 | "target_commitish"->branch, 50 | "name"->"Version "<>localVerStr, 51 | "draft"->True, 52 | "prerelease"->prerelease 53 | |>,"RawJSON"], 54 | headers 55 | |> 56 | ], 57 | "RawJSON" 58 | ]; 59 | uploadUrl=StringReplace["{"~~__~~"}"->"?name="<>pacletFile]@Lookup[createResponse,"upload_url",Message[PublishRelease::createFailed,createResponse@"message"];Abort[]]; 60 | Echo["Uploading paclet file..."]; 61 | uploadFileRespone=Import[ 62 | HTTPRequest[ 63 | uploadUrl, 64 | <| 65 | "Body"->ByteArray@BinaryReadList[pacletFile,"Byte"], 66 | headers, 67 | "ContentType"->"application/zip", 68 | Method->"POST" 69 | |> 70 | ], 71 | "RawJSON" 72 | ]; 73 | Lookup[uploadFileRespone,"url",Message[PublishRelease::uploadFailed,uploadFileRespone@"message"]Abort[];]; 74 | Echo["Publishing release..."]; 75 | publishResponse=Import[ 76 | HTTPRequest[ 77 | createResponse@"url", 78 | <| 79 | "Body"->ExportString[<|"draft"->False|>,"RawJSON"], 80 | headers 81 | |> 82 | ], 83 | "RawJSON" 84 | ]; 85 | Lookup[publishResponse,"url",Message[PublishRelease::publishFailed,publishResponse@"message"]Abort[];]; 86 | Echo["Done."]; 87 | ], 88 | Method->"Queued" 89 | ], 90 | " Prerelease: ", 91 | Checkbox@Dynamic@prerelease 92 | } 93 | ] 94 | ] 95 | ] 96 | Options[PublishRelease]={"Token"->None,"Branch"->"master","Repository"->"MMA-ForScience/ForScience","PackageName"->Automatic}; 97 | SyntaxInformation[PublishRelease]={"ArgumentsPattern"->{OptionsPattern[]}}; 98 | 99 | 100 | End[] 101 | -------------------------------------------------------------------------------- /ForScience/Util/PullUp.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | PullUp::usage=FormatUsage@"PullUp[data,keys,datakey] groups elements of ```data``` by the specified ```keys``` and puts them under ```datakey``` into an association containing those keys. 4 | PullUp[data,gKeys\[Rule]keys,datakey] groups elements of ```data``` by the specified ```gKeys``` and puts them under ```datakey``` into an association containing ```keys``` (values taken from first element). 5 | PullUp[keys,datakey] is the operator form, ```datakey``` is defaulted to '''\"data\"'''. 6 | PullUp[gKeys\[Rule]keys,datakey] is the operator form, ```datakey``` is defaulted to '''\"data\"'''."; 7 | 8 | 9 | Begin["`Private`"] 10 | 11 | 12 | PullUp[gKeys_->keys_,datakey_:"data"]:=GroupBy[Query[gKeys]]/*Values/*Map[Append[Query[Flatten@{keys}]@First@#,datakey->#]&] 13 | PullUp[keys_,datakey_:"data"]:=PullUp[keys->keys,datakey] 14 | PullUp[data_,gKeys_->keys_,datakey_]:=PullUp[gKeys->keys,datakey]@data 15 | PullUp[data_,keys_,datakey_]:=PullUp[data,keys->keys,datakey] 16 | SyntaxInformation[PullUp]={"ArgumentsPattern"->{_,_.,_.}}; 17 | 18 | 19 | End[] 20 | -------------------------------------------------------------------------------- /ForScience/Util/SPrintF.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | SPrintF::usage=FormatUsage@"SPrintF[spec,arg_1,\[Ellipsis]] is equivalent to '''ToString@StringForm[```spec```,```arg_1```,\[Ellipsis]]'''."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | SPrintF[spec__]:=ToString@StringForm@spec 10 | SyntaxInformation[SPrintF]={"ArgumentsPattern"->{__}}; 11 | 12 | 13 | End[] 14 | -------------------------------------------------------------------------------- /ForScience/Util/SimpleTOC.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | SimpleTOC[patterns_,styles_]:=Module[ 4 | {string,numbPatterns,numbStyles,shiftString,tocNumb,cellData,stylesInt}, 5 | string={}; 6 | numbPatterns=Length[patterns]; 7 | tocNumb=0; 8 | If[Length[styles]==0,stylesInt={{Black,Larger,Bold},Black},stylesInt=styles]; 9 | numbStyles=Length[stylesInt]; 10 | ( 11 | ++tocNumb; 12 | shiftString=""; 13 | cellData=NotebookRead[#]; 14 | SetOptions[#,CellTags->"TOC:"<>ToString[tocNumb]]; 15 | For[case=1,case<=numbPatterns,case++, 16 | shiftString=shiftString<>"\t"; 17 | If[cellData[[2]]==patterns[[case]], 18 | AppendTo[string,Hyperlink[shiftString<>cellData[[1]],{EvaluationNotebook[],"TOC:"<>ToString[tocNumb]},BaseStyle->If[case>numbStyles,stylesInt[[-1]],stylesInt[[case]]]]] 19 | ] 20 | ] 21 | )&/@Cells[CellStyle->patterns]; 22 | Column@string 23 | ]; 24 | -------------------------------------------------------------------------------- /ForScience/Util/SkipCommentsStream.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | Begin["`Private`"] 4 | 5 | 6 | Quiet@RemoveInputStreamMethod["SkipComments"]; 7 | DefineInputStreamMethod["SkipComments", 8 | { 9 | "ConstructorFunction"->Function[ 10 | {streamname,caller,opts}, 11 | With[ 12 | {state=Unique["SkipCommentStream"]}, 13 | state["stream"]=OpenRead[streamname]; 14 | If[FailureQ[state["stream"]],14 15 | {False,$Failed}, 16 | state["commentMarker"]="commentMarker"/.Join[opts,{"commentMarker"->"#"}]; 17 | state["eof"]=False; 18 | state["streamEof"]=False; 19 | state["buf"]={}; 20 | state["bufPos"]=0; 21 | state["beginningOfLine"]=True; 22 | {True,state} 23 | ] 24 | ] 25 | ], 26 | "CloseFunction"->Function[state,Close[state["stream"]];ClearAll[state]], 27 | "EndOfFileQFunction"->Function[state,{state["eof"],state}], 28 | "ReadFunction"->Function[ 29 | {state,nBytes}, 30 | { 31 | If[state["eof"], 32 | {}, 33 | Module[ 34 | {read=0,ret=Table[0,nBytes],bPos=state["bufPos"],buf=state["buf"]}, 35 | While[read=Length@buf, 37 | With[ 38 | {line=Read[state["stream"],"Record"]}, 39 | If[line===EndOfFile, 40 | state["eof"]=True; 41 | Break[] 42 | ]; 43 | If[!StringStartsQ[line,state["commentMarker"]], 44 | buf=Append[13]@ToCharacterCode[line]; 45 | bPos=0; 46 | ] 47 | ], 48 | With[ 49 | {new=Min[nBytes-read,Length@buf-bPos]}, 50 | ret[[read+1;;read+new]]=buf[[bPos+1;;bPos+new]]; 51 | read+=new; 52 | bPos+=new; 53 | ] 54 | ] 55 | ]; 56 | state["bufPos"]=bPos; 57 | state["buf"]=buf; 58 | ret[[;;read]] 59 | ] 60 | ], 61 | state 62 | } 63 | ] 64 | } 65 | ]; 66 | 67 | 68 | End[] 69 | -------------------------------------------------------------------------------- /ForScience/Util/SkipMissing.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | SkipMissing::usage=FormatUsage@"SkipMissing[f] behaves as identity for arguments with head missing, otherwise behaves as ```f```. 4 | SkipMissing[keys,f] checks its argument for the keys specified. If any one is missing, returns '''Missing[]''', otherwise ```f``` is applied."; 5 | 6 | 7 | Begin["`Private`"] 8 | 9 | 10 | SkipMissing[f_][arg_]:=If[MissingQ@arg,arg,f@arg] 11 | SkipMissing[keys_List,f_][arg_]:=If[Or@@(MissingQ@Lookup[arg,#]&/@keys),Missing[],f@arg] 12 | SkipMissing[key_,f_][arg_]:=SkipMissing[{key},f][arg] 13 | SyntaxInformation[SkipMissing]={"ArgumentsPattern"->{_.,_}}; 14 | 15 | 16 | End[] 17 | -------------------------------------------------------------------------------- /ForScience/Util/TableToTexForm.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | TableToTexForm::usage=FormatUsage@"TableToTexForm[data] returns the LaTeX representation of a list or a dataset."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | TableToTexFormCore[TableToTexForm,data_,OptionsPattern[]]:=Module[ 10 | {out,normData,newData,asso1,asso2}, 11 | out=""; 12 | normData=Normal[data]; 13 | asso1=AssociationQ[normData]; 14 | asso2=AssociationQ[normData[[1]]]; 15 | 16 | If[OptionValue["vline"]=="all", 17 | If[asso1, 18 | (out=out<>"\\begin{tabular}{ | "<>OptionValue["position"]<>" | "; 19 | Do[out=out<>OptionValue["position"]<>" | ",Length[normData[[1]]]];), 20 | (out=out<>"\\begin{tabular}{ | "; 21 | Do[out=out<>OptionValue["position"]<>" | ",Length[normData[[1]]]];) 22 | ], 23 | If[asso1, 24 | (out=out<>"\\begin{tabular}{ | "<>OptionValue["position"]<>" | "; 25 | Do[out=out<>OptionValue["position"]<>" ",Length[normData[[1]]]];), 26 | (out=out<>"\\begin{tabular}{ | "; 27 | Do[out=out<>OptionValue["position"]<>" ",Length[normData[[1]]]];) 28 | ]; 29 | out=out<>"|"; 30 | ]; 31 | out=out<>"}\\hline\n"; 32 | 33 | If[asso2, 34 | For[j=1,j<=Length[normData[[1]]],j++, 35 | If[j==1 , 36 | out=If[asso1, 37 | out<>"& "<>ToString[Keys[normData[[1]]][[1]]], 38 | out<>ToString[Keys[normData[[1]]][[1]]]], 39 | out=out<>" & "<>ToString[Keys[normData[[1]]][[j]]] 40 | ]; 41 | ]; 42 | out=out<>"\\\\ \\hline \n"; 43 | ]; 44 | 45 | For[i=1,i<=Length[normData],i++, 46 | For[j=If[asso1,0,1,1],j<=Length[normData[[1]]],j++, 47 | If[j==0,out=out<>ToString[Keys[normData][[i]]], 48 | If[j==1 &&!asso1, 49 | out=out<>ToString[normData[[i,1]]], 50 | out=out<>" & "<>ToString[normData[[i,j]]] 51 | ]; 52 | ]; 53 | ]; 54 | If[(OptionValue["hline"]=="all"), 55 | out=out<>" \\\\ \\hline\n", 56 | out=out<>"\\\\ \n" 57 | ]; 58 | ]; 59 | 60 | If[OptionValue["hline"]=="auto", 61 | out=out<> "\\hline \n"]; 62 | out=out<>"\\end{tabular}" 63 | ] 64 | 65 | 66 | TableToTexForm[data_,o:OptionsPattern[]]:=TableToTexFormCore[TableToTexForm,data,o]; 67 | Options[TableToTexForm]={"position"->"c","hline"->"auto","vline"->"auto"}; 68 | Options[TableToTexFormCore]=Options[TableToTexForm]; 69 | SyntaxInformation[TableToTexForm]={"ArgumentsPattern"->{_,OptionsPattern[]}}; 70 | 71 | 72 | End[] 73 | -------------------------------------------------------------------------------- /ForScience/Util/ToAssociationRule.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | ToAssociationRule::usage=FormatUsage@"ToAssociationRule[expr] creates a replacement rule of the form '''```expr```\[RuleDelayed]\[LeftAssociation]\[Ellipsis]\[RightAssociation]''', where each named part of the pattern is assigned to a key in the association."; 4 | 5 | 6 | Begin["`Private`ToAssociationRule`"] 7 | 8 | 9 | interpreter; 10 | stringPattern; 11 | assoc; 12 | Attributes[interpreter]={HoldFirst}; 13 | 14 | ToAssociationRule[expr_,OptionsPattern[]]:= 15 | (expr:>Evaluate[assoc@@Cases[ 16 | expr//.StringExpression[pre___,Verbatim[Pattern][p_,t_],post___]:>StringExpression[pre,Pattern[p,t,stringPattern],post], 17 | Verbatim[Pattern][p_,_,t_:Pattern]:>SymbolName@Unevaluated@p->If[t===stringPattern,interpreter@p,p], 18 | {0,\[Infinity]}, 19 | Heads->True 20 | ]])/.{interpreter->OptionValue[Interpreter],assoc->Association} 21 | 22 | Options[ToAssociationRule]={Interpreter->Interpreter["Number"|"String"]}; 23 | SyntaxInformation[ToAssociationRule]={"ArgumentsPattern"->{_,OptionsPattern[]}}; 24 | 25 | 26 | End[] 27 | -------------------------------------------------------------------------------- /ForScience/Util/ToFunction.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | ToFunction::usage=FormatUsage@"ToFunction[expr] attempts to convert any function constructs inside '''expr''' to pure Functions. Can't convert functions containing SlotSequence. For functions using only indexed Slots, the returned pure function is fully equivalent. If named slots are used, the handling of missing keys/associations is altered."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | ToFunction::slotSeq="Cannot convert function ``, as it contains a SlotSequence (``)."; 10 | ToFunction[expr_]:= 11 | expr//.func:fType_[funcExpr_,fData___]:> 12 | Let[ 13 | { 14 | hFunc=Hold@funcExpr, 15 | res=FirstCase[funcData[fType,fData],{{sltPat_:>sltIdx_,sltSeqPat_:>_}, levelspec_:\[Infinity],_}:>With[ 16 | { 17 | newFunc=If[ 18 | FreeQ[hFunc,sltSeqPat,levelspec], 19 | Let[ 20 | { 21 | maxSlt=Max[Max@Cases[hFunc,sltPat:>If[IntegerQ@sltIdx,sltIdx,1],levelspec],0], 22 | vars=Table[Unique@"fArg",maxSlt], 23 | pFunc=hFunc/.sltPat:>With[{slot=If[IntegerQ@sltIdx,vars[[sltIdx]],vars[[1]][sltIdx]]},slot/;True] 24 | }, 25 | Function@@Prepend[pFunc,vars] 26 | ], 27 | Message[Unevaluated@ToFunction::slotSeq,HoldForm@func,FirstCase[hFunc,sltSeqPat,"##",levelspec]];$Failed 28 | ] 29 | }, 30 | newFunc/;True 31 | ], 32 | $Failed, 33 | {0} 34 | ] 35 | }, 36 | res/;res=!=$Failed 37 | ] 38 | Attributes[ToFunction]={HoldFirst}; 39 | Attributes[funcData]={HoldAll}; 40 | SyntaxInformation[ToFunction]={"ArgumentsPattern"->{_}}; 41 | 42 | 43 | End[] 44 | -------------------------------------------------------------------------------- /ForScience/Util/UpdateForScience.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | UpdateForScience::usage=FormatUsage@"UpdateForScience[] checks whether a newer version of the ForScience package is available. If one is found, it can be downloaded by pressing a button. Use the option '''\"IncludePreReleases\"''' to control whether pre-releases should be ignored."; 4 | 5 | 6 | Begin["`Private`"] 7 | 8 | 9 | UpdateForScience[OptionsPattern[]]:=Let[ 10 | { 11 | latest=First[ 12 | MaximalBy[DateObject@#["published_at"]&]@ 13 | If[OptionValue["IncludePreReleases"],Identity,Select[!#prerelease&]] 14 | [ 15 | Association@@@Import["https://api.github.com/repos/MMA-ForScience/ForScience/releases","JSON"] 16 | ], 17 | <|"tag_name"->"v0.0.0","name"->"","assets"->{}|> 18 | ], 19 | file=SelectFirst[Association@@@#assets,StringMatchQ[#name,__~~".paclet"]&,None]&@latest, 20 | preRel=latest["prerelease"], 21 | version=StringDrop[latest["tag_name"],1], 22 | curVer=First[PacletFind["ForScience"],<|"Version"->"0.0.0"|>]["Version"] 23 | }, 24 | If[ 25 | Order@@(FromDigits/@StringSplit[#,"."]&/@{curVer,version})>0, 26 | Row@{ 27 | SPrintF["Found version ````, current version is ``.",version,If[preRel," (pre-release)",""],curVer], 28 | Button[ 29 | "Download & Install", 30 | UnloadPacletDocumentation["ForScience"]; 31 | PacletUninstall/@PacletFind["ForScience"]; 32 | PacletInstall[URLDownload[#["browser_download_url"],FileNameJoin@{$TemporaryDirectory,#name}]]&@file; 33 | LoadPacletDocumentation["ForScience"]; 34 | Print@If[ 35 | PacletFind["ForScience"][[1]]["Version"]===version, 36 | "Successfully updated", 37 | "Something went wrong, check manually." 38 | ];, 39 | Method->"Queued" 40 | ] 41 | }, 42 | "No newer version available" 43 | ] 44 | ] 45 | Options[UpdateForScience]={"IncludePreReleases"->True}; 46 | SyntaxInformation[UpdateForScience]={"ArgumentsPattern"->{OptionsPattern[]}}; 47 | 48 | 49 | End[] 50 | -------------------------------------------------------------------------------- /ForScience/Util/WindowedMap.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | WindowedMap::usage=FormatUsage@"WindowedMap[func,data,width] calls ```func``` with ```width``` wide windows of ```data```, padding with the elements specified by the '''Padding''' option (0 by default, use '''None''' to disable padding and return a smaller array) and returns the resulting list. 4 | WindowedMap[func,data,{width_1,\[Ellipsis]}] calls ```func``` with ```width_1```,```\[Ellipsis]``` wide windows of arbitrary dimension. 5 | WindowedMap[func,wspec] is the operator form."; 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | 11 | WindowedMap[f_,d_,w_Integer,o:OptionsPattern[]]:=WindowedMap[f,d,{w},o] 12 | WindowedMap[f_,w_Integer,o:OptionsPattern[]][d_]:=WindowedMap[f,d,w,o] 13 | WindowedMap[f_,d_,w:{__Integer}|_Integer,OptionsPattern[]]:= 14 | With[ 15 | {ws=If[Head@w===List,w,{w}]}, 16 | Map[ 17 | f, 18 | Partition[ 19 | If[ 20 | OptionValue@Padding===None, 21 | d, 22 | ArrayPad[d,Transpose@Floor@{ws/2,(ws-1)/2},Nest[List,OptionValue@Padding,Length@ws]] 23 | ], 24 | ws, 25 | Table[1,Length@ws] 26 | ], 27 | {Length@ws} 28 | ] 29 | ] 30 | WindowedMap[f_,w:{__Integer}|_Integer,o:OptionsPattern[]][d_]:=WindowedMap[f,d,w,o] 31 | Options[WindowedMap]={Padding->0}; 32 | SyntaxInformation[WindowedMap]={"ArgumentsPattern"->{_,_,_.,OptionsPattern[]}}; 33 | 34 | 35 | End[] 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 lukas-lang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ForScience 2 | Mathematica paclet that includes many utility functions for general work & paclet development and helps with creating scientific plots. 3 | 4 | ## Motivation 5 | This paclet started as a collection of useful functions and plot designs which we encountered in our daily life as students. We've polished them and made them accessible for everyone in this paclet. The paclet has since grown considerably in several areas. 6 | 7 | ## Content 8 | For a full list of functions, see the paclet documentation in the documentation center. It features detailed descriptions and examples of every function. 9 | > The documentation is an ongoing effort. Currently, only the PacletUtils subpaclet is fully documented, with more to come 10 | The paclet is split into four sub-paclets: 11 | ### Util 12 | General utility functions, including: 13 | - `ImportDataset` - imports files or complex file structures into one Dataset 14 | ![ImportDataset](https://i.imgur.com/rf3JmDD.png) 15 | - `ProgressReport` - progress report with automatic range detection and estimation of remaining time 16 | ![ProgressReport](https://i.imgur.com/VgilQA6.png) 17 | - `IndexedFunction` - indexed `#` and `&` 18 | ![IndexedFunction](https://i.imgur.com/UGhljqm.png) 19 | - `TableToTex` 20 | - Many more 21 | ### PlotUtils 22 | Everything related to creating nice looking plots. Polar plots are significantly more usable with this: Several bugs are fixed, including conflicts of `PlotMarkers->Automatic` and `Joined->True`, inconsistent sizes and more. The plot theme further removes the outermost tick label to prevent it from colliding with the angular axis. 23 | - `"ForScience"` - plot theme with the goal to make more publication ready figures (work in progress) 24 | ![ForScience](https://i.imgur.com/UueGjRj.png) 25 | - `Jet` - color scheme 26 | ![Jet](https://i.imgur.com/0aW6ojw.png) 27 | ### ChemUtils 28 | - `"GROMOS"` - import format for files generated by the Molecular Dynamic program GROMOS 29 | - `MoleculePlot3D` - plots custom molecules, similar to the ones provided by `ChemicalData` 30 | ![MoleculePlot3D](https://i.imgur.com/bhV3Qx7.png) 31 | - Many more 32 | ### PacletUtils 33 | Everything related to paclet development. Contains functions to automate build processes. Also contains the documentation building framework, that support creation of documentation pages. All documentation pages from the ForScience paclet are created using this framework. 34 | - `FormatUsage` - creates nicely formatted usage string 35 | ![FormatUsage](https://i.imgur.com/zGeJx9c.png) 36 | - `BuildPaclet` - paclet build manager 37 | - `DocumentationBuilder` - builds high-quality documentation pages, just like the ones for built-in functions 38 | ![DocumentationBuilder](https://i.imgur.com/Y38jsi4.png) 39 | - Many more 40 | 41 | ## Installation 42 | ### Latest release 43 | - Download the latest .paclet file from [releases](https://github.com/MMA-ForScience/ForScience/releases) 44 | - Execute `PacletInstall["path/to/paclet/ForScience-#.#.#.paclet"]` (with the appropriate path and version number) 45 | - Package can now be loaded with ``<True], 11 | 12 | Cell[CellGroupData[{ 13 | 14 | Cell["Build", "Subsubsection"], 15 | 16 | Cell[BoxData[RowBox[{RowBox[{"DeleteFile", "/@", RowBox[{"FileNames", "[", "\"*.paclet\"", "]"}]}], ";"}]], "Input"], 17 | 18 | Cell[BoxData[RowBox[{"<<", "ForScience`"}]], "Input"], 19 | 20 | Cell[BoxData[ 21 | RowBox[{"paclet", "=", 22 | RowBox[{ 23 | "BuildPaclet", "[", "\[IndentingNewLine]", 24 | RowBox[{ 25 | "\"ForScience\"", ",", "\[IndentingNewLine]", 26 | RowBox[{"{", "UsageCompiler", ",", "CleanBuildActions", ",", "BuildTimeEvaluator", "}"}], ",", "\[IndentingNewLine]", 27 | RowBox[{"{", 28 | InterpretationBox[ 29 | TemplateBox[ 30 | { 31 | " ", 32 | "\" \"", 33 | CheckboxBox[Dynamic@CurrentValue[EvaluationNotebook[], {TaggingRules, "Settings", "BuildDoc"}], BaselinePosition->Baseline], 34 | TogglerBox[ 35 | Dynamic@CurrentValue[EvaluationNotebook[], {TaggingRules, "Settings", "BuildDoc"}], 36 | { 37 | False->StyleBox[DocumentationBuilder,Opacity@0.5], 38 | True->DocumentationBuilder 39 | } 40 | ] 41 | }, 42 | "RowWithSeparators" 43 | ], 44 | If[CurrentValue[EvaluationNotebook[], {TaggingRules, "Settings", "BuildDoc"}], DocumentationBuilder, Nothing] 45 | ], 46 | "}"}] 47 | }], "\[IndentingNewLine]", 48 | "]"}] 49 | }] 50 | ], "Input"] 51 | 52 | }, Open ]], 53 | 54 | Cell[CellGroupData[{ 55 | 56 | Cell["Install", "Subsubsection"], 57 | 58 | Cell[BoxData[RowBox[{"UnloadPacletDocumentation", "[", "\"ForScience\"", "]"}]], "Input"], 59 | 60 | Cell[BoxData[RowBox[{RowBox[{"PacletUninstall", "/@", RowBox[{"PacletFind", "[", "\"ForScience\"", "]"}]}], ";"}]], "Input"], 61 | 62 | Cell[BoxData[RowBox[{"paclet", "=", "Last", "@", RowBox[{"Sort", "@", RowBox[{"FileNames", "[", "\"*.paclet\"", "]"}]}]}]], "Input"], 63 | 64 | Cell[BoxData[RowBox[{"PacletInstall", "[", "paclet", "]"}]], "Input"], 65 | 66 | Cell[BoxData[RowBox[{"LoadPacletDocumentation", "[", "\"ForScience\"", "]"}]], "Input"] 67 | 68 | }, Open ]], 69 | 70 | Cell[CellGroupData[{ 71 | 72 | Cell["Publish", "Subsubsection"], 73 | 74 | Cell[BoxData[RowBox[{"PublishRelease", "[", RowBox[{"\"Token\"", "\[Rule]", RowBox[{"Import", "[", "\"token\"", "]"}]}], "]"}]], "Input"] 75 | 76 | }, Open ]] 77 | }, 78 | FrontEndVersion->"11.1 for Microsoft Windows (64-bit) (April 18, 2017)", 79 | StyleDefinitions->"Default.nb", 80 | Saveable->False, 81 | TaggingRules->{"Settings"->{"BuildDoc"->False}} 82 | ] 83 | --------------------------------------------------------------------------------