import React, { ReactNode, useEffect, useMemo, useRef, useState } from "react"; import "./Explorer.css"; import { Highlight } from "./highlight"; import { buildShadow } from "./shadow"; import OrgAst, { OrgNodeReference } from "./OrgAst"; import { parse_org } from "../../organic/target/wasm32-unknown-unknown/js/wasm"; const default_org_source: string = `* Welcome to the Organic Ast Explorer! Type your Org [fn:1] source in this text box, and it will be parsed by Organic [fn:2] that has been compiled into wasm and embedded in this page. The resulting AST will be rendered to the right. In the AST on the right, you can: 1. Click on an AST node to highlight the corresponding portion of the Org source on the left. 2. Expand/Collapse the children, properties, and standard properties. * Footnotes [fn:1] https://orgmode.org/ [fn:2] https://code.fizz.buzz/talexander/organic `; function Explorer({ defaultValue = default_org_source }) { function handleChange(event: React.ChangeEvent) { setValue(event.target.value); clearHighlights(); } const [value, setValue] = useState(defaultValue); const [highlights, setHighlights] = useState>([]); const astTree = useMemo(() => { const astTree = parse_org(value); console.log(JSON.stringify(astTree)); return astTree; }, [value]); function setHighlight(nodes: OrgNodeReference[]) { let new_highlights = nodes.map((node: OrgNodeReference) => { return new Highlight(node.start - 1, node.end - 1); }); new_highlights.sort(function (a, b) { if (a.start < b.start) return -1; if (a.start > b.start) return 1; return 0; }); setHighlights(new_highlights); } function addHighlight(start: number, end: number) { let new_highlights = [...highlights, new Highlight(start, end)]; new_highlights.sort(function (a, b) { if (a.start < b.start) return -1; if (a.start > b.start) return 1; return 0; }); setHighlights(new_highlights); } function clearHighlights() { setHighlights([]); } const textAreaRef = useRef(null); const shadowRef = useRef(null); function onTextAreaScroll() { if (shadowRef.current !== null && textAreaRef.current !== null) { const textAreaScrollTop = textAreaRef.current.scrollTop; shadowRef.current.scrollTop = textAreaScrollTop; } } useEffect(() => { // Make sure the text area and shadow div start out in sync. onTextAreaScroll(); }, []); return (