diff --git a/.gitignore b/.gitignore index d3c25cd..0cacf5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +.vs/ + # User-specific files *.suo *.user @@ -158,6 +160,9 @@ $RECYCLE.BIN/ # Exclude F# project specific directories and files # =================================================== +# FAKE directory +.fake/ + # NuGet Packages Directory packages/ @@ -169,3 +174,5 @@ temp/ # Test results produced by build TestResults.xml +.paket/paket.exe + diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets deleted file mode 100644 index 83fe906..0000000 --- a/.nuget/NuGet.targets +++ /dev/null @@ -1,136 +0,0 @@ - - - - $(MSBuildProjectDirectory)\..\ - - - false - - - false - - - true - - - false - - - - - - - - - - - $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) - $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) - - - - - $(SolutionDir).nuget - packages.config - - - - - $(NuGetToolsPath)\NuGet.exe - @(PackageSource) - - "$(NuGetExePath)" - mono --runtime=v4.0.30319 $(NuGetExePath) - - $(TargetDir.Trim('\\')) - - -RequireConsent - -NonInteractive - - "$(SolutionDir) " - "$(SolutionDir)" - - - $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) - $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols - - - - RestorePackages; - $(BuildDependsOn); - - - - - $(BuildDependsOn); - BuildPackage; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.paket/paket.bootstrapper.exe b/.paket/paket.bootstrapper.exe new file mode 100644 index 0000000..4e12706 Binary files /dev/null and b/.paket/paket.bootstrapper.exe differ diff --git a/.paket/paket.targets b/.paket/paket.targets new file mode 100644 index 0000000..0fd370f --- /dev/null +++ b/.paket/paket.targets @@ -0,0 +1,41 @@ + + + + + true + + true + $(MSBuildThisFileDirectory) + $(MSBuildThisFileDirectory)..\ + /Library/Frameworks/Mono.framework/Commands/mono + mono + + + + $(PaketToolsPath)paket.exe + $(PaketToolsPath)paket.bootstrapper.exe + "$(PaketExePath)" + $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" + "$(PaketBootStrapperExePath)" $(PaketBootStrapperCommandArgs) + $(MonoPath) --runtime=v4.0.30319 $(PaketBootStrapperExePath) $(PaketBootStrapperCommandArgs) + + $(MSBuildProjectDirectory)\paket.references + $(MSBuildStartupDirectory)\paket.references + $(MSBuildProjectFullPath).paket.references + $(PaketCommand) restore --references-files "$(PaketReferences)" + $(PaketBootStrapperCommand) + + RestorePackages; $(BuildDependsOn); + + + + + + + + + + + + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..95c4007 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM ubuntu:16.04 + +RUN apt-key adv \ + --keyserver hkp://keyserver.ubuntu.com:80 \ + --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF \ + && echo "deb http://download.mono-project.com/repo/debian wheezy main" | \ + tee /etc/apt/sources.list.d/mono-xamarin.list \ + && apt-get update \ + && apt-get install -y \ + mono-complete \ + fsharp \ + python3-pip \ + git \ + && rm -rf /var/lib/apt/lists/* + +RUN pip3 install --upgrade pip && pip3 install jupyter + +WORKDIR / +RUN git clone https://github.com/fsprojects/IfSharp.git + +WORKDIR /IfSharp +RUN git checkout jupyter +RUN ./build.sh +RUN mono bin/ifsharp.exe --install + +WORKDIR / +RUN mkdir notebooks +VOLUME notebooks + +EXPOSE 8888 + +ENTRYPOINT ["jupyter", \ + "notebook", \ + "--no-browser", \ + "--ip='*'", \ + "--port=8888", \ + "--notebook-dir=/notebooks" \ + ] diff --git a/Feature Notebook Jupyter.ipynb b/Feature Notebook Jupyter.ipynb new file mode 100644 index 0000000..93b50f9 --- /dev/null +++ b/Feature Notebook Jupyter.ipynb @@ -0,0 +1,446 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Jupyter notebook backed by an F# kernel" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Examples" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Last value displayed\"" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Last value displayed\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "input.fsx(1,1): error FS0039: The value or constructor 'errors' is not defined" + ] + } + ], + "source": [ + "errors are displayed" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Utility Methods" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "Inline HTML" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Util.Html(\"Inline HTML\") |> Display" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Util.Url(\"http://fsharp.org/img/logo/fsharp256.png\") |> Display" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/latex": [ + "$$f(x) = sin(x)$$" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Util.Math(\"f(x) = sin(x)\") |> Display" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "
Item1Item2
F#10
C#5
C++20
Java1
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let data = [ (\"F#\", 10); (\"C#\", 5); (\"C++\", 20); (\"Java\", 1); ]\n", + "Util.Table(data) |> Display" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## F# Charting" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1+11" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "//#load \"FSCharting.Gtk.fsx\" // Mac/Linux\n", + "#load \"FSCharting.fsx\" // Windows" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "FSharp.Charting.Chart.Bar(data) |> Display" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "FSharp.Charting.Chart.Line(data) |> Display" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Random things" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "let linspace (min : float, max : float, dx : float) =\n", + " if min > max then failwith (sprintf \"min [%f] cannot be greater than max [%f]\" min max)\n", + " if dx = 0.0 then failwith (\"dx cannot be zero\")\n", + " \n", + " let x = ref min\n", + " seq {\n", + " while !x < max do\n", + " x := !x + dx\n", + " yield !x\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "let plot (data:seq, cb) =\n", + " let results = \n", + " seq {\n", + " for x in data do\n", + " yield (x, cb(x))\n", + " }\n", + "\n", + " FSharp.Charting.Chart.Line (results)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAUAAAADwCAYAAABxLb1rAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAACuzSURBVHhe7Z15sF1VlcYtirLU0tZube22LVv/oB0p7erCLtpWHwmJMgZMBAKIiIiKioRJbaOxVRSHl8gLCUkggTCGEAaZwmAAg0FsojIoIIgDdItDab9LoHG+fb+z95KT4zn37Hk4Z/2qTonv3rx31tp7f3tYa+/9lCHDMExPYQFkGKa3sAAyDNNbWAAZhuktLIAMw/QWFkCGYXoLCyDDML2FBZBhmN7CAsgwTG9hAWQYprewADIM01tYABmG6S3JCeBTnsKazDBMGJJSG4gfCyDDMKFIRm1I+FgAGYYJRXJqwwLIMEwoWAAZhukt2QngxMRE8R08O+yww5//u6/P816993DXhXcPd/vS9PD1ix4YvugN76v9Hj/1z9+/7rDhG0/+n5H/BsN/W/i94bNfumvt9/ipf/5ul0OGbzrl50X92+X4LcOnPusFtd/z9UAPbMh6BKjz3a4B27f++PfDWYuni8qHBiz+d3r4zR/+Xn6ru7go+zVff2I4Y7Liv8nB8Hs//YP8RpqkUu8XXfF4yXfi2X1UH382/Sf5DT+4tD85BdExLpWKEIMddnzacPaXRaNdsP6xotItvPzx4cTo/88YNeJ7H0m7EdtiW/a/euxPw9lLhP/+47LHi/9/5Nptxf/fZ+lg+LPBH+U30yOFeo9OAmIH0Vu1+YnCf+9YI/x31Lnb5Lf8wAIo6bMAvmL+iqKyzT/jUfkTwVHnPFb8/MOXPC5/0k1sy/4dax4t/HTShif99NvRwHnuctGIP33V/8mfpkcK9X7vKdF5LLvpCfkT4b99TxM/n7zBn/86KYAwqvq0ofKdLoLeFpUMzwM/336kh8/eLEeGt/3wd/Kn3cOm7OEzMV0bFP4qc89oZAPfYXRY/SwVYtf7Wx743XDGyH9vOXVQiF6Z9Vt/U/hv/+WP/sVnrnBpf9YK0lcBRK+Lqe4H19VPNRaOpnSohO862+9UJCY2Zf/xy4V/pjb9Rv5kez5wvhhFL7gozVF07Hp/mBw9XzwSuzretXZbUT9Pv9nPKJAFUNJHAUSvOmeZqIDV0R+Bkcus0QgGI8FURzG2mJb9g7/4Y+E7+KfJNxg54zt71oxwUiBmvSffYITc5JuLt/62+M7+ywfyJ25hAZT0UQCnNj1RVK7XL7pf/qQerGHhe1Ob0l3LssG07E+7UfgFa6XjePc5Yi0Q073UiFnvl9wg6t8XNjbXKwjjXnKNsKmTtoEFUNJHATzhYjF9e+lbPi5/Us+135WjmFFF7CKmZU+RyjZhw/Rut8npYjqXGrHqPYQNEXL47ye/Gh8lJ6EsB0lcwQIo6ZsAbvvNkwEOJJy2sbesrI8knNJhiknZ/0BOf9GI26a2mB4jJ3Dm4vSmwbHqPQU4DlrV3ikgTQbfRaqMa1gAJX0TwHW3iwr4oXWPKdlOvfD536xfrM4Zk7L/6KUyRaiU+jKOBReJ72+6N61pcKx6/+FLhD8+e42a/6gD/s5DbnsQFkBJ3wTwsNVi+oZphYrt2BGC7/tOTI2BSdkjKo5R3ZV3/Vb+ZDzFNHjkv89fm9Y6aqx6rzr9JVBP8f2zt7idBrMASvokgJiGYfqL9AJMaVVsx7+ZWWz1GnRuGqxb9sWUduQ7+FB1SgufFf9mTMQzBjHqPU1pq4n348DID/8GASWXsABK+iSA1cqkavsxF4ppy8bvqo16ckG37GE//KC7QwZBJPy7zfenMw2OUe8XXy+i5zpBDXQab8Ga9WjU7XJrIQugpE8CiGlEuQKq2p7qNM4W3bJ/73miI/jKHXodweT1fqZxNsSo9/NWiNzTy7+j579Dz5T/7g5369AsgJI+CeBbTxcViU56UbVdbPvSm7rkgG7Zz1qMkdxg+NCv9UYiSJeB/xAQSYXQ9R4jOTo4ApkIOvjogFkAJX0RQFRA7FvF0U20FqVj+5xlovJin2tX0LGfOoG3rdTvBNDg8W911g59E7re26zl+eiAWQAlfRHAugqoYzsd87T66+lM42zRsd92FPJ2GX1PZR0wdL23jeZS9NhVII4FUNIXAayu/wEd28+7Tfz7Lq0D6tgPu2G/aSDo+PVi/fBTV6ZxOELoen/oarH8YprPR/mUKze76YBZACV9EUCqQOXtWzq20zQE28C6go792JQP+033pdI6YCpnLIas98USwCSWX8yXABbfIDqg40YdiQtYACV9EcAilWBUgcoL0Lq20zREdxE7VVTtp1w+rKGaQuuA8GEKhKz3xfLLF6eHbx+NAk1x3QGzAEr6IIC3PihGH3ucun3j07W9bhSZM6r2F6O3UQM+fr3d6I0OUTAdRbokZL3HNkrYrZP/VwftYXfRAbMASvoggLSA/8nK+pOu7V+8TkxDsJ7VBVTtt13AJ2gdEeURm5D1ng6Ptd0PTR2wiwu7WAAlfRBAOtevmsCrazsdj3XImd3IB1S1//3ydGfbhicOohgM35nAKdsh6z3SV+A/2wiuq44IsABK+iCAVAGrUy9d22kdK6V8NhtU7IedrqZedz4sUpHmnR6/AwlV72n/NE7GtmXj3WIr4sFn2HcgLICSrgtgWbSqmNjeJKY5omL/HQ+LxfcDDBKg63C5jmVDqHpP5/+5EC3swMHvQkDPFhZASdcFcMsDYtSBAw2qmNjeNJ3OERX7cTUj7HU1baXptGk+nCtC1fszNgsBPN0yAELMlds5bafTLICSrgsg3bxfdzuZie0UUMFBqbmjYj8dCItEcBfQOlbsQEioeo+8R9jrKnPA1e9jAZR0XwDFCAZTkSomttOZbnOX5x8IUbGfLjZyNWKjI7ViX5oeqt673sJGI0r8rw0sgJKuC+C43DMT22lRG/dc5E6b/S4DIATKYcbo9+13WtwOJES9pwRyl8nfrnbUsABKuiyAaMCoLE1RW1PbaR0m90BIm/2wD3a63v6HLWH4vTEj6SHqvSuxKgNRxYnmb15id1ESC6CkywJI09WmI4hMbXeV2BqbNvt9TVfpZB2UTyxC1PuTNoiAzyevcCeAALfE4ffaTKtZACVdFkBEalFRmk5wMbWdTpaxXYeJTZv9J8oGvMrRCSQEBVZiRtJD1Hu6SuGi293aiQvp8XttAiEsgJIuC+BxF4mRWlME09R2H1ObGLTZf0QxUhsML9U8wr2NFCLpIeq9r6USiqTbXNXKAijpsgBixwEqyo331feUprbT4jYqeM602U8RTNUrHFWhq0aRExgL3/Xe564hmtnYLE2wAEq6LIBtx1fZ2C7udxgUUeFcGWc/Rbt9HF81bndOKHzX+7b1Zxvod9sEp1gAJV0VQIxa2iqJje2HyiPeXZzMEYtx9vsepe2NzmlyUOwPjoHveu9ilNYEdSB4TEeXLICSrgogrdMhYtuEje2p7GiwYZz9tE4HO31w4CrRgVzy7TiBEN/1ngI9Nut045i/SizvmF7SxQIo6aoAqkRqbWynHj7nLXHj7PcdqaUOJFYk3Xe930/eInjDPX78d/CZogNZY3hJFwugpKsCqJKrZ2O7zzWeUIyz/8CVYoThK1ePOpBxI3Sf+K73SFTGFP9n037WiJfdZLclLhsBxIuWnzaq32/7N22f58rb5C3841IQbGxPYSHflnH2z5zEGpO/cw9dLOTb4LPe+wwgEejY8TdMOxCX9nvzZN1Ltr24rmE+K0Is0GhROfCMa8C2tlOel+s0kVA02a8SQLLFxUK+DT7rfYg0H9ttii7t9+bJupdse3Fdw3xWhFhQ5Wg7hNLW9mPWPT6cMRol4aTeHGmyXyWA5IKYe6p91vtQid50UIVJB+LSfm+erHvJthfXNcxnRYiF6vTA1vYF68RC9MnXxFnHsqXJfle3mLVx4gaxTnt1hA7EZ72nAJLvDAE6qsykA3FpvzdP1r1k24vj8/LThsp3cgMLw6gYbQvEtrYjhQN/J/bZdqY02Q97YJevCDCB2/Xwd2Lcsuez3oc69ZrKyeRQDpf2e/Nk3Uvqvnjd9ycmJoqfd/XZ+fDzi4rx/NfOrf3c1fNX/7hL8Xd2Oe6W2s9zfWAP7IJ9dZ+7el6827HF39n58AtqP8/1eeNnf1rYtePTn137uatnp7mTxd95zZEbaj9XfaAHNmQngGV0f18OYGEYFaNtamBre+6R4Cb7aW0J9vkkZiqRr3qPtBfYNMvyvD4VcEgF/tbR5+v7z6X93hSk7iV1X7zt+74qQixUI8DAhe2+DgwIQZ399z4iRMnFzWNtxOxAfNV7EvWjzvUv6jbRepf2e1OQupcs/6z6edv368Dnvnv6kGBrECoF9pq20eYbFWi9J8c9wXX233SfCCC9Y02Yk25ipRK5KPs6Qu8QMo0Eu7TfjycleNHyU6bOiHHfrwPfgQNd3DafAlfehQo4GB60qr1XVPFPG6Eifj6osz9UBJhwfWuaKi7Kvo7Qe8RVl3uquLTfjycDQQIY82w2l+g0YBeVYO2t4u8tiBDJtKXOfpyeHbIBhxYMwpcAkqCHmhGobPmsgwVQQgKY++GehE4Kh4tKcFUx4pweHrAiP//V2R96So9ywiU/oVOJfAkgTeldXYPZhmrKVxUWQAkcESrqFwJqwCqb+F1UgpxPh66zn4I6oQ563Sx3ncw5LWwgxIcAxgjqmO4JZgGUwBGUUe7r5I+QtJ0CXcZVJci1A6naT5v4QzZgEo29pvIXQIoAH3F2uLSeOx7G3xxo+48FUAJHmK4jpIbuKRyuKkGuHUjVfmrAofPyaNoYatQJfAjg4hvE8ss7Awog1fndNS/qZwGUwBGm6wipQQ1YNaDjqhLYbEmKSdX+qRuFHR+4IGxAR2fZwhU+BBABHaxnnvrVsOuZJqlELIASOMLXBdihOWuLEPKTr1azw1UloMhzbh1I1f4vbIQADoanjP43JJRK5HvvcRkfAhhrJmUSuGIBlMARxTrC5PRwzrKw6zCuOXadqAif3ai2IOyqEoQ6Pso1VftjNeDQuYfAhwCa5uTZYpKLygIogSNoHWGm5jpCalADVu0JXVUCVHj83YNW5RUJrtofqwHHuGjehwDGCoZRB6Kz+4QFUEKOiLEQ7RpqwKprIa4qQbH/eHIwnDF6cqJqPzVgkwM2baA9rSE7ENcCSDbESIcy6UBYACXkiNAJsK4pH4KgistKMP8MIb6hEmBdULafGvD8M8I34CfLzt8dJFVcCyDaDWxQDcC5xER8WQAl5IgYC9EuoUqgczKGy0oQeguUC8r2UwMOOQ0ts+9pYgYSavrtWgCx/ob315mGugKdBqLPuJ5BtQNhAZSQI6gAQy5Eu8QkEOGyEsTa02pD2f7Y5U8dSKhDEVwL4LEXiRnU1KY4mRR7nCqWL1QvSmcBlJAjco1kEiaRRJeVIOYIwJSy/ZTLiGP+Y0AdCMoxBK4F8Mi1YgkEpxHF4ISL9ToQFkAJOcJkCpkSdIqJzhTeZSWIPYU0oWz/AfIi9KvuipPMHboDcS2AWDvF+6MdxUC3A2EBlJQdMXuJGEbnyH7LxLvf/H31BuyyEpgsRMembP+BUgBjZQGEDiK4LPtyAC5UEKcKOn78fdUOhAVQUnbEnlOiEeBY9NzAEe54d9zJoIrLSgByOxSB7KcGHPIQhCqhOxCXZZ/C7Em3A2EBlJQdcZxcyM0pkgnoRBHdBuxaAPcrIpmD4dYf5+E/sp8SuWNcTlQG74AnRAfisuxTWD+nzQyqHQgLoKTsiBwjmcD0FBPXAoh7NPAeV0daCNeF7E8lAHbwGduKdA7VSKYNLss+dgSdiHEUHHDbigJTdoTuOkIqpHAoJAgdybSF7KcIeuzDHLATBO+x9hv+38Nl2aO94L1j59DSsWwquZQsgJKyI3KMZAJc6GTSgF0LIHUgiEjnANmvc42ATyavD9eBuCx7ygH8zkNxlz5wP7Cq/1gAJWVH5Hq8u2kDdi2AoSOZtpD9tA0ydgMO2YG4LHsKwMXeR3/y1SIXcMXm9hkcC6Ck6ohYG+JtMG3ArgUwtw6E7J+9RAQfYjdglB/eI0QH4qrsKYKueyKzD2gpSOVcTxZASdURsY5EsgH3IZg0YNcCCHJKhYH9FEGfMRk//1M3kmmDq7JPJYIOdE5EZwGUVB1x/PrHhzNGToy9HqTKkw1Yvwf2IYA5dSCwP9Y9IE2E6kBclX1KW0ipLajcicMCKKk64pgLxXTy5GvyCIRQAz7Y4BgnHwKIhoD3yeF+ENhvGkH3hU4k0wZXZZ9KBJ1QTYVhAZRUHbFe5jTlEsm0acA+BPDEDaIDWXRF+h0I7DeNoPuCAlq+OxBXZZ9KCgxBHUjbBVMsgJKqI3KLZNo0YB8CeOYt4n0+8ZU8BDCVFBhiatNvinPtfCcVuyr7VCLoBJUnLjobBwugpOqI3CKZJqfAED4EMKcOBPZTA05l++Oqr4kO5GjP/nNV9mgneN/YEXQCAwG8T9uAgAVQUucIOBCP74VoF1ADbhvy1+FDAHPqQGA/NeBUjvLXiWTa4KLsKeiAU5RS4Ty5Jtl2OTsLoKTOETlFMm16YB8CCHLJpdzx6c8u3lP3EAmfhEqFcVH2t/1QrD/vszSdzm7rT8QM5NDV49+JBVBS54jQx5ObohP2r8OXAIaKZNry1ztNFO+5//K0Rqs6m/pNcVH2FIBD6lgqUAfS1iZYACV1jshlUz8lob7nXLPpki8BpFSY1DuQf/j394zeczA84uy0BDBEB+Ki7CkFJvYpMFWoAxk3K2IBlNQ5Yu2tKNjB8ISEerY61nxdCHXbekcTvgSQFqJT70BePGNBkg04RCqMi7K3CcD5RCUVhgVQUueIi7eKTekhL6o24awtTxTnx51u2IB9CSAaBPw3eV1awlLlFQctT7IBh+hAXJR9ahF0QiUZv5MCCKPKjwp138vlfgvqgU1HCqo+0mXTfWJt6K2Jra1V+ef3XVW8Zyo5bAR1ICqb+k1xUfapRdAJ6kCQI9uEy7rvpxVpUmeQipFN34ED8aQcybRJgQEuK0EZWojGZd8p8/pF9xfvaRJB90lxKszkdHHTmi9sy55OgcGTGiodiMu676cVaVJnkIqRTd+hVJhY1/ypYJMCA1xWgiqpp8JQA04pBYZ46NdiBrL7Yn/iYlv2FICLeRFSE3Ss2HvPaw4Ouqz7/lqRBnUGqRjZ9J3UU2FsU2CAy0pQJfVUGGrAeM8Ume35VBjbst94txhlHZdgoPChYglrMDZB22Xd99eKNKgzSMXIpu9QKkyqFyTRjgGbBuyyElRJPRUmpWOc6vCdjG9b9p+6SpTvgovS9B8OaMX7NXUgLuu+v1akQZ1BTUZOTEwUn417XvSG9xUOfNncJbWfx36e/9q5xfu96tA1tZ/Hfnaa87ni/ZBqUvd57OflBy4r3u+V81fWfh772fnw84v3QznXfR77efm8pUmX7y7H3VK83zNfuHPt5+UHemBDdgJYpuk7tKk/1QuSTr9ZjFBX3Gw+QlXxjym0EJ3qDXsfLZY4BsO1t6b5fr6T8W3LfkHid2i3pcK4rPv+WpEGdQapGNn0HYT2cSzRfolGMmmKNHWjeaqEy0pQJfVTYagBp5YCQ1AH4utcStuyR4Qa75dqkLAtl9Jl3ffXijSoM0jFyHHfwTHzcGKKHHWOuET79h+ZN2CXlaAKnQqT2j5bwjaC7hvqQCDUPrAp+5RTYIi2GYjLuu+vFWkCo8qPCuO+d9jqdFNhVPY7tqHqI1OQy4Z39Lmp3wRqwG865efyJ+nhOxnfpuzp3VJMgSEoFaapA3FZ9/22Is+McwStI6S2zkEpMLY5bL4FkEZZqaXCUAoMFspTBiN8LMP4yKW0KfvUI+iAkvGbOhAWQMk4R6SaCkMN2LYH9i2AlEvpc1O/CdSAEWlNmT1OFaP8ewx3+ozDpuzRHvBeqR0iUWUfeV1sXQfCAigZ5wgq6NQimRAUvJdtD+xbAH1HMk3B++C9kKqTMidcLDoQH7mUNmWf2j0qTYhlosHw3kf+sgNhAZSMc0SqqTDUgG17YN8CmGoHQodIvHDXI+RP0sRnB2JT9gesFEsbV92VtgAes05E+m+67y87EBZAyThH0GKvz03pJrg6h823APqOZJqy3zIxNXruK/eUP0kTn7mUNmV/yJkiOJjaKTBVxqXCsABK2hwBB+LxsRBtCuUA2gZnfAug70imKXsWa0OD4TP+dif5kzTx2YGYlj1F0FM8RKIKrsbEu9Z1ICyAkjZHzF0xGu5PDoY/+EU6vR02eaNgH/yl3eK4bwGkxoInlQ6EIuh4fNtvC+VS+piBmNqeQwoMQakwdcn4LICSNkfMgwCOnHjlnWmsd5RFxZYQAoBTtfGuaDgpUI6gpy6AwNexYqa255ACQ4xLhWEBlLQ5AsNnODGVVBhXKTAgjACK6fqFiUSCKYKOwFYOAkjHirnuQExtzyUFhqAOpJqMzwIoaXNEapFMlz1wCAH4xFdEwGbdf6UhgOUIeg4CeMyFIpKJ8/dcYmp7agOCNprOpWQBlLQ5ggQnlVQYVykwIIQApNaBlBtwDgJ40gYhgF/+qttDEUxtp0MkUtsd1QTt5qom47MAStockVoqDDVgF0moIQQgtVzKcgPOQQB9pcKY2r7P0rTWdNs4fr0o74WVGRMLoKTNEalFMqkBuzjGKYQApNaB0P5kvFcOAkiRTNcdiInt1BZwSlIunLVFzEA+dhkLYC0qjkjp7DNqwC6OcQolAL4imbqUOzOQgwC2beo3xcR2CsAdfEb6KTAEzUCqqTAsgBIVR6D3hRN97MnUgRrwrDGXvegQSgAocTt2B1I04MnpPx9ym4MAAh8diIntqa2Hq0C5lNXLw1gAJSqOwPAZOwcmr/N3UbUKdz4serO9p9yMBkIJAC1Ex+5Aqg04FwH0ccOeie0uA3AhqUuFYQGUqDji89eKBlxdRwgNNeCPX+5GiEMJAO4twdl2590Wt+FUG3AuAtgUybTBxPbcUmAImoHgJkWCBVCi4ohUhv7UgLHJ2wWhBGBqk8gFRE5bTKoR9FwEsO1+CxNMbM8tBYao60BYACUqjkglkkmnwGCTtwtCCQDuLcF7x75Eu9qAcxFAEckcDN9/gbsOxMT2cgQ9JzDix3ufveXJGQgLoETVEXAgnpiRTESy8A7lobwNoQTAVyRTl2oEPRcBvPa7YgaCY6hcoWt7NYKeE5d867fFEgwOmCVYACWqjkghkuniIqQyIQUgdioMNWC8B5GLAFIHUo1k2qBrO47lxzvMi9yJmXDVXaIDwTmQBAugRNURsSOZdIxTzEZgg49Ipg6Uw/autU+OonIRQFAXybRB1/Yr78KOlMFwfkY5gAR1IHtNsQD+BaqOoHWEWBEwTHvx9yEkrggpAD4imTrgNBr8/XeenacAUgcSa/kj1xQYgmZP1IGwAEpUHUF7Mk+5Jk4FWPE1IcDHXuQukBBSAHxEMnU4a8sTxTrQ5PV+FsJ9Qx1IrACYq2sYYlHtQFgAJaqOuO57Yh1h3oo4U4DPFDdxDYaLvpKnANLx5LhRLAZ0k5mvVAjfUAcSKwWKAnAu9qDHoFr+LIASVUfQOsK+chtVaFJJhjVFTOEHxRUDMaiLoOckgK47EF3bXe5Bj0E1h5YFUKLjiOo6QkhcrwGBkALws2nRgey+OE4aRV3Z5SSAogOZHr7jLDczEB3bfQTgQoOBA2ygDoQFUKLjCB8ipIrrKCAILQAkQqFHEU1pJDkJoLBhMOpA3IiQju0kvnWXC+UCZQFQEJEFUKLjiLp1pBDQiRYpHIlkQ6x1pBBHIoVgD9kJuuhAdGy/TiZiu5p+x4DyQGfKGQgLoETHEdhKAye6WohWpakB2xJaAKgDCR1JpAg+IpllchNA6kBc7MXVsf2jl4q/u+iKuFsZbaFZFDoQFkCJjiNoHQEBiZDQvRrVBmxLaAGIlUv2EXmeY/Xv5iaAsa5DEMfBub+YKTR0ric6EBZAiY4jaB3BxZWUOtDI6ZxvuB15hhaAWB3I4WeJtdv1lST23ASQOkIXHYiO7bT2HWsXjyvKmxlYACU6jiiiYZPTw5mB70TwdbduaAGI1YHQlQbVBpybALo8lk3H9tj7uF1BHQhG0iyAEl1HzF4iTsQIeSjCW2UOluu/GVoAaCEaOzJCQX8TT7UB5yaAdCybiw5E1XaXfzM2tJaOY9FYACW6jqB1hFCHIlADLp9i4ooYAjBrsRhNPPjLMNOpcaPO3AQQuBqNqdqeymHALqB0KGRTZCGAeMny00b1+6r/RgdaiA61p9XntFHXdhccKw8lDdWB0LpjXQOOYb8tc5ZBAAfDex+x60BUbc/9EIQq1IHs+PRny5/Y46UW1RVQW6GZVGjdf1NeRwiBz8CBib9soYXoUB3IuAYcw35bjpapMDfdZ9eBqNoeK3XJFwetenS42+Rg+NxXvFn+xB4vtaiugNoKzaRC6/4bXzl5TfjMPTTxly2UkxcqqXZcA45hvy1UH2w7EFXb91kq1p9vfTBs8r8vcBwa7HnJ7I/Kn9jjpRbVFVBboZlUaN1/07StyhfUgH3sPjHxly2ht1VRAKlu90kM+22hGYFtB6Jq+1uKKeOg2MvdBagD2WnO5+RP7PFSi+oKqK3Q8Hn5UUH1e2XKGeW+mY8h++hv+cjBMrHdltAb65GyhAZct4c6hv220Jow7Wk1RcX20J19CKgDec27L5E/scdLLaorIN0K2/T9iYmJ4jPTZ9eP3V04EesIdZ+7fHb70v8Wf2uHHZ9W+3mOz+sX3V/Y9NRnvaD2c1fPM57/T8Xf+deTbq/9PMcH9QA2vemUn9d+7vL5m5ftXvytf/ngV2s/z/F55gt3Lmza9WN3/fln0AMbrAWw/IJE+b+Jup+NQ+X7ur8TUGY8riv0CeVgIRHaBya2u+CYdSKVaMsDlrkcLdB6bVMAKZb9tiAjAHbZ5IWq2B464BeCcXmhpnipRXUFpFthVb5v0ghCpQZQDpavrWMmtrsAF6TDrs9f6ze3jBpwUwAplv22UC6qTSRYxXZK+epKBJigDsTVspKXWlRXQOWfVT9v+34TKt+pEio5lITW1+kzJra7AHtyYZfvkQU14KZ7NGLZb8ux60QHsvAy80CSiu10+kyM8y99ggEF7HIVWPRWi1BI5adMXQGO+34Tqt8r4+t8vio+I8DAxHYXhIoEtzXgWPbbcoHsGE/c4FcA9zxVBPtcHsKbAnS/Do75ckGetUhi2gjodGOflcP1UL1KLAEIFQnee2p8GeUqgNSB2OwOarOdrjCYtSTOFQY+Wf11MTP4wAUsgMaNwPf0wMdibZWYAkCX7Lg+4IF46NdilD5uD3WuAkgdiE3daLOdAkjvOdfvKD0GrreX9lIAaX2pesacK7b+WFTAty73N82OKQC+D5UQDXgwquTN/stVAEHTEV+qtNlOAaSu7AEu43pw0UsBRAQTDkRE0weXfBtbxgbDw1b7SYEBMQXA955glUh9zgJou5DfZntXI8DELsfdUtjnYnmplwKIvZFw4JFr/QggIr/4/T7vH4kpAMihhMBjb6YPKIDUFAEGOQugbf1os72rEWDiVYeuaa0fqvRSAH0v5PueIoKYAvCNB8UU/+DRVM4HKse45yyAV98tDpU44mwz/7XZHiLIF5MXz1hQ2Odiit9LAQQ+F/J9BwlATAHwGeRR/d05CyDOA4R9+57mXgBpD7CrIEGKPO/VexU2usjl7a0A+hqlhUoTiS0AvtJ8VKN8OQsgsBmljbP94q1idPnOs7orgDgQFRkCLIAWjcDXOl2oROHYAqCyTmfCGpnndfT53RZAm3W6cbav2Cz8959X5HsRehuwHxsaXEzxeyuAdLSOTUZ+HUtvFMLwSc8VMLYA+NpTffLVI/9NTg9P2Tjef7kLIEVqkbKiyzjbXW8VSxGXZZ91LbJxxJ0Pi4X82UvcTlVP2iAq4NSmbgsgJdueOLLXJTQyaluayF0A6XRtkz3V42ynHEOf68+xYQGU2Dpi1kj8UFlcRstCXUQdWwDEWueguCnOJbQ21nZgbe4CaLNU0mQ7rT+P20HTBVgAJbaOoNEGRjMuQNSSTpx2HR2tkoIA7CE33GM9xgUYteD3qRxUkbsAUgcyw+Ci/ibbaVTue/05NiyAEltHuN7RQBFM2yPPVUhBAGi9yVUkHb8Hvw+/t43cBRDMOc1sS1yT7VSfJ6/r3ha4MiyAEltHIIKp2uBUoHWdz1/rPwKXggC4vvVu6SYRWMHvbaMLAmgaSW+ynVKTpm7sbgQYsABKbB2hmnOmyuT15pE9XVIQABqxucjHAgesFA34sm+3C0IXBND02Pom2w9cKUaU93R0CxzBAihx4YjdF6stuqswZ5mogJsdJ1fXkYIAuL55bK9TB8MJxbLoggDiuk/4T3fJpM52KgusQftef44NC6DEhSP2WyYE8PYf2dUaVDpEldGAXUaVm0hFAPbCwaWT9nfPUgAEaRwqdEEAKWqLR0e06myn0fiCi7odAAEsgBIXjnAVCDHtzU1JRQBwIALsvuEeux0huuuxXRBAMHeF8B8OmFClznZaj3WdmJ4iLIASF47QiTyOg9ZzQgRAQCoC4KrhUUekun7aFQE86hyRioUtgKrU2X7caOSH3+MqIp8yLIASF46gS5Js17EgoPg9oQ6hTEUAXI18KYKJ36dCVwSQMgd0OuA622kt21VOZsqwAEpcOYKOr7KpPPssFb/D9w4QIhUBMF3HKoN/N2Ny9DsmB8rrp10RQMpEUEn+Jqq20/rpnOXd3gFCsABKXDniyLVi9KEzDSlDWf0zHW8LG0dKAkDb/1RHb1VoFPl2jSsEuiKAgLb/qXbAVdtp/RR5hX2ABVDiyhFf/qpISP3CdWYViE6WOX69m3w4FVISgA/Jy74XXWFmv8k6YpcEkJZPrrxTbfmkajvWnfHvQ+SfpgALoMSVI2y3sMWogCkJwHnyaKwT1pulYCB1A/9eZwG/SwJIl3S9/wK1+le13faWudxgAZS4dITNCb0xKmBKAlAOJOmuA+L7OJJM1/ddEkA6mu1tK/VzIB/8hfD97A5egt4EC6DEpSNoGqKbRkCNf++psAvQqQmA6Trgdd8TywdIqNahSwIIdNYBy7bT+t/hHT4CvwoLoMSlI06/GdPYwfB95+lN41bKI8gPPiNsBUxNACiPb9XIHzqsvmX07yZxgrbe+mHXBJA6YJWDEcq29239D7AASlw64ls/EdOQ/TRTCT526ePF9jfTCLIpqQnARnnVI7YW6kBnMuqOHLsmgBAw1KMPX9LeAZdtp+UXmxSu3GABlLhuBFSZVC+qwfqVbgqDK1ITAPhC92AJmw38XRNA6oBVrmgg23HqC/7NgavMrtfMFRZAietGQNM4lfPoAOWvuTpOS4cUBYCuGlU9327trcLfJtHjrgkgmCf3BbeNhsn2hZeL0fMxF3b/AIQyLIAS140AR4rPGFWoucvVetRjZfoGbjILTYoCQNu6Pqx4UdIHLhD+m9qkv3zQRQFUvaqVbD9s9bZi2nzz97u//7cMC6DEdSPANAx3NKASqkxp95Xb3277YfgKmKIAUEQc29raUlrga7o/RXXKXKaLAkgXJbVtaYPttP3NJPUod1gAJT4aAUXV2qbBMae/IFUBOECeStx2KASlb5ieJt1FAQS4ZQ9+GbcODdtPvlosN3zumn5sfyvDAijx0QhI2OatGN8LI1qH76muF7omVQEgYWs7mPMAud51+R1m6RtdFUBsx4Rfxh2rBttp9nH5d8KcPpQSnRZAHeN8NQK67vHa79ZPbTHlmC2nb7EuoE5VAOCbWUvEyS73PlI/isGOGaxdzRx9x2T6C1K13xaa2s5a0nwyzvNevVfxnfk9i/4SnRVAGKZjnK9GQIcjNI1ikLM17vMQpCwASCaHf07ZWD+K+ZS8Dc3mENWU7bcFwgb/oB7W8brjby0+71PycxmXZZ9MLSKjdIzz1Qgwitl/+agSTk4Pb7x3+1EgRiy0eB9z83nKAlD4SO7vrd5QVgRKZKDJZvScsv22rJcdLAIc1REyXX6ObAWTfetdwGXZJ1eLUhBAsPAyMYp524pHt4uyLbpiNHoZCeO8FXH3XqYuAB+ROYHVadpR54rUjRM32I2euyyAQJyQs/3WzG2jAfO+8hKvFZpbDrsEC6DEZyOA6ImF+sHw8LMeG26+/3fDT18lGjXWZ2Kt/RGpC8BDv/6jvDFuerhg3bbhXf/9h+HRctsb1ljLnYoJXRdA7AyhlKyFlz8+fPCXfxy+W94f8obPPGztv5zptQBOTEwU3+GHH374ec5zniOVwYzgAlh++Tqafl6HzndNwRoMcv32Xjoo1gW3jnrmFAhhuwuQJH7ImduKfcIHjabDW3/sxn+52G8L0lyQWzlzNJJ+++pHRyPBP/TG9iZc2p+cJ3WM63NF4EbAZd9XXNqfnCd1jHvJS14i/6t/9Nl2wGXfX1zan4wAQviqD8MwjE9YZRiG6S0sgAzD9BYWQIZhegsLIMMwvYUFkGGY3sICyDBMb2EBZBimt7AAMgzTW1gAGYbpLSyADMP0liwFsGmbXN+20lXt7aPNfaJqe1/sb7LThS+y82CTsao/6xJdt69KH8u4TN/KG8Bm1XI38U9WHiUDfTokJ7puX5U+lnGZvpa3armb+CdLj/p0SE503b4qfSzjMn0rb0K13E38k6VHfTokJ2Bf+ek6dTb2wW6iXNZ9s7uK6s/ayNKLPh2SM123l8t4e/piu2q5m/gjSQ/CEHrqUDW+6d/nBvmizZ6u2NtEnX1dt3kcfbFdtdxN/JGlB306JGe6bi+X8fb0xXbVcjfxR5Ye9OmQXOibvaCPNhNs+/a48kdWHoSB1afMuM+6SN/sBX20meib7VV7qzaP+0yVftUghmGYEiyADMP0FhZAhmF6CwsgwzC9hQWQYZjewgLIMExvYQFkGKa3sAAyDNNbWAAZhuktLIAMw/QWFkCGYXoLCyDDML2FBZBhmN7CAsgwTG9hAWQYprewADIM01tYABmG6S0sgAzD9JTh8P8BNJ410ckOLfgAAAAASUVORK5CYII=" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/latex": [ + "$$f(x) = sin(x)$$" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let x = linspace (-10.0, 10.0, 0.1)\n", + "plot(x, fun y -> sin(y)) |> Display\n", + "Util.Math \"f(x) = sin(x)\" |> Display" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(** This actually does animation when running *)\n", + "open System.Threading\n", + "\n", + "for i in 1 .. 100 do\n", + "\n", + " Clear() |> ignore\n", + "\n", + " let x = linspace(-10.0 + float(i), float(i), 0.1) |> Seq.toArray\n", + " let c1 = plot(x, sin)\n", + " let c2 = plot(x, cos)\n", + "\n", + " FSharp.Charting.Chart.Combine([c1; c2])\n", + " |> FSharp.Charting.Chart.WithSize(640, 480)\n", + " |> Display\n", + " |> ignore\n", + " \n", + " Thread.Sleep(20)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "IFSharp", + "language": "fsharp", + "name": "ifsharp" + }, + "language": "fsharp", + "language_info": { + "codemirror_mode": "", + "file_extension": ".fs", + "mimetype": "text/x-fsharp", + "name": "fsharp", + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "4.3.1.0" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Feature Notebook format 4_0.ipynb b/Feature Notebook format 4_0.ipynb index 21865a9..3b3176e 100644 --- a/Feature Notebook format 4_0.ipynb +++ b/Feature Notebook format 4_0.ipynb @@ -104,7 +104,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 2, "metadata": { "collapsed": false }, @@ -125,7 +125,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 1, "metadata": { "collapsed": false }, @@ -154,40 +154,129 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1+11" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/nmurphy/coding/ifsharp/IfSharp_git/bin/FSCharting.Gtk.fsx(6,1): warning FS0211: The search directory '/home/nmurphy/coding/ifsharp/IfSharp_git/bin/../../../bin/' could not be found\n", + "\n", + "\n", + "/home/nmurphy/coding/ifsharp/IfSharp_git/bin/FSCharting.Gtk.fsx(4,1): warning FS0211: The search directory '/home/nmurphy/coding/ifsharp/IfSharp_git/bin/../../../bin/packages/FSharp.Charting.Gtk/lib/net40' could not be found" + ] + } + ], + "source": [ + "#load \"FSCharting.Gtk.fsx\" // Mac/Linux\n", + "//#load \"FSCharting.fsx\" // Windows" + ] + }, + { + "cell_type": "code", + "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "data": { - "image/png": "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" + "text/plain": [ + "" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "IfSharp.FSCharting.Initialize()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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" }, "metadata": {}, "output_type": "display_data" } ], "source": [ - "Chart.Bar(data) |> Display" + "FSharp.Charting.Chart.Bar(data) |> Display " ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { - "image/png": "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" + "text/html": [ + "
Item1Item2
110
25
320
41
" + ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ - "Chart.Line(data) |> Display" + "let data2 = [ (1, 10); (2, 5); (3, 20); (4, 1); ]\n", + "Util.Table(data2) |> Display" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "FSharp.Charting.Chart.Line(data2) |> Display" ] }, { @@ -199,7 +288,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": { "collapsed": false }, @@ -219,7 +308,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "metadata": { "collapsed": false }, @@ -232,19 +321,19 @@ " yield (x, cb(x))\n", " }\n", "\n", - " Chart.Line (results)" + " FSharp.Charting.Chart.Line (results)" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAUAAAADwCAYAAABxLb1rAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAACuzSURBVHhe7Z15sF1VlcYtirLU0tZube22LVv/oB0p7erCLtpWHwmJMgZMBAKIiIiKioRJbaOxVRSHl8gLCUkggTCGEAaZwmAAg0FsojIoIIgDdItDab9LoHG+fb+z95KT4zn37Hk4Z/2qTonv3rx31tp7f3tYa+/9lCHDMExPYQFkGKa3sAAyDNNbWAAZhuktLIAMw/QWFkCGYXoLCyDDML2FBZBhmN7CAsgwTG9hAWQYprewADIM01tYABmG6S3JCeBTnsKazDBMGJJSG4gfCyDDMKFIRm1I+FgAGYYJRXJqwwLIMEwoWAAZhukt2QngxMRE8R08O+yww5//u6/P816993DXhXcPd/vS9PD1ix4YvugN76v9Hj/1z9+/7rDhG0/+n5H/BsN/W/i94bNfumvt9/ipf/5ul0OGbzrl50X92+X4LcOnPusFtd/z9UAPbMh6BKjz3a4B27f++PfDWYuni8qHBiz+d3r4zR/+Xn6ru7go+zVff2I4Y7Liv8nB8Hs//YP8RpqkUu8XXfF4yXfi2X1UH382/Sf5DT+4tD85BdExLpWKEIMddnzacPaXRaNdsP6xotItvPzx4cTo/88YNeJ7H0m7EdtiW/a/euxPw9lLhP/+47LHi/9/5Nptxf/fZ+lg+LPBH+U30yOFeo9OAmIH0Vu1+YnCf+9YI/x31Lnb5Lf8wAIo6bMAvmL+iqKyzT/jUfkTwVHnPFb8/MOXPC5/0k1sy/4dax4t/HTShif99NvRwHnuctGIP33V/8mfpkcK9X7vKdF5LLvpCfkT4b99TxM/n7zBn/86KYAwqvq0ofKdLoLeFpUMzwM/336kh8/eLEeGt/3wd/Kn3cOm7OEzMV0bFP4qc89oZAPfYXRY/SwVYtf7Wx743XDGyH9vOXVQiF6Z9Vt/U/hv/+WP/sVnrnBpf9YK0lcBRK+Lqe4H19VPNRaOpnSohO862+9UJCY2Zf/xy4V/pjb9Rv5kez5wvhhFL7gozVF07Hp/mBw9XzwSuzretXZbUT9Pv9nPKJAFUNJHAUSvOmeZqIDV0R+Bkcus0QgGI8FURzG2mJb9g7/4Y+E7+KfJNxg54zt71oxwUiBmvSffYITc5JuLt/62+M7+ywfyJ25hAZT0UQCnNj1RVK7XL7pf/qQerGHhe1Ob0l3LssG07E+7UfgFa6XjePc5Yi0Q073UiFnvl9wg6t8XNjbXKwjjXnKNsKmTtoEFUNJHATzhYjF9e+lbPi5/Us+135WjmFFF7CKmZU+RyjZhw/Rut8npYjqXGrHqPYQNEXL47ye/Gh8lJ6EsB0lcwQIo6ZsAbvvNkwEOJJy2sbesrI8knNJhiknZ/0BOf9GI26a2mB4jJ3Dm4vSmwbHqPQU4DlrV3ikgTQbfRaqMa1gAJX0TwHW3iwr4oXWPKdlOvfD536xfrM4Zk7L/6KUyRaiU+jKOBReJ72+6N61pcKx6/+FLhD8+e42a/6gD/s5DbnsQFkBJ3wTwsNVi+oZphYrt2BGC7/tOTI2BSdkjKo5R3ZV3/Vb+ZDzFNHjkv89fm9Y6aqx6rzr9JVBP8f2zt7idBrMASvokgJiGYfqL9AJMaVVsx7+ZWWz1GnRuGqxb9sWUduQ7+FB1SgufFf9mTMQzBjHqPU1pq4n348DID/8GASWXsABK+iSA1cqkavsxF4ppy8bvqo16ckG37GE//KC7QwZBJPy7zfenMw2OUe8XXy+i5zpBDXQab8Ga9WjU7XJrIQugpE8CiGlEuQKq2p7qNM4W3bJ/73miI/jKHXodweT1fqZxNsSo9/NWiNzTy7+j579Dz5T/7g5369AsgJI+CeBbTxcViU56UbVdbPvSm7rkgG7Zz1qMkdxg+NCv9UYiSJeB/xAQSYXQ9R4jOTo4ApkIOvjogFkAJX0RQFRA7FvF0U20FqVj+5xlovJin2tX0LGfOoG3rdTvBNDg8W911g59E7re26zl+eiAWQAlfRHAugqoYzsd87T66+lM42zRsd92FPJ2GX1PZR0wdL23jeZS9NhVII4FUNIXAayu/wEd28+7Tfz7Lq0D6tgPu2G/aSDo+PVi/fBTV6ZxOELoen/oarH8YprPR/mUKze76YBZACV9EUCqQOXtWzq20zQE28C6go792JQP+033pdI6YCpnLIas98USwCSWX8yXABbfIDqg40YdiQtYACV9EcAilWBUgcoL0Lq20zREdxE7VVTtp1w+rKGaQuuA8GEKhKz3xfLLF6eHbx+NAk1x3QGzAEr6IIC3PihGH3ucun3j07W9bhSZM6r2F6O3UQM+fr3d6I0OUTAdRbokZL3HNkrYrZP/VwftYXfRAbMASvoggLSA/8nK+pOu7V+8TkxDsJ7VBVTtt13AJ2gdEeURm5D1ng6Ptd0PTR2wiwu7WAAlfRBAOtevmsCrazsdj3XImd3IB1S1//3ydGfbhicOohgM35nAKdsh6z3SV+A/2wiuq44IsABK+iCAVAGrUy9d22kdK6V8NhtU7IedrqZedz4sUpHmnR6/AwlV72n/NE7GtmXj3WIr4sFn2HcgLICSrgtgWbSqmNjeJKY5omL/HQ+LxfcDDBKg63C5jmVDqHpP5/+5EC3swMHvQkDPFhZASdcFcMsDYtSBAw2qmNjeNJ3OERX7cTUj7HU1baXptGk+nCtC1fszNgsBPN0yAELMlds5bafTLICSrgsg3bxfdzuZie0UUMFBqbmjYj8dCItEcBfQOlbsQEioeo+8R9jrKnPA1e9jAZR0XwDFCAZTkSomttOZbnOX5x8IUbGfLjZyNWKjI7ViX5oeqt673sJGI0r8rw0sgJKuC+C43DMT22lRG/dc5E6b/S4DIATKYcbo9+13WtwOJES9pwRyl8nfrnbUsABKuiyAaMCoLE1RW1PbaR0m90BIm/2wD3a63v6HLWH4vTEj6SHqvSuxKgNRxYnmb15id1ESC6CkywJI09WmI4hMbXeV2BqbNvt9TVfpZB2UTyxC1PuTNoiAzyevcCeAALfE4ffaTKtZACVdFkBEalFRmk5wMbWdTpaxXYeJTZv9J8oGvMrRCSQEBVZiRtJD1Hu6SuGi293aiQvp8XttAiEsgJIuC+BxF4mRWlME09R2H1ObGLTZf0QxUhsML9U8wr2NFCLpIeq9r6USiqTbXNXKAijpsgBixwEqyo331feUprbT4jYqeM602U8RTNUrHFWhq0aRExgL3/Xe564hmtnYLE2wAEq6LIBtx1fZ2C7udxgUUeFcGWc/Rbt9HF81bndOKHzX+7b1Zxvod9sEp1gAJV0VQIxa2iqJje2HyiPeXZzMEYtx9vsepe2NzmlyUOwPjoHveu9ilNYEdSB4TEeXLICSrgogrdMhYtuEje2p7GiwYZz9tE4HO31w4CrRgVzy7TiBEN/1ngI9Nut045i/SizvmF7SxQIo6aoAqkRqbWynHj7nLXHj7PcdqaUOJFYk3Xe930/eInjDPX78d/CZogNZY3hJFwugpKsCqJKrZ2O7zzWeUIyz/8CVYoThK1ePOpBxI3Sf+K73SFTGFP9n037WiJfdZLclLhsBxIuWnzaq32/7N22f58rb5C3841IQbGxPYSHflnH2z5zEGpO/cw9dLOTb4LPe+wwgEejY8TdMOxCX9nvzZN1Ltr24rmE+K0Is0GhROfCMa8C2tlOel+s0kVA02a8SQLLFxUK+DT7rfYg0H9ttii7t9+bJupdse3Fdw3xWhFhQ5Wg7hNLW9mPWPT6cMRol4aTeHGmyXyWA5IKYe6p91vtQid50UIVJB+LSfm+erHvJthfXNcxnRYiF6vTA1vYF68RC9MnXxFnHsqXJfle3mLVx4gaxTnt1hA7EZ72nAJLvDAE6qsykA3FpvzdP1r1k24vj8/LThsp3cgMLw6gYbQvEtrYjhQN/J/bZdqY02Q97YJevCDCB2/Xwd2Lcsuez3oc69ZrKyeRQDpf2e/Nk3Uvqvnjd9ycmJoqfd/XZ+fDzi4rx/NfOrf3c1fNX/7hL8Xd2Oe6W2s9zfWAP7IJ9dZ+7el6827HF39n58AtqP8/1eeNnf1rYtePTn137uatnp7mTxd95zZEbaj9XfaAHNmQngGV0f18OYGEYFaNtamBre+6R4Cb7aW0J9vkkZiqRr3qPtBfYNMvyvD4VcEgF/tbR5+v7z6X93hSk7iV1X7zt+74qQixUI8DAhe2+DgwIQZ399z4iRMnFzWNtxOxAfNV7EvWjzvUv6jbRepf2e1OQupcs/6z6edv368Dnvnv6kGBrECoF9pq20eYbFWi9J8c9wXX233SfCCC9Y02Yk25ipRK5KPs6Qu8QMo0Eu7TfjycleNHyU6bOiHHfrwPfgQNd3DafAlfehQo4GB60qr1XVPFPG6Eifj6osz9UBJhwfWuaKi7Kvo7Qe8RVl3uquLTfjycDQQIY82w2l+g0YBeVYO2t4u8tiBDJtKXOfpyeHbIBhxYMwpcAkqCHmhGobPmsgwVQQgKY++GehE4Kh4tKcFUx4pweHrAiP//V2R96So9ywiU/oVOJfAkgTeldXYPZhmrKVxUWQAkcESrqFwJqwCqb+F1UgpxPh66zn4I6oQ563Sx3ncw5LWwgxIcAxgjqmO4JZgGUwBGUUe7r5I+QtJ0CXcZVJci1A6naT5v4QzZgEo29pvIXQIoAH3F2uLSeOx7G3xxo+48FUAJHmK4jpIbuKRyuKkGuHUjVfmrAofPyaNoYatQJfAjg4hvE8ss7Awog1fndNS/qZwGUwBGm6wipQQ1YNaDjqhLYbEmKSdX+qRuFHR+4IGxAR2fZwhU+BBABHaxnnvrVsOuZJqlELIASOMLXBdihOWuLEPKTr1azw1UloMhzbh1I1f4vbIQADoanjP43JJRK5HvvcRkfAhhrJmUSuGIBlMARxTrC5PRwzrKw6zCuOXadqAif3ai2IOyqEoQ6Pso1VftjNeDQuYfAhwCa5uTZYpKLygIogSNoHWGm5jpCalADVu0JXVUCVHj83YNW5RUJrtofqwHHuGjehwDGCoZRB6Kz+4QFUEKOiLEQ7RpqwKprIa4qQbH/eHIwnDF6cqJqPzVgkwM2baA9rSE7ENcCSDbESIcy6UBYACXkiNAJsK4pH4KgistKMP8MIb6hEmBdULafGvD8M8I34CfLzt8dJFVcCyDaDWxQDcC5xER8WQAl5IgYC9EuoUqgczKGy0oQeguUC8r2UwMOOQ0ts+9pYgYSavrtWgCx/ob315mGugKdBqLPuJ5BtQNhAZSQI6gAQy5Eu8QkEOGyEsTa02pD2f7Y5U8dSKhDEVwL4LEXiRnU1KY4mRR7nCqWL1QvSmcBlJAjco1kEiaRRJeVIOYIwJSy/ZTLiGP+Y0AdCMoxBK4F8Mi1YgkEpxHF4ISL9ToQFkAJOcJkCpkSdIqJzhTeZSWIPYU0oWz/AfIi9KvuipPMHboDcS2AWDvF+6MdxUC3A2EBlJQdMXuJGEbnyH7LxLvf/H31BuyyEpgsRMembP+BUgBjZQGEDiK4LPtyAC5UEKcKOn78fdUOhAVQUnbEnlOiEeBY9NzAEe54d9zJoIrLSgByOxSB7KcGHPIQhCqhOxCXZZ/C7Em3A2EBlJQdcZxcyM0pkgnoRBHdBuxaAPcrIpmD4dYf5+E/sp8SuWNcTlQG74AnRAfisuxTWD+nzQyqHQgLoKTsiBwjmcD0FBPXAoh7NPAeV0daCNeF7E8lAHbwGduKdA7VSKYNLss+dgSdiHEUHHDbigJTdoTuOkIqpHAoJAgdybSF7KcIeuzDHLATBO+x9hv+38Nl2aO94L1j59DSsWwquZQsgJKyI3KMZAJc6GTSgF0LIHUgiEjnANmvc42ATyavD9eBuCx7ygH8zkNxlz5wP7Cq/1gAJWVH5Hq8u2kDdi2AoSOZtpD9tA0ydgMO2YG4LHsKwMXeR3/y1SIXcMXm9hkcC6Ck6ohYG+JtMG3ArgUwtw6E7J+9RAQfYjdglB/eI0QH4qrsKYKueyKzD2gpSOVcTxZASdURsY5EsgH3IZg0YNcCCHJKhYH9FEGfMRk//1M3kmmDq7JPJYIOdE5EZwGUVB1x/PrHhzNGToy9HqTKkw1Yvwf2IYA5dSCwP9Y9IE2E6kBclX1KW0ipLajcicMCKKk64pgLxXTy5GvyCIRQAz7Y4BgnHwKIhoD3yeF+ENhvGkH3hU4k0wZXZZ9KBJ1QTYVhAZRUHbFe5jTlEsm0acA+BPDEDaIDWXRF+h0I7DeNoPuCAlq+OxBXZZ9KCgxBHUjbBVMsgJKqI3KLZNo0YB8CeOYt4n0+8ZU8BDCVFBhiatNvinPtfCcVuyr7VCLoBJUnLjobBwugpOqI3CKZJqfAED4EMKcOBPZTA05l++Oqr4kO5GjP/nNV9mgneN/YEXQCAwG8T9uAgAVQUucIOBCP74VoF1ADbhvy1+FDAHPqQGA/NeBUjvLXiWTa4KLsKeiAU5RS4Ty5Jtl2OTsLoKTOETlFMm16YB8CCHLJpdzx6c8u3lP3EAmfhEqFcVH2t/1QrD/vszSdzm7rT8QM5NDV49+JBVBS54jQx5ObohP2r8OXAIaKZNry1ztNFO+5//K0Rqs6m/pNcVH2FIBD6lgqUAfS1iZYACV1jshlUz8lob7nXLPpki8BpFSY1DuQf/j394zeczA84uy0BDBEB+Ki7CkFJvYpMFWoAxk3K2IBlNQ5Yu2tKNjB8ISEerY61nxdCHXbekcTvgSQFqJT70BePGNBkg04RCqMi7K3CcD5RCUVhgVQUueIi7eKTekhL6o24awtTxTnx51u2IB9CSAaBPw3eV1awlLlFQctT7IBh+hAXJR9ahF0QiUZv5MCCKPKjwp138vlfgvqgU1HCqo+0mXTfWJt6K2Jra1V+ef3XVW8Zyo5bAR1ICqb+k1xUfapRdAJ6kCQI9uEy7rvpxVpUmeQipFN34ED8aQcybRJgQEuK0EZWojGZd8p8/pF9xfvaRJB90lxKszkdHHTmi9sy55OgcGTGiodiMu676cVaVJnkIqRTd+hVJhY1/ypYJMCA1xWgiqpp8JQA04pBYZ46NdiBrL7Yn/iYlv2FICLeRFSE3Ss2HvPaw4Ouqz7/lqRBnUGqRjZ9J3UU2FsU2CAy0pQJfVUGGrAeM8Ume35VBjbst94txhlHZdgoPChYglrMDZB22Xd99eKNKgzSMXIpu9QKkyqFyTRjgGbBuyyElRJPRUmpWOc6vCdjG9b9p+6SpTvgovS9B8OaMX7NXUgLuu+v1akQZ1BTUZOTEwUn417XvSG9xUOfNncJbWfx36e/9q5xfu96tA1tZ/Hfnaa87ni/ZBqUvd57OflBy4r3u+V81fWfh772fnw84v3QznXfR77efm8pUmX7y7H3VK83zNfuHPt5+UHemBDdgJYpuk7tKk/1QuSTr9ZjFBX3Gw+QlXxjym0EJ3qDXsfLZY4BsO1t6b5fr6T8W3LfkHid2i3pcK4rPv+WpEGdQapGNn0HYT2cSzRfolGMmmKNHWjeaqEy0pQJfVTYagBp5YCQ1AH4utcStuyR4Qa75dqkLAtl9Jl3ffXijSoM0jFyHHfwTHzcGKKHHWOuET79h+ZN2CXlaAKnQqT2j5bwjaC7hvqQCDUPrAp+5RTYIi2GYjLuu+vFWkCo8qPCuO+d9jqdFNhVPY7tqHqI1OQy4Z39Lmp3wRqwG865efyJ+nhOxnfpuzp3VJMgSEoFaapA3FZ9/22Is+McwStI6S2zkEpMLY5bL4FkEZZqaXCUAoMFspTBiN8LMP4yKW0KfvUI+iAkvGbOhAWQMk4R6SaCkMN2LYH9i2AlEvpc1O/CdSAEWlNmT1OFaP8ewx3+ozDpuzRHvBeqR0iUWUfeV1sXQfCAigZ5wgq6NQimRAUvJdtD+xbAH1HMk3B++C9kKqTMidcLDoQH7mUNmWf2j0qTYhlosHw3kf+sgNhAZSMc0SqqTDUgG17YN8CmGoHQodIvHDXI+RP0sRnB2JT9gesFEsbV92VtgAes05E+m+67y87EBZAyThH0GKvz03pJrg6h823APqOZJqy3zIxNXruK/eUP0kTn7mUNmV/yJkiOJjaKTBVxqXCsABK2hwBB+LxsRBtCuUA2gZnfAug70imKXsWa0OD4TP+dif5kzTx2YGYlj1F0FM8RKIKrsbEu9Z1ICyAkjZHzF0xGu5PDoY/+EU6vR02eaNgH/yl3eK4bwGkxoInlQ6EIuh4fNtvC+VS+piBmNqeQwoMQakwdcn4LICSNkfMgwCOnHjlnWmsd5RFxZYQAoBTtfGuaDgpUI6gpy6AwNexYqa255ACQ4xLhWEBlLQ5AsNnODGVVBhXKTAgjACK6fqFiUSCKYKOwFYOAkjHirnuQExtzyUFhqAOpJqMzwIoaXNEapFMlz1wCAH4xFdEwGbdf6UhgOUIeg4CeMyFIpKJ8/dcYmp7agOCNprOpWQBlLQ5ggQnlVQYVykwIIQApNaBlBtwDgJ40gYhgF/+qttDEUxtp0MkUtsd1QTt5qom47MAStockVoqDDVgF0moIQQgtVzKcgPOQQB9pcKY2r7P0rTWdNs4fr0o74WVGRMLoKTNEalFMqkBuzjGKYQApNaB0P5kvFcOAkiRTNcdiInt1BZwSlIunLVFzEA+dhkLYC0qjkjp7DNqwC6OcQolAL4imbqUOzOQgwC2beo3xcR2CsAdfEb6KTAEzUCqqTAsgBIVR6D3hRN97MnUgRrwrDGXvegQSgAocTt2B1I04MnpPx9ym4MAAh8diIntqa2Hq0C5lNXLw1gAJSqOwPAZOwcmr/N3UbUKdz4serO9p9yMBkIJAC1Ex+5Aqg04FwH0ccOeie0uA3AhqUuFYQGUqDji89eKBlxdRwgNNeCPX+5GiEMJAO4twdl2590Wt+FUG3AuAtgUybTBxPbcUmAImoHgJkWCBVCi4ohUhv7UgLHJ2wWhBGBqk8gFRE5bTKoR9FwEsO1+CxNMbM8tBYao60BYACUqjkglkkmnwGCTtwtCCQDuLcF7x75Eu9qAcxFAEckcDN9/gbsOxMT2cgQ9JzDix3ufveXJGQgLoETVEXAgnpiRTESy8A7lobwNoQTAVyRTl2oEPRcBvPa7YgaCY6hcoWt7NYKeE5d867fFEgwOmCVYACWqjkghkuniIqQyIQUgdioMNWC8B5GLAFIHUo1k2qBrO47lxzvMi9yJmXDVXaIDwTmQBAugRNURsSOZdIxTzEZgg49Ipg6Uw/autU+OonIRQFAXybRB1/Yr78KOlMFwfkY5gAR1IHtNsQD+BaqOoHWEWBEwTHvx9yEkrggpAD4imTrgNBr8/XeenacAUgcSa/kj1xQYgmZP1IGwAEpUHUF7Mk+5Jk4FWPE1IcDHXuQukBBSAHxEMnU4a8sTxTrQ5PV+FsJ9Qx1IrACYq2sYYlHtQFgAJaqOuO57Yh1h3oo4U4DPFDdxDYaLvpKnANLx5LhRLAZ0k5mvVAjfUAcSKwWKAnAu9qDHoFr+LIASVUfQOsK+chtVaFJJhjVFTOEHxRUDMaiLoOckgK47EF3bXe5Bj0E1h5YFUKLjiOo6QkhcrwGBkALws2nRgey+OE4aRV3Z5SSAogOZHr7jLDczEB3bfQTgQoOBA2ygDoQFUKLjCB8ipIrrKCAILQAkQqFHEU1pJDkJoLBhMOpA3IiQju0kvnWXC+UCZQFQEJEFUKLjiLp1pBDQiRYpHIlkQ6x1pBBHIoVgD9kJuuhAdGy/TiZiu5p+x4DyQGfKGQgLoETHEdhKAye6WohWpakB2xJaAKgDCR1JpAg+IpllchNA6kBc7MXVsf2jl4q/u+iKuFsZbaFZFDoQFkCJjiNoHQEBiZDQvRrVBmxLaAGIlUv2EXmeY/Xv5iaAsa5DEMfBub+YKTR0ric6EBZAiY4jaB3BxZWUOtDI6ZxvuB15hhaAWB3I4WeJtdv1lST23ASQOkIXHYiO7bT2HWsXjyvKmxlYACU6jiiiYZPTw5mB70TwdbduaAGI1YHQlQbVBpybALo8lk3H9tj7uF1BHQhG0iyAEl1HzF4iTsQIeSjCW2UOluu/GVoAaCEaOzJCQX8TT7UB5yaAdCybiw5E1XaXfzM2tJaOY9FYACW6jqB1hFCHIlADLp9i4ooYAjBrsRhNPPjLMNOpcaPO3AQQuBqNqdqeymHALqB0KGRTZCGAeMny00b1+6r/RgdaiA61p9XntFHXdhccKw8lDdWB0LpjXQOOYb8tc5ZBAAfDex+x60BUbc/9EIQq1IHs+PRny5/Y46UW1RVQW6GZVGjdf1NeRwiBz8CBib9soYXoUB3IuAYcw35bjpapMDfdZ9eBqNoeK3XJFwetenS42+Rg+NxXvFn+xB4vtaiugNoKzaRC6/4bXzl5TfjMPTTxly2UkxcqqXZcA45hvy1UH2w7EFXb91kq1p9vfTBs8r8vcBwa7HnJ7I/Kn9jjpRbVFVBboZlUaN1/07StyhfUgH3sPjHxly2ht1VRAKlu90kM+22hGYFtB6Jq+1uKKeOg2MvdBagD2WnO5+RP7PFSi+oKqK3Q8Hn5UUH1e2XKGeW+mY8h++hv+cjBMrHdltAb65GyhAZct4c6hv220Jow7Wk1RcX20J19CKgDec27L5E/scdLLaorIN0K2/T9iYmJ4jPTZ9eP3V04EesIdZ+7fHb70v8Wf2uHHZ9W+3mOz+sX3V/Y9NRnvaD2c1fPM57/T8Xf+deTbq/9PMcH9QA2vemUn9d+7vL5m5ftXvytf/ngV2s/z/F55gt3Lmza9WN3/fln0AMbrAWw/IJE+b+Jup+NQ+X7ur8TUGY8riv0CeVgIRHaBya2u+CYdSKVaMsDlrkcLdB6bVMAKZb9tiAjAHbZ5IWq2B464BeCcXmhpnipRXUFpFthVb5v0ghCpQZQDpavrWMmtrsAF6TDrs9f6ze3jBpwUwAplv22UC6qTSRYxXZK+epKBJigDsTVspKXWlRXQOWfVT9v+34TKt+pEio5lITW1+kzJra7AHtyYZfvkQU14KZ7NGLZb8ux60QHsvAy80CSiu10+kyM8y99ggEF7HIVWPRWi1BI5adMXQGO+34Tqt8r4+t8vio+I8DAxHYXhIoEtzXgWPbbcoHsGE/c4FcA9zxVBPtcHsKbAnS/Do75ckGetUhi2gjodGOflcP1UL1KLAEIFQnee2p8GeUqgNSB2OwOarOdrjCYtSTOFQY+Wf11MTP4wAUsgMaNwPf0wMdibZWYAkCX7Lg+4IF46NdilD5uD3WuAkgdiE3daLOdAkjvOdfvKD0GrreX9lIAaX2pesacK7b+WFTAty73N82OKQC+D5UQDXgwquTN/stVAEHTEV+qtNlOAaSu7AEu43pw0UsBRAQTDkRE0weXfBtbxgbDw1b7SYEBMQXA955glUh9zgJou5DfZntXI8DELsfdUtjnYnmplwKIvZFw4JFr/QggIr/4/T7vH4kpAMihhMBjb6YPKIDUFAEGOQugbf1os72rEWDiVYeuaa0fqvRSAH0v5PueIoKYAvCNB8UU/+DRVM4HKse45yyAV98tDpU44mwz/7XZHiLIF5MXz1hQ2Odiit9LAQQ+F/J9BwlATAHwGeRR/d05CyDOA4R9+57mXgBpD7CrIEGKPO/VexU2usjl7a0A+hqlhUoTiS0AvtJ8VKN8OQsgsBmljbP94q1idPnOs7orgDgQFRkCLIAWjcDXOl2oROHYAqCyTmfCGpnndfT53RZAm3W6cbav2Cz8959X5HsRehuwHxsaXEzxeyuAdLSOTUZ+HUtvFMLwSc8VMLYA+NpTffLVI/9NTg9P2Tjef7kLIEVqkbKiyzjbXW8VSxGXZZ91LbJxxJ0Pi4X82UvcTlVP2iAq4NSmbgsgJdueOLLXJTQyaluayF0A6XRtkz3V42ynHEOf68+xYQGU2Dpi1kj8UFlcRstCXUQdWwDEWueguCnOJbQ21nZgbe4CaLNU0mQ7rT+P20HTBVgAJbaOoNEGRjMuQNSSTpx2HR2tkoIA7CE33GM9xgUYteD3qRxUkbsAUgcyw+Ci/ibbaVTue/05NiyAEltHuN7RQBFM2yPPVUhBAGi9yVUkHb8Hvw+/t43cBRDMOc1sS1yT7VSfJ6/r3ha4MiyAEltHIIKp2uBUoHWdz1/rPwKXggC4vvVu6SYRWMHvbaMLAmgaSW+ynVKTpm7sbgQYsABKbB2hmnOmyuT15pE9XVIQABqxucjHAgesFA34sm+3C0IXBND02Pom2w9cKUaU93R0CxzBAihx4YjdF6stuqswZ5mogJsdJ1fXkYIAuL55bK9TB8MJxbLoggDiuk/4T3fJpM52KgusQftef44NC6DEhSP2WyYE8PYf2dUaVDpEldGAXUaVm0hFAPbCwaWT9nfPUgAEaRwqdEEAKWqLR0e06myn0fiCi7odAAEsgBIXjnAVCDHtzU1JRQBwIALsvuEeux0huuuxXRBAMHeF8B8OmFClznZaj3WdmJ4iLIASF47QiTyOg9ZzQgRAQCoC4KrhUUekun7aFQE86hyRioUtgKrU2X7caOSH3+MqIp8yLIASF46gS5Js17EgoPg9oQ6hTEUAXI18KYKJ36dCVwSQMgd0OuA622kt21VOZsqwAEpcOYKOr7KpPPssFb/D9w4QIhUBMF3HKoN/N2Ny9DsmB8rrp10RQMpEUEn+Jqq20/rpnOXd3gFCsABKXDniyLVi9KEzDSlDWf0zHW8LG0dKAkDb/1RHb1VoFPl2jSsEuiKAgLb/qXbAVdtp/RR5hX2ABVDiyhFf/qpISP3CdWYViE6WOX69m3w4FVISgA/Jy74XXWFmv8k6YpcEkJZPrrxTbfmkajvWnfHvQ+SfpgALoMSVI2y3sMWogCkJwHnyaKwT1pulYCB1A/9eZwG/SwJIl3S9/wK1+le13faWudxgAZS4dITNCb0xKmBKAlAOJOmuA+L7OJJM1/ddEkA6mu1tK/VzIB/8hfD97A5egt4EC6DEpSNoGqKbRkCNf++psAvQqQmA6Trgdd8TywdIqNahSwIIdNYBy7bT+t/hHT4CvwoLoMSlI06/GdPYwfB95+lN41bKI8gPPiNsBUxNACiPb9XIHzqsvmX07yZxgrbe+mHXBJA6YJWDEcq29239D7AASlw64ls/EdOQ/TRTCT526ePF9jfTCLIpqQnARnnVI7YW6kBnMuqOHLsmgBAw1KMPX9LeAZdtp+UXmxSu3GABlLhuBFSZVC+qwfqVbgqDK1ITAPhC92AJmw38XRNA6oBVrmgg23HqC/7NgavMrtfMFRZAietGQNM4lfPoAOWvuTpOS4cUBYCuGlU9327trcLfJtHjrgkgmCf3BbeNhsn2hZeL0fMxF3b/AIQyLIAS140AR4rPGFWoucvVetRjZfoGbjILTYoCQNu6Pqx4UdIHLhD+m9qkv3zQRQFUvaqVbD9s9bZi2nzz97u//7cMC6DEdSPANAx3NKASqkxp95Xb3277YfgKmKIAUEQc29raUlrga7o/RXXKXKaLAkgXJbVtaYPttP3NJPUod1gAJT4aAUXV2qbBMae/IFUBOECeStx2KASlb5ieJt1FAQS4ZQ9+GbcODdtPvlosN3zumn5sfyvDAijx0QhI2OatGN8LI1qH76muF7omVQEgYWs7mPMAud51+R1m6RtdFUBsx4Rfxh2rBttp9nH5d8KcPpQSnRZAHeN8NQK67vHa79ZPbTHlmC2nb7EuoE5VAOCbWUvEyS73PlI/isGOGaxdzRx9x2T6C1K13xaa2s5a0nwyzvNevVfxnfk9i/4SnRVAGKZjnK9GQIcjNI1ikLM17vMQpCwASCaHf07ZWD+K+ZS8Dc3mENWU7bcFwgb/oB7W8brjby0+71PycxmXZZ9MLSKjdIzz1Qgwitl/+agSTk4Pb7x3+1EgRiy0eB9z83nKAlD4SO7vrd5QVgRKZKDJZvScsv22rJcdLAIc1REyXX6ObAWTfetdwGXZJ1eLUhBAsPAyMYp524pHt4uyLbpiNHoZCeO8FXH3XqYuAB+ROYHVadpR54rUjRM32I2euyyAQJyQs/3WzG2jAfO+8hKvFZpbDrsEC6DEZyOA6ImF+sHw8LMeG26+/3fDT18lGjXWZ2Kt/RGpC8BDv/6jvDFuerhg3bbhXf/9h+HRctsb1ljLnYoJXRdA7AyhlKyFlz8+fPCXfxy+W94f8obPPGztv5zptQBOTEwU3+GHH374ec5zniOVwYzgAlh++Tqafl6HzndNwRoMcv32Xjoo1gW3jnrmFAhhuwuQJH7ImduKfcIHjabDW3/sxn+52G8L0lyQWzlzNJJ+++pHRyPBP/TG9iZc2p+cJ3WM63NF4EbAZd9XXNqfnCd1jHvJS14i/6t/9Nl2wGXfX1zan4wAQviqD8MwjE9YZRiG6S0sgAzD9BYWQIZhegsLIMMwvYUFkGGY3sICyDBMb2EBZBimt7AAMgzTW1gAGYbpLSyADMP0liwFsGmbXN+20lXt7aPNfaJqe1/sb7LThS+y82CTsao/6xJdt69KH8u4TN/KG8Bm1XI38U9WHiUDfTokJ7puX5U+lnGZvpa3armb+CdLj/p0SE503b4qfSzjMn0rb0K13E38k6VHfTokJ2Bf+ek6dTb2wW6iXNZ9s7uK6s/ayNKLPh2SM123l8t4e/piu2q5m/gjSQ/CEHrqUDW+6d/nBvmizZ6u2NtEnX1dt3kcfbFdtdxN/JGlB306JGe6bi+X8fb0xXbVcjfxR5Ye9OmQXOibvaCPNhNs+/a48kdWHoSB1afMuM+6SN/sBX20meib7VV7qzaP+0yVftUghmGYEiyADMP0FhZAhmF6CwsgwzC9hQWQYZjewgLIMExvYQFkGKa3sAAyDNNbWAAZhuktLIAMw/QWFkCGYXoLCyDDML2FBZBhmN7CAsgwTG9hAWQYprewADIM01tYABmG6S0sgAzD9JTh8P8BNJ410ckOLfgAAAAASUVORK5CYII=" + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -267,17 +356,17 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { - "data": { - "image/png": "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" - }, - "metadata": {}, - "output_type": "display_data" + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/nmurphy/coding/ifsharp/IfSharp_git/input.fsx(12,27): error FS0039: The field, constructor or member 'Combine' is not defined" + ] } ], "source": [ @@ -292,8 +381,8 @@ " let c1 = plot(x, sin)\n", " let c2 = plot(x, cos)\n", "\n", - " Chart.Combine([c1; c2])\n", - " |> Chart.WithSize(640, 480)\n", + " FSharp.Charting.Chart.Combine([c1; c2])\n", + " |> FSharp.Charting.Chart.WithSize(640, 480)\n", " |> Display\n", " |> ignore\n", " \n", @@ -318,8 +407,13 @@ }, "language": "fsharp", "language_info": { + "codemirror_mode": "", + "file_extension": ".fs", + "mimetype": "text/x-fsharp", "name": "fsharp", - "version": "1.0.0" + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "4.3.1.0" } }, "nbformat": 4, diff --git a/IfSharp.sln b/IfSharp.sln index 664c435..1403158 100644 --- a/IfSharp.sln +++ b/IfSharp.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "IfSharp.Kernel", "src\IfSharp.Kernel\IfSharp.Kernel.fsproj", "{2FE619B3-4756-4285-B31F-232607F62D78}" EndProject @@ -52,6 +52,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ipython-profile", "ipython- ProjectSection(SolutionItems) = preProject ipython-profile\static\custom\ifsharp_logo.png = ipython-profile\static\custom\ifsharp_logo.png ipython-profile\ipython_config.py = ipython-profile\ipython_config.py + ipython-profile\ipython_qtconsole_config.py = ipython-profile\ipython_qtconsole_config.py + ipython-profile\kernel.js = ipython-profile\kernel.js EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IfSharpConsole", "src\IfSharpConsole\IfSharpConsole.csproj", "{6BD0E996-0AEE-4FC7-8FB9-46E033D0C7F1}" @@ -60,8 +62,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "static", "static", "{45519D EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "custom", "custom", "{3AAFA64D-CBB6-488E-B395-6A1E7CB554CE}" ProjectSection(SolutionItems) = preProject - ipython-profile\static\custom\custom.css = ipython-profile\static\custom\custom.css - ipython-profile\static\custom\custom.js = ipython-profile\static\custom\custom.js + ipython-profile\static\custom\fsharp.css = ipython-profile\static\custom\fsharp.css ipython-profile\static\custom\fsharp.js = ipython-profile\static\custom\fsharp.js ipython-profile\static\custom\ifsharp_logo.png = ipython-profile\static\custom\ifsharp_logo.png ipython-profile\static\custom\webintellisense-codemirror.js = ipython-profile\static\custom\webintellisense-codemirror.js @@ -91,14 +92,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "img", "img", "{7B100429-EB1 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{8BA6BD71-43AD-4532-AF2C-DA3E4E0EC136}" ProjectSection(SolutionItems) = preProject - lib\FSharp.Compiler.dll = lib\FSharp.Compiler.dll - lib\FSharp.Compiler.Interactive.Settings.dll = lib\FSharp.Compiler.Interactive.Settings.dll - lib\FSharp.Compiler.Server.Shared.dll = lib\FSharp.Compiler.Server.Shared.dll - lib\FSharp.Core.dll = lib\FSharp.Core.dll - lib\FSharp.Core.optdata = lib\FSharp.Core.optdata - lib\FSharp.Core.sigdata = lib\FSharp.Core.sigdata - lib\FSharp.Data.TypeProviders.dll = lib\FSharp.Data.TypeProviders.dll - lib\fsiAnyCpu.exe = lib\fsiAnyCpu.exe lib\NuGet.dll = lib\NuGet.dll lib\README.md = lib\README.md EndProjectSection diff --git a/README.md b/README.md index 21f75bb..fe74683 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,58 @@ # IfSharp -F# implementation for [iPython](http://ipython.org). View the [Feature Notebook](http://nbviewer.ipython.org/github/fsprojects/IfSharp/blob/master/Feature%20Notebook.ipynb) for some of the features that are included. -For more information view the [documentation](http://fsprojects.github.io/IfSharp/). IfSharp is 64-bit *ONLY*. + +F# implementation for [Jupyter](http://jupyter.org/). View the [Feature Notebook](Feature%20Notebook%20format%204_0.ipynb) for some of the features that are included. + +There is documentation for the existing release version: [documentation](http://fsprojects.github.io/IfSharp/) we're updating this for the Jupyter branch. # Compatibility -IfSharp works with iPython Notebook 1.x and 2.x +IfSharp supports Jupyter 4.0, 4.1, 4.2 and works with both Python 2.X and Python 3.X -We have an initial version of Jupyter 4.x support on this branch: https://github.com/fsprojects/IfSharp/tree/jupyter more help is welcome. +If you need IPython 1.x or 2.x support please see the archived https://github.com/fsprojects/IfSharp/tree/ipython-archive # Automatic Installation -See our [release repository](https://github.com/fsprojects/IfSharp/releases). Also, [installation documentation](http://fsprojects.github.io/IfSharp/installation.html). +Previous releases for the IPython notebook are here: [release repository](https://github.com/fsprojects/IfSharp/releases). +Automatic installs for Jupyter will be provided in the future. + +# Running inside a Docker container +The `jupyter` branch has a Docker file for running the F# kernel v. 3.0.0-alpha in a container. +Build the container with: + +`docker build -t ifsharp:3.0.0-alpha .` + +Run it with: + +`docker run -d -v your_local_notebooks_dir:/notebooks -p your_port:8888 ifsharp:3.0.0-alpha` + +The container exposes a volume called `notebooks` where the files get saved. On Linux, connect to the notebook on `http://localhost:your_port` and, on Windows, use `http://your_docker_machine:your_port`. -# Manual Installation +# Manual Installation (Windows) 1. Install [Anaconda](http://continuum.io/downloads) -2. Install [IPython](http://ipython.org/install.html) -3. Run: "ipython profile create ifsharp" in your user directory -4. Open the iF# solution file, restore nuget packages, and compile it -5. Copy the files from IfSharp\ipython-profile to the iFSharp profile directory -6. Open up the copied "ipython_config.py" file and replace "%s" with the path of your compiled ifsharp executable. E.g. "C:\\git\\ifsharp\\bin\\Release\\ifsharp.exe" -7. Run: "ipython notebook --profile ifsharp" to launch the notebook process with the F# kernel. +2. Install [Jupyter](http://jupyter.readthedocs.org/en/latest/install.html) +3. Download current zip release [v3.0.0-alpha4](https://github.com/fsprojects/IfSharp/releases/download/v3.0.0-alpha4/IfSharp.v3.0.0-alpha4.zip) +4. Run IfSharp.exe + +Jupyter with IfSharp can be run via "jupyter notebook" in future + +# Manual Installation (Mac) +1. Install [Anaconda](http://continuum.io/downloads) +2. Install [Jupyter](http://jupyter.readthedocs.org/en/latest/install.html) +3. Install [Mono](http://www.mono-project.com/download/) (tested 4.2.4) +3. Download current zip release [v3.0.0-alpha4](https://github.com/fsprojects/IfSharp/releases/download/v3.0.0-alpha4/IfSharp.v3.0.0-alpha4.zip) +4. Unzip the release then run `mono IfSharp.exe` +5. (workaround: Copy ~/.local/share/jupyter/kernels/ifsharp to /usr/local/share/jupyter/kernels/ifsharp) +6. Run `jupyter notebook` + +The workaround is for IPython/Jupyter changes will be fixed in a future release. + +# Manual Installation (Linux) +1. Install [Jupyter](http://jupyter.readthedocs.org/en/latest/install.html) via pip or Anaconda etc. +2. Install [Mono](http://www.mono-project.com/docs/getting-started/install/linux/) (tested 4.2.4) and F# (tested 4.0). (warning: Mono 4.6 does *not* work due to a [networking bug](https://github.com/fsprojects/IfSharp/issues/90) which is addressed in the upcoming Mono 4.8) +3. Download the current IfSharp zip release [v3.0.0-alpha4](https://github.com/fsprojects/IfSharp/releases/download/v3.0.0-alpha4/IfSharp.v3.0.0-alpha4.zip) +4. Unzip the release to a safe place such as `~/opt/ifsharp`. +5. Run `mono ~/opt/ifsharp/IfSharp.exe` to set up the jupyter config files in `~/.jupyter/` and `~/.local/share/jupyter/kernels/ifsharp/`. + 1. (For XPlot) From the install directory `~/opt/` run `mono paket.bootstrapper.exe` then `mono paket.exe install` +6. Run `jupyter notebook`, the IfSharp kernel should now be one of the supported kernel types. + # Screens ## Intellisense diff --git a/build.cmd b/build.cmd index 72d7a8b..79c3cd5 100644 --- a/build.cmd +++ b/build.cmd @@ -1,7 +1,18 @@ @echo off cls -if not exist packages\FAKE\tools\Fake.exe ( - .nuget\nuget.exe install FAKE -OutputDirectory packages -ExcludeVersion -Prerelease + +.paket\paket.bootstrapper.exe +if errorlevel 1 ( + exit /b %errorlevel% +) + +.paket\paket.exe restore +if errorlevel 1 ( + exit /b %errorlevel% +) + +IF NOT EXIST build.fsx ( + .paket\paket.exe update + packages\FAKE\tools\FAKE.exe init.fsx ) packages\FAKE\tools\FAKE.exe build.fsx %* -pause diff --git a/build.fsx b/build.fsx index 7e66197..c87baf7 100644 --- a/build.fsx +++ b/build.fsx @@ -45,7 +45,7 @@ let testAssemblies = ["tests/*/bin/Release/IfSharp.*Tests*.dll"] // Git configuration (used for publishing documentation in gh-pages branch) // The profile where the project is posted -let gitHome = "https://github.com/BayardRock" +let gitHome = "https://github.com/fsprojects/" // The name of the project on GitHub let gitName = "IfSharp" @@ -71,10 +71,10 @@ Target "AssemblyInfo" (fun _ -> // -------------------------------------------------------------------------------------- // Clean build results & restore NuGet packages -Target "RestorePackages" (fun _ -> - !! "./**/packages.config" - |> Seq.iter (RestorePackage (fun p -> { p with ToolPath = "./.nuget/NuGet.exe" })) -) +//Target "RestorePackages" (fun _ -> +// !! "./**/packages.config" +// |> Seq.iter (RestorePackage (fun p -> { p with ToolPath = "./.nuget/NuGet.exe" })) +//) Target "Clean" (fun _ -> CleanDirs ["bin"; "temp"] @@ -86,7 +86,6 @@ Target "CleanDocs" (fun _ -> // -------------------------------------------------------------------------------------- // Build library & test project - Target "Build" (fun _ -> [ "src/IfSharpConsole/IfSharpConsole.csproj" //; "src/IfSharp.Kernel/IfSharp.Kernel.fsproj" @@ -98,20 +97,12 @@ Target "Build" (fun _ -> // -------------------------------------------------------------------------------------- // Run the unit tests using test runner & kill test runner when complete -Target "RunTests" (fun _ -> - let nunitVersion = GetPackageVersion "packages" "NUnit.Runners" - let nunitPath = sprintf "packages/NUnit.Runners.%s/tools" nunitVersion - ActivateFinalTarget "CloseTestRunner" - - { BaseDirectory = __SOURCE_DIRECTORY__ - Includes = testAssemblies - Excludes = [] } - |> NUnit (fun p -> - { p with - ToolPath = nunitPath - DisableShadowCopy = true - TimeOut = TimeSpan.FromMinutes 20. - OutputFile = "TestResults.xml" }) +Target "xUnit" (fun _ -> + !! "**/bin/**/*.Tests.dll" + |> Fake.Testing.XUnit2.xUnit2 (fun p -> + {p with + TimeOut = TimeSpan.FromMinutes 5. + HtmlOutputPath = Some "xunit.html"}) ) FinalTarget "CloseTestRunner" (fun _ -> @@ -174,10 +165,8 @@ Target "Release" DoNothing Target "All" DoNothing "Clean" - ==> "RestorePackages" ==> "AssemblyInfo" ==> "Build" -// ==> "RunTests" ==> "All" "All" @@ -185,6 +174,8 @@ Target "All" DoNothing ==> "GenerateDocs" ==> "ReleaseDocs" ==> "NuGet" + ==> "xUnit" ==> "Release" + RunTargetOrDefault "All" diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index 0bb169e..5b11f81 --- a/build.sh +++ b/build.sh @@ -1,5 +1,30 @@ #!/bin/bash + +PREFIX="./" + +if test "$OS" = "Windows_NT" +then + # use .Net + EXE="" + FLAGS="" +else + EXE="mono" + FLAGS="-d:MONO" +fi + if [ ! -f packages/FAKE/tools/FAKE.exe ]; then - mono .nuget/NuGet.exe install FAKE -OutputDirectory packages -ExcludeVersion -Prerelease + ${EXE} ${PREFIX}/.paket/paket.bootstrapper.exe + #${EXE} .nuget/NuGet.exe install FAKE -OutputDirectory packages -ExcludeVersion -Prerelease + exit_code=$? + if [ $exit_code -ne 0 ]; then + exit $exit_code + fi +fi +${EXE} ${PREFIX}/.paket/paket.exe restore +exit_code=$? +if [ $exit_code -ne 0 ]; then + exit $exit_code fi -mono packages/FAKE/tools/FAKE.exe build.fsx $@ + +${EXE} ${PREFIX}/packages/FAKE/tools/FAKE.exe $@ --fsiargs ${FLAGS} build.fsx + diff --git a/docs/content/customPrinters.fsx b/docs/content/customPrinters.fsx index 794e903..712dd17 100644 --- a/docs/content/customPrinters.fsx +++ b/docs/content/customPrinters.fsx @@ -3,13 +3,12 @@ // it to define helpers that you do not want to show in the documentation. #I "../../bin" #r "IFSharp.Kernel" -#r "System.Data.dll" -#r "System.Windows.Forms.DataVisualization.dll" -#r "FSharp.Data.TypeProviders.dll" -#r "FSharp.Charting.dll" +// #r "System.Data.dll" +// #r "System.Windows.Forms.DataVisualization.dll" +// #r "FSharp.Data.TypeProviders.dll" +// #r "FSharp.Charting.dll" #r "NetMQ.dll" -open FSharp.Charting open IfSharp.Kernel open IfSharp.Kernel.Globals diff --git a/docs/content/globals.fsx b/docs/content/globals.fsx index 84d0443..abeff89 100644 --- a/docs/content/globals.fsx +++ b/docs/content/globals.fsx @@ -3,8 +3,6 @@ // it to define helpers that you do not want to show in the documentation. #I "../../bin" #r "IFSharp.Kernel" -#r "System.Data.dll" -#r "System.Windows.Forms.DataVisualization.dll" #r "FSharp.Data.TypeProviders.dll" #r "FSharp.Charting.dll" #r "NetMQ.dll" @@ -16,22 +14,26 @@ open IfSharp.Kernel.Globals (** The Display global ================== -The `Display` function is a 'global' function that can be accessed anywhere throughout +The `Display` function is a global function that can be accessed anywhere throughout the notebook. It will take any object and attempt to display it to the user. Built-in supported types are: -* `FSharp.Charting.ChartTypes.GenericChart` * `IfSharp.Kernel.GenericChartWithSize` * `IfSharp.Kernel.TableOutput` * `IfSharp.Kernel.HtmlOutput` * `IfSharp.Kernel.BinaryOutput` +Additional `Display` printers are provided via helper scripts. +
FSharp.Charting.ChartTypes.GenericChart --------------------------------------- *) +#load "FSharp.Charting.Paket.fsx" +#load "FSharp.Charting.fsx" + let fruitData = [| ("Cherries", 100); ("Apples", 200); ("Bananas", 150); ("Peaches", 10) |] Chart.Bar(fruitData) |> Display diff --git a/docs/content/index.fsx b/docs/content/index.fsx index 2283497..7e9682c 100644 --- a/docs/content/index.fsx +++ b/docs/content/index.fsx @@ -38,25 +38,49 @@ Startup script is executed on startup. The script automatically references the following assemblies: * IfSharp.Kernel.dll -* System.Data.dll -* System.Windows.Forms.DataVisualization.dll -* FSharp.Data.TypeProviders.dll -* FSharp.Charting.dll * NetMQ.dll And automatically opens the following namespaces: -* FSharp.Charting * IfSharp.Kernel * IfSharp.Kernel.Global -Integrated NuGet support +Paket support ------------------------ -To automatically download NuGet package, use the #N directive: `#N "Newtonsoft.Json"`. -This will download the package and automatically reference assemblies within the package. -[More NuGet integration documentation](nuget.html). +To download [Paket](https://fsprojects.github.io/Paket/) packages, start with: +*) + +#load "Paket.fsx" + +(** +This will allow you to specify Paket dependencies with a declarative syntax. +*) + +Paket.Package ["Newtonsoft.Json"] + +(** +You will need to load any assemblies you require from those packages using `#r` directives. + +If you need to specify a version, you can use an alternate form: +*) -![NuGet Example](img/NuGet-1.png "NuGet example") +Paket.Version ["Newtonsoft.Json", "~> 9.0.1"] + +(** +Some helper scripts are provided to handle Paket package installation, assembly dependency, and custom `Display` printers for commonly used packages. Each helper script is divided into a script that installs Paket dependencies, and another that loads assemblies and sets up extension functions and `Display` printers. +*) + +#load "Angara.Charting.Paket.fsx" +#load "Angara.Charting.fsx" + +#load "XPlot.Plotly.Paket.fsx" +#load "XPlot.Plotly.fsx" + +#load "FSharp.Charting.Paket.fsx" +#load "FSharp.Charting.fsx" + +(** +[`Angara.Charting`](http://predictionmachines.github.io/Angara.Chart/) and [`XPlot.Plotly`](https://tahahachana.github.io/XPlot/plotly.html) are both feature-rich, cross-platform charting packages. [`FSharp.Charting`](https://fslab.org/FSharp.Charting/) is a Windows-only charting package. Sin chart wave example ---------------------- @@ -85,4 +109,4 @@ Util.Math("f(x) = sin(x)") |> Display (** sin function -*) \ No newline at end of file +*) diff --git a/docs/content/installation.fsx b/docs/content/installation.fsx index 07cce10..9570b4e 100644 --- a/docs/content/installation.fsx +++ b/docs/content/installation.fsx @@ -3,37 +3,47 @@ // it to define helpers that you do not want to show in the documentation. #I "../../bin" #r "IFSharp.Kernel" -#r "System.Data.dll" -#r "System.Windows.Forms.DataVisualization.dll" -#r "FSharp.Data.TypeProviders.dll" -#r "FSharp.Charting.dll" #r "NetMQ.dll" -open FSharp.Charting open IfSharp.Kernel open IfSharp.Kernel.Globals (** -# Automatic Installation -1. Install [Anaconda](http://continuum.io/downloads) -2. Install [IPython](http://ipython.org/install.html) +# Windows Installation -3. Download the latest version of IfSharp from the [release repository](https://github.com/BayardRock/IfSharp/releases). -Run the setup wizard and execute the icon that is placed on the desktop. +## Automatic Installation -Running the executable after it is installed will automatically generate the files necessary -for starting up (if the file structure does not exist) and then execute the `ipython notebook --profile ifsharp` command. +1. Install [Anaconda](http://continuum.io/downloads) +2. Install [IPython](http://ipython.org/install.html) +3. Download the latest version of IfSharp from the [release repository](https://github.com/BayardRock/IfSharp/releases) +4. Run the setup wizard and execute the icon that is placed on the desktop -If the file structure does exist, only the command `ipython notebook --profile ifsharp` is executed. +Running the executable after it is installed will automatically generate the files necessary for starting up (if the file structure does not exist) and then execute the `ipython notebook --profile ifsharp` command. To overwrite the file structure again, invoke ifsharp.exe with the install parameter: `ifsharp.exe --install`. -# Manual Installation +## Manual Installation + 1. Install [Anaconda](http://continuum.io/downloads) 2. Install [IPython](http://ipython.org/install.html) -3. Run: "ipython profile create ifsharp" in your user directory -4. Open the iF# solution file, restore nuget packages, and compile it -5. Copy the files from IfSharp\ipython-profile to the iFSharp profile directory -6. Open up the copied "ipython_config.py" file and replace "%s" with the path of your compiled ifsharp executable. E.g. "C:\\git\\ifsharp\\bin\\Release\\ifsharp.exe" -7. Run: "ipython notebook --profile ifsharp" to launch the notebook process with the F# kernel. -*) \ No newline at end of file +3. Either open the iF# solution file, restore nuget packages, and compile it +4. Or run `./build.cmd` from the command line +5. Run `isharp.exe --install` to set up the F# kernel +6. `jupyter notebook` + +# Linux Installation + +1. Use your package manager to install `mono`, `fsharp`, and `python3` +2. `pip3 install jupyter` +3. Download IfSharp and build it with `./build.sh` +4. Install IfSharp with `mono ./bin/ifsharp.exe --install` +5. `jupyter notebook` + +# OSX Installation + +1. `brew install mono fsharp python3` +2. `pip3 install jupyter` +3. Download IfSharp and build it with `./build.sh` +4. Install IfSharp with `mono ./bin/ifsharp.exe --install` +5. `jupyter notebook` +*) diff --git a/docs/content/nuget.fsx b/docs/content/nuget.fsx deleted file mode 100644 index 44c9049..0000000 --- a/docs/content/nuget.fsx +++ /dev/null @@ -1,39 +0,0 @@ -(*** hide ***) -// This block of code is omitted in the generated HTML documentation. Use -// it to define helpers that you do not want to show in the documentation. -#I "../../bin" -#r "IFSharp.Kernel" -#r "System.Data.dll" -#r "System.Windows.Forms.DataVisualization.dll" -#r "FSharp.Data.TypeProviders.dll" -#r "FSharp.Charting.dll" -#r "NetMQ.dll" - -open FSharp.Charting -open IfSharp.Kernel -open IfSharp.Kernel.Globals - -(** -NuGet integration -================= -IfSharp offers built-in NuGet integration by using preprocessor-like directives. Use the `#N` -directive to automatically download packages and reference them. Example: `#N "Newtonsoft.Json"`. -This will get the latest version of the [Newtonsoft.Json](http://www.nuget.org/packages/Newtonsoft.Json) -package. - -![NuGet Example](img/NuGet-1.png "NuGet example") - -Specifying version and pre-release ----------------------------------- -The version can be specified by adding additional information to the string of the preprocessor directive. - -The full format is as follows: `#N "[/[/pre]]"`. - -Version example: `#N "Newtonsoft.Json/5.0.1"`. This will download version 5.0.1 of the Newtonsoft.Json package. - -Prerelease example: `#N "FSharp.Compiler.Service/0.0.1-beta/pre"`. - -Not supported -------------- -Currently, dependencies are not automatically referenced, however they are downloaded. -*) \ No newline at end of file diff --git a/docs/content/utils.fsx b/docs/content/utils.fsx index 1b28852..1e0faa7 100644 --- a/docs/content/utils.fsx +++ b/docs/content/utils.fsx @@ -5,11 +5,8 @@ #r "IFSharp.Kernel" #r "System.Data.dll" #r "System.Windows.Forms.DataVisualization.dll" -#r "FSharp.Data.TypeProviders.dll" -#r "FSharp.Charting.dll" #r "NetMQ.dll" -open FSharp.Charting open IfSharp.Kernel open IfSharp.Kernel.Globals diff --git a/docs/tools/generate.fsx b/docs/tools/generate.fsx index 9f39e8c..49e475e 100644 --- a/docs/tools/generate.fsx +++ b/docs/tools/generate.fsx @@ -12,22 +12,28 @@ let website = "/IfSharp" let info = [ "project-name", "IfSharp" "project-author", "Bayard Rock (Peter Rosconi)" - "project-summary", "F# implementation of iPython" - "project-github", "http://github.com/BayardRock/IfSharp/" + "project-summary", "F# kernel for Jupyter" + "project-github", "http://github.com/fsprojects/IfSharp/" "project-nuget", "http://nuget.com/packages/IfSharp/" ] // -------------------------------------------------------------------------------------- // For typical project, no changes are needed below // -------------------------------------------------------------------------------------- -#I "../../packages/FSharp.Formatting.2.1.6/lib/net40" -#I "../../packages/RazorEngine.3.3.0/lib/net40/" -#r "../../packages/Microsoft.AspNet.Razor.2.0.30506.0/lib/net40/System.Web.Razor.dll" #r "../../packages/FAKE/tools/FakeLib.dll" +#r "../../packages/docs/FSharp.Compiler.Service/lib/net45/FSharp.Compiler.Service.dll" +#r "../../packages/docs/FSharpVSPowerTools.Core/lib/net45/FSharpVSPowerTools.Core.dll" + +#I "../../packages/docs/FSharp.Formatting/lib/net40/" + +#r "System.Web.Razor.dll" #r "RazorEngine.dll" -#r "FSharp.Literate.dll" #r "FSharp.CodeFormat.dll" +#r "FSharp.Markdown.dll" +#r "FSharp.Formatting.Common.dll" +#r "FSharp.Literate.dll" #r "FSharp.MetadataFormat.dll" + open Fake open System.IO open Fake.FileHelper @@ -48,7 +54,7 @@ let content = __SOURCE_DIRECTORY__ @@ "../content" let output = __SOURCE_DIRECTORY__ @@ "../output" let files = __SOURCE_DIRECTORY__ @@ "../files" let templates = __SOURCE_DIRECTORY__ @@ "templates" -let formatting = __SOURCE_DIRECTORY__ @@ "../../packages/FSharp.Formatting.2.1.6/" +let formatting = __SOURCE_DIRECTORY__ @@ "../../packages/docs/FSharp.Formatting/" let docTemplate = formatting @@ "templates/docpage.cshtml" // Where to look for *.csproj templates (in this order) @@ -60,7 +66,7 @@ let layoutRoots = let copyFiles () = CopyRecursive files output true |> Log "Copying file: " ensureDirectory (output @@ "content") - CopyRecursive (formatting @@ "content") (output @@ "content") true + CopyRecursive (formatting @@ "styles") (output @@ "content") true |> Log "Copying styles and scripts: " // Build API reference from XML comments diff --git a/docs/tools/templates/template.cshtml b/docs/tools/templates/template.cshtml index d9163e6..3f07e86 100644 --- a/docs/tools/templates/template.cshtml +++ b/docs/tools/templates/template.cshtml @@ -44,7 +44,6 @@
  • Globals
  • Utils
  • Custom Printers
  • -
  • NuGet Integration
  • Source Code on GitHub
  • License
  • diff --git a/ifsharpsetup.iss b/ifsharpsetup.iss index 3de8496..54cd9dc 100644 --- a/ifsharpsetup.iss +++ b/ifsharpsetup.iss @@ -2,9 +2,9 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "IFSharp" -#define MyAppVersion "2.0" -#define MyAppPublisher "Bayard Rock" -#define MyAppURL "http://www.bayardrock.com/" +#define MyAppVersion "3.0" +#define MyAppPublisher "F# Community" +#define MyAppURL "https://github.com/fsprojects/IfSharp" #define MyAppExeName "ifsharp.exe" #define BaseDirectory "." @@ -37,18 +37,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl" Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] -Source: "{#BaseDirectory}\bin\ifsharp.exe"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\ifsharp.exe.config"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\FSharp.Charting.dll"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\FSharp.Compiler.Service.dll"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\FSharp.Core.dll"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\FSharp.Core.optdata"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\FSharp.Core.sigdata"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\NetMQ.dll"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\IfSharp.Kernel.dll"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\Newtonsoft.Json.dll"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\NuGet.dll"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#BaseDirectory}\bin\Include.fsx"; DestDir: "{app}"; Flags: ignoreversion +Source: "{#BaseDirectory}\bin\*"; DestDir: "{app}"; Flags: ignoreversion ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] diff --git a/ipython-profile/ipython_config.py b/ipython-profile/ipython_config.py index 63b4d78..ec0bb70 100644 --- a/ipython-profile/ipython_config.py +++ b/ipython-profile/ipython_config.py @@ -1,4 +1,2 @@ c = get_config() -c.KernelManager.kernel_spec = [r"%s", "{connection_file}"] -c.Session.key = b'' -c.Session.keyfile = '' \ No newline at end of file +c.NotebookApp.extra_static_paths = [ r"%kstatic" ] \ No newline at end of file diff --git a/ipython-profile/kernel.js b/ipython-profile/kernel.js new file mode 100644 index 0000000..ea696da --- /dev/null +++ b/ipython-profile/kernel.js @@ -0,0 +1,163 @@ +define(function () { + + var kernelSpecs = requirejs.s.contexts._.config.paths.kernelspecs; + var staticFolder = kernelSpecs + "/ifsharp/static/"; + + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = staticFolder + "custom/fsharp.css"; + document.getElementsByTagName("head")[0].appendChild(link); + + require(['codemirror/addon/mode/loadmode']); + + var onload = function () { + + var md = IPython.notebook.metadata; + if (md.language) { + console.log('language already defined and is :', md.language); + } + else { + md.language = 'fsharp'; + console.log('add metadata hint that language is fsharp...'); + } + + require([staticFolder + 'custom/fsharp.js']); + + IPython.CodeCell.options_default.cm_config.mode = 'fsharp'; + + // callback called by the end-user + function updateMarkers(data) { + // applies intellisense hooks onto all cells + var cells = getCodeCells(); + data.forEach(function (err) { + var cell = cells.cells[err.CellNumber] + var editor = cell.code_mirror; + + // clear our error marks + editor.doc.getAllMarks() + .forEach(function (m) { + if (m.className === 'br-errormarker') { + m.clear(); + } + }); + + var from = { line: err.StartLine, ch: err.StartColumn }; + var to = { line: err.EndLine, ch: err.EndColumn }; + editor.doc.markText(from, to, { title: err.Message, className: 'br-errormarker' }); + }); + } + + function getCodeCells() { + var results = { codes: [], cells: [], selectedCell: null, selectedIndex: 0 }; + IPython.notebook.get_cells() + .forEach(function (c) { + if (c.cell_type === 'code') { + if (c.selected === true) { + results.selectedCell = c; + results.selectedIndex = results.cells.length; + } + results.cells.push(c); + results.codes.push(c.code_mirror.getValue()); + } + }); + + return results; + } + + require([staticFolder + 'custom/webintellisense.js', staticFolder + 'custom/webintellisense-codemirror.js'], function () { + // applies intellisense hooks onto a cell + function applyIntellisense(cell) { + if (cell.cell_type !== 'code') { return; } + + var editor = cell.code_mirror; + if (editor.intellisense == null) { + var intellisense = new CodeMirrorIntellisense(editor); + cell.force_highlight('fsharp'); + cell.code_mirror.setOption('theme', 'neat'); + editor.intellisense = intellisense; + + intellisense.addDeclarationTrigger({ keyCode: 190 }); // `.` + intellisense.addDeclarationTrigger({ keyCode: 32, ctrlKey: true, preventDefault: true, type: 'down' }); // `ctrl+space` + intellisense.addDeclarationTrigger({ keyCode: 191 }); // `/` + intellisense.addDeclarationTrigger({ keyCode: 220 }); // `\` + intellisense.addDeclarationTrigger({ keyCode: 222 }); // `"` + intellisense.addDeclarationTrigger({ keyCode: 222, shiftKey: true }); // `"` + intellisense.addMethodsTrigger({ keyCode: 57, shiftKey: true }); // `(` + intellisense.addMethodsTrigger({ keyCode: 48, shiftKey: true });// `)` + intellisense.onMethod(function (item, position) { + + }); + intellisense.onDeclaration(function (item, position) { + var cells = getCodeCells(); + var codes = cells.codes; + var cursor = cells.selectedCell.code_mirror.doc.getCursor(); + var callbacks = { shell: {}, iopub: {} }; + var line = editor.getLine(cursor.line); + var isSlash = item.keyCode === 191 || item.keyCode === 220; + var isQuote = item.keyCode === 222; + + var isLoadOrRef = line.indexOf('#load') === 0 + || line.indexOf('#r') === 0; + + var isStartLoadOrRef = line === '#load "' + || line === '#r "' + || line === '#load @"' + || line === '#r @"'; + + if (isSlash && !isLoadOrRef) { + return; + } + if (isQuote && !isStartLoadOrRef) { + return; + } + + // v2 + callbacks.shell.reply = function (msg) { + intellisense.setDeclarations(msg.content.matches); + if (msg.content.filter_start_index) + intellisense.setStartColumnIndex(msg.content.filter_start_index); + }; + + callbacks.iopub.output = function (msg) { + updateMarkers(msg.content.data.errors); + }; + + // v1 + callbacks.complete_reply = function (data) { + intellisense.setDeclarations(data.matches); + intellisense.setStartColumnIndex(data.filter_start_index); + }; + + callbacks.output = function (msgType, content, metadata) { + updateMarkers(content.data.errors); + }; + + var content = { + text: JSON.stringify(codes), + line: '', + block: JSON.stringify({ selectedIndex: cells.selectedIndex, ch: cursor.ch, line: cursor.line }), + cursor_pos: cursor.ch + }; + + IPython.notebook.kernel.send_shell_message("intellisense_request", content, callbacks, null, null); + }); + } + } + + // applies intellisense hooks onto all cells + IPython.notebook.get_cells() + .forEach(function (cell) { + applyIntellisense(cell); + }); + + // applies intellisense hooks onto cells that are selected + $([IPython.events]).on('create.Cell', function (event, data) { + applyIntellisense(data.cell); + }); + }); + + } + + return { onload: onload } +}) \ No newline at end of file diff --git a/ipython-profile/static/custom/custom.js b/ipython-profile/static/custom/custom.js deleted file mode 100644 index c1b4467..0000000 --- a/ipython-profile/static/custom/custom.js +++ /dev/null @@ -1,177 +0,0 @@ -$([IPython.events]).on('notebook_loaded.Notebook', function () -{ - var md = IPython.notebook.metadata; - if (md.language) - { - console.log('language already defined and is :', md.language); - } - else - { - md.language = 'fsharp'; - console.log('add metadata hint that language is fsharp...'); - } -}); - -$([IPython.events]).on('app_initialized.NotebookApp', function () -{ - require(['custom/fsharp']); - - IPython.CodeCell.options_default.cm_config.mode = 'fsharp'; - - // callback called by the end-user - function updateMarkers(data) - { - // applies intellisense hooks onto all cells - var cells = getCodeCells(); - data.forEach(function (err) - { - var cell = cells.cells[err.CellNumber] - var editor = cell.code_mirror; - - // clear our error marks - editor.doc.getAllMarks() - .forEach(function (m) - { - if (m.className === 'br-errormarker') - { - m.clear(); - } - }); - - var from = { line: err.StartLine, ch: err.StartColumn }; - var to = { line: err.EndLine, ch: err.EndColumn }; - editor.doc.markText(from, to, { title: err.Message, className: 'br-errormarker' }); - }); - } - - function getCodeCells() - { - var results = { codes: [], cells: [], selectedCell: null, selectedIndex: 0 }; - IPython.notebook.get_cells() - .forEach(function (c) - { - if (c.cell_type === 'code') - { - if (c.selected === true) - { - results.selectedCell = c; - results.selectedIndex = results.cells.length; - } - results.cells.push(c); - results.codes.push(c.code_mirror.getValue()); - } - }); - - return results; - } - - require(['custom/webintellisense', 'custom/webintellisense-codemirror'], function () - { - // applies intellisense hooks onto a cell - function applyIntellisense(cell) - { - if (cell.cell_type !== 'code') { return; } - - var editor = cell.code_mirror; - if (editor.intellisense == null) - { - var intellisense = new CodeMirrorIntellisense(editor); - cell.force_highlight('fsharp'); - cell.code_mirror.setOption('theme', 'neat'); - editor.intellisense = intellisense; - - intellisense.addDeclarationTrigger({ keyCode: 190 }); // `.` - intellisense.addDeclarationTrigger({ keyCode: 32, ctrlKey: true, preventDefault: true, type: 'down' }); // `ctrl+space` - intellisense.addDeclarationTrigger({ keyCode: 191 }); // `/` - intellisense.addDeclarationTrigger({ keyCode: 220 }); // `\` - intellisense.addDeclarationTrigger({ keyCode: 222 }); // `"` - intellisense.addDeclarationTrigger({ keyCode: 222, shiftKey: true }); // `"` - intellisense.addMethodsTrigger({ keyCode: 57, shiftKey: true }); // `(` - intellisense.addMethodsTrigger({ keyCode: 48, shiftKey: true });// `)` - intellisense.onMethod(function (item, position) - { - - }); - intellisense.onDeclaration(function (item, position) - { - var cells = getCodeCells(); - var codes = cells.codes; - var cursor = cells.selectedCell.code_mirror.doc.getCursor(); - var callbacks = { shell: {}, iopub: {} }; - var line = editor.getLine(cursor.line); - var isSlash = item.keyCode === 191 || item.keyCode === 220; - var isQuote = item.keyCode === 222; - - var isLoadOrRef = line.indexOf('#load') === 0 - || line.indexOf('#r') === 0; - - var isStartLoadOrRef = line === '#load "' - || line === '#r "' - || line === '#load @"' - || line === '#r @"'; - - if (isSlash && !isLoadOrRef) - { - return; - } - if (isQuote && !isStartLoadOrRef) - { - return; - } - - // v2 - callbacks.shell.reply = function (msg) - { - intellisense.setDeclarations(msg.content.matches); - intellisense.setStartColumnIndex(data.filter_start_index); - }; - - callbacks.iopub.output = function (msg) - { - updateMarkers(msg.content.data.errors); - }; - - // v1 - callbacks.complete_reply = function (data) - { - intellisense.setDeclarations(data.matches); - intellisense.setStartColumnIndex(data.filter_start_index); - }; - - callbacks.output = function (msgType, content, metadata) - { - updateMarkers(content.data.errors); - }; - - var content = { - text: JSON.stringify(codes), - line: '', - block: JSON.stringify({ selectedIndex: cells.selectedIndex, ch: cursor.ch, line: cursor.line }), - cursor_pos: cursor.ch - }; - debugger; - var msg = IPython.notebook.kernel._get_msg("intellisense_request", content); - IPython.notebook.kernel.shell_channel.send(JSON.stringify(msg)); - IPython.notebook.kernel.set_callbacks_for_msg(msg.header.msg_id, callbacks); - }); - } - } - - // applies intellisense hooks onto all cells - IPython.notebook.get_cells() - .forEach(function (cell) - { - applyIntellisense(cell); - }); - - // applies intellisense hooks onto cells that are selected - $([IPython.events]).on('create.Cell', function (event, data) - { - applyIntellisense(data.cell); - }); - }); - - // replace the image - var img = $('.container img')[0]; - img.src = "/static/custom/ifsharp_logo.png"; -}); \ No newline at end of file diff --git a/ipython-profile/static/custom/custom.css b/ipython-profile/static/custom/fsharp.css similarity index 100% rename from ipython-profile/static/custom/custom.css rename to ipython-profile/static/custom/fsharp.css diff --git a/kernel-spec/kernel.json b/kernel-spec/kernel.json index 0f226a6..6e7e19b 100644 --- a/kernel-spec/kernel.json +++ b/kernel-spec/kernel.json @@ -1,5 +1,5 @@ { - "display_name": "IFSharp", + "display_name": "F#", "argv": ["mono", "%s", "{connection_file}"], "language": "fsharp" } diff --git a/kernel-spec/logo-32x32.png b/kernel-spec/logo-32x32.png index 14879ed..1325896 100644 Binary files a/kernel-spec/logo-32x32.png and b/kernel-spec/logo-32x32.png differ diff --git a/kernel-spec/logo-64x64.png b/kernel-spec/logo-64x64.png index e2519a5..3480151 100644 Binary files a/kernel-spec/logo-64x64.png and b/kernel-spec/logo-64x64.png differ diff --git a/lib/FSharp.Compiler.Interactive.Settings.dll b/lib/FSharp.Compiler.Interactive.Settings.dll deleted file mode 100644 index 5e3452d..0000000 Binary files a/lib/FSharp.Compiler.Interactive.Settings.dll and /dev/null differ diff --git a/lib/FSharp.Compiler.Server.Shared.dll b/lib/FSharp.Compiler.Server.Shared.dll deleted file mode 100644 index afde86c..0000000 Binary files a/lib/FSharp.Compiler.Server.Shared.dll and /dev/null differ diff --git a/lib/FSharp.Compiler.dll b/lib/FSharp.Compiler.dll deleted file mode 100644 index 46c3251..0000000 Binary files a/lib/FSharp.Compiler.dll and /dev/null differ diff --git a/lib/FSharp.Core.dll b/lib/FSharp.Core.dll deleted file mode 100644 index a2a74c6..0000000 Binary files a/lib/FSharp.Core.dll and /dev/null differ diff --git a/lib/FSharp.Core.optdata b/lib/FSharp.Core.optdata deleted file mode 100644 index df33551..0000000 Binary files a/lib/FSharp.Core.optdata and /dev/null differ diff --git a/lib/FSharp.Core.sigdata b/lib/FSharp.Core.sigdata deleted file mode 100644 index f365490..0000000 Binary files a/lib/FSharp.Core.sigdata and /dev/null differ diff --git a/lib/FSharp.Data.TypeProviders.dll b/lib/FSharp.Data.TypeProviders.dll deleted file mode 100644 index 04bfb81..0000000 Binary files a/lib/FSharp.Data.TypeProviders.dll and /dev/null differ diff --git a/lib/fsiAnyCpu.exe b/lib/fsiAnyCpu.exe deleted file mode 100644 index 4d471a2..0000000 Binary files a/lib/fsiAnyCpu.exe and /dev/null differ diff --git a/paket.dependencies b/paket.dependencies new file mode 100644 index 0000000..a353863 --- /dev/null +++ b/paket.dependencies @@ -0,0 +1,19 @@ +framework: >= net451 +source https://www.nuget.org/api/v2 + +nuget FSharp.Compiler.Service +nuget FSharp.Compiler.Tools +nuget AsyncIO = 0.1.20.0 +nuget NetMQ +nuget Newtonsoft.Json +nuget FAKE +nuget xUnit +nuget xunit.runner.console +nuget Paket.Core + +group Docs + + framework: >= net451 + source https://www.nuget.org/api/v2 + + nuget FSharp.Formatting diff --git a/paket.lock b/paket.lock new file mode 100644 index 0000000..362a90b --- /dev/null +++ b/paket.lock @@ -0,0 +1,48 @@ +FRAMEWORK: >= NET451 +NUGET + remote: https://www.nuget.org/api/v2 + AsyncIO (0.1.20) + Chessie (0.6) + FSharp.Core + FAKE (4.41.6) + FSharp.Compiler.Service (8.0) + System.Collections.Immutable (>= 1.2) + System.Reflection.Metadata (>= 1.4.1-beta-24227-04) + FSharp.Compiler.Tools (4.0.1.10) + FSharp.Core (4.0.0.1) + Mono.Cecil (0.9.6.4) + NetMQ (3.3.3.4) + AsyncIO (>= 0.1.20) + Newtonsoft.Json (9.0.1) + Paket.Core (3.23.2) + Chessie (>= 0.6) + FSharp.Core + Mono.Cecil + Newtonsoft.Json + System.Collections.Immutable (1.2) + System.Reflection.Metadata (1.4.1-beta-24430-01) + System.Collections.Immutable (>= 1.2) + xunit (2.1) + xunit.assert (2.1) + xunit.core (2.1) + xunit.abstractions (2.0) + xunit.assert (2.1) + xunit.core (2.1) + xunit.extensibility.core (2.1) + xunit.extensibility.execution (2.1) + xunit.extensibility.core (2.1) + xunit.abstractions (2.0) + xunit.extensibility.execution (2.1) + xunit.extensibility.core (2.1) + xunit.runner.console (2.1) + +GROUP Docs +FRAMEWORK: >= NET451 +NUGET + remote: https://www.nuget.org/api/v2 + FSharp.Compiler.Service (2.0.0.6) + FSharp.Formatting (2.14.4) + FSharp.Compiler.Service (2.0.0.6) + FSharpVSPowerTools.Core (>= 2.3 < 2.4) + FSharpVSPowerTools.Core (2.3) + FSharp.Compiler.Service (>= 2.0.0.3) diff --git a/src/IfSharp.Kernel/App.fs b/src/IfSharp.Kernel/App.fs index afa597c..3c6c79c 100644 --- a/src/IfSharp.Kernel/App.fs +++ b/src/IfSharp.Kernel/App.fs @@ -142,43 +142,58 @@ module App = // add to the payload Kernel.Value.AddPayload(text.ToString()) - /// Installs the ifsharp files if they do not exist, then starts ipython with the ifsharp profile - let InstallAndStart(forceInstall) = + /// Installs the ifsharp files if they do not exist + let Install forceInstall = let thisExecutable = Assembly.GetEntryAssembly().Location - let appData = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) - let ipythonDir = Path.Combine(appData, ".ipython") - let profileDir = Path.Combine(ipythonDir, "profile_ifsharp") - let kernelDir = Path.Combine([| ipythonDir; "kernels" ; "ifsharp" |] ) - let staticDir = Path.Combine(profileDir, "static") + let kernelDir = Config.KernelDir + let staticDir = Config.StaticDir + let tempDir = Config.TempDir let customDir = Path.Combine(staticDir, "custom") let createDir(str) = if Directory.Exists(str) = false then Directory.CreateDirectory(str) |> ignore - createDir appData - createDir ipythonDir - createDir profileDir + createDir kernelDir createDir staticDir + createDir tempDir createDir customDir - createDir kernelDir - let configFile = Path.Combine(profileDir, "ipython_config.py") - let configqtFile = Path.Combine(profileDir, "ipython_qtconsole_config.py") - let kernelFile = Path.Combine(kernelDir, "kernel.json") - if forceInstall || (File.Exists(configFile) = false) then + let allFiles = new System.Collections.Generic.List() + let addFile fn = allFiles.Add(fn); fn + let configFile = Path.Combine(kernelDir, "ipython_config.py") |> addFile + let configqtFile = Path.Combine(kernelDir, "ipython_qtconsole_config.py") |> addFile + let kernelFile = Path.Combine(kernelDir, "kernel.json") |> addFile + let logoFile = Path.Combine(customDir, "ifsharp_logo.png") |> addFile + let kjsFile = Path.Combine(kernelDir, "kernel.js") |> addFile + let fjsFile = Path.Combine(customDir, "fsharp.js") |> addFile + let wjsFile = Path.Combine(customDir, "webintellisense.js") |> addFile + let wcjsFile = Path.Combine(customDir, "webintellisense-codemirror.js") |> addFile + let logo64File = Path.Combine(kernelDir, "logo-64x64.png") |> addFile + let logo32File = Path.Combine(kernelDir, "logo-32x32.png") |> addFile + let versionFile = Path.Combine(kernelDir, "version.txt") |> addFile + let missingFiles = Seq.exists (fun fn -> File.Exists(fn) = false) allFiles + + let differentVersion = File.Exists(versionFile) && File.ReadAllText(versionFile) <> Config.Version + + if forceInstall then printfn "Force install required, performing install..." + else if missingFiles then printfn "One or more files are missing, performing install..." + else if differentVersion then printfn "Different version found, performing install..." + + if forceInstall || missingFiles || differentVersion then - printfn "Config file does not exist, performing install..." + // write the version file + File.WriteAllText(versionFile, Config.Version); // write the startup script let codeTemplate = IfSharpResources.ipython_config() let code = match Environment.OSVersion.Platform with - | PlatformID.Win32Windows -> codeTemplate.Replace("\"mono\",", "") - | PlatformID.Win32NT -> codeTemplate.Replace("\"mono\",", "") + | PlatformID.Win32Windows | PlatformID.Win32NT -> codeTemplate.Replace("\"mono\",", "") | _ -> codeTemplate - let code = code.Replace("%s", thisExecutable) + let code = code.Replace("%kexe", thisExecutable) + let code = code.Replace("%kstatic", staticDir) printfn "Saving custom config file [%s]" configFile File.WriteAllText(configFile, code) @@ -187,34 +202,29 @@ module App = File.WriteAllText(configqtFile, codeqt) // write custom logo file - let logoFile = Path.Combine(customDir, "ifsharp_logo.png") printfn "Saving custom logo [%s]" logoFile IfSharpResources.ifsharp_logo().Save(logoFile) - // write custom css file - let cssFile = Path.Combine(customDir, "custom.css") - printfn "Saving custom css [%s]" cssFile - File.WriteAllText(cssFile, IfSharpResources.custom_css()) + // write fsharp css file + let cssFile = Path.Combine(customDir, "fsharp.css") + printfn "Saving fsharp css [%s]" cssFile + File.WriteAllText(cssFile, IfSharpResources.fsharp_css()) - // write custom js file - let jsFile = Path.Combine(customDir, "custom.js") - printfn "Saving custom js [%s]" jsFile - File.WriteAllText(jsFile, IfSharpResources.custom_js()) + // write kernel js file + printfn "Saving kernel js [%s]" kjsFile + File.WriteAllText(kjsFile, IfSharpResources.kernel_js()) // write fsharp js file - let jsFile = Path.Combine(customDir, "fsharp.js") - printfn "Saving fsharp js [%s]" jsFile - File.WriteAllText(jsFile, IfSharpResources.fsharp_js()) + printfn "Saving fsharp js [%s]" fjsFile + File.WriteAllText(fjsFile, IfSharpResources.fsharp_js()) - // write fsharp js file - let jsFile = Path.Combine(customDir, "webintellisense.js") - printfn "Saving webintellisense js [%s]" jsFile - File.WriteAllText(jsFile, IfSharpResources.webintellisense_js()) + // write webintellisense js file + printfn "Saving webintellisense js [%s]" wjsFile + File.WriteAllText(wjsFile, IfSharpResources.webintellisense_js()) - // write fsharp js file - let jsFile = Path.Combine(customDir, "webintellisense-codemirror.js") - printfn "Saving webintellisense-codemirror js [%s]" jsFile - File.WriteAllText(jsFile, IfSharpResources.webintellisense_codemirror_js()) + // write webintellisense-codemirror js file + printfn "Saving webintellisense-codemirror js [%s]" wcjsFile + File.WriteAllText(wcjsFile, IfSharpResources.webintellisense_codemirror_js()) // Make the Kernel info folder let jsonTemplate = IfSharpResources.ifsharp_kernel_json() @@ -227,36 +237,52 @@ module App = printfn "Saving custom kernel.json file [%s]" kernelFile File.WriteAllText(kernelFile, code) - let logo64File = Path.Combine(kernelDir, "logo-64x64.png") printfn "Saving kernel icon [%s]" logo64File IfSharpResources.ifsharp_64logo().Save(logo64File) - let logo32File = Path.Combine(kernelDir, "logo-32x32.png") printfn "Saving kernel icon [%s]" logo32File IfSharpResources.ifsharp_32logo().Save(logo32File) + printfn "Installing dependencies via Paket" + let dependencies = Paket.Dependencies.Locate(System.IO.Path.GetDirectoryName(thisExecutable)) + dependencies.Install(false) + + /// Starts jupyter in the user's home directory + let StartJupyter () = + + let userDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + printfn "Starting ipython..." let p = new Process() - p.StartInfo.FileName <- "ipython" - p.StartInfo.Arguments <- "notebook --profile ifsharp" - p.StartInfo.WorkingDirectory <- appData + p.StartInfo.FileName <- "jupyter" + p.StartInfo.Arguments <- "notebook" + p.StartInfo.WorkingDirectory <- userDir // tell the user something bad happened - if p.Start() = false then printfn "Unable to start ipython, please install ipython first" + if p.Start() = false then printfn "Unable to start jupyter, please install jupyter first" /// First argument must be an ipython connection file, blocks forever let Start (args : array) = if args.Length = 0 then - - InstallAndStart(true) + Install true + StartJupyter() + + else if args.[0] = "--install" then + Install true else + // Verify kernel installation status + Install false + + // Clear the temporary folder + try + if Directory.Exists(Config.TempDir) then Directory.Delete(Config.TempDir, true) + with exc -> Console.Out.Write(exc.ToString()) // adds the default display printers Printers.addDefaultDisplayPrinters() - // get connection information let fileName = args.[0] let json = File.ReadAllText(fileName) diff --git a/src/IfSharp.Kernel/AssemblyInfo.fs b/src/IfSharp.Kernel/AssemblyInfo.fs index ec6070e..f514221 100644 --- a/src/IfSharp.Kernel/AssemblyInfo.fs +++ b/src/IfSharp.Kernel/AssemblyInfo.fs @@ -1,4 +1,5 @@ -namespace System +// Auto-Generated by FAKE; do not edit +namespace System open System.Reflection [] @@ -9,4 +10,8 @@ open System.Reflection do () module internal AssemblyVersionInformation = - let [] Version = "2.0.2" + let [] AssemblyTitle = "IfSharp.Kernel" + let [] AssemblyProduct = "IfSharp.Kernel" + let [] AssemblyDescription = "A short summary of your project." + let [] AssemblyVersion = "2.0.2" + let [] AssemblyFileVersion = "2.0.2" diff --git a/src/IfSharp.Kernel/Config.fs b/src/IfSharp.Kernel/Config.fs index cccf015..acd0a73 100644 --- a/src/IfSharp.Kernel/Config.fs +++ b/src/IfSharp.Kernel/Config.fs @@ -2,6 +2,7 @@ open System open System.Configuration +open System.IO /// Convenience method for getting a setting with a default value let defaultConfig (name : string, defaultValue) = @@ -9,4 +10,40 @@ let defaultConfig (name : string, defaultValue) = if value = null then defaultValue else value // the configuration properties -let DefaultNuGetSource = defaultConfig("DefaultNuGetSource", "") \ No newline at end of file +let DefaultNuGetSource = defaultConfig("DefaultNuGetSource", "") + +let ActualPlatform = + match Environment.OSVersion.Platform with + | PlatformID.Unix -> + //Mono pretends MacOSX is Unix, undo this by heuristic + if (Directory.Exists("/Applications") + && Directory.Exists("/System") + && Directory.Exists("/Users") + && Directory.Exists("/Volumes")) + then + PlatformID.MacOSX + else + PlatformID.Unix + | p -> p + +//http://jupyter-client.readthedocs.io/en/latest/kernels.html#kernel-specs +let KernelDir = + let thisExecutable = System.Reflection.Assembly.GetEntryAssembly().Location + let userDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + let appData = + match ActualPlatform with + | PlatformID.Win32Windows | PlatformID.Win32NT -> Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + | PlatformID.MacOSX -> Path.Combine(userDir, "Library") + | PlatformID.Unix -> Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) // PlatformID.Unix + | p -> failwithf "Unknown platform: %A" p + let jupyterDir = + match Environment.OSVersion.Platform with + | PlatformID.Unix -> Path.Combine(appData, "jupyter") + | _ -> Path.Combine(appData, "Jupyter") + let kernelsDir = Path.Combine(jupyterDir, "kernels") + let kernelDir = Path.Combine(kernelsDir, "ifsharp") + kernelDir +let StaticDir = Path.Combine(KernelDir, "static") +let TempDir = Path.Combine(StaticDir, "temp") + +let Version = "1" \ No newline at end of file diff --git a/src/IfSharp.Kernel/DirectivePreprocessor.fs b/src/IfSharp.Kernel/DirectivePreprocessor.fs new file mode 100644 index 0000000..87bf5d6 --- /dev/null +++ b/src/IfSharp.Kernel/DirectivePreprocessor.fs @@ -0,0 +1,27 @@ +namespace IfSharp.Kernel + +module DirectivePreprocessor = + + type Line = + | HelpDirective + | FSIOutputDirective + | NugetDirective + | Other + + let determineLineType (idx, (line:string)) = + match line.ToLower() with + | line when line.StartsWith "#n" -> NugetDirective + | line when line.StartsWith "#help" -> HelpDirective + | line when line.StartsWith "#fsioutput" -> FSIOutputDirective + | _ -> Other + + /// Separates into map of directive types + let partitionLines(lines : string[]) = + lines + |> Seq.mapi (fun (idx) (line) -> (idx, line)) + |> Seq.groupBy determineLineType + |> Map.ofSeq + + /// Parses a directive line. Example: #N "Deedle" + let parseDirectiveLine (prefix : string) (line : string) = + line.Substring(prefix.Length + 1).Trim().Trim('"') diff --git a/src/IfSharp.Kernel/Evaluation.fs b/src/IfSharp.Kernel/Evaluation.fs index 461d7e9..90aec03 100644 --- a/src/IfSharp.Kernel/Evaluation.fs +++ b/src/IfSharp.Kernel/Evaluation.fs @@ -8,14 +8,49 @@ open Microsoft.FSharp.Compiler.Interactive.Shell [] module Evaluation = - let internal sbOut = new StringBuilder() - let internal sbErr = new StringBuilder() - let internal inStream = new StringReader("") - let internal outStream = new StringWriter(sbOut) - let internal errStream = new StringWriter(sbErr) - let internal fsiConfig = FsiEvaluationSession.GetDefaultConfiguration() - let internal fsiEval = FsiEvaluationSession.Create(fsiConfig, [|"--noninteractive"|], inStream, outStream, errStream) + /// Extend the `fsi` object with `fsi.AddHtmlPrinter` + let addHtmlPrinter = """ + module FsInteractiveService = + let mutable htmlPrinters = new ResizeArray seq * string)>() + let htmlPrinterParams = System.Collections.Generic.Dictionary() + do htmlPrinterParams.["html-standalone-output"] <- false + + type Microsoft.FSharp.Compiler.Interactive.InteractiveSession with + member x.HtmlPrinterParameters = FsInteractiveService.htmlPrinterParams + member x.AddHtmlPrinter<'T>(f:'T -> seq * string) = + FsInteractiveService.htmlPrinters.Add(typeof<'T>, fun (value:obj) -> + f (value :?> 'T))""" + + /// Start the F# interactive session with HAS_FSI_ADDHTMLPRINTER symbol defined + let internal startSession () = + let sbOut = new StringBuilder() + let sbErr = new StringBuilder() + let inStream = new StringReader("") + let outStream = new StringWriter(sbOut) + let errStream = new StringWriter(sbErr) + let fsiObj = Microsoft.FSharp.Compiler.Interactive.Settings.fsi + let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration(fsiObj, false) + let args = [|"--noninteractive"; "--define:HAS_FSI_ADDHTMLPRINTER" |] + let fsiSession = FsiEvaluationSession.Create(fsiConfig, args, inStream, outStream, errStream) + + // Load the `fsi` object from the right location of the `FSharp.Compiler.Interactive.Settings.dll` + // assembly and add the `fsi.AddHtmlPrinter` extension method; then clean it from FSI output + let origLength = sbOut.Length + let fsiLocation = typeof.Assembly.Location + fsiSession.EvalInteraction("#r @\"" + fsiLocation + "\"") + fsiSession.EvalInteraction(addHtmlPrinter) + sbOut.Remove(origLength, sbOut.Length-origLength) |> ignore + + // Get reference to the extra HTML printers registered inside the FSI session + let extraPrinters = + unbox seq * string)>> + (fsiSession.EvalExpression("FsInteractiveService.htmlPrinters").Value.ReflectionValue) + sbErr, sbOut, extraPrinters, fsiSession + + let internal fsiout = ref false + let internal sbErr, sbOut, extraPrinters, fsiEval = startSession () + /// Gets `it` only if `it` was printed to the console let GetLastExpression() = diff --git a/src/IfSharp.Kernel/FsCompiler.fs b/src/IfSharp.Kernel/FsCompiler.fs index e3d3579..3cf163a 100644 --- a/src/IfSharp.Kernel/FsCompiler.fs +++ b/src/IfSharp.Kernel/FsCompiler.fs @@ -105,6 +105,8 @@ module FsCompilerInternals = | FSharpToolTipElement.None -> () | FSharpToolTipElement.Single(it, comment) -> sb.AppendLine(it) |> buildFormatComment xmlCommentRetriever comment + | FSharpToolTipElement.SingleParameter(it, comment, _) -> + sb.AppendLine(it) |> buildFormatComment xmlCommentRetriever comment | FSharpToolTipElement.Group(items) -> let items, msg = if items.Length > 10 then @@ -130,7 +132,7 @@ module FsCompilerInternals = /// Tries to figure out the names to pass to GetDeclarations or GetMethods. let extractNames (line, charIndex) = - let sourceTok = SourceTokenizer([], "/home/test.fsx") + let sourceTok = SourceTokenizer([], Some "/home/test.fsx") let tokenizer = sourceTok.CreateLineTokenizer(line) let rec gatherTokens (tokenizer:FSharpLineTokenizer) state = seq { @@ -298,6 +300,8 @@ type FsCompiler (executingDirectory : string) = | FSharpToolTipElement.None -> () | FSharpToolTipElement.Single(it, comment) -> sb.AppendLine(it) |> this.BuildFormatComment xmlCommentRetriever comment + | FSharpToolTipElement.SingleParameter(it, comment, _) -> + sb.AppendLine(it) |> this.BuildFormatComment xmlCommentRetriever comment | FSharpToolTipElement.Group(items) -> let items, msg = if items.Length > 10 then @@ -325,7 +329,7 @@ type FsCompiler (executingDirectory : string) = /// Tries to figure out the names to pass to GetDeclarations or GetMethods. member this.ExtractNames (line, charIndex) = - let sourceTok = SourceTokenizer([], "/home/test.fsx") + let sourceTok = SourceTokenizer([], Some "/home/test.fsx") let tokenizer = sourceTok.CreateLineTokenizer(line) let rec gatherTokens (tokenizer:FSharpLineTokenizer) state = seq { @@ -409,7 +413,7 @@ type FsCompiler (executingDirectory : string) = // get the options and parse let options = checker.GetProjectOptionsFromScript(fileName, source, getOptionsTimeFromFile(fileName), arguments, true) |> Async.RunSynchronously - let recent = checker.TryGetRecentTypeCheckResultsForFile(fileName, options, source) + let recent = checker.TryGetRecentCheckResultsForFile(fileName, options, source) let (parse, check) = if recent.IsSome then Debug.WriteLine("Using cached results for file: {0}", fileName) @@ -475,8 +479,11 @@ type FsCompiler (executingDirectory : string) = (List.empty, items, tcr, x.FilterStartIndex) + + /// *** member this.GetToolTipText uses Parser which is internal to the F# Compiler service. Investigate if we need this. + /// Gets tooltip information for the specified information - member this.GetToolTipText (source, lineNumber : int, charIndex : int) = + (*member this.GetToolTipText (source, lineNumber : int, charIndex : int) = let fileName = "/home/Test.fsx" let tcr = this.TypeCheck(source, fileName) @@ -487,3 +494,4 @@ type FsCompiler (executingDirectory : string) = let toolTip = tcr.Check.GetToolTipTextAlternate(lineNumber, charIndex, line, names, identToken) |> Async.RunSynchronously (startIndex, endIndex, this.FormatTip(toolTip, None)) + *) \ No newline at end of file diff --git a/src/IfSharp.Kernel/Globals.fs b/src/IfSharp.Kernel/Globals.fs index bce5ffe..4a36ee3 100644 --- a/src/IfSharp.Kernel/Globals.fs +++ b/src/IfSharp.Kernel/Globals.fs @@ -3,8 +3,6 @@ /// This module provides access to common types and functions so the user can get intellisense [] module Globals = - - type Chart = FSharp.Charting.Chart type Util = IfSharp.Kernel.Util diff --git a/src/IfSharp.Kernel/IfSharp.Kernel.fsproj b/src/IfSharp.Kernel/IfSharp.Kernel.fsproj index 2553332..b71d8f7 100644 --- a/src/IfSharp.Kernel/IfSharp.Kernel.fsproj +++ b/src/IfSharp.Kernel/IfSharp.Kernel.fsproj @@ -1,16 +1,15 @@  - + Debug AnyCPU - 2.0 - 2fe619b3-4756-4285-b31f-232607f62d78 + {2FE619B3-4756-4285-B31F-232607F62D78} Library IfSharp.Kernel IfSharp.Kernel v4.5.1 - 4.3.1.0 + 4.4.0.0 IfSharp.Kernel ..\..\ true @@ -49,7 +48,8 @@ DEBUG;TRACE 3 bin\Debug\IfSharp.Kernel.XML - + + x64 @@ -75,16 +75,16 @@ - + PreserveNewest - + @@ -98,23 +98,10 @@ - - ..\..\packages\FSharp.Charting.0.90.5\lib\net40\FSharp.Charting.dll - True - - - ..\..\packages\FSharp.Compiler.Service.0.0.81\lib\net45\FSharp.Compiler.Service.dll - True + + ..\..\packages\FSharp.Compiler.Tools\tools\FSharp.Compiler.Interactive.Settings.dll - - ..\..\packages\NetMQ.3.3.0.11\lib\net40\NetMQ.dll - True - - - ..\..\packages\Newtonsoft.Json.5.0.8\lib\net45\Newtonsoft.Json.dll - True - ..\..\lib\NuGet.dll @@ -128,6 +115,7 @@ + + + + + + + ..\..\packages\AsyncIO\lib\net40\AsyncIO.dll + True + True + + + + + + + + + ..\..\packages\Chessie\lib\net40\Chessie.dll + True + True + + + + + + + + + ..\..\packages\FSharp.Compiler.Service\lib\net45\FSharp.Compiler.Service.MSBuild.v12.dll + True + True + + + ..\..\packages\FSharp.Compiler.Service\lib\net45\FSharp.Compiler.Service.dll + True + True + + + + + + + + + ..\..\packages\Mono.Cecil\lib\net45\Mono.Cecil.Mdb.dll + True + True + + + ..\..\packages\Mono.Cecil\lib\net45\Mono.Cecil.Pdb.dll + True + True + + + ..\..\packages\Mono.Cecil\lib\net45\Mono.Cecil.Rocks.dll + True + True + + + ..\..\packages\Mono.Cecil\lib\net45\Mono.Cecil.dll + True + True + + + + + + + + + ..\..\packages\NetMQ\lib\net40\NetMQ.dll + True + True + + + + + + + + + ..\..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll + True + True + + + + + + + + + ..\..\packages\Paket.Core\lib\net45\Paket.Core.dll + True + True + + + + + + + + + ..\..\packages\System.Collections.Immutable\lib\netstandard1.0\System.Collections.Immutable.dll + True + True + + + + + + + + + ..\..\packages\System.Reflection.Metadata\lib\netstandard1.1\System.Reflection.Metadata.dll + True + True + + + + \ No newline at end of file diff --git a/src/IfSharp.Kernel/IfSharpResources.fs b/src/IfSharp.Kernel/IfSharpResources.fs index dfb2083..344942c 100644 --- a/src/IfSharp.Kernel/IfSharpResources.fs +++ b/src/IfSharp.Kernel/IfSharpResources.fs @@ -13,8 +13,8 @@ module IfSharpResources = Encoding.UTF8.GetString(array) let ifsharp_logo() = resources.GetObject("ifsharp_logo") :?> System.Drawing.Bitmap - let custom_css() = resources.GetString("custom_css") - let custom_js() = resources.GetString("custom_js") + let fsharp_css() = resources.GetString("fsharp_css") + let kernel_js() = resources.GetString("kernel_js") let fsharp_js() = resources.GetString("fsharp_js") let webintellisense_js() = resources.GetString("webintellisense") let webintellisense_codemirror_js() = resources.GetString("webintellisense-codemirror") diff --git a/src/IfSharp.Kernel/IfSharpResources.resx b/src/IfSharp.Kernel/IfSharpResources.resx index 503e148..819e3b0 100644 --- a/src/IfSharp.Kernel/IfSharpResources.resx +++ b/src/IfSharp.Kernel/IfSharpResources.resx @@ -118,11 +118,11 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ..\..\ipython-profile\static\custom\custom.css;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + ..\..\ipython-profile\static\custom\fsharp.css;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - - ..\..\ipython-profile\static\custom\custom.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + ..\..\ipython-profile\kernel.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 ..\..\ipython-profile\static\custom\fsharp.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 diff --git a/src/IfSharp.Kernel/Include.fsx b/src/IfSharp.Kernel/Include.fsx index 1bea9c2..10687b5 100644 --- a/src/IfSharp.Kernel/Include.fsx +++ b/src/IfSharp.Kernel/Include.fsx @@ -3,13 +3,8 @@ // load base dlls #r "IfSharp.Kernel.dll" -#r "System.Data.dll" -#r "System.Windows.Forms.DataVisualization.dll" -#r "FSharp.Data.TypeProviders.dll" -#r "FSharp.Charting.dll" #r "NetMQ.dll" // open the global functions and methods -open FSharp.Charting open IfSharp.Kernel open IfSharp.Kernel.Globals diff --git a/src/IfSharp.Kernel/Kernel.fs b/src/IfSharp.Kernel/Kernel.fs index e96dd3f..a9995d5 100644 --- a/src/IfSharp.Kernel/Kernel.fs +++ b/src/IfSharp.Kernel/Kernel.fs @@ -5,36 +5,41 @@ open System.Collections.Generic open System.IO open System.Reflection open System.Text +open System.Threading +open System.Security.Cryptography + open Newtonsoft.Json open NetMQ +open NetMQ.Sockets type IfSharpKernel(connectionInformation : ConnectionInformation) = - // startup 0mq stuff - let context = NetMQContext.Create() - // heartbeat - let hbSocket = context.CreateRequestSocket() - do hbSocket.Bind(String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.hb_port)) - - // shell - let shellSocket = context.CreateRouterSocket() - do shellSocket.Bind(String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.shell_port)) + let hbSocket = new RouterSocket() + let hbSocketURL = String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.hb_port) + do hbSocket.Bind(hbSocketURL) // control - let controlSocket = context.CreateRouterSocket() - do controlSocket.Bind(String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.control_port)) + let controlSocket = new RouterSocket() + let controlSocketURL = String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.control_port) + do controlSocket.Bind(controlSocketURL) // stdin - let stdinSocket = context.CreateRouterSocket() - do stdinSocket.Bind(String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.stdin_port)) + let stdinSocket = new RouterSocket() + let stdinSocketURL = String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.stdin_port) + do stdinSocket.Bind(stdinSocketURL) // iopub - let ioSocket = context.CreatePublisherSocket() - do ioSocket.Bind(String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.iopub_port)) + let ioSocket = new PublisherSocket() + let ioSocketURL = String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.iopub_port) + do ioSocket.Bind(ioSocketURL) + + // shell + let shellSocket = new RouterSocket() + let shellSocketURL =String.Format("{0}://{1}:{2}", connectionInformation.transport, connectionInformation.ip, connectionInformation.shell_port) + do shellSocket.Bind(shellSocketURL) - let data = new List() let payload = new List() let compiler = FsCompiler(FileInfo(".").FullName) let mutable executionCount = 0 @@ -56,8 +61,9 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = |> Seq.filter (fun x -> x <> "") |> Seq.map (fun x -> String.Format("{0:yyyy-MM-dd HH:mm:ss} - {1}", DateTime.Now, x)) |> Seq.toArray - - File.AppendAllLines(fileName, messages) + try + File.AppendAllLines(fileName, messages) + with _ -> () /// Logs the exception to the specified file name let handleException (ex : exn) = @@ -83,21 +89,29 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = ser.Serialize(sw, obj) sw.ToString() - let recvAll (socket: NetMQSocket) = socket.ReceiveMessages() + /// Sign a set of strings. + let hmac = new HMACSHA256(Encoding.UTF8.GetBytes(connectionInformation.key)) + let sign (parts:string list) : string = + if connectionInformation.key = "" then "" else + ignore (hmac.Initialize()) + List.iter (fun (s:string) -> let bytes = Encoding.UTF8.GetBytes(s) in ignore(hmac.TransformBlock(bytes, 0, bytes.Length, null, 0))) parts + ignore (hmac.TransformFinalBlock(Array.zeroCreate 0, 0, 0)) + BitConverter.ToString(hmac.Hash).Replace("-", "").ToLower() + let recvAll (socket: NetMQSocket) = socket.ReceiveMultipartBytes() + /// Constructs an 'envelope' from the specified socket let recvMessage (socket: NetMQSocket) = // receive all parts of the message - let message = - recvAll (socket) - |> Seq.map decode - |> Seq.toArray + let message = (recvAll (socket)) |> Array.ofSeq + let asStrings = message |> Array.map decode // find the delimiter between IDS and MSG - let idx = Array.IndexOf(message, "") + let idx = Array.IndexOf(asStrings, "") + let idents = message.[0..idx - 1] - let messageList = message.[idx + 1..message.Length - 1] + let messageList = asStrings.[idx + 1..message.Length - 1] // detect a malformed message if messageList.Length < 4 then failwith ("Malformed message") @@ -114,6 +128,9 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = let metaDataDict = deserializeDict (metadata) let content = ShellMessages.Deserialize (header.msg_type) (contentJson) + let calculated_signature = sign [headerJson; parentHeaderJson; metadata; contentJson] + if calculated_signature <> hmac then failwith("Wrong message signature") + lastMessage <- Some { Identifiers = idents |> Seq.toList; @@ -144,13 +161,20 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = for ident in envelope.Identifiers do msg.Append(ident) + let header = serialize header + let parent_header = serialize envelope.Header + let meta = "{}" + let content = serialize content + let signature = sign [header; parent_header; meta; content] + msg.Append(encode "") - msg.Append(encode "") - msg.Append(encode (serialize header)) - msg.Append(encode (serialize envelope.Header)) + msg.Append(encode signature) + msg.Append(encode header) + msg.Append(encode parent_header) msg.Append(encode "{}") - msg.Append(encode (serialize content)) - socket.SendMessage(msg) + msg.Append(encode content) + socket.SendMultipartMessage(msg) + /// Convenience method for sending the state of the kernel let sendState (envelope) (state) = @@ -168,18 +192,29 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = let kernelInfoRequest(msg : KernelMessage) (content : KernelRequest) = let content = { - protocol_version = [| 4; 0 |]; - ipython_version = Some [| 1; 0; 0 |]; - language_version = [| 1; 0; 0 |]; + protocol_version = "4.0.0"; + implementation = "ifsharp"; + implementation_version = "4.0.0"; + banner = ""; + help_links = [||]; language = "fsharp"; + language_info = + { + name = "fsharp"; + version = "4.3.1.0"; + mimetype = "text/x-fsharp"; + file_extension = ".fs"; + pygments_lexer = ""; + codemirror_mode = ""; + nbconvert_exporter = ""; + }; } + sendStateBusy msg sendMessage shellSocket msg "kernel_info_reply" content /// Sends display data information immediately - let sendDisplayData (contentType) (displayItem) (messageType) = - data.Add( { ContentType = contentType; Data = displayItem } ) - + let sendDisplayData (contentType) (displayItem) (messageType) = if lastMessage.IsSome then let d = Dictionary() @@ -200,6 +235,27 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = let results = compiler.NuGetManager.Preprocess(code) let newCode = String.Join("\n", results.FilteredLines) + if not (Seq.isEmpty results.HelpLines) then + fsiEval.EvalInteraction("#help") + let ifsharpHelp = + """ IF# notebook directives: + + #fsioutput ["on"|"off"];; Toggle output display on/off + """ + let fsiHelp = sbOut.ToString() + pyout (ifsharpHelp + fsiHelp) + sbOut.Clear() |> ignore + + //This is a persistent toggle, just respect the last one + if not (Seq.isEmpty results.FsiOutputLines) then + let lastFsiOutput = Seq.last results.FsiOutputLines + if lastFsiOutput.ToLower().Contains("on") then + fsiout := true + else if lastFsiOutput.ToLower().Contains("off") then + fsiout := false + else + pyout (sprintf "Unreocognised fsioutput setting: %s" lastFsiOutput) + // do nuget stuff for package in results.Packages do if not (String.IsNullOrWhiteSpace(package.Error)) then @@ -220,6 +276,9 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = if not <| String.IsNullOrEmpty(newCode) then fsiEval.EvalInteraction(newCode) + + if fsiout.Value then + pyout (sbOut.ToString()) /// Handles an 'execute_request' message let executeRequest(msg : KernelMessage) (content : ExecuteRequest) = @@ -227,7 +286,6 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = // clear some state sbOut.Clear() |> ignore sbErr.Clear() |> ignore - data.Clear() payload.Clear() // only increment if we are not silent @@ -275,17 +333,16 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = // send all the data if not <| content.silent then - if data.Count = 0 then - let lastExpression = GetLastExpression() - match lastExpression with - | Some(it) -> + let lastExpression = GetLastExpression() + match lastExpression with + | Some(it) -> - let printer = Printers.findDisplayPrinter(it.ReflectionType) - let (_, callback) = printer - let callbackValue = callback(it.ReflectionValue) - sendDisplayData callbackValue.ContentType callbackValue.Data "pyout" + let printer = Printers.findDisplayPrinter(it.ReflectionType) + let (_, callback) = printer + let callbackValue = callback(it.ReflectionValue) + sendDisplayData callbackValue.ContentType callbackValue.Data "pyout" - | None -> () + | None -> () // we are now idle sendStateIdle msg @@ -384,11 +441,12 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = /// Handles a 'shutdown_request' message let shutdownRequest (msg : KernelMessage) (content : ShutdownRequest) = - + logMessage "shutdown request" // TODO: actually shutdown let reply = { restart = true; } - sendMessage shellSocket msg "shutdown_reply" reply + sendMessage shellSocket msg "shutdown_reply" reply; + System.Environment.Exit(0) /// Handles a 'history_request' message let historyRequest (msg : KernelMessage) (content : HistoryRequest) = @@ -401,6 +459,12 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = // TODO: actually handle this () + let inspectRequest (msg : KernelMessage) (content : InspectRequest) = + // TODO: actually handle this + let reply = { status = "ok"; found = false; data = Dictionary(); metadata = Dictionary() } + sendMessage shellSocket msg "inspect_reply" reply + () + /// Loops forever receiving messages from the client and processing them let doShell() = @@ -413,7 +477,6 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = logMessage (sbOut.ToString()) while true do - let msg = recvMessage (shellSocket) try @@ -426,16 +489,28 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = | ShutdownRequest(r) -> shutdownRequest msg r | HistoryRequest(r) -> historyRequest msg r | ObjectInfoRequest(r) -> objectInfoRequest msg r - | _ -> logMessage (String.Format("Unknown content type. msg_type is `{0}`", msg.Header.msg_type)) + | InspectRequest(r) -> inspectRequest msg r + | _ -> logMessage (String.Format("Unknown content type on shell. msg_type is `{0}`", msg.Header.msg_type)) with | ex -> handleException ex + let doControl() = + while true do + let msg = recvMessage (controlSocket) + try + match msg.Content with + | ShutdownRequest(r) -> shutdownRequest msg r + | _ -> logMessage (String.Format("Unexpected content type on control. msg_type is `{0}`", msg.Header.msg_type)) + with + | ex -> handleException ex + /// Loops repeating message from the client let doHeartbeat() = try while true do - hbSocket.Send(hbSocket.Receive()) + let hb = hbSocket.ReceiveMultipartBytes() in + hbSocket.SendMultipartBytes hb with | ex -> handleException ex @@ -455,5 +530,6 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = /// Starts the kernel asynchronously member __.StartAsync() = - Async.Start (async { doHeartbeat() } ) + //Async.Start (async { doHeartbeat() } ) Async.Start (async { doShell() } ) + Async.Start (async { doControl() } ) \ No newline at end of file diff --git a/src/IfSharp.Kernel/MainTypes.fs b/src/IfSharp.Kernel/MainTypes.fs index 759e8ce..c6e4c47 100644 --- a/src/IfSharp.Kernel/MainTypes.fs +++ b/src/IfSharp.Kernel/MainTypes.fs @@ -1,7 +1,6 @@ namespace IfSharp.Kernel open System.Collections.Generic -open FSharp.Charting type ConnectionInformation = { diff --git a/src/IfSharp.Kernel/NuGetManager.fs b/src/IfSharp.Kernel/NuGetManager.fs index fe51168..8c85207 100644 --- a/src/IfSharp.Kernel/NuGetManager.fs +++ b/src/IfSharp.Kernel/NuGetManager.fs @@ -59,6 +59,8 @@ type CustomErrorInfo = type PreprocessResults = { OriginalLines : string[]; + HelpLines : string[]; + FsiOutputLines : string[]; NuGetLines : string[]; FilteredLines : string[]; Packages : NuGetPackage[]; @@ -87,24 +89,6 @@ type CustomInstallCommand() = let semanticVersion = SemanticVersion(version) packageManager.LocalRepository.FindPackage(packageId, semanticVersion) -module NuGetManagerInternals = - - /// Separates a list of lines between into two partitions, the first list are the directive lines, second list is the other lines - let partitionLines(directive) (lines : string[]) = - lines - |> Seq.mapi (fun (idx) (line) -> (idx, line)) - |> Seq.toList - |> List.partition (fun (idx, line) -> line.StartsWith(directive)) - - /// Separates a list of lines between into two partitions, the first list are the directive lines, second list is the other lines - let partitionSource(directive) (source : string) = - let delimiters = [|"\r\n"; "\n"; "\r";|] - partitionLines directive (source.Split(delimiters, StringSplitOptions.None)) - - /// Parses a directive line. Example: #N "Deedle" - let parseDirectiveLine (prefix : string) (line : string) = - line.Substring(prefix.Length + 1).Trim().Trim('"') - /// The NuGetManager class contains methods for downloading nuget packages and such type NuGetManager (executingDirectory : string) = @@ -176,34 +160,54 @@ type NuGetManager (executingDirectory : string) = else let pkg = installer.FindPackage(nugetPackage, version) - let getCompatibleItems targetFramework items = - let retval, compatibleItems = VersionUtility.TryGetCompatibleItems(targetFramework, items) - if retval then compatibleItems else Seq.empty - - let maxFramework = - // try full framework first - if none is supported, fall back - let fullFrameworks = pkg.GetSupportedFrameworks() |> Seq.filter (fun x -> x.Identifier = ".NETFramework") |> Seq.toArray - if Array.length fullFrameworks > 0 then fullFrameworks |> Array.maxBy (fun x -> x.Version) - else pkg.GetSupportedFrameworks() |> Seq.maxBy (fun x -> x.Version) - - let assemblies = - if not(pkg.PackageAssemblyReferences.IsEmpty()) then - let compatibleAssemblyReferences = - getCompatibleItems maxFramework pkg.PackageAssemblyReferences - |> Seq.collect (fun x -> x.References) - |> Set.ofSeq - pkg.AssemblyReferences - |> Seq.filter (fun x -> compatibleAssemblyReferences.Contains x.Name && x.TargetFramework = maxFramework ) - elif pkg.AssemblyReferences.IsEmpty() then - Seq.empty - else - getCompatibleItems maxFramework pkg.AssemblyReferences - - let frameworkAssemblyReferences = getCompatibleItems maxFramework pkg.FrameworkAssemblies - - packagesCache.Add(key, { Package = Some pkg; Assemblies = assemblies; FrameworkAssemblies = frameworkAssemblyReferences; Error = ""; }) - packagesCache.[key] + + if pkg.GetSupportedFrameworks().IsEmpty() then // content-only package + packagesCache.Add(key, { Package = Some pkg; Assemblies = Seq.empty; FrameworkAssemblies = Seq.empty; Error = ""; }); + else + let getCompatibleItems targetFramework items = + let retval, compatibleItems = VersionUtility.TryGetCompatibleItems(targetFramework, items) + if retval then compatibleItems else Seq.empty + + let maxFramework = + // try full framework first - if none is supported, fall back + let fullFrameworks = pkg.GetSupportedFrameworks() |> Seq.filter (fun x -> x.Identifier = ".NETFramework") |> Seq.toArray + if Array.length fullFrameworks > 0 then fullFrameworks |> Array.maxBy (fun x -> x.Version) + else pkg.GetSupportedFrameworks() |> Seq.maxBy (fun x -> x.Version) + + let assemblies = + if not(pkg.PackageAssemblyReferences.IsEmpty()) then + let compatibleAssemblyReferences = + getCompatibleItems maxFramework pkg.PackageAssemblyReferences + |> Seq.collect (fun x -> x.References) + |> Set.ofSeq + pkg.AssemblyReferences + |> Seq.filter (fun x -> compatibleAssemblyReferences.Contains x.Name && x.TargetFramework = maxFramework ) + elif pkg.AssemblyReferences.IsEmpty() then + Seq.empty + else + getCompatibleItems maxFramework pkg.AssemblyReferences + + let frameworkAssemblyReferences = getCompatibleItems maxFramework pkg.FrameworkAssemblies + + packagesCache.Add(key, { Package = Some pkg; Assemblies = assemblies; FrameworkAssemblies = frameworkAssemblyReferences; Error = ""; }) + + try + let name = pkg.Id + "." + pkg.Version.ToString() + let folder = Path.Combine(installer.OutputDirectory, name) + let contentDir = Path.Combine(Directory.GetCurrentDirectory(), "content") + let cfiles = pkg.GetContentFiles(); + if Directory.Exists(contentDir) = false then Directory.CreateDirectory(contentDir) |> ignore + for f in cfiles do + let fullName = Path.Combine(folder, f.Path) + let targetName = Path.Combine(contentDir, f.EffectivePath) + let targetPath = Path.GetDirectoryName(targetName) + if Directory.Exists(targetPath) = false then Directory.CreateDirectory(targetPath) |> ignore + if File.Exists(targetName) then File.Delete(targetName) + File.Copy(fullName, targetName, true) + done + with exc -> Console.Out.WriteLine(exc.ToString()) + packagesCache.[key] ) /// Parses a 'nuget line'. Example #N "[/[/pre]]". @@ -211,7 +215,7 @@ type NuGetManager (executingDirectory : string) = /// prerelease should be used or not. member this.ParseNugetLine (line : string) = - let contents = NuGetManagerInternals.parseDirectiveLine "#N" line + let contents = DirectivePreprocessor.parseDirectiveLine "#N" line if contents.Contains("/") then let splits = contents.Split([| '/' |]) if splits.Length > 2 then @@ -226,7 +230,15 @@ type NuGetManager (executingDirectory : string) = // split the source code into lines, then get the nuget lines let lines = source.Split('\n') - let (nugetLines, otherLines) = NuGetManagerInternals.partitionLines "#N" lines + let linesSplit = DirectivePreprocessor.partitionLines lines + + let orEmpty key = let opt = Map.tryFind key linesSplit + if opt.IsSome then opt.Value else Seq.empty + + let helpLines = DirectivePreprocessor.Line.HelpDirective |> orEmpty + let fsiOutputLines = DirectivePreprocessor.Line.FSIOutputDirective |> orEmpty + let nugetLines = DirectivePreprocessor.Line.NugetDirective |> orEmpty + let otherLines = DirectivePreprocessor.Line.Other |> orEmpty // parse the nuget lines and then download the packages let nugetPackages = @@ -238,14 +250,17 @@ type NuGetManager (executingDirectory : string) = // gather errors let errors = nugetPackages - |> Seq.filter (fun (idx, package) -> String.IsNullOrEmpty(package.Error) = false) + |> Seq.filter (fun (_, package) -> String.IsNullOrEmpty(package.Error) = false) |> Seq.map (fun (idx, package) -> CustomErrorInfo.From("", idx, 0, idx, lines.[idx].Length, package.Error, "Error", "preprocess")) |> Seq.toArray { OriginalLines = lines; - NuGetLines = nugetLines |> Seq.map(fun (idx, line) -> line) |> Seq.toArray; - FilteredLines = otherLines |> Seq.map(fun (idx, line) -> line) |> Seq.toArray; - Packages = nugetPackages |> Seq.map(fun (idx, package) -> package) |> Seq.toArray; + HelpLines = helpLines |> Seq.map(fun (_, line) -> line) |> Seq.toArray; + FsiOutputLines = fsiOutputLines |> Seq.map(fun (_, line) -> line) |> Seq.toArray; + NuGetLines = nugetLines |> Seq.map(fun (_, line) -> line) |> Seq.toArray; + + FilteredLines = otherLines |> Seq.map(fun (_, line) -> line) |> Seq.toArray; + Packages = nugetPackages |> Seq.map(fun (_, package) -> package) |> Seq.toArray; Errors = errors; } diff --git a/src/IfSharp.Kernel/Printers.fs b/src/IfSharp.Kernel/Printers.fs index e0cf05a..145d9c3 100644 --- a/src/IfSharp.Kernel/Printers.fs +++ b/src/IfSharp.Kernel/Printers.fs @@ -3,9 +3,11 @@ open System open System.Text open System.Web -open FSharp.Charting +//open System.Drawing +//open System.Drawing.Imaging +//open System.IO -module Printers = +module Printers = let mutable internal displayPrinters : list BinaryOutput)> = [] @@ -13,17 +15,26 @@ module Printers = let internal htmlEncode(str) = HttpUtility.HtmlEncode(str) /// Adds a custom display printer for extensibility - let internal addDisplayPrinter(printer : 'T -> BinaryOutput) = + let addDisplayPrinter(printer : 'T -> BinaryOutput) = displayPrinters <- (typeof<'T>, (fun (x:obj) -> printer (unbox x))) :: displayPrinters /// Default display printer - let internal defaultDisplayPrinter(x) = + let defaultDisplayPrinter(x) = { ContentType = "text/plain"; Data = sprintf "%A" x } /// Finds a display printer based off of the type - let internal findDisplayPrinter(findType) = - let printers = - displayPrinters + let findDisplayPrinter(findType) = + // Get printers that were registered using `fsi.AddHtmlPrinter` and turn them + // into printers expected here (just contactenate all +""" + (wc.DownloadString("https://cdn.plot.ly/plotly-latest.min.js")) + |> Util.Html + |> Display + +type XPlot.Plotly.PlotlyChart with + + member __.GetPngHtml() = + let html = __.GetInlineHtml() + html + .Replace("Plotly.newPlot(", "Plotly.plot(") + .Replace( + "data, layout);", + "data, layout).then(ifsharpMakePng);") + + member __.GetSvgHtml() = + let html = __.GetInlineHtml() + html + .Replace("Plotly.newPlot(", "Plotly.plot(") + .Replace( + "data, layout);", + "data, layout).then(ifsharpMakeSvg);") + +type XPlot.Plotly.Chart with + + static member Png (chart: PlotlyChart) = + { ContentType = "text/html" + Data = chart.GetPngHtml() + } + + static member Svg (chart: PlotlyChart) = + { ContentType = "text/html" + Data = chart.GetSvgHtml() + } diff --git a/src/IfSharp.Kernel/helpers/paket.bootstrapper.exe b/src/IfSharp.Kernel/helpers/paket.bootstrapper.exe new file mode 100644 index 0000000..0be03a7 Binary files /dev/null and b/src/IfSharp.Kernel/helpers/paket.bootstrapper.exe differ diff --git a/src/IfSharp.Kernel/helpers/paket.dependencies b/src/IfSharp.Kernel/helpers/paket.dependencies new file mode 100644 index 0000000..b689168 --- /dev/null +++ b/src/IfSharp.Kernel/helpers/paket.dependencies @@ -0,0 +1 @@ +source https://www.nuget.org/api/v2 diff --git a/src/IfSharp.Kernel/helpers/paket.lock b/src/IfSharp.Kernel/helpers/paket.lock new file mode 100644 index 0000000..e69de29 diff --git a/src/IfSharp.Kernel/packages.config b/src/IfSharp.Kernel/packages.config deleted file mode 100644 index ebffdac..0000000 --- a/src/IfSharp.Kernel/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/IfSharp.Kernel/paket.references b/src/IfSharp.Kernel/paket.references new file mode 100644 index 0000000..7ab942c --- /dev/null +++ b/src/IfSharp.Kernel/paket.references @@ -0,0 +1,4 @@ +FSharp.Compiler.Service +NetMQ +Newtonsoft.Json +Paket.Core \ No newline at end of file diff --git a/src/IfSharpConsole/App.config b/src/IfSharpConsole/App.config index 4b827ab..b684b39 100644 --- a/src/IfSharpConsole/App.config +++ b/src/IfSharpConsole/App.config @@ -12,7 +12,7 @@ - + diff --git a/src/IfSharpConsole/FSharp.Core.optdata b/src/IfSharpConsole/FSharp.Core.optdata index f85242e..896507e 100644 Binary files a/src/IfSharpConsole/FSharp.Core.optdata and b/src/IfSharpConsole/FSharp.Core.optdata differ diff --git a/src/IfSharpConsole/FSharp.Core.sigdata b/src/IfSharpConsole/FSharp.Core.sigdata index 96d9344..a383f91 100644 Binary files a/src/IfSharpConsole/FSharp.Core.sigdata and b/src/IfSharpConsole/FSharp.Core.sigdata differ diff --git a/src/IfSharpConsole/IfSharpConsole.csproj b/src/IfSharpConsole/IfSharpConsole.csproj index 79904e1..133e6fd 100644 --- a/src/IfSharpConsole/IfSharpConsole.csproj +++ b/src/IfSharpConsole/IfSharpConsole.csproj @@ -38,6 +38,9 @@ icon.ico + + true + false @@ -63,13 +66,6 @@ true - - False - ..\..\packages\FSharp.Compiler.Service.0.0.81\lib\net45\FSharp.Compiler.Service.dll - - - ..\..\packages\NetMQ.3.3.0.11\lib\net40\NetMQ.dll - @@ -83,14 +79,19 @@ - + + paket.lock + + + Designer + PreserveNewest PreserveNewest - + @@ -101,13 +102,81 @@ + + + + + false + + - + - --> + + + + + + ..\..\packages\AsyncIO\lib\net40\AsyncIO.dll + True + True + + + + + + + + + ..\..\packages\FSharp.Compiler.Service\lib\net45\FSharp.Compiler.Service.MSBuild.v12.dll + True + True + + + ..\..\packages\FSharp.Compiler.Service\lib\net45\FSharp.Compiler.Service.dll + True + True + + + + + + + + + ..\..\packages\NetMQ\lib\net40\NetMQ.dll + True + True + + + + + + + + + ..\..\packages\System.Collections.Immutable\lib\netstandard1.0\System.Collections.Immutable.dll + True + True + + + + + + + + + ..\..\packages\System.Reflection.Metadata\lib\netstandard1.1\System.Reflection.Metadata.dll + True + True + + + + \ No newline at end of file diff --git a/src/IfSharpConsole/Program.cs b/src/IfSharpConsole/Program.cs index bfc1d3a..2aa7ea0 100644 --- a/src/IfSharpConsole/Program.cs +++ b/src/IfSharpConsole/Program.cs @@ -1,4 +1,5 @@ using IfSharp.Kernel; +using System.Globalization; namespace IfSharpConsole { @@ -6,6 +7,9 @@ class Program { static void Main(string[] args) { + //System.Diagnostics.Debugger.Launch(); + CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; + CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; App.Start(args); } } diff --git a/src/IfSharpConsole/packages.config b/src/IfSharpConsole/packages.config deleted file mode 100644 index 495e7ac..0000000 --- a/src/IfSharpConsole/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/IfSharpConsole/paket.references b/src/IfSharpConsole/paket.references new file mode 100644 index 0000000..53aa166 --- /dev/null +++ b/src/IfSharpConsole/paket.references @@ -0,0 +1,2 @@ +FSharp.Compiler.Service +NetMQ \ No newline at end of file diff --git a/tests/IfSharp.Kernel.Tests/ExampleTest.fs b/tests/IfSharp.Kernel.Tests/ExampleTest.fs new file mode 100644 index 0000000..faa8eb6 --- /dev/null +++ b/tests/IfSharp.Kernel.Tests/ExampleTest.fs @@ -0,0 +1,11 @@ +module ExampleTest + +open Xunit + +open IfSharp.Kernel + +[] +let ``Trivial example test``() = + + let asSvg = Util.Svg "test" + Assert.Equal("test", asSvg.Svg) \ No newline at end of file diff --git a/tests/IfSharp.Kernel.Tests/IfSharp.Kernel.Tests.fsproj b/tests/IfSharp.Kernel.Tests/IfSharp.Kernel.Tests.fsproj index 18cbd3e..f6946cf 100644 --- a/tests/IfSharp.Kernel.Tests/IfSharp.Kernel.Tests.fsproj +++ b/tests/IfSharp.Kernel.Tests/IfSharp.Kernel.Tests.fsproj @@ -9,11 +9,12 @@ Library IfSharp.Kernel.Tests IfSharp.Kernel.Tests - v4.5 + v4.5.1 4.3.1.0 IfSharp.Kernel.Tests ..\..\ true + true @@ -71,29 +72,159 @@ - + + + - + - - True - - - ..\..\packages\NetMQ.3.3.0.11\lib\net40\NetMQ.dll - True + + ..\..\lib\NuGet.dll - + + + IfSharp.Kernel + {2fe619b3-4756-4285-b31f-232607f62d78} + True + + + + + + + ..\..\packages\AsyncIO\lib\net40\AsyncIO.dll + True + True + + + + + + + + + ..\..\packages\Chessie\lib\net40\Chessie.dll + True + True + + + + + + + + + ..\..\packages\FSharp.Compiler.Service\lib\net45\FSharp.Compiler.Service.MSBuild.v12.dll + True + True + + + ..\..\packages\FSharp.Compiler.Service\lib\net45\FSharp.Compiler.Service.dll + True + True + + + + + + + + + ..\..\packages\FSharp.Core\lib\net40\FSharp.Core.dll + True + True + + + + + + + + + ..\..\packages\NetMQ\lib\net40\NetMQ.dll + True + True + + + + + + + + + ..\..\packages\System.Collections.Immutable\lib\netstandard1.0\System.Collections.Immutable.dll + True + True + + + + + + + + + ..\..\packages\System.Reflection.Metadata\lib\netstandard1.1\System.Reflection.Metadata.dll + True + True + + + + + + + + + ..\..\packages\xunit.abstractions\lib\net35\xunit.abstractions.dll + True + True + + + + + + + + + ..\..\packages\xunit.assert\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll + True + True + + + + + + + + + ..\..\packages\xunit.extensibility.core\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll + True + True + + + + + + + + + ..\..\packages\xunit.extensibility.execution\lib\net45\xunit.execution.desktop.dll + True + True + + + + \ No newline at end of file diff --git a/tests/IfSharp.Kernel.Tests/packages.config b/tests/IfSharp.Kernel.Tests/packages.config deleted file mode 100644 index 91f211a..0000000 --- a/tests/IfSharp.Kernel.Tests/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tests/IfSharp.Kernel.Tests/paket.references b/tests/IfSharp.Kernel.Tests/paket.references new file mode 100644 index 0000000..32c0d9b --- /dev/null +++ b/tests/IfSharp.Kernel.Tests/paket.references @@ -0,0 +1,4 @@ +FSharp.Compiler.Service +Chessie +NetMQ +xUnit