diff --git a/src/app/components/generator/McdocHelpers.ts b/src/app/components/generator/McdocHelpers.ts index 7fefb8ac..436343ae 100644 --- a/src/app/components/generator/McdocHelpers.ts +++ b/src/app/components/generator/McdocHelpers.ts @@ -2,7 +2,7 @@ import * as core from '@spyglassmc/core' import type { JsonNode, JsonPairNode } from '@spyglassmc/json' import { JsonArrayNode, JsonObjectNode, JsonStringNode } from '@spyglassmc/json' import { JsonStringOptions } from '@spyglassmc/json/lib/parser/string.js' -import type { ListType, McdocType, PrimitiveArrayType, UnionType } from '@spyglassmc/mcdoc' +import type { ListType, McdocType, NumericRange, NumericType, PrimitiveArrayType, TupleType, UnionType } from '@spyglassmc/mcdoc' import type { McdocCheckerContext, SimplifiedMcdocType, SimplifiedMcdocTypeNoUnion, SimplifyValueNode } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { simplify } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' @@ -13,7 +13,7 @@ export function getDefault(type: SimplifiedMcdocType, range: core.Range, ctx: co if (type.kind === 'boolean') { return { type: 'json:boolean', range, value: false } } - if (type.kind === 'byte' || type.kind === 'short' || type.kind === 'int' || type.kind === 'long' || type.kind === 'float' || type.kind === 'double') { + if (isNumericType(type)) { const value: core.LongNode = { type: 'long', range, value: BigInt(0) } return { type: 'json:number', range, value, children: [value] } } @@ -46,7 +46,7 @@ export function getDefault(type: SimplifiedMcdocType, range: core.Range, ctx: co } return object } - if (type.kind === 'list' || type.kind === 'byte_array' || type.kind === 'int_array' || type.kind === 'long_array') { + if (isListOrArray(type)) { const array = JsonArrayNode.mock(range) const minLength = type.lengthRange?.min ?? 0 if (minLength > 0) { @@ -106,6 +106,14 @@ export function getDefault(type: SimplifiedMcdocType, range: core.Range, ctx: co return { type: 'json:null', range } } +export function isNumericType(type: McdocType): type is NumericType { + return type.kind === 'byte' || type.kind === 'short' || type.kind === 'int' || type.kind === 'long' || type.kind === 'float' || type.kind === 'double' +} + +export function isListOrArray(type: McdocType): type is ListType | PrimitiveArrayType { + return type.kind === 'list' || type.kind === 'byte_array' || type.kind === 'int_array' || type.kind === 'long_array' +} + export function getItemType(type: ListType | PrimitiveArrayType): McdocType { return type.kind === 'list' ? type.item : type.kind === 'byte_array' ? { kind: 'byte' } @@ -114,6 +122,14 @@ export function getItemType(type: ListType | PrimitiveArrayType): McdocType { : { kind: 'any' } } +export function isFixedList(type: T): type is T & { lengthRange: NumericRange } { + return type.lengthRange?.min !== undefined && type.lengthRange.min === type.lengthRange.max +} + +export function isInlineTuple(type: TupleType) { + return type.items.length <= 4 && type.items.every(isNumericType) +} + export function formatIdentifier(id: string): string { if (id.startsWith('!')) { return '! ' + formatIdentifier(id.substring(1)) diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index 7502a9e0..1011c707 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -15,7 +15,7 @@ import { useFocus } from '../../hooks/useFocus.js' import { generateColor, hexId, randomInt, randomSeed } from '../../Utils.js' import { ItemDisplay } from '../ItemDisplay.jsx' import { Octicon } from '../Octicon.jsx' -import { formatIdentifier, getCategory, getDefault, getItemType, isSelectRegistry, quickEqualTypes, simplifyType } from './McdocHelpers.js' +import { formatIdentifier, getCategory, getDefault, getItemType, isFixedList, isInlineTuple, isListOrArray, isNumericType, isSelectRegistry, quickEqualTypes, simplifyType } from './McdocHelpers.js' export interface McdocContext extends core.CheckerContext {} @@ -52,7 +52,7 @@ function Head({ type, optional, node, makeEdit, ctx }: Props) { if (type.kind === 'enum') { return } - if (type.kind === 'byte' || type.kind === 'short' || type.kind === 'int' || type.kind === 'long' || type.kind === 'float' || type.kind === 'double') { + if (isNumericType(type)) { return } if (type.kind === 'boolean') { @@ -64,8 +64,8 @@ function Head({ type, optional, node, makeEdit, ctx }: Props) { if (type.kind === 'struct') { return } - if (type.kind === 'list' || type.kind === 'byte_array' || type.kind === 'int_array' || type.kind === 'long_array') { - if (type.lengthRange?.min !== undefined && type.lengthRange.min === type.lengthRange.max) { + if (isListOrArray(type)) { + if (isFixedList(type)) { return getItemType(type)), attributes: type.attributes }} optional={optional} node={node} makeEdit={makeEdit} ctx={ctx} /> } return @@ -94,13 +94,17 @@ function Body({ type, optional, node, makeEdit, ctx }: Props } - if (type.kind === 'list' || type.kind === 'byte_array' || type.kind === 'int_array' || type.kind === 'long_array') { + if (isListOrArray(type)) { if (!JsonArrayNode.is(node)) { return <> } - if (type.lengthRange?.min !== undefined && type.lengthRange.min === type.lengthRange.max) { + if (isFixedList(type)) { + const tupleType: TupleType = { kind: 'tuple', items: [...Array(type.lengthRange.min)].map(() => getItemType(type)), attributes: type.attributes } + if (isInlineTuple(tupleType)) { + return <> + } return
- getItemType(type)), attributes: type.attributes }} node={node} makeEdit={makeEdit} ctx={ctx} /> +
} if (node.children?.length === 0) { @@ -111,6 +115,9 @@ function Body({ type, optional, node, makeEdit, ctx }: Props } if (type.kind === 'tuple') { + if (isInlineTuple(type)) { + return <> + } return
@@ -661,7 +668,8 @@ function ListBody({ type: outerType, node, makeEdit, ctx }: Props } - const type = (node.typeDef?.kind === 'list' || node.typeDef?.kind === 'byte_array' || node.typeDef?.kind === 'int_array' || node.typeDef?.kind === 'long_array') ? node.typeDef : outerType + + const type = node.typeDef && isListOrArray(node.typeDef) ? node.typeDef : outerType const canAdd = (type.lengthRange?.max ?? Infinity) > (node?.children?.length ?? 0) const onRemoveItem = useCallback((index: number) => { @@ -774,6 +782,8 @@ function ListBody({ type: outerType, node, makeEdit, ctx }: Props) { const { locale } = useLocale() + const isInline = isInlineTuple(type) + const onRemove = useCallback(() => { makeEdit(() => { return undefined @@ -786,22 +796,42 @@ function TupleHead({ type, optional, node, makeEdit, ctx }: Props) { }) }, [type, ctx]) - if (optional) { - if (node && JsonArrayNode.is(node)) { - return - } else { - return - } - } else { - if (!node || !JsonArrayNode.is(node)) { - return - } - return <> - } + return <> + {optional + ? (JsonArrayNode.is(node) + ? + : ) + : (!JsonArrayNode.is(node) + ? + : <> + )} + {isInline && JsonArrayNode.is(node) && type.items.map((itemType, index) => { + const item = node?.children?.[index] + const child = item?.value + const childType = simplifyType(itemType, ctx) + const makeItemEdit: MakeEdit = (edit) => { + makeEdit((range) => { + const newChild = edit(child?.range ?? node?.range ?? range) + if (newChild === undefined) { + return node + } + node.children[index] = { + type: 'item', + range: newChild.range, + value: newChild, + } + return node + }) + } + return + })} + } function TupleBody({ type, node, makeEdit, ctx }: Props) {