├── .gitattributes ├── .gitignore ├── Documentation ├── FAQ.m └── Home.m ├── M2MD.iml ├── M2MD.nb ├── M2MD ├── Kernel │ └── init.m ├── M2MD.m └── PacletInfo.m ├── README.md ├── Tests └── Basic.m └── build.m /.gitattributes: -------------------------------------------------------------------------------- 1 | *.m linguist-language=Mathematica 2 | *.wlt linguist-language=Mathematica 3 | *.wls linguist-language=Mathematica 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | *.paclet 3 | */FrontEnd/*/*.nb 4 | 5 | -------------------------------------------------------------------------------- /Documentation/FAQ.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | 4 | -------------------------------------------------------------------------------- /Documentation/Home.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | (* ::Title:: *) 4 | (*M2MD Wiki*) 5 | 6 | 7 | (* ::Text:: *) 8 | (*Welcome to M2MD Wiki / Documentation*) 9 | 10 | 11 | (* ::Text:: *) 12 | (*I am not planning to make a proper Mathematica Documentation for this paclet, this should not come as a surprise to anyone who ever tried to do it.*) 13 | 14 | 15 | (* ::Text:: *) 16 | (*I am aware this documentation is minimal, I will try to improve it in future. *) 17 | 18 | 19 | (* ::Text:: *) 20 | (*The paclet comes with a palette. It is outdated and the only available button there will convert a selected notebook to a MD string. The updates are coming.*) 21 | 22 | 23 | (* ::Title:: *) 24 | (*Quick documentation*) 25 | 26 | 27 | (* ::Text:: *) 28 | (*There are two main functions in M2MD`: *) 29 | 30 | 31 | (* ::Text:: *) 32 | (*- `MDExport` which exports to a file and *) 33 | 34 | 35 | (* ::Text:: *) 36 | (*- `M2MD` which returns a Markdown string.*) 37 | 38 | 39 | (* ::Text:: *) 40 | (*They share the same options.*) 41 | 42 | 43 | (* ::Section:: *) 44 | (*MDExport*) 45 | 46 | 47 | (* ::Subsection:: *) 48 | (*Usage*) 49 | 50 | 51 | MDExport[ fileName_String, source_, OptionsPattern[] ] 52 | 53 | 54 | (* ::Text:: *) 55 | (*source can be a `NotebookObject`, `CellObject` or a `Cell`*) 56 | 57 | 58 | (* ::Subsection:: *) 59 | (*Options*) 60 | 61 | 62 | (* ::Subsubsection:: *) 63 | (*`"IgnoredStyles"` - cells of which styles should be ignored *) 64 | 65 | 66 | "IgnoredStyles" -> None | {__String} 67 | 68 | 69 | M2MD[EvaluationNotebook[], "IgnoredStyles" -> {"Code", "Input", "Output"}] 70 | 71 | 72 | (* ::Subsubsection:: *) 73 | (*`"MDElementTemplates"`*) 74 | 75 | 76 | (* ::Text:: *) 77 | (*The converter first creates a symbolic representation of a markdown e.g.: `MDElement["h1", "a title"]` and this option allows us to overwrite existing rules by which it is translated to the final string.*) 78 | 79 | 80 | "MDElementTemplates" -> _Association 81 | 82 | 83 | M2MD[ EvaluationCell[] ] 84 | 85 | 86 | M2MD[ EvaluationCell[], "MDElementTemplates" -> <|"CodeBlock" -> "**``**"|> ] 87 | 88 | 89 | (* ::Text:: *) 90 | (*Default tag/templates are:*) 91 | 92 | 93 | <| 94 | "LaTeXBlock" -> "$$``$$" 95 | , "LaTeXInline"-> "$``$" 96 | , "Image" -> "![``](``)" 97 | , "Hyperlink" -> "[``](``)" 98 | , "Text" -> "``" 99 | , "Bold" -> "**``**" 100 | , "Italic" -> "*``*" 101 | 102 | , "h1" -> "# <*StringReplace[#, \"\n\"->\"
\"]*>" 103 | , "h2" -> "## <*StringReplace[#, \"\n\"->\"
\"]*>" 104 | , "h3" -> "### <*StringReplace[#, \"\n\"->\"
\"]*>" 105 | , "h4" -> "#### <*StringReplace[#, \"\n\"->\"
\"]*>" 106 | , "h5" -> "##### <*StringReplace[#, \"\n\"->\"
\"]*>" 107 | , "h6" -> "###### <*StringReplace[#, \"\n\"->\"
\"]*>" 108 | 109 | , "Comment" -> "[//]: # (``)" 110 | , "CodeBlock" -> TemplateExpression @ StringJoin["```mathematica\n", TemplateSlot[1], "\n```"] 111 | , "Output" -> TemplateExpression @ StringJoin["```\n(*", TemplateSlot[1], "*)\n```"] 112 | 113 | |> 114 | 115 | 116 | (* ::Subsection:: *) 117 | (*Options - image export related*) 118 | 119 | 120 | (* ::Text:: *) 121 | (*Images are created for:*) 122 | (*- `Graphics/Graphics3D` boxes*) 123 | (*- `"Output"` cells that are not `TraditionalForm` or simple enough to export as input string*) 124 | (*- `StandardForm` boxes that have no built in MD interpretation*) 125 | (**) 126 | (*MD images end up in as `![name][url]`*) 127 | 128 | 129 | (* ::Subsubsection:: *) 130 | (*`"ImageNameFunction"`*) 131 | 132 | 133 | "ImagesExportURL" -> Automatic | _ 134 | 135 | 136 | (* ::Text:: *) 137 | (*If an image is based on a `CellObject` then the `Automatic` setting means the name will be the first `CellTag`. If there's no tag then the name is based on the content's expression hash.*) 138 | (*This way re-exporting won't create new files each time it is run.*) 139 | (**) 140 | (*If anything else than `Automatic` is provided then it will be applied to the subject of image export and it is expected to return a string that can be used as a name.*) 141 | 142 | 143 | (* ::Subsubsection:: *) 144 | (*`"ImagesExportURL"`*) 145 | 146 | 147 | "ImagesExportURL" -> Automatic | None | path_String 148 | 149 | 150 | (* ::Text:: *) 151 | (*By default images are exported to `./img`. *) 152 | (*None means no image is created and a `path_` is expected directory.*) 153 | 154 | 155 | (* ::Subsubsection:: *) 156 | (*`"ImagesFetchURL"`*) 157 | 158 | 159 | "ImagesFetchURL" -> "Relative" | Automatic | _String | _URL | _File 160 | 161 | 162 | (* ::Text:: *) 163 | (*This option specifies what gets to the URL part of MD element. *) 164 | (**) 165 | (*- "ImagesFetchURL" -> "Relative"` means the final `url` will be `FileNameJoin[{FileNameTake[exportDir], "name.png"}]`.*) 166 | (**) 167 | (*- `Automatic` means the absolute path to the exported file.*) 168 | (**) 169 | (*- `path : _String | _File` means the final `url` will be `FileNameJoin[{ path, name<>extensions}]`.*) 170 | (**) 171 | (*- `_URL` is an important one because it acts the same way as above but the path will be created using `URLBuild` instead of `FileNameJoin`*) 172 | (**) 173 | 174 | 175 | (* ::Subsubsection:: *) 176 | (*`"OverwriteImages"`*) 177 | 178 | 179 | "OverwriteImages" -> True | False 180 | 181 | 182 | (* ::Text:: *) 183 | (*If the subject to export as image was not changed then a new image won't be created by default.*) 184 | 185 | 186 | (* ::Subsection:: *) 187 | (*Examples*) 188 | 189 | 190 | "ImagesExportURL" -> Automatic 191 | 192 | 193 | (* ::Text:: *) 194 | (*\!\[image-76f31cc4-1dc6-4bc7-bbe8-0631e2681af6\]\(C:\Users\user\Documents\img\image-76f31cc4-1dc6-4bc7-bbe8-0631e2681af6.png\)*) 195 | 196 | 197 | "ImagesExportURL" -> Automatic, "ImagesFetchURL" -> "Relative" 198 | 199 | 200 | (* ::Text:: *) 201 | (*\!\[image-303bcb72-bb88-430f-9ba1-62ee0d928d33\]\(img\image-303bcb72-bb88-430f-9ba1-62ee0d928d33.png\)*) 202 | 203 | 204 | "ImagesExportURL" -> Automatic, "ImagesFetchURL" -> URL["Test"] 205 | 206 | 207 | (* ::Text:: *) 208 | (*\!\[image-822b8334-f5fd-43b2-8274-3a40165fe89c\]\(Test/image-822b8334-f5fd-43b2-8274-3a40165fe89c.png\)*) 209 | 210 | 211 | SetOptions[PreviousCell[], CellTags -> {"my-name"}]; 212 | M2MD[PreviousCell[], "ImagesExportURL" -> $TemporaryDirectory, "ImagesFetchURL" -> URL@"online"] 213 | 214 | 215 | (* ::Text:: *) 216 | (*\!\[my-name\]\(online/my-name.png\)*) 217 | 218 | 219 | (* ::Section:: *) 220 | (*Possible issues*) 221 | 222 | 223 | (* ::Text:: *) 224 | (*- while repeatedly exported images should be exported to the same path keep in mind that no one will (yet) clean up those which were not overwritten.*) 225 | 226 | 227 | SetDirectory@NotebookDirectory[]; 228 | 229 | 230 | MDExport["Home.md", EvaluationNotebook[]] 231 | 232 | 233 | SystemOpen@% 234 | -------------------------------------------------------------------------------- /M2MD.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /M2MD.nb: -------------------------------------------------------------------------------- 1 | Notebook[{ 2 | 3 | Cell[CellGroupData[{ 4 | Cell["M2MD", "Title",ExpressionUUID->"6d30d064-6ad5-4d00-ad1b-e90016088dff"], 5 | 6 | Cell[BoxData[{ 7 | RowBox[{"PacletDirectoryAdd", " ", "@", " ", 8 | RowBox[{"NotebookDirectory", "[", "]"}]}], "\n", 9 | RowBox[{"SetDirectory", "@", 10 | RowBox[{"NotebookDirectory", "[", "]"}]}]}], "Input", 11 | CellLabel->"In[1]:=",ExpressionUUID->"5384612b-2fdb-4dd0-896d-9c2a84aa7f4e"], 12 | 13 | Cell[BoxData[ 14 | RowBox[{"\[IndentingNewLine]", 15 | RowBox[{"<<", "M2MD`"}]}]], "Input", 16 | CellLabel->"In[3]:=",ExpressionUUID->"aeb9f136-81cb-44fa-b647-6baeefdfeb0d"], 17 | 18 | Cell[BoxData[ 19 | RowBox[{"M2MD", "[", 20 | RowBox[{ 21 | RowBox[{"Cell", "[", 22 | RowBox[{"\"\\"", ",", " ", "\"\\""}], "]"}], ",", " ", 23 | RowBox[{"\"\\"", " ", "\[Rule]", " ", 24 | RowBox[{"<|", 25 | RowBox[{"\"\\"", "\[Rule]", "\"\<\>\""}], "|>"}]}]}], 26 | "]"}]], "Input", 27 | CellLabel->"In[4]:=",ExpressionUUID->"b07a7c12-8c95-4337-8025-bd973939e722"], 28 | 29 | Cell[BoxData[ 30 | RowBox[{ 31 | RowBox[{"CreateDocument", "[", "\[IndentingNewLine]", 32 | RowBox[{"{", 33 | RowBox[{"Cell", "[", 34 | RowBox[{ 35 | RowBox[{"M2MD", " ", "@", " ", 36 | RowBox[{"EvaluationNotebook", "[", "]"}]}], ",", "\"\\""}], 37 | "]"}], "}"}], "\[IndentingNewLine]", "]"}], 38 | "\[IndentingNewLine]"}]], "Input", 39 | CellLabel-> 40 | "In[305]:=",ExpressionUUID->"dabb488c-d0c3-44a6-a3f4-9ac1cddbec74"], 41 | 42 | Cell[CellGroupData[{ 43 | 44 | Cell["build", \ 45 | "Subsection",ExpressionUUID->"106fee2a-c44e-4872-85cf-70e13c705c5d"], 46 | 47 | Cell[BoxData[ 48 | RowBox[{"<<", "build.m"}]], "Input", 49 | CellLabel->"In[7]:=",ExpressionUUID->"5ac09f12-8b81-4f73-916d-f0b2c0360f4b"], 50 | 51 | Cell[BoxData[ 52 | RowBox[{"PreviousCell", "[", "]"}]], "Input", 53 | CellLabel->"In[37]:=",ExpressionUUID->"abfe85b0-5012-404f-85cc-97d95d6924f5"], 54 | 55 | Cell[BoxData[ 56 | RowBox[{"SetDirectory", "@", "$TemporaryDirectory"}]], "Input", 57 | CellLabel->"In[20]:=",ExpressionUUID->"92e6ee82-80c3-472c-87a3-4f585b0f016a"], 58 | 59 | Cell[BoxData[{ 60 | RowBox[{"Directory", "[", "]"}], "\[IndentingNewLine]", 61 | RowBox[{"nb", "=", 62 | RowBox[{ 63 | RowBox[{"Notebooks", "[", "]"}], "[", 64 | RowBox[{"[", "2", "]"}], "]"}]}]}], "Input", 65 | CellLabel->"In[26]:=",ExpressionUUID->"7a977348-f0c9-4ecf-ac42-bfe37b019021"], 66 | 67 | Cell[BoxData[ 68 | RowBox[{ 69 | RowBox[{"MDExport", "[", 70 | RowBox[{"\"\\"", ",", "nb", ",", 71 | RowBox[{"\"\\"", "\[Rule]", "\"\\""}]}], 72 | "]"}], "//", "AbsoluteTiming"}]], "Input", 73 | CellLabel->"In[28]:=",ExpressionUUID->"025f7c36-8f61-4289-819d-b87d43162bb0"], 74 | 75 | Cell[BoxData[ 76 | RowBox[{"SystemOpen", "@", 77 | RowBox[{"%", "[", 78 | RowBox[{"[", "2", "]"}], "]"}]}]], "Input", 79 | CellLabel->"In[29]:=",ExpressionUUID->"e33cf853-1695-46b7-8632-2c3284124825"], 80 | 81 | Cell[BoxData[ 82 | RowBox[{ 83 | RowBox[{"Notebooks", "[", "]"}], "[", 84 | RowBox[{"[", "2", "]"}], "]"}]], "Input", 85 | CellLabel-> 86 | "In[150]:=",ExpressionUUID->"71e89637-89d5-43c2-a335-281fa7a21993"], 87 | 88 | Cell[BoxData[ 89 | RowBox[{"PrintDefinitions", "@", "M2MD"}]], "Input", 90 | CellLabel->"In[1]:=",ExpressionUUID->"0099416c-8c5d-4497-9915-73781789714b"] 91 | }, Open ]], 92 | 93 | Cell[CellGroupData[{ 94 | 95 | Cell["tests", \ 96 | "Section",ExpressionUUID->"d6f7fe7e-3205-41c8-bb96-753bc71c7aa5"], 97 | 98 | Cell[BoxData[ 99 | RowBox[{"reports", "=", 100 | RowBox[{"TestReport", " ", "/@", " ", 101 | RowBox[{"FileNames", "[", 102 | RowBox[{"\"\<*.m\>\"", ",", " ", 103 | RowBox[{"{", 104 | RowBox[{"FileNameJoin", "[", 105 | RowBox[{"{", 106 | RowBox[{ 107 | RowBox[{"NotebookDirectory", "[", "]"}], ",", " ", "\"\\""}], 108 | "}"}], "]"}], "}"}]}], "]"}]}]}]], "Input", 109 | CellLabel->"In[28]:=",ExpressionUUID->"5b53fe5f-f56a-432c-b96f-b38c91a9e46a"], 110 | 111 | Cell[BoxData[ 112 | RowBox[{ 113 | RowBox[{"reports", "[", 114 | RowBox[{"[", "1", "]"}], "]"}], "[", "\"\\"", "]"}]], "Input", 115 | CellLabel->"In[29]:=",ExpressionUUID->"d22e7dbc-33d2-4263-aca5-976ce47d7d6e"], 116 | 117 | Cell[BoxData[ 118 | RowBox[{"Internal`InheritedBlock", "[", 119 | RowBox[{ 120 | RowBox[{"{", 121 | RowBox[{ 122 | RowBox[{"$MDEnvironment", " ", "=", " ", "True"}], ",", " ", "M2MD"}], 123 | "}"}], ",", " ", 124 | RowBox[{ 125 | RowBox[{ 126 | RowBox[{"Attributes", "[", "M2MD", "]"}], "=", 127 | RowBox[{"{", "}"}]}], ";", "\n", " ", 128 | RowBox[{"Nest", "[", "\n", " ", 129 | RowBox[{ 130 | RowBox[{"ReplaceAll", "[", 131 | RowBox[{"DownValues", "[", "M2MD", "]"}], "]"}], "\n", " ", ",", " ", 132 | RowBox[{"Hold", " ", "@", " ", 133 | RowBox[{"M2MD", " ", "@", " ", 134 | RowBox[{"Cell", "[", 135 | RowBox[{"\"\<1+\\n2\>\"", ",", " ", "\"\\""}], "]"}]}]}], 136 | "\n", " ", ",", " ", "2"}], "\n", " ", "]"}]}]}], "\n", 137 | "]"}]], "Code",ExpressionUUID->"acda713c-39b6-43f6-9906-9a4d1b4bd2dc"], 138 | 139 | Cell[BoxData[ 140 | RowBox[{"Internal`InheritedBlock", "[", 141 | RowBox[{ 142 | RowBox[{"{", 143 | RowBox[{ 144 | RowBox[{"$MDEnvironment", " ", "=", " ", "True"}], ",", " ", "M2MD"}], 145 | "}"}], ",", " ", 146 | RowBox[{ 147 | RowBox[{ 148 | RowBox[{"Attributes", "[", "M2MD", "]"}], "=", 149 | RowBox[{"{", "}"}]}], ";", "\n", " ", 150 | RowBox[{"MatchQ", "[", "\n", " ", 151 | RowBox[{ 152 | RowBox[{"Hold", " ", "@", " ", 153 | RowBox[{"M2MD", "[", 154 | RowBox[{"\"\\"", ",", " ", 155 | RowBox[{"Cell", "[", 156 | RowBox[{"\"\<1+\\n2\>\"", ",", " ", "\"\\""}], "]"}]}], 157 | " ", "]"}]}], ",", "\n", " ", 158 | RowBox[{ 159 | RowBox[{"Verbatim", "[", "Hold", "]"}], " ", "@", " ", 160 | RowBox[{ 161 | RowBox[{"Verbatim", "[", "M2MD", "]"}], "[", " ", 162 | RowBox[{ 163 | RowBox[{"_", "?", 164 | RowBox[{"(", 165 | RowBox[{"Echo", "@*", "InputStyleQ", "@*", "Echo"}], ")"}]}], ",", 166 | " ", "cell_"}], "]"}]}]}], "\n", " ", "]"}]}]}], "\n", 167 | "]"}]], "Code",ExpressionUUID->"b660457c-2bdc-4366-8399-11881b371d72"], 168 | 169 | Cell[BoxData[ 170 | RowBox[{ 171 | RowBox[{"docsCell", "=", " ", 172 | RowBox[{"Cell", "[", 173 | RowBox[{ 174 | RowBox[{"TextData", "[", 175 | RowBox[{"{", "\n", " ", 176 | RowBox[{"\"\\"", ",", "\n", " ", 177 | RowBox[{"Cell", "[", 178 | RowBox[{ 179 | RowBox[{"BoxData", "[", "\n", " ", 180 | RowBox[{"TemplateBox", "[", 181 | RowBox[{ 182 | RowBox[{"{", 183 | RowBox[{ 184 | RowBox[{"Cell", "[", "\n", " ", 185 | RowBox[{"TextData", "[", "\"\\"", "]"}], "]"}], 186 | ",", "\"\\""}], "}"}], ",", "\n", " ", 187 | "\"\\"", ",", "\n", " ", 188 | RowBox[{"BaseStyle", "->", 189 | RowBox[{"{", "\"\\"", "}"}]}]}], "]"}], "]"}], 190 | ",", " ", "\"\\"", ",", "\n", " ", 191 | RowBox[{"FontFamily", "->", "\"\\""}]}], "]"}], 192 | ",", "\n", " ", "\"\<:\>\""}], "\n", "}"}], "]"}], ",", " ", 193 | "\"\\"", ",", "\n", " ", 194 | RowBox[{"CellID", "->", "480966610"}]}], "]"}]}], 195 | ";"}]], "Code",ExpressionUUID->"17228c84-e586-4f6b-a13f-f29ffa583483"], 196 | 197 | Cell[BoxData[ 198 | RowBox[{"M2MD", " ", "@", " ", 199 | "docsCell"}]], \ 200 | "Code",ExpressionUUID->"b15620a5-c442-4376-8866-06558d8c78f5"], 201 | 202 | Cell[BoxData[{ 203 | RowBox[{ 204 | RowBox[{"ClearAll", "@", "foo"}], ";"}], "\n", 205 | RowBox[{ 206 | RowBox[{"foo", "[", 207 | RowBox[{"lbl_String", ",", " ", "url_String"}], "]"}], ":=", 208 | RowBox[{ 209 | RowBox[{"StringTemplate", "[", "\"\<[``](``)\>\"", "]"}], "[", 210 | RowBox[{"lbl", ",", " ", "url"}], "]"}]}], "\n", 211 | RowBox[{ 212 | RowBox[{ 213 | RowBox[{"foo", "[", 214 | RowBox[{"lbl_String", ",", " ", 215 | RowBox[{"url_String", "?", 216 | RowBox[{"(", 217 | RowBox[{"StringContainsQ", "[", "\"\\"", "]"}], 218 | ")"}]}]}], " ", "]"}], ":=", " ", 219 | RowBox[{"(", "\n", " ", 220 | RowBox[{"(*", 221 | RowBox[{"do", " ", "whatever"}], "*)"}], "\n", " ", "url", "\n", 222 | ")"}]}], "\n"}], "\n", 223 | RowBox[{"M2MD", "[", "\n", " ", 224 | RowBox[{"docsCell", ",", " ", "\n", " ", 225 | RowBox[{"\"\\"", " ", "->", " ", 226 | RowBox[{"<|", "\n", " ", 227 | RowBox[{"\"\\"", " ", "->", " ", "\"\<<*foo[#, #2]*>\>\""}], 228 | "\n", " ", "|>"}]}]}], "\n", 229 | "]"}]}], "Code",ExpressionUUID->"e891cb52-c138-477c-9943-ba5593fe1b49"], 230 | 231 | Cell[BoxData[ 232 | RowBox[{"Block", "[", 233 | RowBox[{ 234 | RowBox[{"{", 235 | RowBox[{"M2MD`Private`$MDEnvironment", " ", "=", " ", "True"}], "}"}], 236 | ",", "\n", " ", 237 | RowBox[{ 238 | RowBox[{"Hold", " ", "@", " ", 239 | RowBox[{"M2MD", " ", "@", " ", 240 | RowBox[{"Cell", "[", 241 | RowBox[{"\"\\"", ",", " ", "\"\\""}], "]"}]}]}], " ", "/.", 242 | " ", 243 | RowBox[{"DownValues", "[", "M2MD", "]"}]}]}], " ", "\n", 244 | "]"}]], "Code",ExpressionUUID->"2d9c47f4-7372-416e-864c-74f5b40d1194"], 245 | 246 | Cell[BoxData[ 247 | RowBox[{"M2MD`Private`ItemStyleQ", "@", 248 | "\"\\""}]], \ 249 | "Code",ExpressionUUID->"1726243c-4ed4-43f6-8674-c1eac603b6d6"], 250 | 251 | Cell[BoxData[ 252 | RowBox[{"PrintDefinitions", " ", "@", " ", 253 | "M2MD"}]], "Code",ExpressionUUID->"90270b62-de2c-4b73-b046-f207223282eb"], 254 | 255 | Cell[BoxData[ 256 | RowBox[{"M2MD", " ", "@", " ", 257 | RowBox[{"Cell", "[", 258 | RowBox[{ 259 | RowBox[{"BoxData", "[", "\n", " ", 260 | RowBox[{"RowBox", "[", 261 | RowBox[{"{", " ", 262 | RowBox[{ 263 | RowBox[{"RowBox", "[", 264 | RowBox[{"{", 265 | RowBox[{ 266 | "\"\\"", ",", " ", "\"\<=\>\"", ",", " ", "\"\< \>\"", ",", 267 | " ", 268 | RowBox[{"GraphicsBox", "[", 269 | RowBox[{"DiskBox", "[", 270 | RowBox[{"{", 271 | RowBox[{"0", ",", " ", "0"}], "}"}], "]"}], "]"}]}], "}"}], 272 | "]"}], ",", " ", "\"\<;\>\""}], "}"}], "]"}], "]"}], ",", " ", 273 | "\"\\""}], " ", 274 | "]"}]}]], "Code",ExpressionUUID->"0b323a58-8dc8-45ca-90b5-1899b05c0f4a"], 275 | 276 | Cell[BoxData[ 277 | RowBox[{"M2MD", " ", "@", " ", 278 | RowBox[{"Cell", "[", 279 | RowBox[{ 280 | RowBox[{"TextData", "[", 281 | RowBox[{"{", "\n", " ", 282 | RowBox[{"\"\\"", ",", "\n", " ", 283 | RowBox[{"Cell", "[", 284 | RowBox[{"BoxData", "[", "\n", " ", 285 | RowBox[{"GraphicsBox", "[", 286 | RowBox[{ 287 | RowBox[{"DiskBox", "[", 288 | RowBox[{"{", 289 | RowBox[{"0", ",", " ", "0"}], "}"}], "]"}], ",", " ", 290 | RowBox[{"ImageSize", " ", "->", " ", "20"}]}], "]"}], "]"}], " ", 291 | "]"}]}], "\n", "}"}], "]"}], ",", " ", "\"\\""}], 292 | "]"}]}]], "Code",ExpressionUUID->"0fc696eb-cc54-4fa0-9592-990669609c7b"], 293 | 294 | Cell[CellGroupData[{ 295 | 296 | Cell["style rules", \ 297 | "Subsection",ExpressionUUID->"f717b148-997b-430a-82e6-35c156df8026"], 298 | 299 | Cell[BoxData[ 300 | RowBox[{"SetDirectory", "@", "$TemporaryDirectory"}]], "Input", 301 | CellLabel->"In[16]:=",ExpressionUUID->"1715cf84-6479-4d8c-923f-d86b5f270f40"], 302 | 303 | Cell[BoxData[{ 304 | RowBox[{"M2MD", "[", " ", 305 | RowBox[{"Cell", "[", 306 | RowBox[{"\"\\"", ",", " ", "\"\\""}], "]"}], "]"}], "\n", 307 | RowBox[{"M2MD", "[", " ", 308 | RowBox[{ 309 | RowBox[{"Cell", "[", 310 | RowBox[{"\"\\"", ",", " ", "\"\\""}], "]"}], ",", " ", 311 | RowBox[{"\"\\"", " ", "\[Rule]", " ", 312 | RowBox[{"<|", 313 | RowBox[{"\"\\"", " ", "\[Rule]", " ", "\"\\""}], 314 | "|>"}]}]}], "]"}], "\n", 315 | RowBox[{"M2MD", "[", " ", 316 | RowBox[{ 317 | RowBox[{"Cell", "[", 318 | RowBox[{"\"\\"", ",", " ", "\"\\""}], "]"}], ",", " ", 319 | RowBox[{"\"\\"", " ", "\[Rule]", " ", 320 | RowBox[{"<|", 321 | RowBox[{"\"\\"", " ", "\[Rule]", " ", 322 | RowBox[{"{", 323 | RowBox[{"\"\\"", ",", " ", 324 | RowBox[{ 325 | RowBox[{"BoxesToTeXString", "[", "#", "]"}], "&"}]}], "}"}]}], 326 | "|>"}]}]}], "]"}], "\n", 327 | RowBox[{"M2MD", "[", " ", 328 | RowBox[{ 329 | RowBox[{"Cell", "[", 330 | RowBox[{"\"\\"", ",", " ", "\"\\""}], "]"}], ",", " ", 331 | RowBox[{"\"\\"", " ", "\[Rule]", " ", 332 | RowBox[{"<|", 333 | RowBox[{"\"\\"", " ", "\[Rule]", " ", 334 | RowBox[{"{", 335 | RowBox[{"\"\\"", ",", " ", 336 | RowBox[{ 337 | RowBox[{"ToImageElement", "[", "#", "]"}], "&"}]}], "}"}]}], 338 | "|>"}]}]}], "]"}]}], "Code", 339 | CellLabel->"In[17]:=",ExpressionUUID->"eaf31bfc-43b5-4ee1-9b62-dbe6f3ef2ecd"], 340 | 341 | Cell[" specification {tag_, parsers___} translates to ", \ 342 | "Text",ExpressionUUID->"7ed0adeb-bc69-4543-9cad-b1be780426dc"], 343 | 344 | Cell[BoxData[ 345 | RowBox[{"MDElement", "[", 346 | RowBox[{"tag", ",", " ", 347 | RowBox[{"parser1", "[", 348 | RowBox[{"cell_", ",", " ", "style_"}], "]"}], ",", " ", 349 | RowBox[{"parser2", "[", 350 | RowBox[{"cell_", ",", " ", "style_"}], "]"}], ",", " ", "___"}], 351 | "]"}]], "Code",ExpressionUUID->"ea446a9d-91d4-454c-9818-c9eeba495d9e"], 352 | 353 | Cell["\<\ 354 | Built-in parsers : 355 | BoxesToMDString 356 | BoxesToInputString 357 | ToImageElement 358 | BoxesToTeXString 359 | 360 | But you can use whatever you want as long as it returns something that \ 361 | MDElementTemplate for given tag can handle.\ 362 | \>", "Text",ExpressionUUID->"1a0ae989-cd03-44cf-a988-9fc47f4c2a9f"], 363 | 364 | Cell["We can go even further and define a brand new conversion:", \ 365 | "Text",ExpressionUUID->"5de9ed14-c9e5-41b7-9bd9-867373faab85"], 366 | 367 | Cell[BoxData[ 368 | RowBox[{"\n", 369 | RowBox[{"M2MD", "[", " ", 370 | RowBox[{ 371 | RowBox[{"Cell", "[", 372 | RowBox[{"\"\\"", ",", " ", "\"\\""}], "]"}], ",", " ", 373 | "\[IndentingNewLine]", 374 | RowBox[{"\"\\"", " ", "\[Rule]", " ", 375 | RowBox[{"<|", 376 | RowBox[{"\"\\"", " ", "\[Rule]", " ", "\"\\""}], 377 | "|>"}]}], ",", "\[IndentingNewLine]", " ", 378 | RowBox[{"\"\\"", "\[Rule]", 379 | RowBox[{"<|", 380 | RowBox[{"\"\\"", "\[Rule]", "\"\<# ((((``))))\\n---\>\""}], 381 | "|>"}]}]}], "\[IndentingNewLine]", "]"}]}]], "Input", 382 | CellLabel->"In[25]:=",ExpressionUUID->"41679aa3-cd5b-4514-b3d6-a26534bf015b"], 383 | 384 | Cell["\<\ 385 | Together with \[OpenCurlyDoubleQuote]IgnoredStyles\[CloseCurlyDoubleQuote] \ 386 | option you can probably get a lot done with standard documentation notebooks\ 387 | \>", "Text",ExpressionUUID->"a2c7340a-2a89-4013-8189-ddf7fb74b415"], 388 | 389 | Cell[BoxData[{ 390 | RowBox[{ 391 | RowBox[{"nb", "=", 392 | RowBox[{"NotebookPut", "@", 393 | RowBox[{"Notebook", "@", 394 | RowBox[{"List", "@", 395 | RowBox[{"Cell", "[", 396 | RowBox[{ 397 | RowBox[{"BoxData", "@", 398 | RowBox[{"ToBoxes", "@", 399 | RowBox[{"Graphics", "@", 400 | RowBox[{"Disk", "[", "]"}]}]}]}], ",", "\"\\""}], 401 | "]"}]}]}]}]}], ";"}], "\[IndentingNewLine]", 402 | RowBox[{"Block", "[", 403 | RowBox[{ 404 | RowBox[{"{", 405 | RowBox[{"Export", "=", "export"}], "}"}], ",", "\[IndentingNewLine]", 406 | RowBox[{"MDExport", "[", "\[IndentingNewLine]", 407 | RowBox[{ 408 | "\"\\"", ",", "\[IndentingNewLine]", "nb", ",", 409 | "\[IndentingNewLine]", 410 | RowBox[{"\"\\"", "\[Rule]", 411 | RowBox[{"URL", "[", "\"\\"", "]"}]}]}], "\[IndentingNewLine]", 412 | "]"}]}], "\[IndentingNewLine]", "]"}], "\[IndentingNewLine]", 413 | RowBox[{"NotebookClose", "@", "nb"}], "\[IndentingNewLine]"}], "Input", 414 | CellLabel->"In[25]:=",ExpressionUUID->"c039053a-85d4-460a-aa52-949f83cac01f"], 415 | 416 | Cell[BoxData[ 417 | RowBox[{"BoxesToInputString", "@", 418 | RowBox[{"First", "@", 419 | RowBox[{"NotebookRead", "@", 420 | RowBox[{"PreviousCell", "[", "]"}]}]}]}]], "Input", 421 | CellLabel->"In[37]:=",ExpressionUUID->"3df94578-38f1-46b3-b99d-06502cb2765d"] 422 | }, Open ]] 423 | }, Open ]] 424 | }, Open ]] 425 | }, 426 | WindowSize->{1225, 872}, 427 | WindowMargins->{{Automatic, 115}, {Automatic, 60}}, 428 | PrivateNotebookOptions->{"FileOutlineCache"->False}, 429 | TrackCellChangeTimes->False, 430 | FrontEndVersion->"12.2 for Microsoft Windows (64-bit) (November 25, 2020)", 431 | StyleDefinitions->"Default.nb", 432 | ExpressionUUID->"a30fb722-6a7c-4cb4-b35e-8e13214df9f1" 433 | ] 434 | 435 | -------------------------------------------------------------------------------- /M2MD/Kernel/init.m: -------------------------------------------------------------------------------- 1 | << M2MD`M2MD` -------------------------------------------------------------------------------- /M2MD/M2MD.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | (* ::Chapter:: *) 4 | (* Metadata*) 5 | 6 | 7 | (* Mathematica Package *) 8 | 9 | (* :Title: M2MD *) 10 | (* :Context: M2MD` *) 11 | (* :Author: Kuba (kuba.pod@gmail.com) *) 12 | (* :Date: Thu 24 May 2018 12:44:21 *) 13 | 14 | (* :Keywords: *) 15 | (* :Discussion: *) 16 | 17 | 18 | 19 | (*TODO: inline cells support*) 20 | 21 | 22 | (* ::Chapter:: *) 23 | (* Begin package*) 24 | 25 | 26 | BeginPackage["M2MD`"]; 27 | 28 | 29 | Unprotect["`*", "`*`*"] 30 | ClearAll["`*", "`*`*"] 31 | 32 | 33 | M2MD::usage = "M2MD[obj] converts object to markdown string"; 34 | 35 | MDExport::usage = "MDExport[\"path/to.md\", obj]" 36 | MDExport::fmerr = "Skipping an unknown front matter format." 37 | 38 | WithLineBreaks::usage = "WithLineBreaks[string] is a helper function that prepends double space to new lines, md parsers consider is a line break." 39 | 40 | BoxesToMDString; 41 | BoxesToInputString; 42 | ToImageElement; 43 | BoxesToTeXString; 44 | 45 | SmartWrap; 46 | 47 | 48 | Begin["`Private`"]; 49 | 50 | 51 | (* ::Chapter:: *) 52 | (* Implementation code*) 53 | 54 | 55 | (* ::Section:: *) 56 | (*MDExport*) 57 | 58 | 59 | $BoxesToStringType = "InputText"; 60 | $CodeLanguage = "mathematica"; 61 | 62 | MDExport // Options = { 63 | "ImagesExportURL" -> Automatic, (*Automatic | None | path_String*) 64 | "ImagesFetchURL" -> "Relative", (*Automatic | "Relative" | path_String*) 65 | "ImageNameFunction" -> Automatic, 66 | "OverwriteImages" -> True, (*boole*) 67 | 68 | "IgnoredStyles" -> None, 69 | "CellStyleRules" -> Automatic, (* style_ \[Rule] tag_ | style_ \[Rule] {tag, Function[{style, _Cell}, _]..}*) 70 | 71 | "CodeLanguage" -> $CodeLanguage, 72 | 73 | "BoxesToStringType" -> $BoxesToStringType, (*whatever ExportPacket supports*) 74 | 75 | "MDElementTemplates"-> Automatic, (* _String | template_ *) 76 | "FrontMatter" -> <||> (* _Association for JSON flavor or _String for user provided YAML or TOML string*) 77 | } 78 | 79 | 80 | MDExport[path_String , obj_, patt : OptionsPattern[]]:= Module[{mdString, options} 81 | , mdString = Check[ 82 | M2MD[ 83 | obj 84 | , patt (*will overwrite that path if needed*) 85 | , "ImagesExportURL" -> FileNameJoin[{FileNameDrop @ ExpandFileName @ path, "img"}] 86 | , "ImagesFetchURL" -> "Relative" 87 | ] 88 | , Return[ $Failed, Module] 89 | ] 90 | ; options = <|Options @ MDExport, patt|> 91 | ; mdString = AddFrontMatter[mdString, Lookup[options, "FrontMatter"] ] 92 | 93 | ; Export[ 94 | path 95 | , mdString 96 | , "Text" 97 | , CharacterEncoding -> "UTF8" 98 | ] 99 | ] 100 | 101 | 102 | MDEnvironment // Options = Options @ MDExport; 103 | 104 | MDEnvironment[___, OptionsPattern[] ]:= Function[ 105 | expr 106 | , Internal`InheritedBlock[ 107 | { M2MD, MDElement, $MDMonitor = Hold, $BoxesToStringType, $CodeLanguage } 108 | 109 | , MDElementLoad @ OptionValue @ "MDElementTemplates" 110 | ; M2MDLoad @ OptionValue @ "CellStyleRules" 111 | 112 | ; $BoxesToStringType = OptionValue["BoxesToStringType"] 113 | ; $CodeLanguage = OptionValue["CodeLanguage"] 114 | 115 | ; expr 116 | ] 117 | , HoldAll 118 | 119 | ] 120 | 121 | 122 | (* ::Section:: *) 123 | (*LoadDefinitions*) 124 | 125 | 126 | LoadDefinitions // Options = { 127 | "StandardizationFunction" -> Identity, 128 | "DefinitionFunction" -> Hold 129 | }; 130 | 131 | LoadDefinitions[ definitions : KeyValuePattern[{}], OptionsPattern[] ]:= With[ 132 | { standardize = OptionValue["StandardizationFunction"] 133 | , define = OptionValue["DefinitionFunction"] 134 | } 135 | , Module[{std} 136 | , std = standardize /@ Normal @ definitions 137 | ; std = ToDownValue /@ std 138 | ; std /. RuleDelayed[ _[rhs_], lhs_] :> define[rhs, lhs] 139 | ] 140 | ] 141 | 142 | ToDownValue[ rule_[ rhs:Except[_HoldPattern] , lhs_] ] := HoldPattern[rhs] :> lhs; 143 | ToDownValue[ r_Rule ] := RuleDelayed @@ r 144 | 145 | 146 | (* ::Section::Closed:: *) 147 | (*M2MD (whatever to MD)*) 148 | 149 | 150 | (* ::Subsection::Closed:: *) 151 | (*sugar*) 152 | 153 | 154 | (*M2MD // Attributes = {HoldAllComplete}; again at the end*) 155 | 156 | M2MD[args___] /; Not @ TrueQ @ $MDEnvironment := Internal`InheritedBlock[ 157 | {$MDEnvironment = True, M2MD} 158 | , Attributes[M2MD] = {} 159 | ; Update[M2MD] 160 | ; MDEnvironment[args] @ M2MD[args] 161 | ] 162 | 163 | 164 | 165 | 166 | M2MD // Options = MDExport // Options 167 | 168 | 169 | M2MD[nb_NotebookObject, patt: OptionsPattern[]] := Module[ 170 | { cells, ignoredStyles = OptionValue["IgnoredStyles"] } 171 | , cells = Cells @ nb 172 | ; If[ MatchQ[ignoredStyles, {__String}] 173 | , cells = DeleteCases[cells, Alternatives @@ Cells[nb, CellStyle->ignoredStyles]] 174 | ] 175 | 176 | ; ProcessMDString @ 177 | CombineMDCells @ 178 | Map[ M2MD[#, patt]& ] @ 179 | cells 180 | ] 181 | 182 | 183 | CombineMDCells = StringJoin @ Map[ToString] @ Riffle[#, "\n\n"]& 184 | 185 | 186 | (* Syntactic sugar *) 187 | 188 | (*TODO: cell groups, raw data, style data *) 189 | 190 | 191 | 192 | M2MD[ contents_TextData, opt: OptionsPattern[]]:= M2MD[ Cell[contents, "Text"], opt] 193 | M2MD[ contents_BoxData, opt: OptionsPattern[]] := M2MD[ Cell[contents, "Output"], opt] 194 | 195 | M2MD[ str_String, opt: OptionsPattern[]]:= str; 196 | 197 | M2MD[ boxes_, opt : OptionsPattern[] ]:= BoxesToMDString[boxes, False, opt] 198 | 199 | M2MD[cellObj_CellObject, opt : OptionsPattern[] ] := M2MD[NotebookRead[cellObj], opt]; 200 | 201 | 202 | M2MD[ cell : Cell[_, style_, ___], opt : OptionsPattern[] ] := M2MD[style, cell, opt]; 203 | 204 | (*Convertions*) 205 | 206 | 207 | (* ::Subsection::Closed:: *) 208 | (*style rules*) 209 | 210 | 211 | $CellStyleRules = { 212 | "Title"->"h1", 213 | "Subtitle"->"Bold", 214 | "Subsubtitle"->"Bold", 215 | "Section"->"h2", 216 | "Subsection"->"h3", 217 | "Subsubsection"->"h4", 218 | "Subsubsubsection"->"h5", 219 | "Subsubsubsubsection"->"h6" 220 | } 221 | 222 | 223 | M2MDSet[style_String , tag_String]:= M2MDSet[style , {tag, BoxesToMDString[#[[1]]]& }] 224 | 225 | M2MDSet[style_String , {tag_String, parsers___}]:= DownValues[M2MD] = Insert[ 226 | DownValues[M2MD] 227 | , HoldPattern @ M2MD[style, cell_Cell, opt: OptionsPattern[] ] :> MDElement[tag, Sequence @@ Through[{parsers}[cell, style] ] ] 228 | , 2 229 | ] 230 | (*M2MDSet[style_String , {tag_, parsers__}]:= M2MD[style, cell_, opt: OptionsPattern[] ] := MDElement[tag, BoxesToMDString @ First @ cell]*) 231 | 232 | 233 | M2MDLoad[defs_]:=LoadDefinitions[ defs, "DefinitionFunction" -> M2MDSet ] 234 | 235 | 236 | M2MDLoad @ $CellStyleRules 237 | 238 | 239 | (*etc*) 240 | 241 | M2MD[ "SlideShowNavigationBar", ___]:= MDElement["ThematicBreak"] 242 | 243 | M2MD[ "Output", Cell[inputForm_String, ___], ___]:= MDElement["CodeBlock", inputForm, $CodeLanguage]; 244 | 245 | M2MD[ style_?InputStyleQ , cell:_[_?InputFormQ, ___], opts: OptionsPattern[] 246 | ] := MDElement["CodeBlock", BoxesToInputString @ cell, CodeLanguage[style, cell] ] 247 | 248 | M2MD[ style_?NumberedStyleQ , cell_, opts: OptionsPattern[]]:= MDElement["NumberedItem", ItemLevel[style], BoxesToMDString[cell, False, opts] ] 249 | M2MD[ style_?ParagraphStyleQ, cell_, opts: OptionsPattern[]]:= MDElement["Paragraph" , ItemLevel[style], BoxesToMDString[cell, False, opts] ] 250 | M2MD[ style_?ItemStyleQ , cell_, opts: OptionsPattern[]]:= MDElement["Item" , ItemLevel[style], BoxesToMDString[cell, False, opts] ] 251 | 252 | 253 | 254 | CodeLanguage["Input" | "Code", _]:=$CodeLanguage; 255 | CodeLanguage["ExternalLanguage", cell_]:= Lookup[Rest @ Rest @ Apply[List] @ cell, CellEvaluationLanguage, "Python"] /. "NodeJS" -> "javascript" // ToLowerCase 256 | CodeLanguage[___]:= "" 257 | 258 | 259 | InputStyleQ = MemberQ[{"Input", "Code", "Program", "ExternalLanguage"}, #]& (*TODO, language*) 260 | NumberedStyleQ = StringContainsQ["itemNumbered", IgnoreCase -> True] 261 | ParagraphStyleQ = StringContainsQ["itemParagraph", IgnoreCase -> True] 262 | ItemStyleQ = StringContainsQ["item", IgnoreCase -> True] 263 | 264 | ItemLevel = StringCount[#, "sub", IgnoreCase -> True]& 265 | 266 | 267 | (* ::Subsection:: *) 268 | (*content rules*) 269 | 270 | 271 | M2MD[style_, cell:_[_TextData|_String, ___], opt : OptionsPattern[] 272 | ] := MDElement["Text", BoxesToMDString[ cell, False, opt ] ] 273 | 274 | M2MD[style_, cell:_[_?OutputFormQ, ___], OptionsPattern[] 275 | ]:= MDElement["Output", BoxesToInputString @ cell ] 276 | 277 | M2MD[style_, cell:_[BoxData @ FormBox[_, TraditionalForm], ___], OptionsPattern[] 278 | ] := MDElement["LaTeXBlock", BoxesToTeXString @ cell ]; 279 | 280 | M2MD[style_, cell:_[_BoxData, ___], opt : OptionsPattern[] 281 | ] := ToImageElement[cell, opt] 282 | 283 | (*default behaviour for cell styles*) 284 | M2MD[args___] := MDElement["Comment", ToString[ Head /@ {args}] ]; 285 | 286 | M2MD // Attributes = {HoldAllComplete}; 287 | 288 | InputFormQ = OutputFormQ = FreeQ @ Except[BoxData|TextData|List|RowBox|SuperscriptBox, _Symbol] 289 | 290 | 291 | BoxesToMDString[cell_Cell, inlineCell_:True, opt : OptionsPattern[]]:= parseData @ First @ cell 292 | 293 | BoxesToMDString[boxes_, inlineCell_:True, opt : OptionsPattern[]]:= parseData[boxes] 294 | 295 | 296 | 297 | 298 | (* ::Section:: *) 299 | (*AddFrontMatter*) 300 | 301 | 302 | AddFrontMatter[ md_String, fm_String]:= fm <> "\n" <> md 303 | 304 | AddFrontMatter[ md_String, fm_Association ]:= Module[{ fmString } 305 | , If[ 306 | Length @ fm == 0 307 | , Return[ md, Module] 308 | ] 309 | ; fmString = Check[ 310 | ExportString[ fm, "RawJSON" ] 311 | , Return[ md, Module] 312 | ] 313 | 314 | ; AddFrontMatter[ md, fmString ] 315 | ] 316 | 317 | AddFrontMatter[ md_, else_]:= (Message[MDExport::fmerr]; md) 318 | 319 | 320 | 321 | (* ::Section:: *) 322 | (*ToImageElement*) 323 | 324 | 325 | ToImageElement // Options = M2MD // Options 326 | 327 | ToImageElement[box:Except[_Cell], patt : OptionsPattern[] ]:=ToImageElement[ Cell[BoxData @ box, "Output"], patt] 328 | 329 | ToImageElement[cell_, patt : OptionsPattern[]]:= 330 | With[ 331 | { overwriteQ = OptionValue["OverwriteImages"] 332 | , exportURL = OptionValue["ImagesExportURL"] 333 | , fetchURL = OptionValue["ImagesFetchURL"] 334 | } 335 | , Module[{ baseName, exportDir, exportPath, fetchDir, fetchPath, res} 336 | 337 | , baseName = ToImageName[False, cell, patt] 338 | 339 | ; exportDir = Switch[ exportURL 340 | , Automatic , FileNameJoin[{Directory[], "img"}] 341 | , _String | _File, exportURL /. File -> Identity 342 | , None | _ , Return["", Module] 343 | ] 344 | ; exportPath = FileNameJoin[{exportDir, baseName<>".png"}] 345 | 346 | ; fetchDir = Switch[ fetchURL 347 | , Automatic , exportDir 348 | , "Relative" , FileNameTake @ exportDir (*img/*) 349 | , _String | _URL | _File, fetchURL 350 | , _ , Return["", Module] 351 | ] 352 | ; fetchPath = urlNameJoin[{fetchDir, baseName<>".png"}] 353 | 354 | ; If[ 355 | overwriteQ && FileExistsQ[exportPath] 356 | , $MDMonitor["Skipping existing image:", baseName] 357 | ; Return[MDElement["Image", baseName, fetchPath], Module] 358 | ] 359 | 360 | 361 | ; If[ Not @ DirectoryQ @ exportDir, CreateDirectory[exportDir, CreateIntermediateDirectories->True]] 362 | 363 | ; res = Export[exportPath, cell] 364 | 365 | ; If[ res === $Failed 366 | , Return[ MDElement["Comment", "Failed to export image"], Module] 367 | ] 368 | 369 | ; MDElement["Image", baseName, fetchPath] 370 | ]] 371 | 372 | 373 | 374 | 375 | 376 | urlNameJoin[list_List ? (MemberQ[_URL]) ] := URLBuild[list /. URL -> Identity] 377 | urlNameJoin[list_List ] := FileNameJoin[ list /. File -> Identity] 378 | 379 | 380 | ToImageName // Options = Options @ MDExport; 381 | 382 | ToImageName[boxes_ , "ExpressionHash"]:= Hash[boxes, "Expression", "Base36String"] 383 | ToImageName[cell_CellObject, "ExpressionHash"]:= Hash[First @ NotebookRead @ cell, "Expression", "Base36String"] 384 | 385 | ToImageName[cellObj_:False , boxes_, OptionsPattern[] ]:= ToImageName[cellObj, boxes, OptionValue["ImageNameFunction"]] 386 | 387 | ToImageName[_, boxes_ , Automatic]:= ToImageName[boxes, "ExpressionHash"] 388 | ToImageName[cell_CellObject, boxes_, Automatic]:= FirstCellTag @ cell // Replace[{} :> ToImageName[boxes, "ExpressionHash"] ] 389 | ToImageName[cell_, boxes_ , foo_] := foo[cell, boxes] // Replace[Except[_String] :> ToImageName[boxes, "ExpressionHash"] ] 390 | 391 | 392 | 393 | FirstCellTag[cell_CellObject]:= FirstCellTag @ CurrentValue[EvaluationCell[], CellTags] 394 | FirstCellTag[tag_String]:=tag; 395 | FirstCellTag[{}]:={}; 396 | FirstCellTag[{tag_String, ___}]:=tag; 397 | 398 | 399 | 400 | 401 | 402 | (* ::Section:: *) 403 | (*Cell Contents To MD*) 404 | 405 | 406 | parseData[ cell_Cell ]:= parseData @ First @ cell 407 | parseData[TextData[boxes_]]:= parseData @ boxes 408 | parseData[RowBox[row_]]:= parseData @ row; 409 | parseData[list_List] := StringJoin[parseData /@ list]; 410 | 411 | parseData[string_String] := string; 412 | 413 | (*all cells below are inline cells*) 414 | parseData[ Cell[BoxData[boxes_?inlineImageQ], ___] ]:= ToImageElement[boxes] 415 | inlineImageQ = Not @* FreeQ[GraphicsBox | Graphics3DBox | DynamicModuleBox ] 416 | 417 | 418 | parseData[ Cell[BoxData[boxes_?InputFormQ], ___]]:= MDElement["CodeInline", BoxesToInputString @ boxes] 419 | 420 | parseData[ Cell[BoxData[FormBox[boxes_, TraditionalForm]], ___] ]:= MDElement["LaTeXInline", BoxesToTeXString @ boxes] 421 | 422 | 423 | 424 | 425 | parseData[ Cell[BoxData[boxes_], ___] ] := parseData @ boxes (*TODO: with box replacements*) 426 | 427 | 428 | parseData[InterpretationBox[boxes_, ___] ]:= parseData @ boxes 429 | 430 | 431 | (* ::Subsection:: *) 432 | (*forms/styles*) 433 | 434 | 435 | parseData[StyleBox[expr_, a___, FontWeight -> "Bold", b___]] := MDElement["Bold", parseData @ StyleBox[expr, a, b]] 436 | parseData[StyleBox[expr_, a___, FontSlant -> "Italic", b___]] := MDElement["Italic", parseData @ StyleBox[expr, a, b]] 437 | parseData[StyleBox[expr_, ___]] := MDElement["Text", parseData @ expr] 438 | 439 | 440 | parseData[FormBox[boxes : Except[_TagBox], TraditionalForm, ___]] := MDElement["LaTeXInline", BoxesToTeXString@boxes] 441 | 442 | 443 | (* ::Subsection:: *) 444 | (*hyperlinks*) 445 | 446 | 447 | parseData[ TemplateBox[{lbl_, {url_, tag_}, note_}, "HyperlinkDefault", ___] 448 | ] := MDElement["Hyperlink", lbl, url] 449 | 450 | parseData[ TemplateBox[{lbl_, url_}, "HyperlinkURL", ___] 451 | ] := MDElement["Hyperlink", lbl, url] 452 | 453 | 454 | (* Insert > Hyperlink interpretation, the problem is that the label is a plain string rather than a string 455 | which underwent ToBoxes so I will assume it is 'ready'. I am not sure how many edge case we have here. *) 456 | parseData[ ButtonBox[lbl_, ___, BaseStyle -> "Hyperlink", ButtonData -> { url_String | URL[url_], ___ }, ___] 457 | ] := MDElement["Hyperlink", lbl, url ] 458 | 459 | parseData[ ButtonBox[lbl_, ___, ButtonData -> (s_String ? (StringStartsQ["paclet:"])), ___] ]:= 460 | MDElement["Hyperlink", lbl, "https://reference.wolfram.com/language/" <> StringTrim[s, "paclet:"]] 461 | 462 | parseData[ TemplateBox[{lbl_, ref_}, "RefLink"|"RefLinkPlain"|"StringTypeLink", ___] 463 | ]:= MDElement["Hyperlink", lbl, "https://reference.wolfram.com/language/" <> StringTrim[ref, "paclet:"]] 464 | 465 | 466 | (* ::Subsection:: *) 467 | (*defaults*) 468 | 469 | 470 | (*default behaviour for boxes*) 471 | parseData[boxes_] := ToImageElement[boxes]; 472 | 473 | 474 | (* ::Section:: *) 475 | (*MDElement*) 476 | 477 | 478 | (* ::Subsection:: *) 479 | (*$MDElementTemplates*) 480 | 481 | 482 | (* Functions used in templates should be package exported symbols *) 483 | 484 | 485 | $MDElementTemplates = <| 486 | "LaTeXBlock" -> "$$``$$" 487 | , "LaTeXInline"-> "$``$" 488 | , "Image" -> "![``](``)" 489 | , "Hyperlink" -> "[``](``)" 490 | , "Text" -> "``" 491 | , "Bold" -> TemplateExpression @ SmartWrap[TemplateSlot[1], "**"] 492 | , "Italic" -> TemplateExpression @ SmartWrap[TemplateSlot[1], "*"] 493 | 494 | , "ThematicBreak"-> "---" 495 | 496 | , "h1" -> "# <*WithLineBreaks @ #*>" 497 | , "h2" -> "## <*WithLineBreaks @ #*>" 498 | , "h3" -> "### <*WithLineBreaks @ #*>" 499 | , "h4" -> "#### <*WithLineBreaks @ #*>" 500 | , "h5" -> "##### <*WithLineBreaks @ #*>" 501 | , "h6" -> "###### <*WithLineBreaks @ #*>" 502 | 503 | , "Item" -> "<*StringRepeat[\" \", 4 # ]*>- <*WithLineBreaks @ #2*>" 504 | , "NumberedItem" -> "<*StringRepeat[\" \", 4 # ]*>1. <*WithLineBreaks @ #2*>" 505 | , "Paragraph" -> "<*StringRepeat[\" \", 4(#+1) ]*><*WithLineBreaks @ #2*>" 506 | 507 | 508 | , "Comment" -> "[//]: # (``)" 509 | , "CodeBlock" -> TemplateExpression @ StringJoin["```", TemplateSlot[2], "\n", TemplateSlot[1], "\n```"] 510 | , "CodeInline"-> TemplateExpression @ StringJoin["`", TemplateSlot[1], "`"] 511 | , "Output" -> TemplateExpression @ StringJoin["```\n(*", TemplateSlot[1], "*)\n```"] 512 | 513 | |>; 514 | 515 | 516 | SmartWrap[body_String, wrap_String]:= Module[{once=False}, StringReplace[ 517 | body 518 | , { StartOfString ~~ p:(" "...) :> p <> wrap 519 | , p:(" "...) ~~ EndOfString :> If[!once, once=True; wrap <>p, ""] (*because otherwise it matches twice O_o *) 520 | } 521 | ] 522 | ] 523 | 524 | 525 | (* ::Subsection:: *) 526 | (*sugar*) 527 | 528 | 529 | MDElement::unknownTag = "Unknown MDElement tag: ``."; 530 | 531 | MDElement["Hyperlink", a___, b:Except[_String], c___] := MDElement["Hyperlink", a, parseData @ b, c] 532 | 533 | MDElement["CodeBlock", code_]:=MDElement["CodeBlock", code, ""] 534 | 535 | MDElement[args___]:= ( Message[MDElement::unknownTag, args]; ""); 536 | 537 | 538 | 539 | 540 | 541 | (* ::Subsection:: *) 542 | (*loading*) 543 | 544 | 545 | MDElementDefine[tag_String , lhs_String]:= MDElement[tag, args___]:= Block[ 546 | { WithLineBreaks = StringReplace[#, "\n" -> " \n"]& } 547 | , StringTemplate[lhs][args] 548 | ] 549 | 550 | MDElementDefine[tag_String , lhs_] := MDElement[tag, args___]:= Block[ 551 | { WithLineBreaks = StringReplace[#, "\n" -> " \n"]& } 552 | , TemplateApply[lhs, {args}] 553 | ] 554 | 555 | MDElementLoad[defs_]:=LoadDefinitions[ defs, "DefinitionFunction" -> MDElementDefine ] 556 | 557 | 558 | MDElementLoad @ $MDElementTemplates 559 | 560 | 561 | (* ::Section:: *) 562 | (*ProcessMDString*) 563 | 564 | 565 | ProcessMDString[ md_String ]:= StringReplace[md, 566 | { 567 | FromCharacterCode[8232] -> "\n" (*line separator*) 568 | , "```"~~ ("\n"...)~~"```\n" -> "\n" (*merge next output and input cells*) 569 | 570 | (*TODO: restrict it to pre v12.1 and maybe only include it in BoxesToString?*) 571 | , "\\[Rule]" -> "->" 572 | , "\\[RuleDelayed]" -> ":>" 573 | , "\\[LessEqual]" -> "<=" 574 | , "\\[GreaterEqual]" -> ">=" 575 | , "\\[NotEqual]" -> "!=" 576 | , "\\[Equal]" -> "==" 577 | , "\\[InlinePart]" -> "@>" 578 | , "\\[TwoWayRule]" -> "<->" 579 | 580 | } 581 | ] 582 | 583 | 584 | (* ::Section:: *) 585 | (*Utilities*) 586 | 587 | 588 | (* ::Subsection:: *) 589 | (*BoxToString*) 590 | 591 | 592 | BoxesToTeXString[boxes_] := Check[ 593 | Convert`TeX`BoxesToTeX[boxes], 594 | ToImageElement @ boxes 595 | ] 596 | 597 | 598 | 599 | 600 | 601 | (*InputText is nice but has a fixed page width...*) 602 | BoxesToInputString[ Cell[data_, a___, CellLabel->_,b___] ]:=BoxesToInputString[ Cell[data,a,b]] 603 | BoxesToInputString[ Cell[programData_String, ___] ]:= programData; 604 | BoxesToInputString[ programData_String] := programData 605 | BoxesToInputString[ boxes_]:= BoxesToInputString[ BoxData @ boxes] 606 | BoxesToInputString[ boxes_BoxData]:= Module[ 607 | {tagged, mark = "ORYGINALMARK", iNL = FromCharacterCode@{62371}} 608 | , 609 | tagged = boxes /. (n : "\n" | iNL) :> mark <> n; 610 | tagged = First @ FrontEndExecute @ FrontEnd`ExportPacket[tagged, "InputText"]; 611 | 612 | StringReplace[ 613 | tagged, 614 | { 615 | (mark ~~ "\r"...~~"\n") :> "\n", 616 | "\\"~~"\r"...~~"\n" -> "", (* in strings *) 617 | ("\r"...) ~~ "\n" ~~ " " ... -> "" 618 | } 619 | ] 620 | ] 621 | 622 | 623 | 624 | 625 | BoxesToString[ boxes:Except[_BoxData|_Cell], type_:"InputText"]:=BoxesToString[BoxData @ boxes, type] 626 | BoxesToString[ boxes_, type_:"InputText"]:= First @ FrontEndExecute @ FrontEnd`ExportPacket[boxes, type] 627 | 628 | 629 | (* ::Chapter:: *) 630 | (* End package*) 631 | 632 | 633 | End[]; 634 | 635 | EndPackage[]; 636 | -------------------------------------------------------------------------------- /M2MD/PacletInfo.m: -------------------------------------------------------------------------------- 1 | (* Created with the Wolfram Language : www.wolfram.com *) 2 | Paclet[Name -> "M2MD", Version -> "0.7.0", MathematicaVersion -> "10.4+", 3 | Description -> "Notebook to Markdown converter", 4 | Creator -> "Kuba (kuba.pod@gmail.com)", 5 | Extensions -> {{"Kernel", Context -> "M2MD`"}, {"FrontEnd"}}] 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Consider it beta before it hits 1.0.0. It is undergoing major upgrades, as far as the time is found, in 2020 so let me know if anything is off. 2 | 3 | For quick **documentation** see: https://github.com/kubaPod/M2MD/wiki 4 | 5 | # M2MD 6 | 7 | Simple converter of Mathematica notebooks to markdown. 8 | 9 | It originates from https://mathematica.stackexchange.com/q/84556/5478, it didn't evolve too much but I'm open to feedback as it is not something I use on daily basis. 10 | 11 | ## Installation 12 | 13 | ### Quick 14 | 15 | ResourceFunction["GitHubInstall"][ "kubapod", "m2md" ] 16 | 17 | ### Manual 18 | 19 | Go to 'releases' tab and download appropriate .paclet file. 20 | 21 | Run `PacletInstall @ path/to/the.paclet` file 22 | 23 | ### Via ``MPM` `` 24 | 25 | If you don't have ``MPM` `` yet, run: 26 | 27 | Import["https://raw.githubusercontent.com/kubapod/mpm/master/install.m"] 28 | 29 | and then: 30 | 31 | Needs @ "MPM`" 32 | MPM`MPMInstall["kubapod", "m2md"] 33 | 34 | From now on there should be a M2MD item in your Palettes menu. 35 | -------------------------------------------------------------------------------- /Tests/Basic.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | (* Mathematica Source File *) 4 | (* Created by Mathematica Plugin for IntelliJ IDEA *) 5 | (* :Author: user *) 6 | (* :Date: 2020-01-20 *) 7 | 8 | 9 | (* ::Section:: *) 10 | (*init*) 11 | 12 | 13 | Needs @ "M2MD`"; 14 | AppendTo[$ContextPath, "M2MD`Private`"]; 15 | 16 | 17 | VerificationTest[ M2MD @ "string", "string", TestID -> "String"] 18 | 19 | 20 | (* ::Section:: *) 21 | (*tests*) 22 | 23 | 24 | (* ::Subsection:: *) 25 | (*FrontMatter*) 26 | 27 | 28 | 29 | path = CreateFile[]; 30 | testExport = Import[ 31 | MDExport[path, Cell["Test", "Title"],##],"String"]&; 32 | 33 | 34 | VerificationTest[ 35 | testExport["FrontMatter" -> <||>] 36 | , "# Test" 37 | , TestID -> "front matter: empty -> none" 38 | ] 39 | 40 | 41 | VerificationTest[ 42 | testExport[] 43 | , "# Test" 44 | , TestID -> "no front matter" 45 | ] 46 | 47 | 48 | VerificationTest[ 49 | testExport["FrontMatter" -> <|"title"->"CODE"|>] 50 | , "{\r\n\t\"title\":\"CODE\"\r\n}\r\n# Test" 51 | , TestID -> "simple json front matter" 52 | ] 53 | 54 | 55 | VerificationTest[ 56 | testExport["FrontMatter" -> $Failed] 57 | , "# Test" 58 | , {MDExport::fmerr} 59 | , TestID -> "unknown front matter" 60 | ] 61 | 62 | 63 | VerificationTest[ 64 | testExport["FrontMatter" -> "---\ntitle: The Title\n---\n"] 65 | , "---\r\ntitle: The Title\r\n---\r\n\r\n# Test" 66 | , TestID -> "custom front matter string" 67 | ] 68 | 69 | 70 | DeleteFile @ path; 71 | Remove @ path; 72 | 73 | 74 | (* ::Subsection:: *) 75 | (*Image*) 76 | 77 | 78 | VerificationTest[ 79 | ToImageName @ RowBox[{}] 80 | , "10tvi4mw3rg8l" 81 | , TestID -> "ToImageName[boxes]" 82 | ] 83 | 84 | 85 | VerificationTest[ 86 | ToImageName @ "test" 87 | , "0bleddx8vw5yk" 88 | , TestID -> "ToImageName basic" 89 | ] 90 | 91 | 92 | VerificationTest[ 93 | Block[{ToImageElement, MDElement,StringJoin}, 94 | ToString@M2MD@Cell[TextData[{ 95 | Cell[BoxData[ GraphicsBox[{ Disk[{0,0}] } ]] ], 96 | " IGraph/M" 97 | }], "Title"] 98 | ] 99 | , "MDElement[h1, StringJoin[{ToImageElement[GraphicsBox[{Disk[{0, 0}]}]], IGraph/M}]]" 100 | , TestID -> "Inline image in std form" 101 | ] 102 | 103 | 104 | VerificationTest[ 105 | Block[{ToImageElement, MDElement,StringJoin}, 106 | ToString @ M2MD @ Cell[TextData[{ 107 | "test ", 108 | Cell[BoxData[ 109 | FormBox[ 110 | GraphicsBox[DiskBox[{0, 0}], 111 | ImageSize->20], TraditionalForm]], 112 | FormatType->"TraditionalForm"] 113 | }], "Section"] 114 | 115 | ] 116 | , "MDElement[h2, StringJoin[{test , ToImageElement[FormBox[GraphicsBox[DiskBox[{0, 0}], ImageSize -> 20], TraditionalForm]]}]]" 117 | , TestID -> "Inline image" 118 | ] 119 | 120 | 121 | (* ::Subsection:: *) 122 | (*Cells*) 123 | 124 | 125 | VerificationTest[ M2MD @ Cell["asdasd", "Text"], "asdasd", TestID -> "simple text"] 126 | VerificationTest[ M2MD @ Cell[TextData@"asdasd", "Text"], "asdasd", TestID -> "simple text data"] 127 | VerificationTest[ M2MD @ Cell[TextData@"asdasd", "Whatever"], "asdasd", TestID -> "unknown text style"] 128 | 129 | 130 | (* ::Subsection:: *) 131 | (*CodeBlocks*) 132 | 133 | 134 | VerificationTest[ 135 | M2MD@Cell["asd", "Code"] 136 | , "```mathematica\nasd\n```" 137 | , TestID -> "Code Language" 138 | ] 139 | 140 | 141 | VerificationTest[ 142 | M2MD[ Cell["asd", "Code"], "CodeLanguage" -> "whatever"] 143 | , "```whatever\nasd\n```" 144 | , TestID -> "Custom Language" 145 | ] 146 | 147 | 148 | VerificationTest[ 149 | M2MD[ Cell["asd", "Program"]] 150 | , "```\nasd\n```" 151 | , TestID -> "ProgramLanguage" 152 | ] 153 | 154 | 155 | VerificationTest[ 156 | M2MD@Cell["asd", "ExternalLanguage", CellEvaluationLanguage->"Julia"] 157 | , "```julia\nasd\n```" 158 | , TestID -> "Julia language" 159 | ] 160 | 161 | 162 | VerificationTest[ 163 | M2MD@Cell["asd", "ExternalLanguage", CellEvaluationLanguage->"NodeJS"] 164 | , "```javascript\nasd\n```" 165 | , TestID -> "NodeJS language" 166 | ] 167 | 168 | 169 | VerificationTest[ 170 | M2MD @ Cell["1+\n2", "Program"] 171 | , "```\n1+\n2\n```" 172 | , TestID -> "Program cell" 173 | ] 174 | 175 | 176 | VerificationTest[ 177 | M2MD @ Cell[BoxData[ RowBox[{"<<", "M2MD`"}]], "Input"] 178 | , "```mathematica\n<< M2MD`\n```" 179 | , TestID -> "InputBlock" 180 | ] 181 | 182 | 183 | VerificationTest[ 184 | M2MD @ Cell[BoxData[RowBox[{"M2MD"," ","@"}]],"Code", CellLabel->"TEST"] 185 | , "```mathematica\nM2MD @\n```" 186 | , TestID -> "Cell to InputCode" 187 | ] 188 | 189 | 190 | $inputCell = Cell[BoxData[RowBox[{"foo","[","\[IndentingNewLine]",RowBox[{"bar","[","\[IndentingNewLine]",RowBox[{"1",",","2"}],"\[IndentingNewLine]","]"}],"\[IndentingNewLine]","]"}]],"Input"]; 191 | 192 | VerificationTest[ 193 | M2MD[$inputCell, "BoxesToStringType" -> "InputText"] 194 | , "```mathematica\nfoo[\n bar[\n 1, 2 \n ] \n ]\n```" 195 | , TestID -> "BoxesToStringType" 196 | ] 197 | 198 | 199 | (* ::Subsection:: *) 200 | (*Items*) 201 | 202 | 203 | VerificationTest[ M2MD @ Cell["Test", "Item"] , "- Test", TestID -> "Item"] 204 | VerificationTest[ M2MD @ Cell["Test", "Subitem"] , " - Test", TestID -> "SubItem"] 205 | VerificationTest[ M2MD @ Cell["Test\nTest", "Subsubitem"] , " - Test \nTest", TestID -> "Subsubitem linebreaks" ] 206 | 207 | 208 | VerificationTest[ M2MD @ Cell["Test", "ItemParagraph"], " Test", TestID -> "ItemParagraph"] 209 | VerificationTest[ M2MD @ Cell["Test", "SubitemParagraph"], " Test", TestID -> "SubitemParagraph"] 210 | VerificationTest[ M2MD @ Cell["Test\nTest", "SubsubitemParagraph"], " Test \nTest", TestID -> "SubsubitemParagraph"] 211 | 212 | 213 | VerificationTest[ M2MD @ Cell["Test", "ItemNumbered"], "1. Test", TestID -> "ItemNumbered"] 214 | VerificationTest[ M2MD @ Cell["Test", "SubitemNumbered"], " 1. Test", TestID -> "SubitemNumbered"] 215 | VerificationTest[ M2MD @ Cell["Test\nTest", "SubsubitemNumbered"], " 1. Test \nTest", TestID -> "SubsubitemNumbered"] 216 | 217 | 218 | (* ::Subsection:: *) 219 | (*Output cells*) 220 | 221 | 222 | VerificationTest[ 223 | M2MD @ Cell["test","Output",CellLabel->"Out[79]//InputForm="] 224 | , "```mathematica\ntest\n```" 225 | , TestID -> "InputForm output" 226 | ] 227 | 228 | 229 | VerificationTest[ 230 | M2MD @ Cell[BoxData["\"E:\\\\Idea Projects\\\\M2MD\""],"Output",CellLabel->"Out[6]="] 231 | , "```\n(*\"E:\\\\Idea Projects\\\\M2MD\"*)\n```" 232 | , TestID -> "Simple output" 233 | ] 234 | 235 | 236 | (* ::Subsection:: *) 237 | (*CellContents*) 238 | 239 | 240 | VerificationTest[ 241 | M2MD@Cell[TextData[ButtonBox["National Renewable Energy Laboratory (NREL) data", 242 | BaseStyle->"Hyperlink", 243 | ButtonData->{ 244 | URL["www.wolfram.com"], None}, 245 | ButtonNote->"www.wolfram.com"]], "Subsection"] 246 | , "### [National Renewable Energy Laboratory (NREL) data](www.wolfram.com)" 247 | , TestID -> "Subsection>Hyperlink" 248 | ] 249 | 250 | 251 | VerificationTest[ 252 | parseData@ButtonBox["the C/igraph documentation", 253 | BaseStyle->"Hyperlink", 254 | ButtonData->{ 255 | URL["http://igraph.org/c/doc/"], None}, 256 | ButtonNote->"http://igraph.org/c/doc/"] 257 | , "[the C/igraph documentation](http://igraph.org/c/doc/)" 258 | , TestID -> "parseData@ButtonBox[\"the C/igraph documentation\",BaseStyle->\"Hyp..." 259 | ] 260 | 261 | 262 | VerificationTest[ 263 | $CellData= Cell[BoxData @ ToBoxes @ TraditionalForm[HypergeometricPFQ[{Subscript[a, 1],Subscript[a, 2]},{Subscript[b, 1],Subscript[b, 2]},x]], "Output"]; 264 | 265 | ToString[DisplayForm@$CellData,TeXForm]=== 266 | ToString[RawBoxes@$CellData,TeXForm]=== 267 | ToString[TeXForm@RawBoxes@$CellData]=== 268 | ToString[TeXForm@RawBoxes@$CellData[[1]]]=== (*boxdata stripped*) 269 | ToString[TeXForm@RawBoxes@$CellData[[1,1]]]=== 270 | ToString[TeXForm@RawBoxes@$CellData[[1,1,1,1]]]=== 271 | Convert`TeX`BoxesToTeX @ $CellData 272 | 273 | , True 274 | , TestID -> "BoxesToTeX" 275 | ] 276 | 277 | 278 | 279 | 280 | (* ::Subsection:: *) 281 | (*Smart Wrap*) 282 | 283 | 284 | VerificationTest[ 285 | M2MD@Cell[TextData[{ 286 | "a", 287 | StyleBox["s ", FontWeight->"Bold"], 288 | "d" 289 | }], "Section"] 290 | , "## a**s** d" 291 | , TestID -> "smart wrap cell" 292 | ] 293 | 294 | 295 | VerificationTest[ 296 | SmartWrap["test", "**"] 297 | , "**test**" 298 | , TestID -> "smart wrap 1" 299 | ] 300 | 301 | 302 | VerificationTest[ 303 | SmartWrap[" test", "**"] 304 | , " **test**" 305 | , TestID -> "smart wrap 2" 306 | ] 307 | 308 | 309 | VerificationTest[ 310 | SmartWrap[" test ", "**"] 311 | , " **test** " 312 | , TestID -> "smart wrap 3" 313 | ] 314 | 315 | 316 | VerificationTest[ 317 | SmartWrap["test ", "**"] 318 | , "**test** " 319 | , TestID -> "smart wrap 4" 320 | ] 321 | 322 | 323 | M2MD[ 324 | Cell[TextData[{StyleBox["Note:", FontWeight -> "Bold", 325 | FontSlant -> "Italic"], 326 | StyleBox[" foo bar baz.", FontSlant -> "Italic"]}], "Text"] 327 | ] 328 | 329 | 330 | (* ::Subsection:: *) 331 | (*Inline cells*) 332 | 333 | 334 | VerificationTest[ 335 | M2MD @ Cell[TextData[{ 336 | "Use ", 337 | Cell[BoxData[ RowBox[{"Print", "[", RowBox[{"1", "+", "1"}], "]"}]]], " to print stuff."}], "Text"] 338 | , "Use `Print[1 + 1]` to print stuff." 339 | , TestID -> "CodeInline" 340 | ] 341 | 342 | 343 | VerificationTest[ 344 | M2MD @ Cell[TextData[{ 345 | "test ", 346 | Cell[BoxData[ FormBox[ RowBox[{"1", "+", "1"}], TraditionalForm]], FormatType->"TraditionalForm"] 347 | }], "Subsection"] 348 | , "### test $1+1$" 349 | , TestID -> "LaTeXInline" 350 | ] 351 | 352 | 353 | VerificationTest[ 354 | M2MD @ Cell[TextData @ {"Inline code: ", Cell[BoxData[{"1+1"}]]}, "Title"] 355 | , "# Inline code: `1 + 1`" 356 | , TestID -> "Inline code" 357 | ] 358 | 359 | 360 | 361 | VerificationTest[ 362 | M2MD[ 363 | Cell[ 364 | TextData[{ 365 | "asdasd ", 366 | StyleBox["adsd",FontWeight->"Bold"], 367 | 368 | Cell[BoxData[RowBox[{"1","*","1"}]]] 369 | }] 370 | ,"Text" 371 | ], 372 | "ImagesExportURL" -> None ] 373 | , "asdasd **adsd**`1*1`" 374 | , TestID -> "Inline cells" 375 | ] 376 | 377 | 378 | (* ::Subsection:: *) 379 | (*StyleRules*) 380 | 381 | 382 | (* ::Section:: *) 383 | (*end*) 384 | 385 | 386 | $ContextPath = DeleteCases["M2MD`Private`"] @ $ContextPath; 387 | -------------------------------------------------------------------------------- /build.m: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | BeginPackage["M2MDBuild`"]; 4 | 5 | 6 | Needs["M2MD`"]; 7 | 8 | 9 | 10 | 11 | M2MDBild; 12 | 13 | Begin["`Private`"]; 14 | 15 | $project = DirectoryName[$InputFileName /. "" :> NotebookFileName[]]; 16 | $projectName = FileBaseName @ $project; 17 | 18 | M2MDBild[]:= buildMainPalette[]; 19 | 20 | needDirectory[dir_] := If[ DirectoryQ @ dir,dir, CreateDirectory[dir, CreateIntermediateDirectories -> True]]; 21 | 22 | buildMainPalette[]:=Module[{nb, deployDir} 23 | , nb = mainPalette[] 24 | ; deployDir = needDirectory @ FileNameJoin[{$project, $projectName, "FrontEnd", "Palettes"}] 25 | ; NotebookSave[ 26 | nb 27 | , FileNameJoin[{deployDir, $projectName <> ".nb"}] 28 | ] 29 | ; NotebookClose[nb] 30 | ]; 31 | 32 | mainPalette[]:= CreatePalette[ 33 | DynamicModule[{processing = False, path = None} 34 | , With[{ 35 | progressBar = ProgressIndicator[Appearance -> "Indeterminate"] 36 | , button = Button[ 37 | "Convert Notebook to Markdown" 38 | , processing = True 39 | ; Needs["M2MD`"] 40 | ; path = If[!StringQ[path], $HomeDirectory, path] 41 | ; path = SystemDialogInput["FileSave", {path, {"Markdown files"->{"*.md"},"Text files"->{"*.md","*.txt"},"All files"->{"*"}}}] 42 | ; If[path != $Canceled, 43 | SetDirectory@DirectoryName[path] 44 | ; MDExport[path,InputNotebook[]]] 45 | ; processing = False 46 | , Method -> "Queued" 47 | , FrameMargins -> 15 48 | , ImageMargins -> 15 49 | ] 50 | } 51 | , PaneSelector[ 52 | {True -> progressBar, False -> button} 53 | , Dynamic[processing + 0] (* XD https://mathematica.stackexchange.com/q/173940/5478*) 54 | , Alignment -> {Center,Center} 55 | ] 56 | 57 | ] 58 | ] 59 | , WindowTitle -> $projectName 60 | 61 | ]; 62 | 63 | 64 | 65 | 66 | 67 | End[]; 68 | EndPackage[]; 69 | --------------------------------------------------------------------------------