Skip to content

feat(playground): keep connections across save/reopen + add a Connect-to-Database picker#758

Merged
tnaum-ms merged 22 commits into
mainfrom
dev/tnaum/query-playground-saved-files
Jun 25, 2026
Merged

feat(playground): keep connections across save/reopen + add a Connect-to-Database picker#758
tnaum-ms merged 22 commits into
mainfrom
dev/tnaum/query-playground-saved-files

Conversation

@tnaum-ms

@tnaum-ms tnaum-ms commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Query Playgrounds bind each .documentdb.js document to a cluster/database connection, but that binding previously lived only in an in-memory map keyed by the document URI. It was therefore lost whenever the URI changed (saving an untitled scratch file to disk, or Save As) or when a saved playground was reopened in a fresh VS Code session — leaving the file silently disconnected.

This PR makes playground connections survive those transitions and adds a first-class way to (re)connect a playground.

What's included

Keep the connection through saves

  • untitled → file and file → file (Save As): at save time the new file is correlated with the still-open source document by content, and the connection is re-keyed onto the new URI.

Connect / reconnect on demand

  • New Query Playground: Connect to Database command plus always-visible affordances: the "Not connected" CodeLens and StatusBar item now open a connection picker instead of a dead-end info popup.
  • Connect-on-run: running an unconnected playground opens the picker, then continues the run (cancel = no-op).
  • Switch database: clicking the connection lens while connected opens a modal to switch databases. The current connection is only replaced after a new database is picked, so cancelling the picker preserves it.

Silent in-session restore

  • A non-persistent, in-memory "last binding per URI" map restores a playground's connection on reopen within the same session, provided the target cluster still has live credentials. No disk persistence and no file mutation; it falls back to the Connect affordance otherwise. (A true process restart intentionally relies on the Connect affordance.)

Reusable tree picker (pickTreeNode)

  • A generic, telemetry-instrumented quick pick that drills the Connections tree (folders → clusters → databases) by reusing the tree's own getChildren, so folder nesting and connect/auth-on-expand are inherited for free.
  • UX: a busy/loading indicator while a level loads (e.g. while a cluster connection is established), tree-matching icons, the host shown as a second line to disambiguate same-named connections, uniform two-line rows, Back pinned to the top, and graceful empty / no-connections states.

Design notes

  • Uses the stable clusterId (never treeId) for cache/credential lookups, per the dual-ID rule.
  • Open-time restore never reads the document body, so it is safe against the empty-model open race.
  • Dedicated telemetry: documentdb.pickTreeNode (source / outcome / steps), playground.connect, playground.connectOnRun.

Testing

  • New unit tests for pickTreeNode and PlaygroundService (save-time transfer, in-session restore, lifecycle, connection management).
  • Full Jest suite, lint, and TypeScript build are green.

tnaum-ms added 17 commits June 24, 2026 18:04
Correlate the untitled→file save on onDidSaveTextDocument instead of
onDidOpenTextDocument. For a brand-new file the file's document model is
still empty when it opens, so content correlation failed and the connection
was dropped on close. The save event fires after the text is populated and
while the untitled is still open, making the re-key reliable.

Also gate correlation by document length via offsetAt (no full-text
materialization) and skip documents over a size cap.
Broaden the save-time connection correlation to accept any open playground as
the source (untitled from a first save, or a file from "Save As…"), excluding
the saved file itself. Previously only an untitled source was considered, so
re-saving an already-saved playground under a new name dropped its connection
when the original file closed.

