tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

syntax-probe.mjs (3451B)


      1 // Browser-only presentation checks: no evaluator/compiler substitution.
      2 const assert=(ok,message)=>{if(!ok)throw Error(message);};
      3 export async function syntaxProbe(){
      4  const source=document.getElementById('source-editor'), input=document.getElementById('expression-editor');
      5  const wrapper=input.closest('.syntax-editor'), mirror=wrapper.querySelector('pre');
      6  assert(source.closest('.syntax-editor').querySelector('.syntax-definition'),'Loaded source declarations are highlighted');
      7  const sourceMirror=source.closest('.syntax-editor').querySelector('pre');
      8  await new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r)));
      9  assert(Math.abs(sourceMirror.scrollHeight-source.scrollHeight)<=2,'Source mirror wrapping matches native editor: '+JSON.stringify({viewport:innerWidth,input:source.scrollHeight,mirror:sourceMirror.scrollHeight,width:source.clientWidth,mirrorWidth:sourceMirror.style.width,lineHeight:getComputedStyle(source).lineHeight}));
     10  assert(document.querySelector('#source .syntax-definition'),'Read-only source gets the same tokens');
     11  assert(wrapper.querySelector('[aria-hidden=true]')&&input.tagName==='TEXTAREA','Decorative mirror leaves native accessible textarea intact');
     12  const saved=input.value;
     13  const set=value=>{input.value=value;input.dispatchEvent(new Event('input'));};
     14  const caret=position=>{input.focus();input.setSelectionRange(position,position);input.dispatchEvent(new Event('select'));};
     15  set('let named x = [x (t)] in named "λ🙂["');
     16  assert(mirror.querySelector('.syntax-definition').textContent==='named','Local declaration color');
     17  assert(mirror.textContent===input.value,'Mirror preserves all source bytes');
     18  assert(getComputedStyle(mirror.querySelector('.syntax-definition')).color!==getComputedStyle(mirror.querySelector('.syntax-name')).color,'Definition names have a dedicated color');
     19  caret(input.value.indexOf('['));assert(mirror.querySelectorAll('.bracket-match').length===2,'Caret highlights bracket pair');
     20  caret(input.value.lastIndexOf('['));assert(!mirror.querySelector('.bracket-match'),'String brackets are ignored');
     21  input.setSelectionRange(0,4);input.dispatchEvent(new Event('select'));assert(!mirror.querySelector('.bracket-match'),'Range selections have no caret match');
     22  set('(t');caret(0);assert(mirror.querySelector('.bracket-unmatched'),'Unclosed delimiter marked without compiler interaction');
     23  set('<img src=x onerror="throw 1">');assert(!mirror.querySelector('img')&&mirror.textContent===input.value,'Source cannot become HTML');
     24  input.dispatchEvent(new CompositionEvent('compositionstart'));
     25  assert(wrapper.classList.contains('composing')&&getComputedStyle(wrapper.querySelector('.syntax-viewport')).visibility==='hidden','IME uses visible native text, not a duplicate mirror');
     26  set('"λ🙂"');input.dispatchEvent(new CompositionEvent('compositionend'));assert(!wrapper.classList.contains('composing'),'IME restores highlighting');
     27  set(Array(80).fill('"λ🙂" t (t t) '+ 'longIdentifier '.repeat(12)).join('\n')+'\n');
     28  await new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r)));
     29  assert(Math.abs(mirror.scrollHeight-input.scrollHeight)<=2,'Wrapping and trailing-line heights match native textarea');
     30  input.scrollTop=300;input.dispatchEvent(new Event('scroll'));
     31  assert(mirror.style.transform.includes('-300px'),'Scrolling keeps decoration aligned');
     32  input.blur();set(saved);input.scrollTop=0;input.dispatchEvent(new Event('scroll'));
     33 }