diff --git a/Dockerfile b/Dockerfile index 4c78ce6..751b28a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,8 +29,8 @@ RUN apt-get install --yes --no-install-recommends git ####################################################################################################### # Go and GoNB Libraries ####################################################################################################### -ENV GO_VERSION=1.22.2 -ENV GONB_VERSION="v0.10.1" +ENV GO_VERSION=1.22.5 +ENV GONB_VERSION="v0.10.2" ENV GOROOT=/usr/local/go ENV GOPATH=/opt/go ENV PATH=$PATH:$GOROOT/bin:$GOPATH/bin diff --git a/README.md b/README.md index cc11e0b..4c9e7b8 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ +* **NEW**: Now supported by [Jupytext](https://github.com/mwouts/jupytext): it allows one to write the notebook as a normal. + Go file, and use [Jupytext](https://github.com/mwouts/jupytext) to convert to a notebook (with markdown support, etc). + See [example](https://github.com/mwouts/jupytext/issues/1244#issuecomment-2202097837). * Auto-complete and contextual help while coding. * Rich content display: HTML, markdown (with latex), images, javascript, svg, videos, etc. * Widgets (sliders, buttons) support: interact using HTML elements. Create your own widgets! @@ -26,6 +29,7 @@ start up, since each cell is compiled. * Run cell's `Test*` and `Benchmark*` functions with `go test`, simply adding `%test` to cell. * Support for `go.mod` and `go.work`, to allow local development. Including importing specific versions of libraries. +* Debug using [gdlv](https://github.com/aarzilli/gdlv), a GUI for the [delve](https://github.com/go-delve/delve) debugger (see %help). * Shell command executions with `!` -- handy at times, for instance to install packages. * Reported to work with Github Codespace, [VSCode](docs/VSCode.md), Binder, Google's Colab, etc. * Very well documented and supported. diff --git a/cmd/nbexec/nbexec.go b/cmd/nbexec/nbexec.go index 66ffc76..59965af 100644 --- a/cmd/nbexec/nbexec.go +++ b/cmd/nbexec/nbexec.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "github.com/go-rod/rod" + "github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/proto" "github.com/janpfeifer/gonb/common" "github.com/janpfeifer/must" @@ -267,9 +268,17 @@ func startJupyterNotebook() { } // Notebook instrumenting using go-rod. - func executeNotebook(url string, inputBoxes []string) { - page := rod.New().MustConnect().MustPage(url) + // Use system's Google Chrome is available, for sandboxing: + var controlURL string + chromePath, err := exec.LookPath("google-chrome") + if err == nil { + controlURL = launcher.New().Bin(chromePath).MustLaunch() + } else { + klog.Warningf("Using rod downloaded chromium, with --no-sandbox") + controlURL = launcher.New().NoSandbox(true).MustLaunch() + } + page := rod.New().ControlURL(controlURL).MustConnect().MustPage(url) klog.V(1).Infof("Waiting for opening of page %q", url) page.MustWaitStable() diff --git a/cmd/ndlv/ndlv b/cmd/ndlv/ndlv new file mode 100755 index 0000000..f5afd05 --- /dev/null +++ b/cmd/ndlv/ndlv @@ -0,0 +1,28 @@ +#!/bin/bash + +# ndlv is a wrapper script that executes gdlv, a delve GUI, from inside a gonb notebook. +# It will fail if not being executed by gonb, or if dlv and gdlv are not installed, or if no cell has +# yet been executed to be debugged. + +if [[ "${GONB_TMP_DIR}" == "" ]] ; then + echo "No being executed from a gonb notebook cell, GONB_TMP_DIR not set." 1>&2 + exit 1 +fi +if [[ "$(which dlv)" == "" ]] ; then + echo "Can't find delve (dlv) debugger. See https://github.com/go-delve/delve/tree/master/Documentation/installation" 1>&2 + exit 1 +fi +if [[ "$(which gdlv)" == "" ]] ; then + echo "Can't find gdlv GUI for delve debugger. See https://github.com/aarzilli/gdlv" 1>&2 + exit 1 +fi + +go_binary="$(basename "${GONB_TMP_DIR}")" +cd "${GONB_TMP_DIR}" || ( echo "Can't cd to GONB_TMP_DIR=${GONB_TMP_DIR}" ; exit 1 ) + +if [[ ! -e "${go_binary}" ]] ; then + echo "Cell binary is not available (${GONB_TMP_DIR}/${go_binary}) -- has cell already been executed ?" 1>&2 + exit 1 +fi + +gdlv debug "${go_binary}" \ No newline at end of file diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8a8b4fd..0ff7c45 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,19 @@ # GoNB Changelog +## 0.10.2, 2024/07/10 Added Jupytext support and `ndlv` script for debugging cells. + +* [Jupytext](https://jupytext.readthedocs.io/en/latest/) integration [#120](https://github.com/janpfeifer/gonb/discussions/120): + * Many thanks for [Marc Wouts](github.com/mwouts) for [adding support in Jupytext](https://github.com/mwouts/jupytext/releases/tag/v1.16.3), + and @HaveF for the help and starting the topic. + * Handle special commands to be prefixed with `//gonb:` -- this allows special commands to be parseable Go code, and makes it easier for IDEs. + * Ignore `package` tag -- as opposed to raising an error: also to make easy on IDEs that may require a `package` tag. + * Added special variation: `%exec ` that creates a main function that calls `` + and sets the program arguments (flags) to the given values. +* Added `ndlv` wrapper script for starting [gdlv](https://github.com/aarzilli/gdlv) on cell binary. + * Many thanks for @HaveF for the help -- see [#122](https://github.com/janpfeifer/gonb/discussions/122) +* Notebook testing: changed `nbexec` to use system's google-chrome if available (with sandbox), or let Rod download + chromium, but then use with --no-sandbox (since there is no SUID on the binaries). + ## 0.10.1, 2024/04/14 Added support for Apache ECharts * Interrupt and Shutdown: diff --git a/docs/coverage.txt b/docs/coverage.txt index 4e281ca..5123801 100644 --- a/docs/coverage.txt +++ b/docs/coverage.txt @@ -9,7 +9,7 @@ github.com/janpfeifer/gonb/main.go SetUpKlog 66.7% github.com/janpfeifer/gonb/cache/cache.go New 75.0% github.com/janpfeifer/gonb/cache/cache.go AssertNoError 50.0% github.com/janpfeifer/gonb/cache/cache.go MustNew 100.0% -github.com/janpfeifer/gonb/cache/cache.go NewInTmp 72.7% +github.com/janpfeifer/gonb/cache/cache.go NewInTmp 68.2% github.com/janpfeifer/gonb/cache/cache.go MustNewInTmp 100.0% github.com/janpfeifer/gonb/cache/cache.go NewHidden 80.0% github.com/janpfeifer/gonb/cache/cache.go MustNewHidden 100.0% @@ -28,7 +28,7 @@ github.com/janpfeifer/gonb/cache/cache.go CacheWith 91.7% github.com/janpfeifer/gonb/cache/cache.go Cache 0.0% github.com/janpfeifer/gonb/cmd/nbexec/nbexec.go main 72.2% github.com/janpfeifer/gonb/cmd/nbexec/nbexec.go startJupyterNotebook 73.2% -github.com/janpfeifer/gonb/cmd/nbexec/nbexec.go executeNotebook 81.8% +github.com/janpfeifer/gonb/cmd/nbexec/nbexec.go executeNotebook 78.6% github.com/janpfeifer/gonb/cmd/nbexec/nbexec.go checkForInputBoxes 95.7% github.com/janpfeifer/gonb/common/common.go Panicf 0.0% github.com/janpfeifer/gonb/common/common.go Pause 0.0% @@ -165,9 +165,9 @@ github.com/janpfeifer/gonb/internal/comms/namedpipes.go *State.ProgramUnsubscr github.com/janpfeifer/gonb/internal/comms/namedpipes.go *State.deliverProgramSubscriptionsLocked 62.5% github.com/janpfeifer/gonb/internal/dispatcher/comms.go handleComms 55.6% github.com/janpfeifer/gonb/internal/dispatcher/dispatcher.go RunKernel 80.0% -github.com/janpfeifer/gonb/internal/dispatcher/dispatcher.go handleShellMsg 71.0% +github.com/janpfeifer/gonb/internal/dispatcher/dispatcher.go handleShellMsg 78.4% github.com/janpfeifer/gonb/internal/dispatcher/dispatcher.go handleBusyMessage 47.8% -github.com/janpfeifer/gonb/internal/dispatcher/dispatcher.go handleShutdownRequest 83.3% +github.com/janpfeifer/gonb/internal/dispatcher/dispatcher.go handleShutdownRequest 84.6% github.com/janpfeifer/gonb/internal/dispatcher/dispatcher.go handleExecuteRequest 87.5% github.com/janpfeifer/gonb/internal/dispatcher/dispatcher.go HandleInspectRequest 0.0% github.com/janpfeifer/gonb/internal/dispatcher/dispatcher.go handleCompleteRequest 0.0% @@ -185,7 +185,7 @@ github.com/janpfeifer/gonb/internal/goexec/composer.go *Declarations.RenderFun github.com/janpfeifer/gonb/internal/goexec/composer.go *Declarations.RenderTypes 100.0% github.com/janpfeifer/gonb/internal/goexec/composer.go *Declarations.RenderConstants 100.0% github.com/janpfeifer/gonb/internal/goexec/composer.go *Constant.Render 100.0% -github.com/janpfeifer/gonb/internal/goexec/composer.go *State.createGoFileFromLines 85.7% +github.com/janpfeifer/gonb/internal/goexec/composer.go *State.createGoFileFromLines 81.8% github.com/janpfeifer/gonb/internal/goexec/composer.go *State.createCodeFileFromDecls 58.8% github.com/janpfeifer/gonb/internal/goexec/composer.go *State.createAlternativeFileFromDecls 0.0% github.com/janpfeifer/gonb/internal/goexec/composer.go *State.createCodeFromDecls 71.9% @@ -258,6 +258,7 @@ github.com/janpfeifer/gonb/internal/goexec/parser.go *State.readMainGo 77.8 github.com/janpfeifer/gonb/internal/goexec/parser.go *State.SetCellTests 100.0% github.com/janpfeifer/gonb/internal/goexec/parser.go *State.DefaultCellTestArgs 100.0% github.com/janpfeifer/gonb/internal/goexec/parser.go IsEmptyLines 77.8% +github.com/janpfeifer/gonb/internal/goexec/parser.go TrimGonbCommentPrefix 100.0% github.com/janpfeifer/gonb/internal/goexec/tracking.go newTrackingInfo 100.0% github.com/janpfeifer/gonb/internal/goexec/tracking.go *State.Track 100.0% github.com/janpfeifer/gonb/internal/goexec/tracking.go *State.lockedTrack 54.8% @@ -344,10 +345,10 @@ github.com/janpfeifer/gonb/internal/kernel/kernel.go *SyncSocket.RunLocked github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.IsStopped 66.7% github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.StoppedChan 100.0% github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.Stop 72.2% -github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.HandleInterrupt 88.2% +github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.HandleInterrupt 58.8% github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.SubscribeInterrupt 80.0% github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.UnsubscribeInterrupt 75.0% -github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.callInterruptSubscribers 42.9% +github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.CallInterruptSubscribers 42.9% github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.ExitWait 100.0% github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.Stdin 100.0% github.com/janpfeifer/gonb/internal/kernel/kernel.go *Kernel.Shell 100.0% @@ -395,7 +396,7 @@ github.com/janpfeifer/gonb/internal/nbtests/nbtests.go Sequence 63.2% github.com/janpfeifer/gonb/internal/nbtests/nbtests.go OutputLine 100.0% github.com/janpfeifer/gonb/internal/nbtests/nbtests.go InputLine 100.0% github.com/janpfeifer/gonb/internal/specialcmd/cellmagic.go IsGoCell 0.0% -github.com/janpfeifer/gonb/internal/specialcmd/cellmagic.go ExecuteSpecialCell 80.0% +github.com/janpfeifer/gonb/internal/specialcmd/cellmagic.go ExecuteSpecialCell 81.0% github.com/janpfeifer/gonb/internal/specialcmd/cellmagic.go cellCmdWritefile 87.5% github.com/janpfeifer/gonb/internal/specialcmd/cellmagic.go writeLinesToFile 85.7% github.com/janpfeifer/gonb/internal/specialcmd/cellmagic.go cellCmdScript 50.0% @@ -404,9 +405,9 @@ github.com/janpfeifer/gonb/internal/specialcmd/definitions.go displayEnumeratio github.com/janpfeifer/gonb/internal/specialcmd/definitions.go listDefinitions 100.0% github.com/janpfeifer/gonb/internal/specialcmd/definitions.go removeDefinitionImpl 87.5% github.com/janpfeifer/gonb/internal/specialcmd/definitions.go removeDefinitions 75.0% -github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go Parse 84.0% -github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go joinLine 85.7% -github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go execSpecialConfig 54.4% +github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go Parse 84.6% +github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go joinLine 87.5% +github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go execSpecialConfig 55.3% github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go execShell 100.0% github.com/janpfeifer/gonb/internal/specialcmd/specialcmd.go splitCmd 97.0% github.com/janpfeifer/gonb/internal/specialcmd/track.go execTrack 27.3% diff --git a/docs/gopher_notebook.jpg b/docs/gopher_notebook.jpg new file mode 100644 index 0000000..85fe70e Binary files /dev/null and b/docs/gopher_notebook.jpg differ diff --git a/examples/tests/bash_script.ipynb b/examples/tests/bash_script.ipynb index 68c7267..5b513f5 100644 --- a/examples/tests/bash_script.ipynb +++ b/examples/tests/bash_script.ipynb @@ -34,7 +34,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/tmp/gonb_9dda40ab\n" + "/tmp/gonb_af538290\n" ] } ], @@ -59,7 +59,7 @@ "output_type": "stream", "text": [ "/home/janpf/Projects/gonb/examples/tests\n", - "/tmp/gonb_9dda40ab\n", + "/tmp/gonb_af538290\n", "/home/janpf/Projects/gonb\n", "/home/janpf/Projects/gonb\n" ] @@ -86,7 +86,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.1" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/comms.ipynb b/examples/tests/comms.ipynb index 2b04e21..bcc01a3 100644 --- a/examples/tests/comms.ipynb +++ b/examples/tests/comms.ipynb @@ -308,7 +308,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.1" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/dom.ipynb b/examples/tests/dom.ipynb index 72f17e7..d0462ec 100644 --- a/examples/tests/dom.ipynb +++ b/examples/tests/dom.ipynb @@ -88,7 +88,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, @@ -152,7 +152,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, @@ -197,7 +197,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/functions.ipynb b/examples/tests/functions.ipynb index 594dc89..09bb883 100644 --- a/examples/tests/functions.ipynb +++ b/examples/tests/functions.ipynb @@ -65,7 +65,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/goflags.ipynb b/examples/tests/goflags.ipynb index b546e1f..d6bdb8a 100644 --- a/examples/tests/goflags.ipynb +++ b/examples/tests/goflags.ipynb @@ -153,9 +153,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "gonb_bc1385f7/main.go:8:\tA\t\t100.0%\n", - "gonb_bc1385f7/main.go:12:\tB\t\t0.0%\n", - "gonb_bc1385f7/main.go:17:\tmain\t\t100.0%\n", + "gonb_17c43c9d/main.go:8:\tA\t\t100.0%\n", + "gonb_17c43c9d/main.go:12:\tB\t\t0.0%\n", + "gonb_17c43c9d/main.go:17:\tmain\t\t100.0%\n", "total\t\t\t\t(statements)\t75.0%\n" ] } @@ -238,14 +238,14 @@ "name": "stderr", "output_type": "stream", "text": [ - "# gonb_bc1385f7\n", + "# gonb_17c43c9d\n", "./main.go:10:6: can inline (*Point).ManhattanLen\n", "./main.go:16:12: inlining call to flag.Parse\n", - "./main.go:18:28: inlining call to (*Point).ManhattanLen\n", - "./main.go:18:13: inlining call to fmt.Println\n", + "./main.go:18:27: inlining call to (*Point).ManhattanLen\n", + "./main.go:18:12: inlining call to fmt.Println\n", "./main.go:10:7: p does not escape\n", - "./main.go:18:13: ... argument does not escape\n", - "./main.go:18:28: ~r0 escapes to heap\n" + "./main.go:18:12: ... argument does not escape\n", + "./main.go:18:27: ~r0 escapes to heap\n" ] } ], @@ -267,7 +267,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/gonbui.ipynb b/examples/tests/gonbui.ipynb index 5eaa375..73f9ffe 100644 --- a/examples/tests/gonbui.ipynb +++ b/examples/tests/gonbui.ipynb @@ -22,7 +22,7 @@ "output_type": "stream", "text": [ "%goflags=[\"--cover\" \"--covermode=set\"]\n", - "GOCOVERDIR=/tmp/gonb_test_coverage.4mPvyGK2OJ\n" + "GOCOVERDIR=/tmp/gonb_test_coverage.9v84ceUYUN\n" ] } ], @@ -133,7 +133,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/gotest.ipynb b/examples/tests/gotest.ipynb index 7828e19..bd26a82 100644 --- a/examples/tests/gotest.ipynb +++ b/examples/tests/gotest.ipynb @@ -287,12 +287,12 @@ "text": [ "goos: linux\n", "goarch: amd64\n", - "pkg: gonb_36f3efc5\n", - "cpu: Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz\n", + "pkg: gonb_ce57b149\n", + "cpu: 12th Gen Intel(R) Core(TM) i9-12900K\n", "BenchmarkFibonacciA32\n", - "BenchmarkFibonacciA32-8 \t 50\t 22759741 ns/op\n", + "BenchmarkFibonacciA32-24 \t 177\t 6414568 ns/op\n", "BenchmarkFibonacciB32\n", - "BenchmarkFibonacciB32-8 \t28774705\t 39.85 ns/op\n", + "BenchmarkFibonacciB32-24 \t107397702\t 12.40 ns/op\n", "PASS\n", "coverage: [no statements]\n" ] @@ -334,10 +334,10 @@ "text": [ "goos: linux\n", "goarch: amd64\n", - "pkg: gonb_36f3efc5\n", - "cpu: Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz\n", - "BenchmarkFibonacciA32-8 \t 52\t 22831751 ns/op\n", - "BenchmarkFibonacciB32-8 \t31159093\t 40.49 ns/op\n", + "pkg: gonb_ce57b149\n", + "cpu: 12th Gen Intel(R) Core(TM) i9-12900K\n", + "BenchmarkFibonacciA32-24 \t 174\t 6429990 ns/op\n", + "BenchmarkFibonacciB32-24 \t100000000\t 11.82 ns/op\n", "PASS\n", "coverage: [no statements]\n" ] @@ -361,7 +361,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/gowork.ipynb b/examples/tests/gowork.ipynb index 1ace7a5..280ae25 100644 --- a/examples/tests/gowork.ipynb +++ b/examples/tests/gowork.ipynb @@ -36,7 +36,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/tmp/gonb_tests_gowork_0srvuOWg" + "/tmp/gonb_tests_gowork_FGEWmVkv" ] } ], @@ -64,7 +64,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Temporary test package: /tmp/gonb_tests_gowork_0srvuOWg\n" + "Temporary test package: /tmp/gonb_tests_gowork_FGEWmVkv\n" ] } ], @@ -136,7 +136,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "\t- Added replace rule for module \"a.com/a/pkg\" to local directory \"/tmp/gonb_tests_gowork_0srvuOWg\".\n" + "\t- Added replace rule for module \"a.com/a/pkg\" to local directory \"/tmp/gonb_tests_gowork_FGEWmVkv\".\n" ] } ], @@ -155,9 +155,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "module gonb_5cff895d\n", + "module gonb_26040e0f\n", "\n", - "go 1.22.0\n", + "go 1.22.4\n", "\n", "replace a.com/a/pkg => TMP_PKG\n" ] @@ -186,7 +186,7 @@ "text/html": [ "List of files/directories being tracked:\n", "
    \n", - "
  • /tmp/gonb_tests_gowork_0srvuOWg
  • \n", + "
  • /tmp/gonb_tests_gowork_FGEWmVkv
  • \n", "