The "exactly one match" guard still prevents ambiguous re-keys, and a regular
save of an already-connected playground remains a no-op (its URI is unchanged).
Renamed migrateConnectionFromSavedUntitled → migrateConnectionToSavedFile and
added file→file Save As + regular-save coverage.
Drop the confusing 'migration' terminology: rename migrateConnectionToSavedFile
→ transferConnectionToSavedFile and MAX_MIGRATION_TEXT_LENGTH →
MAX_TRANSFER_TEXT_LENGTH, reword comments, and change the trace message to
'transferred connection on save'. Updated test labels accordingly.
Add a generic, telemetry-instrumented quick-pick helper that drills through
a tree (folders -> clusters -> databases) by repeatedly calling getChildren,
presenting one quick pick per level until a leaf matching a given contextValue
is selected. Reuses the live Connections branch data provider so folder nesting
and connect/auth-on-expand are inherited for free. Includes unit tests.
Add vscode-documentdb.command.playground.connect which prompts the user (via
the reusable pickTreeNode picker, stopping at the database level) and binds the
active playground to the selected cluster/database. Uses the stable clusterId
for the connection per the dual-ID rule. Registered and contributed to the
command palette when a playground editor is active.
The 'Not connected' CodeLens and StatusBar affordances now invoke the new
connect command instead of the dead-end info popup, turning the always-visible
disconnected indicator into a one-click connect action.
Running an unconnected playground (e.g. a saved file reopened in a fresh
session) now launches the connection picker instead of showing a dead-end
message; if the user picks a database the run continues, otherwise it bails
quietly. Instrumented via a dedicated playground.connectOnRun telemetry event.
Keep a non-persistent, in-memory last-binding-per-URI map that survives document
close. When a playground is reopened within the same session, restore its
binding silently if the target cluster still has live credentials; otherwise
leave it disconnected for the Connect affordance. Never reads the document body,
so it is safe against the empty-model open race. Cleared on explicit
removeConnection and on dispose.
Regenerate the localization bundle for the new connect/reconnect strings and
simplify the pickTreeNode test l10n mock to satisfy no-base-to-string lint.
- Show a busy/loading indicator while a level loads by handing showQuickPick a
  promise of picks with a loadingPlaceHolder, so the quick pick stays visible
  while a cluster connection is being established instead of vanishing.
- Carry each tree item's own iconPath onto the quick pick so clusters, folders
  and databases use the same icons as the tree.
- Replace the warning toast for an empty level with an in-list 'Empty' entry
  alongside Back, and a 'No connections found' entry at an empty root.
- Connected connection lens now opens a modal offering to switch the playground
  to a different database; the switch is deferred so cancelling the picker keeps
  the existing connection (setConnection only runs once a new db is selected).
- Empty-level entry drops its description (label only).
- No-connections entry uses detail (second line) instead of description.
Add an optional getDetail callback to pickTreeNode (with matchOnDetail so it's
searchable) and supply it from the playground connect flow: cluster picks show
their host(s), database picks show 'cluster · host'. This disambiguates items
that share a display name. Host comes from the non-secret connection string on
the cluster model and is omitted gracefully when unavailable.
Mirror the Azure credentials-management browser: list real items first, then a
separator, then the navigation actions (Back when nested, Exit always) pinned to
the bottom of the list. Selecting Exit cancels the picker.
Give every selectable pick a detail line so rows have a consistent two-line
height: folders show a static 'Folder' tag, local emulators 'Local emulators',
clusters their host (fallback 'Cluster'), and databases 'cluster · host'
(fallback cluster name). Fixes the ragged mix of one- and two-line rows.
- Add an optional getGroup callback to pickTreeNode; when a level has 2+ groups,
  items are organized under separator headers (Folders, Connections) in
  first-seen order; single-group levels stay flat.
- Database picks now show a static 'Database' detail instead of cluster·host.
- The Empty entry gains a 'No entries' description.
- Remove the Exit entry (Esc still cancels).
- Move Back to the top of each nested level, followed by a separator.
- Drop the Folders/Connections group headers; rely on the tree's natural
  folders-before-connections order.
- Empty entry now shows 'No entries' as a detail line.
@tnaum-ms tnaum-ms changed the title fix: query playground feat(playground): keep connections across save/reopen + add a Connect-to-Database picker Jun 24, 2026
@tnaum-ms tnaum-ms linked an issue Jun 24, 2026 that may be closed by this pull request
@tnaum-ms tnaum-ms added this to the 0.9.1 milestone Jun 24, 2026
tnaum-ms added 2 commits June 24, 2026 23:24
- Add docs/ai-and-plans/PRs/758-query-playground-connections/summary.md
  capturing what was built and, crucially, why (the two-sub-problem split,
  why cross-session needs an actionable picker, dual-ID, UX rationale).
- Add a module-level overview to pickTreeNode.ts explaining its purpose,
  design (live-tree walk), termination/safety invariants, telemetry, and
  reuse; enrich the function doc.
