add ast explorer (#460)

* add crude ast explorer

* tsc
This commit is contained in:
Kurt Hutten
2023-09-14 08:51:23 +10:00
committed by GitHub
parent 0466f04d82
commit e02bc76bdb
2 changed files with 186 additions and 0 deletions

View File

@ -0,0 +1,182 @@
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { useEffect, useRef, useState } from 'react'
import { useStore } from 'useStore'
export function AstExplorer() {
const { ast, setHighlightRange, selectionRanges } = useStore((s) => ({
ast: s.ast,
setHighlightRange: s.setHighlightRange,
selectionRanges: s.selectionRanges,
}))
const pathToNode = getNodePathFromSourceRange(
ast,
selectionRanges.codeBasedSelections?.[0]?.range
)
const node = getNodeFromPath(ast, pathToNode).node
const [filterKeys, setFilterKeys] = useState<string[]>(['start', 'end'])
return (
<div className="relative" style={{ width: '300px' }}>
<div className="">
filter out keys:<div className="w-2 inline-block"></div>
{['start', 'end', 'type'].map((key) => {
return (
<label key={key} className="inline-flex items-center">
<input
type="checkbox"
className="form-checkbox"
checked={filterKeys.includes(key)}
onChange={(e) => {
if (filterKeys.includes(key)) {
setFilterKeys(filterKeys.filter((k) => k !== key))
} else {
setFilterKeys([...filterKeys, key])
}
}}
/>
<span className="mr-2">{key}</span>
</label>
)
})}
</div>
<div
className="h-full relative"
onMouseLeave={(e) => {
setHighlightRange([0, 0])
}}
>
<pre className=" text-xs overflow-y-auto" style={{ width: '300px' }}>
<DisplayObj obj={ast} filterKeys={filterKeys} node={node} />
</pre>
</div>
</div>
)
}
function DisplayBody({
body,
filterKeys,
node,
}: {
body: { start: number; end: number; [key: string]: any }[]
filterKeys: string[]
node: any
}) {
return (
<>
{body.map((b, index) => {
return (
<div className="my-2" key={index}>
<DisplayObj obj={b} filterKeys={filterKeys} node={node} />
</div>
)
})}
</>
)
}
function DisplayObj({
obj,
filterKeys,
node,
}: {
obj: { start: number; end: number; [key: string]: any }
filterKeys: string[]
node: any
}) {
const { setHighlightRange, setCursor2 } = useStore((s) => ({
setHighlightRange: s.setHighlightRange,
setCursor2: s.setCursor2,
}))
const ref = useRef<HTMLPreElement>(null)
const [hasCursor, setHasCursor] = useState(false)
const [isCollapsed, setIsCollapsed] = useState(false)
useEffect(() => {
if (
node?.start === obj?.start &&
node?.end === obj?.end &&
node.type === obj?.type
) {
ref?.current?.scrollIntoView?.({ behavior: 'smooth', block: 'center' })
setHasCursor(true)
} else {
setHasCursor(false)
}
}, [node.start, node.end, node.type])
return (
<pre
ref={ref}
className={`ml-2 border-l border-violet-600 pl-1 ${
hasCursor ? 'bg-violet-100/25' : ''
}`}
onMouseEnter={(e) => {
setHighlightRange([obj?.start || 0, obj.end])
e.stopPropagation()
}}
onMouseMove={(e) => {
e.stopPropagation()
setHighlightRange([obj?.start || 0, obj.end])
}}
onClick={(e) => {
setCursor2({ type: 'default', range: [obj?.start || 0, obj.end || 0] })
e.stopPropagation()
}}
>
{isCollapsed ? (
<button
className="m-0 p-0 border-0"
onClick={() => setIsCollapsed(false)}
>
{'>'}type: {obj.type}
</button>
) : (
<span className="flex">
{/* <button className="m-0 p-0 border-0 mb-auto" onClick={() => setIsCollapsed(true)}>{'⬇️'}</button> */}
<ul className="inline-block">
{Object.entries(obj).map(([key, value]) => {
if (filterKeys.includes(key)) {
return null
} else if (Array.isArray(value)) {
return (
<li key={key}>
{`${key}: [`}
<DisplayBody
body={value}
filterKeys={filterKeys}
node={node}
/>
{']'}
</li>
)
} else if (
typeof value === 'object' &&
value !== null &&
value?.end
) {
return (
<li key={key}>
{key}:
<DisplayObj
obj={value}
filterKeys={filterKeys}
node={node}
/>
</li>
)
} else if (
typeof value === 'string' ||
typeof value === 'number'
) {
return (
<li key={key}>
{key}: {value}
</li>
)
}
})}
</ul>
</span>
)}
</pre>
)
}

View File

@ -6,6 +6,7 @@ import { useState } from 'react'
import { ActionButton } from '../components/ActionButton' import { ActionButton } from '../components/ActionButton'
import { faCheck } from '@fortawesome/free-solid-svg-icons' import { faCheck } from '@fortawesome/free-solid-svg-icons'
import { isReducedMotion } from 'lang/util' import { isReducedMotion } from 'lang/util'
import { AstExplorer } from './AstExplorer'
type SketchModeCmd = Extract< type SketchModeCmd = Extract<
Extract<EngineCommand, { type: 'modeling_cmd_req' }>['cmd'], Extract<EngineCommand, { type: 'modeling_cmd_req' }>['cmd'],
@ -94,6 +95,9 @@ export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
> >
Send sketch mode command Send sketch mode command
</ActionButton> </ActionButton>
<div style={{ height: '400px' }} className="overflow-y-auto">
<AstExplorer />
</div>
</section> </section>
</CollapsiblePanel> </CollapsiblePanel>
) )