Editor: field hints show the column's raw data type, not the encoding type

This commit is contained in:
2026-06-29 11:06:37 +03:00
parent 8aa05e503d
commit 01df58acf1
+14 -10
View File
@@ -10,8 +10,11 @@
* - **Hover** — a column's inferred type + cardinality/range (from the stored
* profile); over a `calculate`/`filter`/`expr` string, a live validity check
* via core/expr-validate. Monaco merges these with the schema's own hovers.
* - **Inlay hints** — a faint `: <type>` beside each `field`, annotation without
* touching the text.
* - **Inlay hints** — a faint `·<data-type>` beside each `field` (the column's
* raw type: number/string/date/boolean), annotation without touching the text.
* Deliberately the *data* type, not the encoding `type` — they share the line,
* so a `: quantitative` here would read as annotating the adjacent `"type"`,
* which the hint never describes.
*
* All three read the active draft's bound dataset (services/active-dataset) and
* the cursor's JSON context (core/spec-cursor) at provide-time via `getState()` —
@@ -22,10 +25,8 @@
*/
import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main';
import { defaultFieldType } from '@core/chart-builder';
import { validateExpression } from '@core/expr-validate';
import { stringValueAtOffset, valueKeyAtOffset } from '@core/spec-cursor';
import type { ColumnType } from '@core/type-inference';
import { useSnippetStore } from '../stores/SnippetStore';
import {
availableFieldsAt,
@@ -40,15 +41,14 @@ const FIELD_KEYS = new Set(['field', 'groupby']);
/** Property values that hold a Vega expression (where the validity check fires). */
const EXPR_KEYS = new Set(['calculate', 'filter', 'expr']);
/** A short type label for a source field; null type (derived) is shown elsewhere. */
const typeLabel = (type: ColumnType): string => defaultFieldType(type);
/** Markdown hover for a field hint: type + stats for source, a note for derived. */
function fieldHoverContents(hint: FieldHint, info: DataInfo): { value: string }[] {
if (hint.derived || hint.type === null) {
return [{ value: `**${hint.name}** · _derived by a transform_` }];
}
const lines = [`**${hint.name}** · \`${typeLabel(hint.type)}\``];
// The column's raw data type (number/string/date/boolean) — same vocabulary as
// the inlay hint, not the Vega-Lite encoding `type` the user declares.
const lines = [`**${hint.name}** · \`${hint.type}\``];
const stat = info.columnStats.find((s) => s.name === hint.name);
if (stat) {
if (stat.numericExtent)
@@ -91,7 +91,7 @@ export function configureSpecDatasetHints(): void {
kind: f.derived
? monaco.languages.CompletionItemKind.Variable
: monaco.languages.CompletionItemKind.Field,
detail: f.derived || f.type === null ? 'derived field' : typeLabel(f.type),
detail: f.derived || f.type === null ? 'derived field' : f.type,
insertText: f.name,
range,
})),
@@ -167,7 +167,11 @@ export function configureSpecDatasetHints(): void {
if (!type) continue; // unknown or derived (no type to annotate)
hints.push({
position: { lineNumber: line, column: match.index + match[0].length + 1 },
label: `: ${typeLabel(type)}`,
// The column's raw data type (number/string/date/boolean), not the
// Vega-Lite encoding `type`: a `: quantitative` here would read as an
// annotation of the adjacent `"type"`, which the hint never describes.
// The leading `·` marks it as a data fact about the field, not JSON syntax.
label: `·${type}`,
kind: monaco.languages.InlayHintKind.Type,
paddingLeft: true,
});