runAll and runSelected had their own early isConnected guard that showed the
dead-end 'no connection' warning and returned before executePlaygroundCode ran,
so the connect-on-run picker never triggered. Remove those guards (and the now
unused PlaygroundService import); executePlaygroundCode launches the connection
picker for unconnected playgrounds and continues the run.
@tnaum-ms tnaum-ms marked this pull request as ready for review June 24, 2026 23:31
@tnaum-ms tnaum-ms requested a review from a team as a code owner June 24, 2026 23:31
Copilot AI review requested due to automatic review settings June 24, 2026 23:31

Copilot AI 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.

Pull request overview

This PR improves the Query Playground UX by (1) preserving the in-memory cluster/database binding across URI-changing saves (untitled → file, and Save As) and (2) adding a first-class “Connect to Database” flow (CodeLens, StatusBar, and connect-on-run) backed by a reusable Connections-tree-driven picker.

Changes:

  • Added a generic pickTreeNode utility that browses the live Connections tree via getChildren and returns a leaf node by contextValue, with dedicated telemetry and “Back/Empty/No connections” UX.
  • Updated playground connection lifecycle to transfer bindings on save (content correlation) and to optionally restore a remembered binding on reopen within the same session (credential-gated).
  • Added a new “Query Playground: Connect to Database” command and rewired disconnected affordances + connect-on-run to use the picker.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/utils/pickItem/pickTreeNode.ts New reusable quick-pick tree browser with telemetry and navigation UX.
src/utils/pickItem/pickTreeNode.test.ts Unit tests for tree browsing, filtering, Back/Empty/No connections, icon/detail passthrough.
src/documentdb/playground/PlaygroundService.ts Save-time connection transfer + in-session reopen restore + StatusBar connect affordance updates.
src/documentdb/playground/PlaygroundService.test.ts Tests for save-transfer and in-session restore behavior.
src/documentdb/playground/PlaygroundCodeLensProvider.ts Disconnected CodeLens now routes to Connect instead of info.
src/documentdb/playground/PlaygroundCodeLensProvider.test.ts Updated expectations for the new Connect CodeLens behavior.
src/documentdb/playground/constants.ts Added new playground command id for connect/reconnect.
src/documentdb/ClustersExtension.ts Registered the new connect command.
src/commands/playground/runSelected.ts Removed dead-end “not connected” warning (execution path now handles connect-on-run).
src/commands/playground/runAll.ts Removed dead-end “not connected” warning (execution path now handles connect-on-run).
src/commands/playground/executePlaygroundCode.ts Connect-on-run: prompt to connect when unbound, then continue execution.
src/commands/playground/connectDatabase.ts New connect command + “switch database” modal + conversion from picked DatabaseItem → PlaygroundConnection.
package.json Contributed the new command and enabled it for documentdb-playground editors.
l10n/bundle.l10n.json Added/updated localized strings for picker UX and connect affordances.
docs/ai-and-plans/PRs/758-query-playground-connections/summary.md Design/decision log for the feature set and rationale.

Comment thread src/documentdb/playground/PlaygroundService.ts
Comment thread src/documentdb/playground/PlaygroundService.test.ts
Comment thread src/documentdb/playground/PlaygroundService.test.ts
Comment thread src/commands/playground/connectDatabase.ts
Comment thread src/commands/playground/executePlaygroundCode.ts Outdated
Comment thread src/commands/playground/connectDatabase.ts
tnaum-ms added 3 commits June 24, 2026 23:48
Thread a telemetrySource parameter through promptAndConnectPlayground so the
documentdb.pickTreeNode event distinguishes the explicit connect affordance
(playground.connect) from the connect-on-run path (playground.connectOnRun).

Addresses Copilot review feedback on PR #758.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Code Quality Checks

Check Status How to fix
Localization (l10n) ✅ Passed
ESLint ✅ Passed
Prettier formatting ✅ Passed

This comment is updated automatically on each push.

@github-actions

Copy link
Copy Markdown
Contributor

📦 Build Size Report

Metric Base (main) PR Delta
VSIX (vscode-documentdb-0.9.1.vsix) 8.00 MB 8.01 MB ⬆️ +2 KB (+0.0%)
Webview bundle (views.js) 5.88 MB 5.88 MB ✅ 0 KB (0.0%)

Download artifact · updated automatically on each push.

@tnaum-ms tnaum-ms merged commit 90ea259 into main Jun 25, 2026
8 checks passed
@tnaum-ms tnaum-ms deleted the dev/tnaum/query-playground-saved-files branch June 25, 2026 00:03
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.

Query Playground Run button disappears after saving the file

2 participants