feat: collection view & autocompletion UX improvements (Step 4.6)#532
Merged
tnaum-ms merged 23 commits intofeature/shell-integrationfrom Mar 19, 2026
Merged
feat: collection view & autocompletion UX improvements (Step 4.6)#532tnaum-ms merged 23 commits intofeature/shell-integrationfrom
tnaum-ms merged 23 commits intofeature/shell-integrationfrom
Conversation
Documentation links like [DocumentDB Docs](https://...) were not rendered as
clickable hyperlinks in Monaco's completion detail panel. Monaco requires
{ value: string, isTrusted: true } on MarkdownStrings to enable link rendering.
Set isTrusted: true on operator documentation MarkdownStrings in
mapOperatorToCompletionItem. This is safe because the documentation content
comes entirely from documentdb-constants (operator descriptions we control),
not from user-generated content.
Previously, ClusterSession reset the SchemaAnalyzer when the user changed their query. This meant queries returning 0 results left the autocompletion field list empty. Now the SchemaAnalyzer accumulates field knowledge monotonically across queries within the same session — new fields are added, type statistics enriched. Trade-off: type statistics represent aggregated observations across all queries, not a single query snapshot. This is acceptable since the UI shows approximate type info (e.g., 'mostly String') rather than absolute percentages. Added a future work discussion in docs/plan/future-work.md about potential strategies for separating cumulative vs. per-query statistics if needed.
$not is a field-level operator (e.g., { price: { $not: { $gt: 1.99 } } }),
not a root-level logical combinator like $and/$or/$nor. It was incorrectly
included in KEY_POSITION_OPERATORS, causing it to appear at query root (where
it's invalid) and be hidden at operator position (where users need it).
Changes:
- Remove '$not' from KEY_POSITION_OPERATORS in completionKnowledge.ts
- Update JSDoc to document why $not is excluded
- Update tests: expect $not at operator position, not at key position
At value position in the project editor, show 1 (include) and 0 (exclude) instead of filter-specific completions (operators, BSON constructors, etc.). At value position in the sort editor, show 1 (ascending) and -1 (descending). These are the most common values for projection and sort fields. Projection operators like $slice and $elemMatch remain available via operator-position completions for advanced use cases.
When the user clears the editor content (removing the initial '{ }'), field
completions now insert '{ fieldName: $1 }' instead of 'fieldName: $1' to
produce valid query syntax. Operator snippets already include their own braces
and are not double-wrapped.
A 'needsWrapping' flag is computed in registerLanguage.ts by checking whether
the editor text contains a '{' character. When true, field completions in the
'all completions' fallback path get wrapped with outer braces.
Added ':', ',', and '[' to the completion provider's triggerCharacters list. These positions are already handled by the cursor context parser (value after ':', new key after ',', array element after '[') but previously required manual Ctrl+Space invocation. Added string-literal detection (isCursorInsideString) to suppress completions when trigger characters appear inside string values. Uses a forward scan counting unescaped quotes to determine if the cursor is inside a string. When inside a string, returns empty suggestions to prevent the popup.
Extended the HoverProvider to show type information when hovering over field
names in the query editor. When a field is recognized from the completion
store (populated by SchemaAnalyzer), the hover shows:
- Field name (bold)
- BSON type (e.g., Number, String, Date)
- Sparse indicator when the field is not present in all documents
Operators/BSON constructors take priority over field names to avoid
ambiguity. Statistics use relative language ('sparse') rather than
absolute numbers since the SchemaAnalyzer accumulates data across queries.
Also set isTrusted: true on operator hover content to make doc links
clickable (consistent with the completion documentation fix).
…ypes
Three hover provider fixes:
1. Quoted string keys: Hover now works for quoted field names like
{"address.street": 1}. Monaco's getWordAtPosition treats quotes/dots
as word boundaries, so a new extractQuotedKey helper manually extracts
the full quoted key from the line content.
2. isTrusted on field hovers: Field hover content now has isTrusted: true,
making any future links in field hovers clickable. (Operator hovers
already had this from a previous commit.)
3. Redesigned field hover format:
- Field name bold, with 'sparse: not present in all documents' in
subscript on the same line (no em-dashes)
- 'Inferred Types' bold section header
- Comma-separated type list (using displayTypes from all observed
BSON types for polymorphic fields)
Also threaded bsonTypes/displayTypes through FieldCompletionData from
FieldEntry for polymorphic field support.
Added tests verifying that operator categories appear at the correct
completion positions:
- Key position: only logical combinators ($and, $or, $nor) and meta
operators ($comment, $expr, etc.) — no field-level operators
- Value position: all field-level categories — comparison ($gt, $eq),
evaluation ($regex), element ($exists, $type), array ($all,
$elemMatch, $size), and field-level $not
- Operator position: same as value position
This confirms $all is correctly excluded from key position (it's a
field-level array operator: { tags: { $all: [...] } }), not a root-level
query combinator.
When a field completion inserts 'rating: ', the completion popup did not reappear for the value position. Now, typing a space after ':' or ',' triggers the suggestion popup after 50ms. This provides a smooth autocomplete flow: select field → space → see value suggestions. Implemented via onDidChangeModelContent listener that detects single-space insertions preceded by ':' or ',' and programmatically calls editor.action.triggerSuggest. Wired into all three editors (filter, project, sort) with proper cleanup.
Monaco renders hover markdown links as <a> tags, but the webview CSP blocks direct navigation to external URLs. Added a delegated click handler on the query editor container that intercepts <a> clicks with http/https hrefs and routes them through the existing trpcClient.common.openUrl mutation, which calls vscode.env.openExternal on the extension host side.
11 tasks
EMPTY editor (no braces) now shows key-position completions (fields +
root operators) with { } wrapping, instead of showing all operators.
UNKNOWN context remains as the full discovery fallback.
Changes:
- createCompletionItems: route needsWrapping+unknown to new
createEmptyEditorCompletions (key-position items with wrapping)
- createAllCompletions: now pure UNKNOWN fallback (no needsWrapping param)
- New tdd/ folder with behavior spec (readme.completionBehavior.md) and
26 category-based TDD tests verifying the completion matrix
- Updated existing category tests: KEY position allows 'evaluation'
(because $expr/$text are key-position operators), UNKNOWN now shows
everything
- Updated completions/README.md: added Empty position, fixed flow docs
The TDD tests check categories (from description label) and sortText
prefixes, not specific operator names, for resilience to
documentdb-constants changes.
…pletions Added `standalone?: boolean` to `OperatorEntry`. When `false`, the operator is excluded from completion lists but remains in the registry for hover docs. Operators marked as standalone: false: - Geospatial sub-operators: $box, $center, $centerSphere, $geometry, $maxDistance, $minDistance, $polygon (only valid inside $geoWithin/$near) - Positional projection: $ (not a standalone filter/sort operator) - Sort modifier: $natural (not valid as a filter value) Changes: - packages/documentdb-constants/src/types.ts: added `standalone` field - packages/documentdb-constants/scripts/generate-from-reference.ts: parse `- **Standalone:** false` from overrides, emit in generated code. Also fixed bitwise BSON type from 'int' to 'int32' to match SchemaAnalyzer. - packages/documentdb-constants/resources/overrides/operator-overrides.md: added standalone: false overrides for 9 operators - src/webviews/documentdbQuery/completions/createCompletionItems.ts: filter `e.standalone !== false` in all three completion builders (all/value/operator)
…ab stops ESC key handling (MonacoEditor.tsx): - Added context precondition '!suggestWidgetVisible && !inSnippetMode' to the Escape command so Monaco's built-in handlers dismiss the suggest widget or exit snippet mode before our handler fires. Tab key handling (MonacoAutoHeight.tsx): - Replaced onKeyDown Tab interception with addAction using precondition '!inSnippetMode'. During snippet tab-stop navigation, Monaco's built-in Tab handler takes over. After the snippet session ends (final tab stop or ESC), Tab reverts to moving focus out.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves the DocumentDB collection query editor UX by refining autocompletion/hover behavior (including EMPTY-editor wrapping and string-literal suppression), enhancing hover tooltips (clickable links + field type info), and tightening keyboard accessibility (ESC/Tab behavior), backed by new TDD contract tests.
Changes:
- Add string-literal detection and expand completion trigger characters; introduce EMPTY-editor
{ ... }wrapping behavior and refine operator inclusion (e.g.,$not,standalone). - Enhance hover tooltips with clickable documentation links, quoted-key hover support, and inferred field type display.
- Add behavior-contract (TDD) and unit tests for completion behavior, quoted-key extraction, and string detection.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/webviews/documentdbQuery/tdd/readme.completionBehavior.md | New behavior contract spec for completion categories, wrapping, and sorting by cursor position |
| src/webviews/documentdbQuery/tdd/completionBehavior.test.ts | New TDD contract tests validating completion categories/sort order across cursor contexts |
| src/webviews/documentdbQuery/tdd/README.md | Documentation of the TDD contract and how to treat failures |
| src/webviews/documentdbQuery/registerLanguage.ts | Adds trigger characters, suppresses completions inside strings, adds EMPTY-editor wrapping flag, improves hover for quoted keys |
| src/webviews/documentdbQuery/isCursorInsideString.ts | New helper to detect whether cursor is inside a string literal |
| src/webviews/documentdbQuery/isCursorInsideString.test.ts | Unit tests for string-literal detection edge-cases |
| src/webviews/documentdbQuery/extractQuotedKey.test.ts | Tests for quoted-key extraction used by hover |
| src/webviews/documentdbQuery/documentdbQueryHoverProvider.ts | Hover now supports field type info + trusted/clickable links |
| src/webviews/documentdbQuery/documentdbQueryHoverProvider.test.ts | Tests for operator hover trust + new field hover behavior |
| src/webviews/documentdbQuery/documentdbQueryCompletionProvider.test.ts | Updates completion tests for isTrusted docs, $not positioning, wrapping, and editor-specific value completions |
| src/webviews/documentdbQuery/completions/mapCompletionItems.ts | Marks completion documentation as trusted (clickable links) |
| src/webviews/documentdbQuery/completions/createCompletionItems.ts | Implements EMPTY-editor wrapping behavior, editor-specific value completion lists, and filters non-standalone ops |
| src/webviews/documentdbQuery/completions/completionKnowledge.ts | Removes $not from key-position operator set and documents rationale |
| src/webviews/documentdbQuery/completions/README.md | Documents new EMPTY vs UNKNOWN behavior and snippet wrapping rules |
| src/webviews/documentdb/collectionView/components/queryEditor/QueryEditor.tsx | Adds smart-trigger suggest behavior, snippet cancel handling, and hover-link click delegation |
| src/webviews/components/MonacoEditor.tsx | ESC handler respects suggest widget and snippet mode via context precondition |
| src/webviews/components/MonacoAutoHeight.tsx | Tab focus-out behavior now respects snippet tab stops via action preconditions |
| src/utils/json/data-api/autocomplete/toFieldCompletionItems.ts | Threads polymorphic type lists (bsonTypes/displayTypes) into field completion data |
| src/documentdb/ClusterSession.ts | Preserves schema analyzer knowledge across query changes (no reset) |
| packages/documentdb-constants/src/types.ts | Adds standalone?: boolean to operator entries |
| packages/documentdb-constants/src/queryOperators.ts | Marks certain operators as standalone: false |
| packages/documentdb-constants/scripts/generate-from-reference.ts | Supports standalone overrides + fixes bitwise applicable BSON types |
| packages/documentdb-constants/resources/overrides/operator-overrides.md | Documents and adds Standalone overrides for specific operators |
| .github/copilot-instructions.md | Adds guidance for treating TDD: tests as behavior contracts |
src/webviews/documentdb/collectionView/components/queryEditor/QueryEditor.tsx
Show resolved
Hide resolved
src/webviews/documentdb/collectionView/components/queryEditor/QueryEditor.tsx
Show resolved
Hide resolved
Field names originate from user database schema and should not be rendered as trusted markdown. This change: - Removes isTrusted from field hovers (keeps supportHtml for formatting) - Escapes markdown metacharacters in field names and type strings - Adds escapeMarkdown utility in src/webviews/utils/ for reuse - Updates tests accordingly
Moves extractQuotedKey and tryMatchAsClosingQuote from registerLanguage.ts into a dedicated extractQuotedKey.ts module. This decouples the pure string helper from the Monaco registration wiring, making tests less brittle and enabling easier reuse.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 4.6: Collection View & Autocompletion UX Improvements
Post-review UX fixes and enhancements for the autocompletion system built in Steps 4–4.5.
Changes
Completion Provider Fixes
isTrusted: trueon completion documentation MarkdownStrings so[DocumentDB Docs](https://...)links render as clickable hyperlinksSchemaAnalyzer.reset()on query change so field knowledge accumulates monotonically; queries returning 0 results no longer wipe the field list$notmoved to field-level — Removed$notfromKEY_POSITION_OPERATORS; it now correctly appears at operator/value position ({ price: { $not: { $gt: 1.99 } } }) instead of key position where it is invalid1(include) and0(exclude) instead of filter-specific completions1(ascending) and-1(descending){ fieldName: $1 }and operator snippets keep their full brace-wrapping:,,,[to trigger characters with string-literal detection (isCursorInsideString) to suppress completions inside strings:and,— After selecting a field completion, typing space triggers the suggestion popup viaeditor.action.triggerSuggest(50ms delay){ }wrapping, instead of showing all operators. UNKNOWN remains as full discovery fallback.standalone?: booleantoOperatorEntry. Non-standalone operators (geospatial sub-operators,$,$natural) are excluded from completion lists but remain in the registry for hover documentation.Hover Provider Enhancements
HoverProviderto show field type info (bold field name, sparse indicator, "Inferred Types" section with all observed types for polymorphic fields)extractQuotedKeyhelper so hover works on"address.street"key names (Monaco'sgetWordAtPositiontreats.and"as word boundaries)<a>clicks throughtrpcClient.common.openUrl.mutate()→vscode.env.openExternal(webview CSP blocks direct navigation)bsonTypes/displayTypesthroughFieldCompletionDatafromFieldEntryfor multi-type field displayAccessibility Fixes
addActionwithprecondition: '!inSnippetMode'so Monaco's built-in snippet Tab handler takes over during snippet sessions.cancelSnippetSession()viasnippetController2.cancel(): cancels on editor blur and when delimiter characters (,,},]) are typed.Testing
isCursorInsideStringtests — 14 edge-case tests for string-literal detectionextractQuotedKeytests — Edge-case coverage for quoted key extractionFiles Changed
src/webviews/documentdbQuery/completions/— completion knowledge, item creation, mappingsrc/webviews/documentdbQuery/registerLanguage.ts— trigger chars, hover provider, quoted key extractionsrc/webviews/documentdbQuery/documentdbQueryHoverProvider.ts— field hover, isTrusted, formatsrc/webviews/documentdbQuery/isCursorInsideString.ts— new module for string detectionsrc/webviews/documentdb/collectionView/components/queryEditor/QueryEditor.tsx— smart-trigger, link handlersrc/webviews/components/MonacoEditor.tsx— ESC context precondition for suggest/snippet modesrc/webviews/components/MonacoAutoHeight.tsx— Tab respects snippet mode via addActionsrc/documentdb/ClusterSession.ts— removed schema reset on query changesrc/utils/json/data-api/autocomplete/toFieldCompletionItems.ts— bsonTypes/displayTypes threadingpackages/documentdb-constants/— standalone flag, int32 BSON type fix, operator overridessrc/webviews/documentdbQuery/tdd/— TDD behavior contract tests