Skip to content

Feat/integrate debugger - #241

Merged
fdncred merged 33 commits into
nushell:mainfrom
rvhelden:feat/integrate-debugger
Oct 24, 2026
Merged

fdncred merged 33 commits into
nushell:mainfrom
rvhelden:feat/integrate-debugger

Conversation

@rvhelden

@rvhelden rvhelden commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

The PR for integrating the new nushell debugger protocol into vscode.
With this PR comes all the features of the original repo https://github.com/rvhelden/vscode-nushell-dap

@rvhelden

rvhelden commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

@fdncred Here is the PR for the debugger in vscode

@fdncred

fdncred commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

wow, that's a lot of changes. LOL

@fdncred fdncred left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notes from my clanker.

Lint, both typechecks, compile and the 33 unit tests pass on d3b563b. The comments below come from reading through the debugger code. The examples for script-signatures.ts and split-args.ts come from running those helpers under node with the inputs shown.

The resolve.ts change is probably the one to settle first, since it affects language server and terminal users who never open the debugger. Most of the rest is the launch-time signature parsing. With some common signatures it skips the argument prompt when the script needs arguments, and with others it shows the prompt when the script needs none.

Comment thread client/src/nushell/resolve.ts Outdated
Comment thread client/src/debug/launch/script-signatures.ts Outdated
Comment thread client/src/debug/launch/script-signatures.ts Outdated
Comment thread client/src/debug/launch/script-signatures.ts Outdated
Comment thread client/src/debug/launch/entry-point.ts Outdated
Comment thread client/src/debug/launch/split-args.ts Outdated
Comment thread client/src/debug/restart/restart-on-save.ts
Comment thread client/src/debug/visualize/pinned-variable.ts Outdated
Comment thread client/src/debug/adapter/descriptor-factory.ts Outdated
@rvhelden
rvhelden force-pushed the feat/integrate-debugger branch from 9be2a1b to 2b2d722 Compare October 9, 2026 20:11
@rvhelden

rvhelden commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Also reverted much of the unrelated changes to keep it as small as possible

@rvhelden
rvhelden force-pushed the feat/integrate-debugger branch from 2b2d722 to 61f7edb Compare October 9, 2026 20:17
@fdncred

fdncred commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

let's do one more review to make sure things look in order and look at the ci issue as well.

@fdncred fdncred left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notes from my clanker, second pass at 61f7edb.

Earlier review threads

All nine are addressed. Three of them have a follow-up, posted inline.

  • resolve.ts bad setting: the PATH fallback and the warning are back. Follow-up: the warning now fires from every caller, including quietNushell, so one F5 can show it twice.
  • script-signatures.ts (three threads: space-separated params, [^\]]* and nested modules, dashed optionals and comments): moving to ast --json fixes all of them, and only top-level defs are read now. Follow-up: a def with an attribute in front (@example, @category, @search-terms) comes back wrapped in AttributeBlock and gets skipped. That brings back the original bug, no prompt and main runs without its argument.
  • entry-point.ts relative program: absoluteProgram resolves it. Follow-up: a relative cwd is used as the base for program, but it goes to the adapter unresolved.
  • split-args.ts: --name="hello world" x now gives ['--name=hello world', 'x'].
  • restart-on-save.ts: only manual saves of the session's program restart now.
  • pinned-variable.ts: pinVariable/unpinVariable bump generation. Follow-up (low): the first fetch in visualizeVariable isn't guarded.
  • descriptor-factory.ts: throws CancellationError, so only the resolver's message shows.

CI failure

The build failure on 61f7edb has nothing to do with the PR. GitHub builds the Docker image for lannonbr/vsce-action@master before the job's steps run, and its FROM node:20-slim pull got 504 Gateway Timeout from auth.docker.io three times in a row. Checkout, npm install, typecheck:webview, test:unit and vsce package were all skipped, so this head has never had a real CI run. Re-running the job should fix it. Since this comes from a fork, the re-run may need maintainer approval like the earlier action_required run did.

On 61f7edb locally: npm run lint, tsc -p client --noEmit, npm run typecheck:webview, npm run test:unit (35 pass), npm run compile and vsce package all succeed, and the .vsix has out/debug/ir-panel.html, visualize-panel.html and visualize-panel.webview.js.

Optional, and it could be its own PR: the workflow now runs actions/setup-node, so - run: npx @vscode/vsce package could replace lannonbr/vsce-action@master. That drops the Docker Hub pull from every run and stops depending on an unpinned @master.

