{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f6c85fc5-9435-4201-9d21-6a17513880d3",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Welcome to GoNB\n",
    "\n",
    "[*GoNB*](https://github.com/janpfeifer/gonb) is a *Go* notebook kernel. It allows one to easily run *Go* code in a *Jupyter Notebook* and variations.\n",
    "\n",
    "In this tutorial we will walk through most of its features, and explain how it works.\n",
    "\n",
    "See the [README.md's Installation section](https://github.com/janpfeifer/gonb#installation). It also includes a Docker with Jupyter+GoNB pre-installed, that makes it trivial.\n",
    "\n",
    "## Hello World!\n",
    "\n",
    "Whenever a cell is executed, *gonb* saves the cell content of the cell to a *Go* file, auto-imports missing dependencies (when it can guess), compiles and runs it. It may seem a lot, but its pretty fast (except\n",
    "maybe the first cell run that is usually a little slower) and feels interactive. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "430b4b44-0012-4086-8460-5ba81d20a699",
   "metadata": {},
   "outputs": [],
   "source": [
    "func main() {\n",
    "    fmt.Printf(\"Hello World!\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a936cc25-a399-43d8-8590-4bbf116a9c15",
   "metadata": {},
   "source": [
    "Easy, right ? Now when trying different things, to avoid having to write `func main()` at every cell, *gonb* provides a short cut: \"%%\". Anything after a \"%%\" will be wrapped inside a `func main() { ... }`.\n",
    "\n",
    "So, let's redo our example above:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "781a15d2-469e-4c93-b45e-c18067787f97",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%\n",
    "fmt.Printf(\"Hello World!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1917b045-6b6a-47bb-a9ed-dfac8fecaca7",
   "metadata": {},
   "source": [
    "## Globals and Updates\n",
    "\n",
    "Imports, functions, constants, types and variables global declarations are memorized once executed, and carry over from one cell to another.\n",
    "\n",
    "So one can work on different functions let's say on different cells.\n",
    "\n",
    "Let's test it out:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c73bb12-8e2e-49a8-ae72-60d2c022e25f",
   "metadata": {},
   "outputs": [],
   "source": [
    "type Number interface { ~int | ~float64 | ~float32 }\n",
    "\n",
    "func incr[T Number](x T) T {\n",
    "    return x+T(1)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35bb337a-1a73-4d0d-a411-3eec6c1e8507",
   "metadata": {},
   "source": [
    "Ok, now we have `incr` defined to any numeric type, we can use it in all our future cells.\n",
    "\n",
    "Some quick tests:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1414e76-3fa9-4046-827b-e449cc638e81",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%\n",
    "x := incr(1)\n",
    "y := incr(math.Pi)\n",
    "fmt.Printf(\"incr: x=%d, y=%f\\n\", x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c1b28f7-b36f-4ec3-9239-99f8352c01fb",
   "metadata": {},
   "source": [
    "> **Note**: Only the various declarations are carried over from one cell to another, not the results of the execution, including updates to variables.\n",
    "> \n",
    "> So for instance, if we initialize a variable `startValue` with 1, then increment it in one cell. Next time we execute a new cell, it will be again initialized to 1. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5549f231-4061-4ec7-9e1c-8ae56c7c82d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "var startValue = float32(1)\n",
    "\n",
    "%%\n",
    "startValue = incr(startValue)\n",
    "fmt.Printf(\"current startValue=%f\\n\", startValue)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df2be63f-991f-4c4d-acf3-d705fa2b6d96",
   "metadata": {},
   "source": [
    "Now if we execute again, `startValue` is again initialized to 1:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2111a24e-2627-463a-b936-d8476ba69005",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%\n",
    "fmt.Printf(\"current startValue=%f\\n\", startValue)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4fc0797-df7f-4652-8a22-e8b489580e1d",
   "metadata": {},
   "source": [
    "If one wants to save results calculated from one cell to another, GoNB includes the `github.com/janpfeifer/gonb/cache` package that makes it trivial to save and load previously generated results.\n",
    "\n",
    "**Example**: Below `VeryExpensive` is only called once for `CachedValue`, so you will notice that if you run the cell multiple times, it will display always the same number, while `NonCachedValue` will always call `VeryExpensive` again, and display another number. So the string \"...calculating...\" is printed twice only the first time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d59e0cbd-0d2a-42e5-8f94-29936130f437",
   "metadata": {},
   "outputs": [],
   "source": [
    "// Temporary fix until new release v0.6.0 propagates.\n",
    "import (\n",
    "    \"math/rand\"\n",
    "    \"github.com/janpfeifer/gonb/cache\"\n",
    ")\n",
    "\n",
    "func VeryExpensive() int {\n",
    "    fmt.Println(\"\\t...VeryExpensive() call...\")\n",
    "    return rand.Intn(1000)\n",
    "}\n",
    "\n",
    "var (\n",
    "    CachedValue = cache.Cache(\"expensive\", VeryExpensive)\n",
    "    NonCachedValue = VeryExpensive()\n",
    ")\n",
    "    \n",
    "%%\n",
    "fmt.Printf(\"NonCachedValue=%d\\n\", NonCachedValue)\n",
    "fmt.Printf(\"   CachedValue=%d\\n\", CachedValue)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a5470b4-43ea-47ae-a111-63ab77376bd0",
   "metadata": {},
   "source": [
    "The `cache` package has many more features, check out [its documentation](https://pkg.go.dev/github.com/janpfeifer/gonb/cache).\n",
    "\n",
    "Now, we don't want to have `NonCachedValue` execute `VeryExpensive` at every cell, so let's remove its definition.\n",
    "See how to manage memorized definitions using `%help`, in the \"Managing Memorized Definitions\" section, it's displayed at the end of the tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "892deacf-a02c-4979-827c-59dedfdc0564",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%rm NonCachedValue CachedValue"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83c85334-6323-4b75-b9a2-184c0d83ed1c",
   "metadata": {},
   "source": [
    "## Imports\n",
    "\n",
    "A few things to remember from imports in *gonb*:\n",
    "\n",
    "* Like all other global declarations, they are memorized and carry over from one cell to the other. Still it's good practice to have each cell import what it needs -- no harm in importing the same thing multiple times.\n",
    "* *gonb* runs *goimports* before compiling the code: that means it will be able to automatically add import statements for builtin packages (in the examples above we never wrote `import \"fmt\"`, and it just worked).\n",
    "* *gonb* runs `go get` before compiling the code. This automatically fetches an external import dependency. That is convenient in most cases, but in case you want to get an external *Go* module at an specific version, you can do it manually with something like `!*go get <github.com/user/my_go_module>@<my_version>`. See below on running shell commands.\n",
    "\n",
    "Let's create a simple example that imports a delighful progress-bar library. Notice it automatically fetches the lastest version of the library `github.com/schollz/progressbar/v3` -- and the execution of the cell the first time may take a few seconds because of that.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cbfa0e91-0209-48f5-962e-fc43dbc6b5bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "import progressbar \"github.com/schollz/progressbar/v3\"\n",
    "\n",
    "%%\n",
    "bar := progressbar.NewOptions(100, \n",
    "                              progressbar.OptionUseANSICodes(true),\n",
    "                              progressbar.OptionShowIts(),\n",
    "                              progressbar.OptionSetItsString(\"steps\"))\n",
    "for i := 0; i < 100; i++ {\n",
    "    bar.Add(1)\n",
    "    time.Sleep(40 * time.Millisecond)\n",
    "}\n",
    "fmt.Printf(\"\\nDone\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "452a879c-f36a-4c77-8c72-7daeeee6c319",
   "metadata": {},
   "source": [
    "## Displaying HTML, Image and others\n",
    "\n",
    "One of the things that makes working in Notebooks better than using a terminal is that one can display rich content, like dynamically generated images, plots, HTML, even videos and sound.\n",
    "\n",
    "We'll follow with a few examples of what is already supported.\n",
    "\n",
    "### Improved Hello World!\n",
    "\n",
    "*gonb* includes the accompanying library *gonbUI* that handles the interfacing to the Notebook through a very simple API:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "48c217c5-6712-496e-8281-1179a487ad08",
   "metadata": {},
   "outputs": [],
   "source": [
    "import \"github.com/janpfeifer/gonb/gonbui\"\n",
    "\n",
    "%%\n",
    "gonbui.DisplayHtml(`<span style=\"background:pink; color:#111; border-radius: 3px; border: 3px solid orange; font-size: 18px;\">I 🧡 GoNB!</span>`)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31d7080f-d94d-4e2d-92d8-e8173244c21c",
   "metadata": {},
   "source": [
    "### Markdown\n",
    "\n",
    "Markdown output is also supported -- and used for the `%help` command, see the bottom of the tutorial.\n",
    "\n",
    "This also renders math formulas using latex, use `$x^2$` for formulas inlined in text, or\n",
    "`$$x^2$$` for formulas in their own line.\n",
    "\n",
    "Example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9b76a18c-faa8-449e-b56a-8453de99dbc3",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%\n",
    "gonbui.DisplayMarkdown(\"#### Objective\\n\\n1. Have fun coding **Go**;\\n1. Profit...\\n\"+\n",
    "                       `$$f(x) = \\int_{-\\infty}^{\\infty} e^{-x^2} dx$$`)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78f82dcb-3bf4-4c43-8f41-3b8dc3fe3461",
   "metadata": {},
   "source": [
    "### Fractals\n",
    "\n",
    "Let's draw a fractal, using another fun package: [github.com/benc-uk/gofract](\"github.com/benc-uk/gofract\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e5f298cd-a260-493c-9c59-dd3d79706116",
   "metadata": {},
   "outputs": [],
   "source": [
    "import \"github.com/benc-uk/gofract/pkg/fractals\"\n",
    "import \"github.com/benc-uk/gofract/pkg/colors\"\n",
    "\n",
    "%%\n",
    "imgWidth := 320\n",
    "\n",
    "// Default fractal\n",
    "f := fractals.Fractal{\n",
    "    FractType:    \"mandelbrot\",\n",
    "    Center:       fractals.ComplexPair{-0.6, 0.0},\n",
    "    MagFactor:    1.0,\n",
    "    MaxIter:      90,\n",
    "    W:            3.0,\n",
    "    H:            2.0,\n",
    "    ImgWidth:     imgWidth,\n",
    "    JuliaSeed:    fractals.ComplexPair{0.355, 0.355},\n",
    "    InnerColor:   \"#000000\",\n",
    "    FullScreen:   false,\n",
    "    ColorRepeats: 2,\n",
    "}\n",
    "gradient := colors.GradientTable{}\n",
    "gradient.AddToTable(\"#000762\", 0.0)\n",
    "gradient.AddToTable(\"#0B48C3\", 0.2)\n",
    "gradient.AddToTable(\"#ffffff\", 0.4)\n",
    "gradient.AddToTable(\"#E3A000\", 0.5)\n",
    "gradient.AddToTable(\"#000762\", 0.9)\n",
    "imgHeight := int(float64(imgWidth) * float64(f.H/f.W))\n",
    "img := image.NewRGBA(image.Rect(0, 0, f.ImgWidth, imgHeight))\n",
    "lastRenderTime := f.Render(img, gradient)\n",
    "fmt.Printf(\"lastRenderTime=%v\\n\", lastRenderTime)\n",
    "gonbui.DisplayImage(img)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87fb9828-c892-447b-9eb1-0717a96c342b",
   "metadata": {},
   "source": [
    "### SVG Drawing\n",
    "\n",
    "From the amazing [SVGo library](https://github.com/ajstarks/svgo), I really wish I was that creative.\n",
    "Below is Antony Stark's Shining example, [demoed here](https://speakerdeck.com/ajstarks/svgo-code-plus-picture-examples)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "48d813a8-8d4e-4ab7-b374-10a73977ec7b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import \"bytes\"\n",
    "import svgo \"github.com/ajstarks/svgo\"\n",
    "import \"github.com/janpfeifer/gonb/gonbui\"\n",
    "\n",
    "func Shining(width, height int) string {\n",
    "    buf := bytes.NewBuffer(nil)\n",
    "    canvas := svgo.New(buf)\n",
    "    xp := []int{50, 70, 70, 50, 30, 30}\n",
    "    yp := []int{40, 50, 75, 85, 75, 50}\n",
    "    xl := []int{0, 0, 50, 100, 100}\n",
    "    yl := []int{100, 40, 10, 40, 100}\n",
    "    bgcolor := \"rgb(227,78,25)\"\n",
    "    bkcolor := \"rgb(153,29,40)\"\n",
    "    stcolor := \"rgb(65,52,44)\"\n",
    "    stwidth := 12\n",
    "    stylefmt := \"stroke:%s;stroke-width:%d;fill:%s\"\n",
    "    canvas.Start(width, height)\n",
    "    canvas.Def()\n",
    "    canvas.Gid(\"unit\")\n",
    "    canvas.Polyline(xl, yl, \"fill:none\")\n",
    "    canvas.Polygon(xp, yp)\n",
    "    canvas.Gend()\n",
    "    canvas.Gid(\"runit\")\n",
    "    canvas.TranslateRotate(150, 180, 180)\n",
    "    canvas.Use(0, 0, \"#unit\")\n",
    "    canvas.Gend()\n",
    "    canvas.Gend()\n",
    "    canvas.DefEnd()\n",
    "    canvas.Rect(0, 0, width, height, \"fill:\"+bgcolor)\n",
    "    canvas.Gstyle(fmt.Sprintf(stylefmt, stcolor, stwidth, bkcolor))\n",
    "    for y := 0; y < height; y += 130 {\n",
    "        for x := -50; x < width; x += 100 {\n",
    "            canvas.Use(x, y, \"#unit\")\n",
    "            canvas.Use(x, y, \"#runit\")\n",
    "        }\n",
    "    } \n",
    "    canvas.Gend()\n",
    "    canvas.End()\n",
    "    return buf.String()\n",
    "}\n",
    "\n",
    "%%\n",
    "gonbui.DisplaySvg(Shining(500, 500))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61449cd0-4a9a-4934-ae73-5aa504edf3fb",
   "metadata": {},
   "source": [
    "### Plotting with [Margaid (SVG)](https://github.com/erkkah/margaid)\n",
    "\n",
    "A real pearl!\n",
    "\n",
    "Since its latest update is not yet \"released\" (tagged in Git), we needed to get the version on the specific commit. See \"Executing Shell Commands\" below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1fda7399-ecb1-44c2-b05c-fb52d188822a",
   "metadata": {},
   "outputs": [],
   "source": [
    "!*go get -u github.com/erkkah/margaid@d60b2efd2f5acc5d8fbbe13eaf85f1532e11a2fb"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6904a8ef-5a4c-412f-ad75-58910d933b76",
   "metadata": {},
   "outputs": [],
   "source": [
    "import \"bytes\"\n",
    "import \"github.com/janpfeifer/gonb/gonbui\"\n",
    "import mg \"github.com/erkkah/margaid\"\n",
    "\n",
    "func mgPlot(width, height int) string {\n",
    "    randomSeries := mg.NewSeries()\n",
    "    rand.Seed(time.Now().Unix())\n",
    "    for i := float64(0); i < 10; i++ {\n",
    "        randomSeries.Add(mg.MakeValue(i+1, 200*rand.Float64()))\n",
    "    }\n",
    "\n",
    "    testSeries := mg.NewSeries()\n",
    "    multiplier := 2.1\n",
    "    v := 0.33\n",
    "    for i := float64(0); i < 10; i++ {\n",
    "        v *= multiplier\n",
    "        testSeries.Add(mg.MakeValue(i+1, v))\n",
    "    }\n",
    "\n",
    "    diagram := mg.New(width, height,\n",
    "        mg.WithAutorange(mg.XAxis, testSeries),\n",
    "        mg.WithAutorange(mg.YAxis, testSeries),\n",
    "        mg.WithAutorange(mg.Y2Axis, testSeries),\n",
    "        mg.WithProjection(mg.YAxis, mg.Log),\n",
    "        mg.WithInset(70),\n",
    "        mg.WithPadding(2),\n",
    "        mg.WithColorScheme(90),\n",
    "        mg.WithBackgroundColor(\"#f8f8f8\"),\n",
    "    )\n",
    "\n",
    "    diagram.Line(testSeries, mg.UsingAxes(mg.XAxis, mg.YAxis), mg.UsingMarker(\"square\"), mg.UsingStrokeWidth(1))\n",
    "    diagram.Smooth(testSeries, mg.UsingAxes(mg.XAxis, mg.Y2Axis), mg.UsingStrokeWidth(3.14))\n",
    "    diagram.Smooth(randomSeries, mg.UsingAxes(mg.XAxis, mg.YAxis), mg.UsingMarker(\"filled-circle\"))\n",
    "    diagram.Axis(testSeries, mg.XAxis, diagram.ValueTicker('f', 0, 10), false, \"X\")\n",
    "    diagram.Axis(testSeries, mg.YAxis, diagram.ValueTicker('f', 1, 2), true, \"Y\")\n",
    "\n",
    "    diagram.Frame()\n",
    "    diagram.Title(\"A diagram of sorts 📊 📈\")\n",
    "    buf := bytes.NewBuffer(nil)\n",
    "    diagram.Render(buf)\n",
    "    return buf.String()\n",
    "}\n",
    "\n",
    "%%\n",
    "gonbui.DisplaySvg(mgPlot(640, 480))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f534d6c-57c4-4ec1-ba71-310d73fa5802",
   "metadata": {},
   "source": [
    "### Animated Plots with `UpdateHtml`\n",
    "\n",
    "Still using Margaid but now we animate a `Sin(x)` plot varying the frequency from 0.0 to 10.0, every 10 milliseconds. This demonstrates `gonbui.UpdateHtml(id, html)`: it allows a transient HTML cell to be updated in the middle of the execution of a cell.\n",
    "\n",
    "> **Note**: This also works with markdown, use `gonbui.UpdateMarkdown(id, markdown)` instead. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "293ceefc-c50c-4817-98fe-5f15ee4d0754",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import (\n",
    "    \"bytes\"\n",
    "    \"math\"\n",
    "    \"time\"\n",
    "    \n",
    "     \"github.com/janpfeifer/gonb/gonbui\"\n",
    "     mg \"github.com/erkkah/margaid\"\n",
    ")\n",
    "\n",
    "\n",
    "func mgSinPlot(width, height int, freq float64) string {\n",
    "    series := mg.NewSeries()\n",
    "    const numPoints = 100\n",
    "    for i := 0; i < numPoints; i++ {\n",
    "        x := float64(i) / float64(numPoints) * 2.0 * math.Pi * freq\n",
    "        series.Add(mg.MakeValue(x, math.Sin(x)))\n",
    "    }\n",
    "    diagram := mg.New(width, height,\n",
    "        mg.WithAutorange(mg.XAxis, series),\n",
    "        mg.WithAutorange(mg.YAxis, series),\n",
    "        mg.WithBackgroundColor(\"#f8f8f8\"),\n",
    "    )\n",
    "    diagram.Smooth(series, mg.UsingAxes(mg.XAxis, mg.YAxis), mg.UsingStrokeWidth(3.14))\n",
    "    diagram.Frame()\n",
    "    diagram.Title(\"Animated Sine\")\n",
    "    buf := bytes.NewBuffer(nil)\n",
    "    diagram.Render(buf)\n",
    "    return buf.String()\n",
    "}\n",
    "\n",
    "%%\n",
    "htmlCellId := \"sin_plot\"+gonbui.UniqueId()\n",
    "plotSvg := \"\"\n",
    "ticker := time.NewTicker(10 * time.Millisecond)\n",
    "for freq := 0.0; freq <= 10.0; freq += 0.05 {\n",
    "    plotSvg = mgSinPlot(1024, 400, freq)\n",
    "    gonbui.UpdateHtml(htmlCellId, plotSvg)\n",
    "    <-ticker.C\n",
    "}\n",
    "ticker.Stop()\n",
    "\n",
    "// Erase transient image and re-display it so it can be persisted.\n",
    "gonbui.UpdateHtml(htmlCellId, \"\")\n",
    "gonbui.DisplayHtml(plotSvg)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b6d9d8f-de5e-4154-9f09-21bb3b2f545f",
   "metadata": {},
   "source": [
    "### Plotting with [Gonum Plot](https://github.com/gonum/plot) (SVG and PNG)\n",
    "\n",
    "Another great plotting library."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "58248364-f1b3-4e56-adc7-7034a1f6c56d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import (\n",
    "    \"bytes\"\n",
    "    \"math/rand\"\n",
    "\n",
    "    \"github.com/janpfeifer/gonb/gonbui\"\n",
    "    \"gonum.org/v1/plot\"\n",
    "    \"gonum.org/v1/plot/plotter\"\n",
    "    \"gonum.org/v1/plot/plotutil\"\n",
    "    \"gonum.org/v1/plot/vg\"\n",
    ")\n",
    "\n",
    "// randomPoints returns some random x, y points.\n",
    "func randomPoints(n int) plotter.XYs {\n",
    "    pts := make(plotter.XYs, n)\n",
    "    for i := range pts {\n",
    "        if i == 0 {\n",
    "            pts[i].X = rand.Float64()\n",
    "        } else {\n",
    "            pts[i].X = pts[i-1].X + rand.Float64()\n",
    "        }\n",
    "        pts[i].Y = pts[i].X + 10*rand.Float64()\n",
    "    }\n",
    "    return pts\n",
    "}\n",
    "\n",
    "func GonumPlotExample(width, height int, format string) []byte {\n",
    "    rand.Seed(int64(0))\n",
    "\n",
    "    p := plot.New()\n",
    "\n",
    "    p.Title.Text = \"Plotutil example\"\n",
    "    p.X.Label.Text = \"X\"\n",
    "    p.Y.Label.Text = \"Y\"\n",
    "\n",
    "    err := plotutil.AddLinePoints(p,\n",
    "        \"First\", randomPoints(15),\n",
    "        \"Second\", randomPoints(15),\n",
    "        \"Third\", randomPoints(15))\n",
    "    if err != nil {\n",
    "        panic(err)\n",
    "    }\n",
    "    \n",
    "    buf := bytes.NewBuffer(nil)\n",
    "    writerTo, err := p.WriterTo(vg.Points(float64(width)), vg.Points(float64(height)), format)\n",
    "    if err != nil {\n",
    "        panic(err)\n",
    "    }\n",
    "    writerTo.WriteTo(buf)\n",
    "    return buf.Bytes()\n",
    "}\n",
    "\n",
    "%%\n",
    "gonbui.DisplayPng(GonumPlotExample(400, 200, \"png\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c311053c-84f4-43d4-84a3-87e9d987907f",
   "metadata": {},
   "source": [
    "The version in SVG looks better though:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5e2269c-3f8c-4f61-9b73-317d0c71498c",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%\n",
    "gonbui.DisplaySvg(string(GonumPlotExample(400, 200, \"svg\")))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "565909db-5b70-442e-a717-2873f21410c5",
   "metadata": {},
   "source": [
    "### Plotting with [Plotly](https://plotly.com/)\n",
    "\n",
    "[Plotly](https://plotly.com/) has really fancy plots, worth exploring for generating exquisite reports.\n",
    "\n",
    "Currently supported using [`github.com/MetalBlueberry/go-plotly`](https://github.com/MetalBlueberry/go-plotly) library.\n",
    "\n",
    "**Experimental**: but if you bump into any issues please report, and we'll try to fix it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae872acc-d74d-4215-a7d7-bec772d9c191",
   "metadata": {},
   "outputs": [],
   "source": [
    "import (\n",
    "    \"github.com/janpfeifer/gonb/gonbui/plotly\"\n",
    "    grob \"github.com/MetalBlueberry/go-plotly/generated/v2.34.0/graph_objects\"\n",
    "    ptypes \"github.com/MetalBlueberry/go-plotly/pkg/types\"\n",
    ")\n",
    "\n",
    "%%\n",
    "fig := &grob.Fig{\n",
    "    Data: []ptypes.Trace{\n",
    "        &grob.Bar{\n",
    "            X: ptypes.DataArray([]float64{1, 2, 3}),\n",
    "            Y: ptypes.DataArray([]float64{1, 2, 3}),\n",
    "        },\n",
    "    },\n",
    "    Layout: &grob.Layout{\n",
    "        Title: &grob.LayoutTitle{\n",
    "            Text: \"A Figure Specified By Go Struct\",\n",
    "        },\n",
    "        Template: plotly.PlotlyDarkTheme,\n",
    "    },\n",
    "}\n",
    "plotly.DisplayFig(fig)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aadf447d-22e4-486f-a749-36834d9bf24c",
   "metadata": {},
   "source": [
    "### Desktop App Development with Fyne\n",
    "\n",
    "If using a Jupyter-Lab ran with access to X11, one can also experiment with desktop UI programs. Let's try the [*Hello World*](https://developer.fyne.io/started/hello) from [Fyne](https://fyne.io/), a popular, high quality graphical application toolkit for Go.\n",
    "\n",
    "> **Important Note**: Disabled by default: since this have many requirements, and won't run in a test environment. But feel free to\n",
    "> copy&paste the code to a new cell and run it!\n",
    "\n",
    "> **Note 1**: Fyne needs to link many C++ libraries, so the firt time this is used it takes a few minutes to compile everything. But\n",
    "> afterwards it becomes immediate and interactive.\n",
    "\n",
    "> **Note 2**: To compile it it requires some libraries present in the system. See [Fyne's Getting Started](https://developer.fyne.io/started/).\n",
    "\n",
    "> **Note 3**: Remember to close the newly created small \"Hello World!\" window: GoNB is blocking while running a cell, and the cell only finishes to execute when the window is closed. Here we add a timeout just in case.\n",
    "\n",
    "```go\n",
    "import (\n",
    "    \"log\"\n",
    "    \"time\"\n",
    "\n",
    "    \"fyne.io/fyne/v2/app\"\n",
    "    \"fyne.io/fyne/v2/widget\"\n",
    ")\n",
    "\n",
    "%%\n",
    "a := app.New()\n",
    "w := a.NewWindow(\"Hello World\")\n",
    "go func() {\n",
    "    <- time.Tick(10 * time.Second)\n",
    "    log.Printf(\"Timed out, exiting...\")\n",
    "    os.Exit(1)\n",
    "}()\n",
    "\n",
    "w.SetContent(widget.NewLabel(\"Hello World!\"))\n",
    "w.ShowAndRun()\n",
    "fmt.Println(\"Goodbye!\")\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dcccb4f1-a9b6-40e4-9d57-013e7103c4a9",
   "metadata": {},
   "source": [
    "## Init Functions\n",
    "\n",
    "Since there is always only one definition per function name, it's not possible for\n",
    "each cell to have it's own init() function.\n",
    "Instead GoNB converts any function named `func init_something()` to `init()` before\n",
    "compiling and executing the cells. \n",
    "This way each cell can create its own `func init_...()` and have it called at every\n",
    "cell execution.\n",
    "\n",
    "Example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03452fc9-f43c-4fb1-b1ce-50df961a214d",
   "metadata": {},
   "outputs": [],
   "source": [
    "func init_a() {\n",
    "    fmt.Println(\"init_a\")\n",
    "}\n",
    "%%\n",
    "fmt.Println(\"main\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13a230fd-1179-4ec3-b24c-4f89e0913920",
   "metadata": {},
   "outputs": [],
   "source": [
    "func init_b() {\n",
    "    fmt.Println(\"init_b\")\n",
    "}\n",
    "%%\n",
    "fmt.Println(\"main\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00882646-c238-48e1-a2ee-18944d9fd302",
   "metadata": {},
   "source": [
    "Now, we don't want to have `init_a` and `init_b` on all of our cells below. So we remove those definitions -- see how to manage memorized definitions using `%help`, in the \"Managing Memorized Definitions\" section, it's displayed at the end of the tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0b8683aa-0bf0-4058-8992-1c5b9e2c2b0c",
   "metadata": {},
   "outputs": [],
   "source": [
    "%rm init_a init_b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0037e1db-61a2-4fd8-bc6f-d8a45eda5b5d",
   "metadata": {},
   "source": [
    "## Flags\n",
    "\n",
    "The `%%` command can also be used to set arguments to the execution of the cell. This makes it easy to configure \n",
    "different runs of the same code using flags. This is something handy when testing or developing code that is shared\n",
    "with a normal code that already used flags.\n",
    "\n",
    "Also,`%%` not only wraps the code following it in a `func main() { ... }` but also automatically adds a call to `flag.Parse()`. \n",
    "\n",
    "Example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e2e9992-3a74-4825-87d8-773bdc30b05f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import (\n",
    "    \"flag\"\n",
    "    \"fmt\"\n",
    ")\n",
    "\n",
    "var flagWho = flag.String(\"who\", \"\", \"Your name!\")\n",
    "\n",
    "%% --who=world\n",
    "fmt.Printf(\"Hello %s!\\n\", *flagWho)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d42c111-54f3-4a1d-b99d-4005ac49f9e5",
   "metadata": {},
   "source": [
    "Alternatively one can also set the arguments for execution with `%args`, if not using `%%`, as in:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27c7aaad-b0d3-46f0-b1f6-dc66722fe5bd",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%args --who=Wally\n",
    "\n",
    "func main() {\n",
    "    flag.Parse()\n",
    "    fmt.Printf(\"Where is %s?\", *flagWho)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "871031ff-5012-4d49-a398-9c7e1a7614ca",
   "metadata": {},
   "source": [
    "## Interacting with the DOM \n",
    "\n",
    "The package `gonbui/dom` allows one to manipulate the DOM in the browser in arbitrary ways. \n",
    "\n",
    "It is very useful if implementing new widgets. Things to check out for:\n",
    "\n",
    "* `dom.TransientJavascript`: Executes Javascript in a transient cell output and then erases it (so it's not saved with the notebook).\n",
    "* `dom.Append`: Appends HTML to a any element identified by an Id.\n",
    "* `dom.SetInnerHtml` and `dom.GetInnerHtml`: Manipulate the `innerHTML` of any element.\n",
    "* `dom.Persist`: Takes the `innerHTML` of a transient element, and then displays it normally with `gonbui.DisplayHtml`, so it can be saved with the notebook. Useful if using Javascript libraries (plotting for instance) that dyncamically generate content.\n",
    "\n",
    "Check the library documentation for more! "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f873cdb6-9bc7-45a0-9d93-2053099498d8",
   "metadata": {},
   "source": [
    "## Widgets\n",
    "\n",
    "**GoNB** allows you to use the browser not only to output rich content (HTML, Widgets), but also interact with HTML UI elements.\n",
    "\n",
    "It provides the `widgets` package with a few basic widgets for you to use, and a `gonbui/comms` package that you can use to create new widgets -- see `docs/FrontEndCommunication.md` file for more details on that.\n",
    "\n",
    "Let's reuse our Sine curve plotting function below, and connect it to a slider. And then\n",
    "create a button to stop the interaction when we are done.\n",
    "\n",
    "(To allow this notebook to run automatically, we add a timeout -- if your playing with\n",
    "the tutorial with a live Jupyter Notebook, feel free to change it or remove the timeout)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "632205e9-a72c-4e1a-a429-6a02394ce44a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import (\n",
    "    \"github.com/janpfeifer/gonb/gonbui\"\n",
    "    \"github.com/janpfeifer/gonb/gonbui/dom\"\n",
    "    \"github.com/janpfeifer/gonb/gonbui/widgets\"    \n",
    ")\n",
    "\n",
    "%%\n",
    "freq := 5.5 // From 1.0 to 10.0, controlled by a slider\n",
    "\n",
    "divId := dom.CreateTransientDiv()\n",
    "slider := widgets.Slider(0, 100, 50).AppendTo(divId).Done()\n",
    "freqHtml := fmt.Sprintf(`&nbsp;<span id=\"sin_freq\" style=\"font-family: monospace; font-style: italic; font-size: small; border: 1px solid; border-style: inset; padding-right:5px;\">%6.2f</span>&nbsp;`, freq)\n",
    "dom.Append(divId, freqHtml)\n",
    "button := widgets.Button(\"Ok\").AppendTo(divId).Done()\n",
    "\n",
    "// Initial plot.\n",
    "htmlCellId := \"sin_plot\"+gonbui.UniqueId()\n",
    "plotSvg := mgSinPlot(800, 240, freq)  // Defined in the cell above.\n",
    "gonbui.UpdateHtml(htmlCellId, plotSvg)\n",
    "\n",
    "// Listen to slider, button and a timeout timer.\n",
    "sliderChan := slider.Listen().LatestOnly()\n",
    "buttonChan := button.Listen()\n",
    "loop:\n",
    "for {\n",
    "    select {\n",
    "    case <- buttonChan.C:\n",
    "        break loop\n",
    "    case value := <-sliderChan.C:\n",
    "        freq := 1.0 + 9.0 * float64(value) / 100.0\n",
    "        dom.SetInnerText(\"sin_freq\", fmt.Sprintf(\"%6.2f\", freq))\n",
    "        plotSvg = mgSinPlot(800, 240, freq)  // Defined in the cell above.\n",
    "        gonbui.UpdateHtml(htmlCellId, plotSvg)\n",
    "    case <- time.After(1000 * time.Millisecond):\n",
    "        break loop\n",
    "    }\n",
    "}\n",
    "\n",
    "// Erase transient UI and image and re-display it so it can be persisted.\n",
    "gonbui.UpdateHtml(htmlCellId, \"\")\n",
    "dom.Persist(divId)\n",
    "gonbui.DisplayHtml(plotSvg)\n",
    "gonbui.DisplayHtml(\"<p>Done.</p>\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "626a3bd7-35c8-424d-af44-005940178224",
   "metadata": {},
   "source": [
    "### User Input\n",
    "\n",
    "There are different ways to provide input to a program in GoNB. We list them below and introduce a new one:\n",
    "\n",
    "1. Simply define constants or variables in the cell and use them. The cell itself is a good way to enter input.\n",
    "2. Add flags, and pass the values of flags after the `%%` command. For instance `%% --x=10` will run your cell with the flag `x` set to 10. This is handy for instance to test a function with different values, each one in a different cell.\n",
    "3. Read the input from an external file. One can edit the file in Jupyter or another text editor. It's easy to set `os.Stdin` to the desired file.\n",
    "4. Use HTML widgets (see `gonbui/widgets` package, described above).\n",
    "5. Jupyter Notebooks also provides a form of input request, where it displays an in-place text box. The `gonbui` package has a function to do that, the results of which can be read in from the `stdin` afterwards. See the following example:\n",
    "\n",
    "```go\n",
    "import (\n",
    "    \"fmt\"\n",
    "    \"github.com/janpfeifer/gonb/gonbui\"\n",
    ")\n",
    "\n",
    "%%\n",
    "gonbui.RequestInput(\"Tell me a number: \", false)\n",
    "var x int\n",
    "_, err := fmt.Scan(&x)\n",
    "if err != nil { panic(err) }\n",
    "fmt.Printf(\"The number you typed was %d\\n\", x)\n",
    "\n",
    "gonbui.RequestInput(\"Tell me a secret: \", true)\n",
    "var secret string\n",
    "_, err = fmt.Scan(&secret)\n",
    "if err != nil { panic(err) }\n",
    "fmt.Printf(\"Shh! Your secret was %q\\n\", secret)\n",
    "```\n",
    "\n",
    "The output would be something like:\n",
    "\n",
    "```\n",
    "Tell me a number:  42\n",
    "The number you typed was 42\n",
    "Tell me a secret:  ······\n",
    "Shh! Your secret was \"I🧡GoNB!\"\n",
    "```\n",
    "\n",
    "> **Note**: Not executed by default because it breaks the automatic tests, but try it out on a new cell!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2e0a724-0594-4462-88a4-6856a0dc2cb4",
   "metadata": {},
   "source": [
    "## Executing Shell Commands\n",
    "\n",
    "There are two variations to execute shell commands. They differ only on the directory\n",
    "from where they are executed.\n",
    "\n",
    "* `!` prefix executes what comes next should be executed as a shell command, on the same directory\n",
    "  where the kernel is executed -- typically the same directory where the notebook files is saved.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "724e33f4-c5fa-4fbe-9d90-e411369a74f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "!go version\n",
    "!pwd ; ls -l"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58a9dd1a-c30a-4d89-aada-efb91a989f20",
   "metadata": {},
   "source": [
    "* `!*` prefix executes what comes next as a shell command, on the temporary directory used \n",
    "  to compile the *Go* program when executing the cells. This includes the `go.mod` file, that\n",
    "  can be manipulated for special use cases, like importing a specific version of a module, \n",
    "  or to `redirect` a module to a local directory for development (see `Replace` section below)\n",
    "  \n",
    "Example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be207993-219d-4a7f-a130-5111971fb867",
   "metadata": {},
   "outputs": [],
   "source": [
    "!*pwd ; ls -l"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7155d4a-1076-4b63-bee5-bf0ae2baaeec",
   "metadata": {},
   "source": [
    "You can also use a `\\` at the end of the line to extend the shell command to multiple lines.\n",
    "\n",
    "Example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8688859a-ec3b-4e54-99b4-abbd8542091d",
   "metadata": {},
   "outputs": [],
   "source": [
    "!((ii=0)) ;\\\n",
    "while ((ii < 5)) ; do \\\n",
    "  printf \"\\rCounting: ${ii} ...\" ;\\\n",
    "  sleep 1;\\\n",
    "  ((ii+=1));\\\n",
    "done;\\\n",
    "echo"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81f89ec8-242c-4acf-9ece-af896fb1ad5f",
   "metadata": {},
   "source": [
    "If some shell program requires some input from the user, you can precede it with a `%with_inputs` or `%with_password` (for hidden input) and it will open a text field for typing some arbitrary text input. Example:\n",
    "\n",
    "```shell\n",
    "%with_password\n",
    "!sudo -S apt update\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4b125e1-f0e2-4180-8029-644208c59270",
   "metadata": {},
   "source": [
    "## Debugging A Cell\n",
    "\n",
    "While **GoNB** doesn't (yet) talk the debug protocol with JupyterLab, it's easy to start a GUI debugger\n",
    "from a cell, if being executed on the same machine as the browser.\n",
    "\n",
    "The common Go debugger recommendation is [delve](https://github.com/go-delve/delve), and in particular its front-end\n",
    "[gdlv](https://github.com/aarzilli/gdlv). And to make it simpler **GoNB** includes a small wrapper script \n",
    "[`ndlv`](https://github.com/janpfeifer/gonb/blob/main/cmd/ndlv/ndlv) to\n",
    "set the directory and program name to the last cell executed. Copy or link that script somewhere in your `PATH`\n",
    "(maybe `${HOME}/bin` if you have such directory set up).\n",
    "\n",
    "To open the debugger, after executing a cell you want to debug, you create and execute a new cell with this single shell command:\n",
    "\n",
    "```\n",
    "!ndlv\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "514b3ca4-3a1e-4b14-964b-ed8efa4f1d3e",
   "metadata": {},
   "source": [
    "## Environment Variables\n",
    "\n",
    "For convenience, **GoNB** defines the following environment variables -- available for the shell scripts (`!` and `!*`) and for the _Go_ cells:\n",
    "\n",
    "* `GONB_DIR`: the directory where commands are executed from. This can be changed with `%cd`.\n",
    "* `GONB_TMP_DIR`: the directory where the temporary Go code, with the cell code, is stored and compiled. This is the directory where `!*` scripts are executed. It only changes when a kernel is restarted, and a new temporary directory is created.\n",
    "* `GONB_PIPE`: is the _named pipe_ directory used to communicate rich content (HTML, images) to the kernel. Only available for _Go_ cells, and a new one is created at every execution. This is used by the `gonbui` functions described above, and doesn't need to be accessed directly."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b49634dd-4703-4a3f-88c7-a8687ee35369",
   "metadata": {},
   "source": [
    "## Tests and Benchmarks\n",
    "\n",
    "**GoNB** allows also executing the cell with `go test`, to demo or debug tests and benchmarks. For that just mark the cell with `%test` and if no other parameters are given, it will run the tests and benchmarks defined in the current cell.\n",
    "\n",
    "If it is the only command in the cell, it will run all tests defined so far. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "02dbf874-ab19-4fb8-8d91-901dadac808c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import \"github.com/stretchr/testify/require\"\n",
    "\n",
    "func TestIncr(t *testing.T) {\n",
    "    require.Equal(t, 2, incr(1))    \n",
    "}\n",
    "\n",
    "func BenchmarkIncr(b *testing.B) {\n",
    "    for i := 0; i < b.N; i++ {\n",
    "        _ = incr(i)\n",
    "    }\n",
    "}\n",
    "\n",
    "%test"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25230a6f-9412-4cde-8759-b6058d10e012",
   "metadata": {},
   "source": [
    "Alternatively, you can provide extra flags that are passed to the test binary. For instance, to execute all benchmarks defined so far, use:\n",
    "\n",
    "```\n",
    "%test -test.bench=. -test.run=Bechmark\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc3e1354-7a50-4db9-a82b-ad42d6e9c102",
   "metadata": {},
   "source": [
    "## Developing Go libraries with a notebook: `go.mod` and `go.work`\n",
    "\n",
    "*GoNB* uses `go.mod` and understands `go.work` -- but won't create it by default.\n",
    "\n",
    "### \"Replace\" modules in `go.mod`\n",
    "\n",
    "Using the `!*` command above we can easily \"replace\" a module to a local directory. This can be very handy\n",
    "for developing a library in a powerful IDE on the side, and using the *GoNB* notebook to execute tests\n",
    "and experiments. Changes in the library (in the IDE) when saved immediate take effect on the next cell execution.\n",
    "\n",
    "\n",
    "The accompaining library *gonbui* was implemented mostly in this fashion using a notebook, that started with:\n",
    "\n",
    "```\n",
    "!*go mod edit -replace github.com/janpfeifer/gonb=/home/janpf/Projects/gonb\n",
    "```\n",
    "\n",
    "Check out the results with:\n",
    "\n",
    "```\n",
    "!*cat go.mod\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3391f12e-131b-45b8-b270-6ba06abaac8c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "!*go mod edit -replace \"github.com/janpfeifer/gonb=${HOME}/Projects/gonb\"\n",
    "!*cat go.mod"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e46fc85-fb7a-43dd-9434-43d2d5faded8",
   "metadata": {},
   "source": [
    "### \"Use\" modules in `go.work`\n",
    "\n",
    "Another way to refer to modules that are being co-developed in local file is using [Go workspaces](https://go.dev/doc/tutorial/workspaces).\n",
    "                                                                                                   \n",
    "**GoNB** can be configure it automatically when the kernel is started: during **GoNB** installation use the `gonb -install -go_work=<...>` flag to set the default used directories of the `go.work`. Notice if set, it automatically include the `\".\"` directory to the list, if not already set.\n",
    "\n",
    "Alternatively, one can also configure the kernel's `go.work` directly from the notebook itself. Example:                                                                      "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "45cf4a12-4b93-480f-bd83-2d375e7a475d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "!*rm -f go.work && go work init && go work use . \"$GONB_DIR\"\n",
    "%goworkfix"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29f2828e-a7e7-4264-aca8-c47ed4377c17",
   "metadata": {},
   "source": [
    "> **Note**:\n",
    "> - **GoNB** uses `go get` to automatically fetch missing imports, but it (by design) ignores `go.work` `use` definitions.\n",
    ">   and attempt to fetch them anyway.\n",
    ">   The special command `%goworkfix` handles that by adding a `replace` entry in `go.mod` for\n",
    ">   all modules pointed to by `go.work`.\n",
    "> - The env variable `GONB_DIR` to refer to the current kernel directory (changed with `%cd`). This\n",
    ">   is just an example, instead here you would use the path to some library you have locally."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6594c119-e2e9-4d93-a4bd-506b4ab190fc",
   "metadata": {},
   "source": [
    "### Tracked directories\n",
    "\n",
    "**GoNB** tracks for changes in local files in target directories of `replace` rules in `go.mod` or `use` rules in `go.work`. This way auto-complete and contextual help stay up-to-date with changes on local files you may be editing on a separate editor.\n",
    "\n",
    "See `%track` and `%untrack` to list and control tracking. For instance, in our tutorial, this is what **GoNB** is tracking:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "557c7d2e-4726-453c-9471-2f340d06a1e6",
   "metadata": {},
   "source": [
    "### Custom `go build` flags\n",
    "\n",
    "Sometimes it can be desirable to change the Go flags used to build the cells being executed.\n",
    "For instance to check for coverage, or enable race condition detection, etc.\n",
    "\n",
    "**GoNB** supports this in two ways:\n",
    "\n",
    "1. You can tell **GoNB** to use arbitrary `go build` flags with `%goflags <values ...>`. `%goflags` without any values displays current settings, and `%goflags \"\"` will reset to the default (no extra flags).\n",
    "2. In some cases it's not that one wants **GoNB** to build the binary with the flags, but simply run `go build ...` to see the results. This is supported by simply running `!*go build ...`, which is conveniently executed (the `!*` prefix) in the temporary directory where the cells code maintained by **GoNB** is located (in a `main.go`). E.g.: To look at optimization decisions, use `!*go build -gcflags=-m`.\n",
    "\n",
    "See an example in the `examples/tests/gcflags.ipynb` file, which is used as an integration test."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c54e53a-037c-451e-9fd4-116d27c58296",
   "metadata": {},
   "source": [
    "## More ... and Help\n",
    "\n",
    "Some other features:\n",
    "\n",
    "* **Errors reported** by the Go compiler are parsed and a **_mouse-over_ context is displayed**. This facilitates understanding the error, since the line numbers reported by Go are not the same as in the cell being executed.\n",
    "* **Contextual Help**: can be activated with **\"Shift+Tab\"\"** (floating note) or **\"Control+I\"** (for a separate sub-window). Notice it may be slow if some new import needs to be fetched -- consider executing once some code with the imports before using the contextual help.\n",
    "\n",
    "The library is new, and there is still development going on, with still some features in the pipeline (more on the displaying side, auto-complete). For those\n",
    "who enjoy coding, help in improving *GoNB* is very welcome!\n",
    "\n",
    "Finally, there is also the `%help` command, which lists all the currently supported features:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba9172b8-ede5-44cd-baa1-a58a1d668a98",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%help"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Go (gonb)",
   "language": "go",
   "name": "gonb"
  },
  "language_info": {
   "codemirror_mode": "",
   "file_extension": ".go",
   "mimetype": "text/x-go",
   "name": "go",
   "nbconvert_exporter": "",
   "pygments_lexer": "",
   "version": "go1.26.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