\n" ] }, @@ -208,9 +208,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "module gonb_5cff895d\n", + "module gonb_26040e0f\n", "\n", - "go 1.22.0\n" + "go 1.22.4\n" ] } ], @@ -280,7 +280,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/hello.ipynb b/examples/tests/hello.ipynb index eff25e3..fe6d224 100644 --- a/examples/tests/hello.ipynb +++ b/examples/tests/hello.ipynb @@ -33,9 +33,57 @@ } ], "source": [ + "// Package abc is ignored by GoNB. It always use \"package main\" to execute the cell.\n", + "package abc\n", + "\n", "%%\n", "fmt.Println(\"Hello World!\")" ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "621d6604-e016-40a9-9ccd-734747e70187", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Works with //gonb:\n" + ] + } + ], + "source": [ + "// Make sure the //gonb: prefix will work as well.\n", + "//gonb:%%\n", + "fmt.Println(\"Works with //gonb:\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "bfdcc7f3-a282-4db9-a0dd-e8e58362fd9e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "%exec worked for x=17\n" + ] + } + ], + "source": [ + "// Test %exec and flags\n", + "var x = flag.Int(\"x\", 0, \"value of x to feed f()\")\n", + "\n", + "func f() {\n", + " fmt.Printf(\"%%exec worked for x=%d\\n\", *x)\n", + "}\n", + "\n", + "//gonb:%exec f -x=17" + ] } ], "metadata": { @@ -51,7 +99,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/init.ipynb b/examples/tests/init.ipynb index fb227a9..50967c0 100644 --- a/examples/tests/init.ipynb +++ b/examples/tests/init.ipynb @@ -156,7 +156,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/input_boxes.ipynb b/examples/tests/input_boxes.ipynb index e930d15..c4cf9b2 100644 --- a/examples/tests/input_boxes.ipynb +++ b/examples/tests/input_boxes.ipynb @@ -22,7 +22,7 @@ "output_type": "stream", "text": [ "%goflags=[\"--cover\" \"--covermode=set\"]\n", - "GOCOVERDIR=/tmp/gonb_test_coverage.4mPvyGK2OJ\n" + "GOCOVERDIR=/tmp/gonb_test_coverage.9v84ceUYUN\n" ] } ], @@ -196,7 +196,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/script.ipynb b/examples/tests/script.ipynb index ed0ff24..9f11bac 100644 --- a/examples/tests/script.ipynb +++ b/examples/tests/script.ipynb @@ -105,7 +105,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.1" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/widgets.ipynb b/examples/tests/widgets.ipynb index 0ef8a65..414c655 100644 --- a/examples/tests/widgets.ipynb +++ b/examples/tests/widgets.ipynb @@ -95,7 +95,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, @@ -189,7 +189,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, @@ -304,7 +304,7 @@ { "data": { "text/html": [ - "
" + "
" ] }, "metadata": {}, @@ -416,7 +416,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.0" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tests/writefile.ipynb b/examples/tests/writefile.ipynb index 93fd717..5913c58 100644 --- a/examples/tests/writefile.ipynb +++ b/examples/tests/writefile.ipynb @@ -10,7 +10,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Cell contents written to \"/tmp/gonb_nbtests_writefile_2376410267/poetry.txt\".\n" + "Cell contents written to \"/tmp/gonb_nbtests_writefile_1110953076/poetry.txt\".\n" ] } ], @@ -32,7 +32,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Cell contents appended to \"/tmp/gonb_nbtests_writefile_2376410267/poetry.txt\".\n" + "Cell contents appended to \"/tmp/gonb_nbtests_writefile_1110953076/poetry.txt\".\n" ] } ], @@ -55,7 +55,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.1" + "version": "go1.22.4" } }, "nbformat": 4, diff --git a/examples/tutorial.ipynb b/examples/tutorial.ipynb index 6a66a0f..867f7ff 100644 --- a/examples/tutorial.ipynb +++ b/examples/tutorial.ipynb @@ -1624,6 +1624,29 @@ "```" ] }, + { + "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", @@ -1930,7 +1953,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 1, "id": "ba9172b8-ede5-44cd-baa1-a58a1d668a98", "metadata": { "tags": [] @@ -1981,13 +2004,16 @@ "\n", "### Special non-Go Commands\n", "\n", - "- `%%` or `%main`: Marks the lines as follows to be wrapped in a `func main() {...}` during\n", + "- `%% []` or `%main []`: Marks the lines as follows to be wrapped in a `func main() {...}` during\n", " execution. A shortcut to quickly execute code. It also automatically includes `flag.Parse()`\n", - " as the very first statement. Anything `%%` or `%main` are taken as arguments\n", + " as the very first statement. Anything after`%%` or `%main` are taken as arguments\n", " to be passed to the program -- it resets previous values given by `%args`.\n", - "- `%args`: Sets arguments to be passed when executing the Go code. This allows one to\n", + "- `%args `: Sets arguments to be passed when executing the Go code. This allows one to\n", " use flags as a normal program. Notice that if a value after `%%` or `%main` is given, it will\n", " overwrite the values here.\n", + "- `%exec []`: this will call the function `my_func()`, and optionally set the program arguments.\n", + " Behind the scenes it creates a trivial `func main()` that parses the flags and calls `my_func()` (without any\n", + " parameters or return values).\n", "- `%autoget` and `%noautoget`: Default is `%autoget`, which automatically does `go get` for\n", " packages not yet available.\n", "- `%cd []`: Change current directory of the Go kernel, and the directory from where\n", @@ -2005,7 +2031,13 @@ "- `%with_password`: will prompt for a password passed to the next shell command.\n", " Do this is if your next shell command requires a password.\n", "\n", - "Notice all these commands are executed **before** any Go code in the same cell.\n", + "**Notes**: \n", + "\n", + "1. The special commands below can be used in the start of the line as is, or prefixed by a `//gonb:`, which may be easier\n", + "on some IDEs if editing the code externally (since these special commands are not proper Go). \n", + "So `//gonb:%%` is the same as `%%` \n", + "2. All these commands are executed **before** any Go code in the same cell.\n", + "\n", "\n", "### Managing Memorized Definitions\n", "\n", @@ -2036,6 +2068,23 @@ "Notice that when the cell is executed, first all shell commands are executed, and only after that, if there is\n", "any Go code in the cell, it is executed.\n", "\n", + "### Running a Debugger\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", + "\n", "### Tracking of Go Files In Development:\n", "\n", "A convenient way to develop programs or libraries in **GoNB** is to use replace\n", @@ -2202,7 +2251,7 @@ "name": "go", "nbconvert_exporter": "", "pygments_lexer": "", - "version": "go1.22.1" + "version": "go1.22.3" } }, "nbformat": 4, diff --git a/go.mod b/go.mod index cc3aad4..cf524bf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/janpfeifer/gonb -go 1.21 +go 1.22 require ( github.com/MetalBlueberry/go-plotly v0.4.0 @@ -8,7 +8,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 github.com/go-language-server/protocol v0.7.0 github.com/go-language-server/uri v0.2.0 - github.com/go-rod/rod v0.114.3 + github.com/go-rod/rod v0.116.1 github.com/go-zeromq/zmq4 v0.16.0 github.com/gofrs/uuid v4.4.0+incompatible github.com/gowebapi/webapi v0.0.0-20221221115732-41cedfc27a0b @@ -33,9 +33,9 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/encoding v0.4.0 // indirect - github.com/ysmood/fetchup v0.2.3 // indirect + github.com/ysmood/fetchup v0.2.4 // indirect github.com/ysmood/goob v0.4.0 // indirect - github.com/ysmood/got v0.34.1 // indirect + github.com/ysmood/got v0.40.0 // indirect github.com/ysmood/gson v0.7.3 // indirect github.com/ysmood/leakless v0.8.0 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/go.sum b/go.sum index 4ef2647..e629247 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-rod/rod v0.114.3 h1:gAUT2Bc2wy0tQL5KEet05HNDvmndaHAGCjQ01TB2efA= github.com/go-rod/rod v0.114.3/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw= +github.com/go-rod/rod v0.116.1 h1:BDMZY3qm/14SmvHBV7DoFUhXeJ2MbUYgumQ88b+v2WE= +github.com/go-rod/rod v0.116.1/go.mod h1:3Ash9fYwznqz9S1uLQgQRStur4fCXjoxxGW+ym6TYjU= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-zeromq/goczmq/v4 v4.2.2 h1:HAJN+i+3NW55ijMJJhk7oWxHKXgAuSBkoFfvr8bYj4U= github.com/go-zeromq/goczmq/v4 v4.2.2/go.mod h1:Sm/lxrfxP/Oxqs0tnHD6WAhwkWrx+S+1MRrKzcxoaYE= @@ -181,12 +183,17 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= +github.com/ysmood/fetchup v0.2.4 h1:2kfWr/UrdiHg4KYRrxL2Jcrqx4DZYD+OtWu7WPBZl5o= +github.com/ysmood/fetchup v0.2.4/go.mod h1:hbysoq65PXL0NQeNzUczNYIKpwpkwFL4LXMDEvIQq9A= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= github.com/ysmood/gop v0.0.2 h1:VuWweTmXK+zedLqYufJdh3PlxDNBOfFHjIZlPT2T5nw= github.com/ysmood/gop v0.0.2/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk= +github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg= github.com/ysmood/got v0.34.1 h1:IrV2uWLs45VXNvZqhJ6g2nIhY+pgIG1CUoOcqfXFl1s= github.com/ysmood/got v0.34.1/go.mod h1:yddyjq/PmAf08RMLSwDjPyCvHvYed+WjHnQxpH851LM= +github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q= +github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY= github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= diff --git a/internal/goexec/composer.go b/internal/goexec/composer.go index 7bafc0b..d50db38 100644 --- a/internal/goexec/composer.go +++ b/internal/goexec/composer.go @@ -333,36 +333,50 @@ func (s *State) createGoFileFromLines(filePath string, cellId int, lines []strin }() w.Write("package main\n\n") - var createdFuncMain bool - isFirstLine := true + var needsClosingMain bool for ii, line := range lines { - if strings.HasPrefix(line, "%main") || strings.HasPrefix(line, "%%") { + trimmedLine := TrimGonbCommentPrefix(line) + if strings.HasPrefix(trimmedLine, "%main") || strings.HasPrefix(trimmedLine, "%%") { // Write preamble of func main() and associate to the "%%" line: fileToCellLines[w.Line] = ii fileToCellLines[w.Line+1] = ii w.Write("func main() {\n\tflag.Parse()\n") - createdFuncMain = true - isFirstLine = false + needsClosingMain = true + continue + } + if strings.HasPrefix(trimmedLine, "%exec") { + parts := strings.Split(trimmedLine, " ") + if len(parts) < 2 { + err = errors.Errorf("%%exec requires the name of a function to execute, none given in line %d: %q", ii, line) + } + mainFuncName := parts[1] + for jj := range 4 { + fileToCellLines[w.Line+jj] = ii + } + w.Write(fmt.Sprintf(`func main() { + flag.Parse() + %s() +} +`, mainFuncName)) continue } if _, found := skipLines[ii]; found { + // Other special commands. continue } if ii == cursorInCell.Line { // Use current line for cursor, but add column. cursorInFile = w.CursorPlusDelta(Cursor{Col: cursorInCell.Col}) } - if isFirstLine && strings.HasPrefix(line, "package") { - err = errors.Errorf("Please don't set a `package` in any of your cells: GoNB will set a `package main` automatically for you when compiling your cells. Cell #%d Line %d: %q", - cellId, ii+1, line) - return + if strings.HasPrefix(line, "package") { + // Ignore package line, since GoNB will rewrite the package to main. + continue } fileToCellLines[w.Line] = ii // Registers line mapping. w.Write(line) w.Write("\n") - isFirstLine = false } - if createdFuncMain { + if needsClosingMain { w.Write("\n}\n") } if w.Error() != nil { diff --git a/internal/goexec/composer_test.go b/internal/goexec/composer_test.go index 91630f5..e9d4c58 100644 --- a/internal/goexec/composer_test.go +++ b/internal/goexec/composer_test.go @@ -3,7 +3,6 @@ package goexec import ( "fmt" . "github.com/janpfeifer/gonb/common" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "os" "strings" @@ -64,11 +63,16 @@ func TestCreateGoFileFromLines(t *testing.T) { require.Equalf(t, cellLines[cellLineIdx], newLine, "Line mapping look wrong: file line %d --> cell line %d", ii, cellLineIdx) } - // Test check for invalid "package xxx" in cell. + // Test check "package xxx" in cell is ignored. cellLines = strings.Split("package xxx\n%%\nfmt.Println(\"Hello\")\n", "\n") skipLines = MakeSet[int]() cursorInCell = NoCursor _, _, err = s.createGoFileFromLines(s.CodePath(), 1, cellLines, skipLines, cursorInCell) - require.Errorf(t, err, "Expected error for unnecessary setting of `package`.") - assert.Contains(t, err.Error(), "Please don't set a `package`") + require.NoErrorf(t, err, "Failed createGoFileFromLines with: %q", cellLines) + contentBytes, err = os.ReadFile(s.CodePath()) + require.NoErrorf(t, err, "Failed os.ReadFile(%q)", s.CodePath()) + content = string(contentBytes) + require.Contains(t, content, "func main() {") + require.Contains(t, content, "Hello") + require.NotContains(t, content, "xxx", "`package xxx` should have been discarded") } diff --git a/internal/goexec/parser.go b/internal/goexec/parser.go index e0551c9..2dd650d 100644 --- a/internal/goexec/parser.go +++ b/internal/goexec/parser.go @@ -574,3 +574,17 @@ func IsEmptyLines(lines []string, skipLines Set[int]) bool { } return true } + +// GonbCommentPrefix allows one to enter the special commands (`%%`, `!`) prefixed as a Go comment, so +// it doesn't conflict with Go IDEs. +// Particularly useful if using Jupytext. +const GonbCommentPrefix = "//gonb:" + +// TrimGonbCommentPrefix removes a prefixing "//gonb:" (GonbCommentPrefix) from line, if there is such a prefix. +// This is optionally used to escape special commands. +func TrimGonbCommentPrefix(line string) string { + if strings.HasPrefix(line, GonbCommentPrefix) { + line = line[len(GonbCommentPrefix):] + } + return line +} diff --git a/internal/nbtests/nbtests_test.go b/internal/nbtests/nbtests_test.go index bdbe215..9da088f 100644 --- a/internal/nbtests/nbtests_test.go +++ b/internal/nbtests/nbtests_test.go @@ -216,10 +216,20 @@ func TestHello(t *testing.T) { } f := executeNotebook(t, "hello") err := Check(f, - Match(OutputLine(2), - Separator, - "Hello World!", - Separator), + Sequence( + Match(OutputLine(2), + Separator, + "Hello World!", + Separator), + Match(OutputLine(3), + Separator, + "Works with //gonb:", + Separator), + Match(OutputLine(4), + Separator, + "%exec worked for x=17", + Separator), + ), *flagPrintNotebook) require.NoError(t, err) diff --git a/internal/specialcmd/cellmagic.go b/internal/specialcmd/cellmagic.go index 12a8df2..3d094dc 100644 --- a/internal/specialcmd/cellmagic.go +++ b/internal/specialcmd/cellmagic.go @@ -25,6 +25,7 @@ var ( // // The first line may contain special commands that change the interpretation of the cell, e.g.: "%%script", "%%writefile". func IsGoCell(firstLine string) bool { + firstLine = goexec.TrimGonbCommentPrefix(firstLine) parts := strings.Split(firstLine, " ") return !CellSpecialCommands.Has(parts[0]) } @@ -36,7 +37,8 @@ func ExecuteSpecialCell(msg kernel.Message, goExec *goexec.State, lines []string if len(lines) == 0 { return } - parts := splitCmd(lines[0]) + line := goexec.TrimGonbCommentPrefix(lines[0]) + parts := splitCmd(line) if len(parts) == 0 || !CellSpecialCommands.Has(parts[0]) { return } diff --git a/internal/specialcmd/help.md b/internal/specialcmd/help.md index 645f7d0..4eab1b0 100644 --- a/internal/specialcmd/help.md +++ b/internal/specialcmd/help.md @@ -40,13 +40,16 @@ This way each cell can create its own `init_...()` and have it called at every c ### Special non-Go Commands -- `%%` or `%main`: Marks the lines as follows to be wrapped in a `func main() {...}` during +- `%% []` or `%main []`: Marks the lines as follows to be wrapped in a `func main() {...}` during execution. A shortcut to quickly execute code. It also automatically includes `flag.Parse()` - as the very first statement. Anything `%%` or `%main` are taken as arguments + as the very first statement. Anything after`%%` or `%main` are taken as arguments to be passed to the program -- it resets previous values given by `%args`. -- `%args`: Sets arguments to be passed when executing the Go code. This allows one to +- `%args `: Sets arguments to be passed when executing the Go code. This allows one to use flags as a normal program. Notice that if a value after `%%` or `%main` is given, it will overwrite the values here. +- `%exec []`: this will call the function `my_func()`, and optionally set the program arguments. + Behind the scenes it creates a trivial `func main()` that parses the flags and calls `my_func()` (without any + parameters or return values). - `%autoget` and `%noautoget`: Default is `%autoget`, which automatically does `go get` for packages not yet available. - `%cd []`: Change current directory of the Go kernel, and the directory from where @@ -64,7 +67,13 @@ This way each cell can create its own `init_...()` and have it called at every c - `%with_password`: will prompt for a password passed to the next shell command. Do this is if your next shell command requires a password. -Notice all these commands are executed **before** any Go code in the same cell. +**Notes**: + +1. The special commands below can be used in the start of the line as is, or prefixed by a `//gonb:`, which may be easier +on some IDEs if editing the code externally (since these special commands are not proper Go). +So `//gonb:%%` is the same as `%%` +2. All these commands are executed **before** any Go code in the same cell. + ### Managing Memorized Definitions @@ -95,6 +104,23 @@ Notice all these commands are executed **before** any Go code in the same cell. Notice that when the cell is executed, first all shell commands are executed, and only after that, if there is any Go code in the cell, it is executed. +### Running a Debugger + +While **GoNB** doesn't (yet) talk the debug protocol with JupyterLab, it's easy to start a GUI debugger +from a cell, if being executed on the same machine as the browser. + +The common Go debugger recommendation is [delve](https://github.com/go-delve/delve), and in particular its front-end +[gdlv](https://github.com/aarzilli/gdlv). And to make it simpler **GoNB** includes a small wrapper script +[`ndlv`](https://github.com/janpfeifer/gonb/blob/main/cmd/ndlv/ndlv) to +set the directory and program name to the last cell executed. Copy or link that script somewhere in your `PATH` +(maybe `${HOME}/bin` if you have such directory set up). + +To open the debugger, after executing a cell you want to debug, you create and execute a new cell with this single shell command: + +``` +!ndlv +``` + ### Tracking of Go Files In Development: A convenient way to develop programs or libraries in **GoNB** is to use replace diff --git a/internal/specialcmd/specialcmd.go b/internal/specialcmd/specialcmd.go index ad5e6d9..32adf80 100644 --- a/internal/specialcmd/specialcmd.go +++ b/internal/specialcmd/specialcmd.go @@ -50,12 +50,13 @@ func Parse(msg kernel.Message, goExec *goexec.State, execute bool, codeLines []s if usedLines.Has(lineNum) { continue } + line = goexec.TrimGonbCommentPrefix(line) if len(line) > 1 && (line[0] == '%' || line[0] == '!') { var cmdStr string - cmdStr = joinLine(codeLines, lineNum, usedLines) + cmdStr = joinLine(codeLines, lineNum, usedLines) // Gather current line and following if line ends with a `\` symbol cmdType := cmdStr[0] cmdStr = cmdStr[1:] - for cmdStr[0] == ' ' { + for cmdStr[0] == ' ' || cmdStr[0] == '\t' { cmdStr = cmdStr[1:] // Skip initial space } if len(cmdStr) == 0 { @@ -87,14 +88,17 @@ func Parse(msg kernel.Message, goExec *goexec.State, execute bool, codeLines []s return } -// joinLine starts from fromLine and joins consecutive lines if the current line terminates with a `\n`, +// joinLine starts from fromLine and joins consecutive lines if the current line terminates with a `\\\n`, // allowing multi-line commands to be issued. // +// It removes the `//gonb:` prefix from all the joined lines, if the prefix is present. +// // It returns the joined lines with the '\\\n' replaced by a space, and appends the used lines (including // fromLine) to usedLines. func joinLine(lines []string, fromLine int, usedLines Set[int]) (cmdStr string) { for ; fromLine < len(lines); fromLine++ { - cmdStr += lines[fromLine] + line := goexec.TrimGonbCommentPrefix(lines[fromLine]) + cmdStr += line usedLines.Insert(fromLine) if cmdStr[len(cmdStr)-1] != '\\' { return @@ -121,9 +125,17 @@ func execSpecialConfig(msg kernel.Message, goExec *goexec.State, cmdStr string, switch parts[0] { // Configures how cell will be executed. - case "%", "main", "args", "test": + case "%", "main", "args", "test", "exec": // Set arguments for execution, allows one to set flags, etc. - goExec.Args = parts[1:] + if parts[0] == "exec" { + if len(parts) == 1 { + return errors.Errorf("%%exec requires the name of the function to execute") + } + // The function to execute is in parts[1], but it will be extracted later when parsing the cell code. + goExec.Args = parts[2:] + } else { + goExec.Args = parts[1:] + } klog.V(2).Infof("Program args to use (%%%s): %+q", parts[0], goExec.Args) if parts[0] == "test" { goExec.CellIsTest = true