) {
const { locale } = useLocale()
const isInline = isInlineTuple(type)
const onRemove = useCallback(() => {
ctx.makeEdit(() => {
return undefined
})
}, [ctx])
const onSetDefault = useCallback(() => {
ctx.makeEdit((range) => {
return getDefault(type, range, ctx)
})
}, [type, ctx])
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)
return
})}
>
}
interface TupleHeadItemProps extends Props {
child: JsonNode | undefined
childType: SimplifiedMcdocType
index: number
node: JsonArrayNode
}
function TupleHeadItem({ child, childType, index, node, ctx }: TupleHeadItemProps) {
const makeItemEdit = useCallback((edit) => {
ctx.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
})
}, [index, node, ctx])
const itemCtx = useMemo(() => {
return { ...ctx, makeEdit: makeItemEdit }
}, [ctx, makeItemEdit])
return
}
function TupleBody({ type, node, ctx }: Props) {
if (!JsonArrayNode.is(node)) {
return <>>
}
return <>
{type.items.map((itemType, index) => {
const item = node?.children?.[index]
const child = item?.value
const childType = simplifyType(itemType, ctx)
return
})}
>
}
interface TupleBodyItemProps extends Props {
child: JsonNode | undefined
childType: SimplifiedMcdocType
index: number
node: JsonArrayNode
}
function TupleBodyItem({ child, childType, index, node, ctx }: TupleBodyItemProps) {
const makeItemEdit = useCallback((edit) => {
ctx.makeEdit(() => {
const newChild = edit(child?.range ?? node.range)
if (newChild === undefined) {
return node
}
node.children[index] = {
type: 'item',
range: newChild.range,
value: newChild,
}
return node
})
}, [index, node, ctx])
const itemCtx = useMemo(() => {
return { ...ctx, makeEdit: makeItemEdit }
}, [ctx, makeItemEdit])
return
}
function LiteralHead({ type, optional, node, ctx }: Props) {
return
}
const ANY_TYPES: SimplifiedMcdocType[] = [
{ kind: 'boolean' },
{ kind: 'double' },
{ kind: 'string' },
{ kind: 'list', item: { kind: 'any' } },
{ kind: 'struct', fields: [ { kind: 'pair', key: { kind: 'string' }, type: { kind: 'any' } }] },
]
function AnyHead({ optional, node, ctx }: Props) {
const { locale } = useLocale()
const selectedType = selectAnyType(node)
const onSelect = useCallback((newValue: string) => {
ctx.makeEdit((range) => {
const newSelected = ANY_TYPES.find(t => t.kind === newValue)
if (!newSelected) {
return undefined
}
return getDefault(newSelected, range, ctx)
})
}, [ctx])
return <>
{selectedType && }
>
}
function AnyBody({ optional, node, ctx }: Props) {
const selectedType = selectAnyType(node)
if (!selectedType) {
return <>>
}
return
}
function selectAnyType(node: JsonNode | undefined) {
switch (node?.type) {
case 'json:boolean': return ANY_TYPES[0]
case 'json:number': return ANY_TYPES[1]
case 'json:string': return ANY_TYPES[2]
case 'json:array': return ANY_TYPES[3]
case 'json:object': return ANY_TYPES[4]
default: return undefined
}
}
interface KeyProps {
label: string | number | boolean
doc?: string
raw?: boolean
}
function Key({ label, doc, raw }: KeyProps) {
const [shown, setShown] = useFocus()
const cleanDoc = useMemo(() => {
if (!doc) {
return doc
}
return DOMPurify.sanitize(marked(doc), { FORBID_ATTR: ['style'] })
}, [doc])
return
}
interface ErrorsProps {
type: SimplifiedMcdocType
node: core.AstNode | undefined
ctx: McdocContext
}
function Errors({ type, node, ctx }: ErrorsProps) {
const errors = useMemo(() => {
if (node === undefined) {
return []
}
const errors = ctx.err.errors
// Get all errors inside the current node
.filter(e => core.Range.containsRange(node.range, e.range, true))
// Unless they are inside a child node
.filter(e => !node.children?.some(c => (c.type === 'item' || c.type === 'pair') && core.Range.containsRange(c.range, e.range, true)))
// Filter out "Missing key" errors
.filter(e => !(core.Range.length(e.range) === 1 && (type.kind === 'struct' || (type.kind === 'union' && JsonNode.is(node) && (selectUnionMember(type, node) ?? type.members[0]).kind === 'struct'))))
// Hide warnings if there are errors
return errors.find(e => e.severity === 3)
? errors.filter(e => e.severity === 3)
: errors
}, [type, node, ctx])
return <>
{errors.map(e => )}
>
}
interface ErrorIndicatorProps {
error: core.LanguageError
}
function ErrorIndicator({ error }: ErrorIndicatorProps) {
const [active, setActive] = useFocus()
return setActive()}>
{Octicon.issue_opened}
}
function useToggles() {
const [toggleState, setToggleState] = useState(new Map())
const [toggleAll, setToggleAll] = useState(undefined)
const expand = useCallback((key: string) => (evt: MouseEvent) => {
if (evt.ctrlKey) {
setToggleState(new Map())
setToggleAll(true)
} else {
setToggleState(state => new Map(state.set(key, true)))
}
}, [])
const collapse = useCallback((key: string) => (evt: MouseEvent) => {
if (evt.ctrlKey) {
setToggleState(new Map())
setToggleAll(false)
} else {
setToggleState(state => new Map(state.set(key, false)))
}
}, [])
const isToggled = useCallback((key: string) => {
if (!(toggleState instanceof Map)) return false
return toggleState.get(key) ?? toggleAll
}, [toggleState, toggleAll])
return { expand, collapse, isToggled }
}