2023-08-18 15:41:06 -04:00
let inFlightRequest = null ;
const inputElement = document . querySelector ( "#org-input" ) ;
const outputElement = document . querySelector ( "#parse-output" ) ;
2023-08-18 18:32:23 -04:00
const astTreeElement = document . querySelector ( "#ast-tree" ) ;
2023-08-18 15:41:06 -04:00
function abortableFetch ( request , options ) {
const controller = new AbortController ( ) ;
const signal = controller . signal ;
return {
abort : ( ) => controller . abort ( ) ,
ready : fetch ( request , { ... options , signal } )
} ;
}
2023-08-18 18:32:23 -04:00
function clearOutput ( ) {
2023-08-18 17:10:55 -04:00
outputElement . innerHTML = "" ;
2023-08-18 18:32:23 -04:00
astTreeElement . innerHTML = "" ;
}
function renderParseResponse ( response ) {
clearOutput ( ) ;
console . log ( response ) ;
renderSourceBox ( response ) ;
renderAstTree ( response ) ;
}
function renderSourceBox ( response ) {
2023-08-18 17:10:55 -04:00
const lines = response . input . split ( /\r?\n/ ) ;
const numLines = lines . length ;
const numDigits = Math . log10 ( numLines ) + 1 ;
2023-08-18 17:20:45 -04:00
outputElement . style . paddingLeft = ` calc( ${ numDigits + 1 } ch + 10px) ` ;
2023-08-18 17:10:55 -04:00
for ( let line of lines ) {
let wrappedLine = document . createElement ( "code" ) ;
2023-08-18 17:20:45 -04:00
wrappedLine . textContent = line ? line : "\n" ;
2023-08-18 17:10:55 -04:00
outputElement . appendChild ( wrappedLine ) ;
}
2023-08-18 15:41:06 -04:00
}
2023-08-18 18:32:23 -04:00
function renderAstTree ( response ) {
2023-08-18 19:20:23 -04:00
renderAstNode ( response . input , 0 , response . tree ) ;
2023-08-18 18:32:23 -04:00
}
2023-08-18 19:20:23 -04:00
function renderAstNode ( originalSource , depth , astNode ) {
const nodeElem = document . createElement ( "div" ) ;
nodeElem . classList . add ( "ast_node" ) ;
2023-08-18 18:32:23 -04:00
2023-08-18 19:20:23 -04:00
let sourceForNode = originalSource . slice ( astNode . position . start _character - 1 , astNode . position . end _character - 1 ) ;
2023-08-18 18:32:23 -04:00
// Since sourceForList is a string, JSON.stringify will escape with backslashes and wrap the text in quotation marks, ensuring that the string ends up on a single line. Coincidentally, this is the behavior we want.
2023-08-18 19:20:23 -04:00
let escapedSource = JSON . stringify ( sourceForNode ) ;
2023-08-18 18:32:23 -04:00
2023-08-18 19:20:23 -04:00
nodeElem . innerText = ` ${ astNode . name } : ${ escapedSource } ` ;
nodeElem . style . marginLeft = ` ${ depth * 20 } px ` ;
astTreeElement . appendChild ( nodeElem ) ;
for ( let child of astNode . children ) {
renderAstNode ( originalSource , depth + 1 , child ) ;
}
2023-08-18 18:32:23 -04:00
}
2023-08-18 15:41:06 -04:00
inputElement . addEventListener ( "input" , async ( ) => {
let orgSource = inputElement . value ;
if ( inFlightRequest != null ) {
inFlightRequest . abort ( ) ;
inFlightRequest = null ;
}
2023-08-18 18:32:23 -04:00
clearOutput ( ) ;
2023-08-18 15:41:06 -04:00
let newRequest = abortableFetch ( "/parse" , {
method : "POST" ,
cache : "no-cache" ,
body : orgSource ,
} ) ;
inFlightRequest = newRequest ;
2023-08-18 17:40:19 -04:00
let response = null ;
try {
response = await inFlightRequest . ready ;
}
catch ( err ) {
if ( err . name === "AbortError" ) return ;
}
2023-08-18 15:41:06 -04:00
renderParseResponse ( await response . json ( ) ) ;
} ) ;
2023-08-18 17:10:55 -04:00
function highlightLine ( htmlName , lineOffset ) {
const childOffset = lineOffset + 1 ;
const codeLineElement = document . querySelector ( ` . ${ htmlName } > code:nth-child( ${ childOffset } ) ` ) ;
codeLineElement ? . classList . add ( "highlighted" )
}
function unhighlightLine ( htmlName , lineOffset ) {
const childOffset = lineOffset + 1 ;
const codeLineElement = document . querySelector ( ` . ${ htmlName } > code:nth-child( ${ childOffset } ) ` ) ;
codeLineElement ? . classList . remove ( "highlighted" )
}