Minor, no inline comment

  • shared/html.ts makeNonce uses Math.random(). crypto.randomBytes(16).toString('base64') is the usual choice for a CSP nonce.
  • test:unit passes a glob to node --test, which needs Node 21 or newer. CI pins 22, but CONTRIBUTING doesn't mention it, and on Node 20 the command fails.
  • The Show IR button sits in the title bar of every Nushell editor, even with no debug session or with a nu older than 0.116. Adding inDebugMode to its when clause would hide it outside debugging, if that's what you want.
  • The comment on LOCALS_REFERENCE in visualize-request.ts points at "the fork's crates/nu-dap/src/state.rs". nu-dap is upstream now, so the comment could point there.
  • In restart-on-save.ts, the isNuDocument check is redundant once isSessionProgram matches the path.
  • const nushell = async () => ensureNushell(); in extension.ts wraps a sync call in a promise. Meanwhile configuration-provider.ts imports resolveNushell directly instead of using the injected resolver, so the debugger gets nu two different ways.
  • On main, the not-found error told the user to run "Nushell: Start Language Server" after installing. The new message dropped that. A setting change restarts the server, but installing nu onto PATH doesn't.

Comment thread client/src/debug/launch/script-signatures.ts Outdated
Comment thread client/src/debug/launch/script-signatures.ts Outdated
Comment thread client/src/nushell/resolve.ts Outdated
Comment thread client/src/extension.ts
Comment thread client/src/debug/launch/configuration-provider.ts
Comment thread client/src/debug/launch/entry-point.ts Outdated
Comment thread client/src/debug/adapter/descriptor-factory.ts Outdated
Comment thread client/src/debug/visualize/visualize-command.ts
Comment thread client/src/debug/launch/script-signatures.ts
Comment thread examples/debug/08_main_entrypoint.nu Outdated
rvhelden pushed a commit to rvhelden/vscode-nushell-lang that referenced this pull request Oct 11, 2026
Moves logic touched by the second PR nushell#241 review into vscode-free modules,
unchanged, so node:test can reach it: launch paths, nu resolution, the
language-server restart on setting changes, the visualize request guard and
the DAP version check. Adds tests for the behaviour the review asks for;
19 of them fail until the follow-up fixes land.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rvhelden
rvhelden force-pushed the feat/integrate-debugger branch from 990ebc8 to 4bd1a5e Compare October 11, 2026 09:20
ronaldvanhelden and others added 11 commits October 11, 2026 13:49
Second review (fdncred, 10 Oct):
- a def with an @Attribute in front of it is not found (nushell#10)
- the prompt label keeps the input/output types (nushell#11); the test asks for
  nushell's usage line, built from the parsed signature as suggested
- nu exiting before reading the script raises an uncaught EPIPE (nushell#18)

From parsing ~1,800 real scripts (nushell, nu_scripts, nupm, awesome-nu):
- a script with ~1,100+ top-level defs is not analysed
- \" inside double quotes at the argument prompt loses its quote

6 tests fail until the follow-up fixes land.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`@example`/`@category` wrap a def in an AttributeBlock in the AST, so
`def main` went unnoticed and its arguments were never asked for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The label was the signature's source text minus its first and last
character, and that text also covers input/output types, so the prompt
read `main [x: int]: nothing -> in]`. It is now nushell's usage line,
built from the parsed parameters as suggested in review:
`main {flags} <name> (count) ...rest`. That also keeps multi-line
signatures, comments and raw-string defaults out of the label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ast --json also serializes the compiled IR, ~99% of its output; about
1,100 top-level defs pushed it past the 256 MB buffer, and the launch
skipped the entry-point and argument prompts without a word. nu now
prints only the statements (2 MB for 1,500 defs). Found by parsing
~1,800 real scripts (nu_scripts' completions/mvn.nu).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
splitArgs closed the quote at an escaped " and kept the backslashes.
Inside double quotes, backslashes before a quote now follow the Windows
command-line rule; elsewhere they stay as typed, so paths still work.
Found by parsing ~1,800 real scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
resolveNushell showed the fallback warning itself, so the quiet lookup
before a launch warned too and one F5 warned twice. As suggested in
review, it now returns fellBackFrom without side effects (replacing the
unread source field), and ensureNushell warns once per stale value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Typing a path in the Settings UI restarted the server, and warned, on
every keystroke; a user-stopped server was started again; and two
overlapping changes raced into 'already running'. As suggested in
review: restart only a running server, after the setting is quiet for
500 ms, only when the resolved nu differs, one restart at a time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`"cwd": "scripts"` was resolved for reading the program but reached
nu --dap unchanged, which resolved it against the extension host's
directory, so $env.PWD did not match the workspace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README's example config has "args": [], so copying it skipped the
prompt and main failed on the missing argument.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An entryPoint of "" or " main " found no def, so the argument prompt was
skipped and the adapter failed with a missing argument. Found by parsing
~1,800 real scripts and comparing with nu-dap's entry_point().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ronaldvanhelden and others added 3 commits October 11, 2026 13:51
VS Code resolves the launch configuration before it asks the adapter
factory, so with nu older than 0.116 the user picked an entry point and
typed arguments, and only then heard debugging needs 0.116. The check is
now a shared function that the configuration provider calls first; the
factory still calls it and finds the result cached.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A slow $big arriving after a fast $small painted over it and pinned
$big. As suggested in review, a pick now takes a generation before the
fetch and drops a stale answer. Picks count separately from the pinned
refresh, so a step during a pending pick drops the refresh, not the pick.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fdncred

fdncred commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Please respond to each review comment when you're done so we can know that each issue has been addressed. Thanks for this!

@rvhelden

Copy link
Copy Markdown
Contributor Author

Sorry wasn't done yet, was backtracking some of my changes, I will let you know when I'm finished with the changes and comment on each of your findings with how I resolved it.

ronaldvanhelden and others added 10 commits October 11, 2026 18:06
The unit test script passed a glob to node --test, which only Node 21+
expands; on Node 20 it ran no tests. The tests now use node --test's own
discovery from client/out-unit, which works on 20 and later.
engines.node, CONTRIBUTING and CI name the version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lannonbr/vsce-action@master pulled a Docker image on every run (a PR run
failed on a Docker Hub 504) and tracked an unpinned branch.
npx @vscode/vsce package uses the version in the lockfile. Also moves
actions/checkout to v4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The restart only ran for a running server, so a server that never
started (no nu at activation) or was stopped stayed down after the
setting was fixed. A change now (re)starts it in all cases; the debounce,
the one-at-a-time queue and the skip for an unchanged nu stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Math.random() is not a cryptographically secure source, so a nonce built
from it is guessable. Use 16 random bytes, base64-encoded, which is also
the format the CSP nonce-source grammar expects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The IR panel only fills from nuDapIr events, so outside a Nushell
session the title-bar button opened an empty panel in every Nushell
editor. Gating it on debugType also hides it for nu older than 0.116,
which can't start a session. The command stays in the palette.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nu-dap is part of upstream Nushell now, so the comment names
crates/nu-dap/src/state.rs in nushell/nushell instead of the fork.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
isSessionProgram already requires the saved document to be the session's
program, so the language check adds nothing, and it would skip a restart
for a debugged script without a .nu extension or nushell language id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ensureNushell is synchronous, so wrapping it in an async arrow only added
needless awaits. The launch configuration provider keeps resolving `nu`
quietly on purpose; its comment now says why it bypasses the injected one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Changing nushellExecutablePath restarts the server, but installing nu
onto PATH does not, so the not-found message needs the hint main had.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reverts f1384c1. As asked in review, a nushellExecutablePath change
restarts a running server only: a server the user stopped with
"Nushell: Stop Language Server" stays stopped. To start one that never
came up, run "Nushell: Start Language Server", as the not-found message
says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rvhelden
rvhelden force-pushed the feat/integrate-debugger branch from 4bd1a5e to f1ab13a Compare October 24, 2026 12:29
@rvhelden

Copy link
Copy Markdown
Contributor Author

@fdncred Done reviewing comments, and the minor items you told in you review commit

  • CI: packages with npx @vscode/vsce package instead of lannonbr/vsce-action.
  • makeNonce now uses crypto.randomBytes(16).
  • Node 20 LTS is now required and documented; test:unit no longer uses a glob.
  • Show IR now only shows during a Nushell debug session.
  • LOCALS_REFERENCE now points at upstream Nushell.
  • Removed the redundant isNuDocument check.
  • Removed the async wrapper; quietNushell keeps resolveNushell on purpose (no warning).
  • The not-found message has the "Nushell: Start Language Server" hint again.

@fdncred

fdncred commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

great work!

@fdncred
fdncred merged commit ffd0e93 into nushell:main Oct 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants