From c6de242323db96dbe58127b8ef7104080a7e5f16 Mon Sep 17 00:00:00 2001 From: Amrita mishra Date: Tue, 24 Feb 2026 19:36:24 +0530 Subject: [PATCH 1/4] Frontend prototype: clickable AST/ASR node navigation --- components/Editor.js | 56 ++++++++++++++++++++++++++++++-------- components/LoadLFortran.js | 13 ++++----- components/ResultBox.js | 31 ++++++++++++++++----- components/TextBox.js | 19 +++++++------ pages/index.js | 47 ++++++++++++++++++++++++++++---- 5 files changed, 125 insertions(+), 41 deletions(-) diff --git a/components/Editor.js b/components/Editor.js index 17c8508..b9dd0d4 100644 --- a/components/Editor.js +++ b/components/Editor.js @@ -1,32 +1,66 @@ -import React from "react"; +import React, { useRef, useEffect } from "react"; import AceEditor from "react-ace"; -import "ace-builds/src-noconflict/mode-java"; +// Import Ace modes and themes import "ace-builds/src-noconflict/mode-fortran"; -import "ace-builds/src-noconflict/theme-github"; import "ace-builds/src-noconflict/theme-monokai"; import "ace-builds/src-noconflict/ext-language_tools"; -function Editor({sourceCode, setSourceCode}) { +const Editor = ({ sourceCode, setSourceCode, editorRef }) => { + const aceEditorRef = useRef(null); + + // This useEffect manually attaches the jump function to the ref + // This bypasses the {retry: f} issue caused by Next.js dynamic imports + // Add this inside the Editor component function + useEffect(() => { + if (editorRef) { + // This forcefully overwrites the {retry: f} object + editorRef.current = { + jumpToLine(line) { + if (aceEditorRef.current) { + const editor = aceEditorRef.current.editor; + + // 1. Move cursor and scroll (Ace is 1-based) + editor.gotoLine(line, 0, true); + + // 2. Visually highlight by selecting the line + editor.selection.moveCursorTo(line - 1, 0); + editor.selection.selectLine(); + + // 3. Focus the editor to show the highlight clearly + editor.focus(); + } + } + }; + } + }, [editorRef]); + return ( setSourceCode(code)} - name="UNIQUE_ID_OF_DIV" + onChange={(code) => setSourceCode(code)} + name="LFORTRAN_EDITOR" editorProps={{ $blockScrolling: true }} value={sourceCode} width="100%" height="100%" showPrintMargin={false} + setOptions={{ + enableBasicAutocompletion: true, + enableLiveAutocompletion: true, + enableSnippets: true, + showLineNumbers: true, + tabSize: 4, + }} style={{ fontFamily: '"Fira code", "Fira Mono", monospace', fontSize: 14, - // minHeight: "100%", - // border: "0.1px solid black" }} /> - ) -} + ); +}; + -export default Editor; +export default Editor; \ No newline at end of file diff --git a/components/LoadLFortran.js b/components/LoadLFortran.js index b82e2c7..f79736b 100644 --- a/components/LoadLFortran.js +++ b/components/LoadLFortran.js @@ -90,27 +90,27 @@ async function setup_lfortran_funcs(lfortran_funcs, myPrint) { lfortran_funcs.emit_ast_from_source = function (source_code) { try { return compiler_funcs.emit_ast_from_source(source_code); } - catch (e) { console.log(e); myPrint(e + "\nERROR: AST could not be generated from the code"); return 0; } + catch (e) { myPrint(e + "\nERROR: AST could not be generated from the code"); return 0; } } lfortran_funcs.emit_asr_from_source = function (source_code) { try { return compiler_funcs.emit_asr_from_source(source_code); } - catch (e) { console.log(e); myPrint(e + "\nERROR: ASR could not be generated from the code"); return 0; } + catch (e) { myPrint(e + "\nERROR: ASR could not be generated from the code"); return 0; } } lfortran_funcs.emit_wat_from_source = function (source_code) { try { return compiler_funcs.emit_wat_from_source(source_code); } - catch (e) { console.log(e); myPrint(e + "\nERROR: WAT could not be generated from the code"); return 0; } + catch (e) {myPrint(e + "\nERROR: WAT could not be generated from the code"); return 0; } } lfortran_funcs.emit_cpp_from_source = function (source_code) { try { return compiler_funcs.emit_cpp_from_source(source_code); } - catch (e) { console.log(e); myPrint(e + "\nERROR: CPP could not be generated from the code"); return 0; } + catch (e) { myPrint(e + "\nERROR: CPP could not be generated from the code"); return 0; } } lfortran_funcs.emit_py_from_source = function (source_code) { try { return compiler_funcs.emit_py_from_source(source_code); } - catch (e) { console.log(e); myPrint(e + "\nERROR: LLVM could not be generated from the code"); return 0; } + catch (e) { myPrint(e + "\nERROR: LLVM could not be generated from the code"); return 0; } } lfortran_funcs.compile_code = function (source_code) { @@ -118,7 +118,6 @@ async function setup_lfortran_funcs(lfortran_funcs, myPrint) { return compiler_funcs.emit_wasm_from_source(source_code); } catch (e) { - console.log(e); myPrint(e + "\nERROR: The code could not be compiled. Either there is a compile-time error or there is an issue at our end.") return 0; } @@ -150,7 +149,6 @@ async function setup_lfortran_funcs(lfortran_funcs, myPrint) { if (exit_code.val == 0) { return; } - console.log(err_msg); stdout_print(`\n${err_msg}\nERROR: The code could not be executed. Either there is a runtime error or there is an issue at our end.`); } }; @@ -177,7 +175,6 @@ function LoadLFortran({ await setup_lfortran_funcs(lfortran_funcs, myPrint); setModuleReady(true); openNotification("LFortran Module Initialized!", "bottomRight"); - console.log("LFortran Module Initialized!"); }, [moduleReady]); // update the callback if the state changes return ( diff --git a/components/ResultBox.js b/components/ResultBox.js index e40a24b..42c2f76 100644 --- a/components/ResultBox.js +++ b/components/ResultBox.js @@ -4,21 +4,36 @@ import { Button } from "antd"; import { CopyOutlined } from "@ant-design/icons"; import { Segmented } from "antd"; -function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotification }) { +// Added onNodeClick to props +function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotification, onNodeClick }) { const isMobile = useIsMobile(); const [showCopy, setShowCopy] = useState(false); + + // Step 1: Handle clicks on the AST/ASR nodes + const handleOutputClick = (e) => { + // 1. Get the attribute and the text of the element that was actually clicked + const line = e.target.getAttribute("data-line"); + const clickedText = e.target.innerText; + + if (line && onNodeClick) { + onNodeClick(parseInt(line)); + } else if (!line) { + } + }; + function copyTextToClipboard(e) { - e.stopPropagation(); // Prevents the click from reaching the container below + e.stopPropagation(); const parser = new DOMParser(); const doc = parser.parseFromString(output, 'text/html'); navigator.clipboard.writeText(doc.documentElement.textContent); openNotification(`${activeTab} output copied`, "bottomRight"); - setShowCopy(false); // Hide the icon after the user copies the text + setShowCopy(false); } + return (
isMobile && setShowCopy(!showCopy)} // Only toggle if on mobile + onClick={() => isMobile && setShowCopy(!showCopy)} > @@ -44,6 +59,8 @@ function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotif
                 
@@ -52,4 +69,4 @@ function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotif
); } -export default ResultBox; +export default ResultBox; \ No newline at end of file diff --git a/components/TextBox.js b/components/TextBox.js index 6e37cdf..170eacf 100644 --- a/components/TextBox.js +++ b/components/TextBox.js @@ -1,19 +1,21 @@ import { Button, Tabs, Dropdown, Menu, Space } from "antd"; const { TabPane } = Tabs; -import { DownOutlined, PlayCircleOutlined,FullscreenOutlined,FullscreenExitOutlined} from "@ant-design/icons"; +import { DownOutlined, PlayCircleOutlined, FullscreenOutlined, FullscreenExitOutlined } from "@ant-design/icons"; import { useIsMobile } from "./useIsMobile"; import React, { useState, useEffect } from 'react'; import preinstalled_programs from "../utils/preinstalled_programs"; import dynamic from 'next/dynamic' -const Editor = dynamic(import('./Editor'), { - ssr: false -}) -function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleName, activeTab, handleUserTabChange, myHeight }) { +// 1. Keep your original dynamic import but rename it to DynamicEditor +const Editor = dynamic(() => import('./Editor'), { + ssr: false, +}); + +// Added editorRef to the destructured props +function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleName, activeTab, handleUserTabChange, myHeight, editorRef }) { const isMobile = useIsMobile(); const [isFullScreen, setIsFullScreen] = useState(false); - // Listen for fullscreen changes (Esc key, browser buttons, etc.) useEffect(() => { const handler = () => setIsFullScreen(!!document.fullscreenElement); document.addEventListener("fullscreenchange", handler); @@ -49,7 +51,6 @@ function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleN }); } - const examples_menu = (); const extraOperations = { right: ( @@ -57,7 +58,6 @@ function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleN onClick={handleFullScreen} icon={isFullScreen ? : } > - {/* Now both text options only show on desktop, keeping mobile clean */} {!isMobile && (isFullScreen ? " Exit Fullscreen" : " Fullscreen")}
), @@ -106,4 +107,4 @@ function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleN ); } -export default TextBox; +export default TextBox; \ No newline at end of file diff --git a/pages/index.js b/pages/index.js index 2781033..9eed51e 100644 --- a/pages/index.js +++ b/pages/index.js @@ -4,7 +4,7 @@ import LoadLFortran from "../components/LoadLFortran"; import preinstalled_programs from "../utils/preinstalled_programs"; import { useIsMobile } from "../components/useIsMobile"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; // Added useRef import { Col, Row, Spin } from "antd"; import { notification } from "antd"; import { LoadingOutlined } from "@ant-design/icons"; @@ -46,6 +46,10 @@ export default function Home() { const [activeTab, setActiveTab] = useState("STDOUT"); const [output, setOutput] = useState(""); const [dataFetch, setDataFetch] = useState(false); + + // Step 1: Initialize the Ref for the Editor + const editorRef = useRef(null); + const isMobile = useIsMobile(); const myHeight = ((!isMobile) ? "calc(100vh - 170px)" : "calc(50vh - 85px)"); @@ -61,6 +65,15 @@ export default function Home() { } }, [moduleReady, dataFetch]); + // Step 2: Jump Handler to be passed to ResultBox + const jumpToEditorLine = (line) => { + + // Use jumpToLine (the API we exposed in Editor.js) + if (editorRef.current && typeof editorRef.current.jumpToLine === 'function') { + editorRef.current.jumpToLine(line); + } + }; + async function fetchData() { const url = window.location.search; const gist = "https://gist.githubusercontent.com/"; @@ -109,7 +122,6 @@ export default function Home() { if (wasm_bytes_response) { const [exit_code, ...compile_result] = wasm_bytes_response.split(","); if (exit_code !== "0") { - // print compile-time error found by lfortran to output setOutput(ansi_up.ansi_to_html(compile_result) + `\nCompilation Time: ${duration_compile} ms`); } else { @@ -124,12 +136,32 @@ export default function Home() { } else if (key == "AST") { const res = lfortran_funcs.emit_ast_from_source(sourceCode); if (res) { - setOutput(ansi_up.ansi_to_html(res)); + // 1. Convert to HTML first so tags aren't escaped + let htmlOutput = ansi_up.ansi_to_html(res); + + // 2. Inject the span into the HTML string + let finalOutput = htmlOutput.replace("Declaration", + `Declaration` + ); + finalOutput = finalOutput.replace("Subroutine", + `Subroutine` + ); + setOutput(finalOutput); } } else if (key == "ASR") { const res = lfortran_funcs.emit_asr_from_source(sourceCode); if (res) { - setOutput(ansi_up.ansi_to_html(res)); + // 1. Convert to HTML first + let htmlOutput = ansi_up.ansi_to_html(res); + + // 2. Inject the span (Replacing "Declaration" as it's a common top-level node) + let finalOutput = htmlOutput.replace("Declaration", + `Declaration` + ); + finalOutput = finalOutput.replace("Subroutine", + `Subroutine` + ); + setOutput(finalOutput); } } else if (key == "WAT") { const res = lfortran_funcs.emit_wat_from_source(sourceCode); @@ -144,7 +176,6 @@ export default function Home() { } else if (key == "PY") { setOutput("Support for PY is not yet enabled"); } else { - console.log("Unknown key:", key); setOutput("Unknown key: " + key); } setActiveTab(key); @@ -171,6 +202,8 @@ export default function Home() { activeTab={activeTab} handleUserTabChange={handleUserTabChange} myHeight={myHeight} + // Step 4: Pass the editorRef to TextBox + editorRef={editorRef} > @@ -181,6 +214,8 @@ export default function Home() { handleUserTabChange={handleUserTabChange} myHeight={myHeight} openNotification={openNotification} + // Step 5: Pass the jump handler to ResultBox + onNodeClick={jumpToEditorLine} > ) : (
@@ -198,4 +233,4 @@ export default function Home() { ); -} +} \ No newline at end of file From 9540c83923c654de3e68f91b7235bc19a9137c58 Mon Sep 17 00:00:00 2001 From: Amrita mishra Date: Tue, 24 Feb 2026 23:50:58 +0530 Subject: [PATCH 2/4] Refine precise span highlighting using Ace Range API --- components/Editor.js | 40 +++++++++++++++++++++++----------- components/ResultBox.js | 26 +++++++++++++--------- components/TextBox.js | 4 +--- pages/index.js | 48 ++++++++++++++++++++++++++++------------- 4 files changed, 77 insertions(+), 41 deletions(-) diff --git a/components/Editor.js b/components/Editor.js index b9dd0d4..7fda32e 100644 --- a/components/Editor.js +++ b/components/Editor.js @@ -9,26 +9,40 @@ import "ace-builds/src-noconflict/ext-language_tools"; const Editor = ({ sourceCode, setSourceCode, editorRef }) => { const aceEditorRef = useRef(null); - // This useEffect manually attaches the jump function to the ref - // This bypasses the {retry: f} issue caused by Next.js dynamic imports - // Add this inside the Editor component function useEffect(() => { if (editorRef) { - // This forcefully overwrites the {retry: f} object + editorRef.current = { - jumpToLine(line) { + jumpToRange(rangeData) { + if (aceEditorRef.current) { const editor = aceEditorRef.current.editor; - - // 1. Move cursor and scroll (Ace is 1-based) - editor.gotoLine(line, 0, true); + const ace = window.ace; + + if (ace) { + const Range = ace.require("ace/range").Range; + + const startRow = rangeData.startLine - 1; + const startCol = rangeData.startCol - 1; + const endRow = rangeData.endLine - 1; + const endCol = rangeData.endCol - 1; + + // 1. Force scroll to the line first + editor.scrollToLine(rangeData.startLine, true, true); + + // 2. Clear any existing cursor or selection to prevent conflicts + editor.selection.clearSelection(); - // 2. Visually highlight by selecting the line - editor.selection.moveCursorTo(line - 1, 0); - editor.selection.selectLine(); + // 3. Create and apply the precise range + const newRange = new Range(startRow, startCol, endRow, endCol); + editor.selection.setRange(newRange); - // 3. Focus the editor to show the highlight clearly - editor.focus(); + // 4. Force focus and center the view on the highlight + editor.focus(); + // Optional: helps if the code block is very long + editor.renderer.scrollSelectionIntoView(); + } else { + } } } }; diff --git a/components/ResultBox.js b/components/ResultBox.js index 42c2f76..cf72fae 100644 --- a/components/ResultBox.js +++ b/components/ResultBox.js @@ -4,21 +4,27 @@ import { Button } from "antd"; import { CopyOutlined } from "@ant-design/icons"; import { Segmented } from "antd"; -// Added onNodeClick to props +// onNodeClick to props function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotification, onNodeClick }) { const isMobile = useIsMobile(); const [showCopy, setShowCopy] = useState(false); - // Step 1: Handle clicks on the AST/ASR nodes const handleOutputClick = (e) => { - // 1. Get the attribute and the text of the element that was actually clicked - const line = e.target.getAttribute("data-line"); - const clickedText = e.target.innerText; + // .closest to handle nested spans + const target = e.target.closest("[data-start-line]"); + + if (target) { + const rangeData = { + startLine: parseInt(target.getAttribute("data-start-line")), + startCol: parseInt(target.getAttribute("data-start-col")), + endLine: parseInt(target.getAttribute("data-end-line")), + endCol: parseInt(target.getAttribute("data-end-col")) + }; - if (line && onNodeClick) { - onNodeClick(parseInt(line)); - } else if (!line) { - } + if (onNodeClick) { + onNodeClick(rangeData); + } + } }; function copyTextToClipboard(e) { @@ -59,7 +65,7 @@ function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotif
                 
import('./Editor'), { ssr: false, }); -// Added editorRef to the destructured props function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleName, activeTab, handleUserTabChange, myHeight, editorRef }) { const isMobile = useIsMobile(); const [isFullScreen, setIsFullScreen] = useState(false); @@ -89,7 +87,7 @@ function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleN
), diff --git a/pages/index.js b/pages/index.js index 9eed51e..6a10a65 100644 --- a/pages/index.js +++ b/pages/index.js @@ -64,14 +64,13 @@ export default function Home() { handleUserTabChange("STDOUT"); } }, [moduleReady, dataFetch]); - // Step 2: Jump Handler to be passed to ResultBox - const jumpToEditorLine = (line) => { + const jumpToEditorLine = (rangeData) => { - // Use jumpToLine (the API we exposed in Editor.js) - if (editorRef.current && typeof editorRef.current.jumpToLine === 'function') { - editorRef.current.jumpToLine(line); - } + // Use jumpToRange (updated from jumpToLine to match your new Editor.js API) + if (editorRef.current && typeof editorRef.current.jumpToRange === 'function') { + editorRef.current.jumpToRange(rangeData); + } }; async function fetchData() { @@ -95,7 +94,6 @@ export default function Home() { ); }) .catch((error) => { - console.error("Error fetching data:", error); openNotification("error fetching .", "bottomRight"); }); } else { @@ -136,31 +134,51 @@ export default function Home() { } else if (key == "AST") { const res = lfortran_funcs.emit_ast_from_source(sourceCode); if (res) { - // 1. Convert to HTML first so tags aren't escaped let htmlOutput = ansi_up.ansi_to_html(res); - // 2. Inject the span into the HTML string + // Demo 1: Tag "Declaration" covering the integer parameter line (Line 2) let finalOutput = htmlOutput.replace("Declaration", - `Declaration` + `Declaration` ); + + // Demo 2: Tag "Subroutine" covering the subroutine definition (Line 9) finalOutput = finalOutput.replace("Subroutine", - `Subroutine` + `Subroutine` ); + setOutput(finalOutput); } } else if (key == "ASR") { const res = lfortran_funcs.emit_asr_from_source(sourceCode); if (res) { - // 1. Convert to HTML first let htmlOutput = ansi_up.ansi_to_html(res); - // 2. Inject the span (Replacing "Declaration" as it's a common top-level node) + // Demo 1: Exact span for Declaration let finalOutput = htmlOutput.replace("Declaration", - `Declaration` + `Declaration` ); + + // Demo 2: Exact span for Subroutine finalOutput = finalOutput.replace("Subroutine", - `Subroutine` + `Subroutine` ); + setOutput(finalOutput); } } else if (key == "WAT") { From 20cb5c1d332fa859f57b5128515996a99bc0da48 Mon Sep 17 00:00:00 2001 From: Amrita mishra Date: Wed, 25 Feb 2026 01:48:36 +0530 Subject: [PATCH 3/4] Support precise multi-line span highlighting using Ace Range API --- components/Editor.js | 20 +++++++++----------- pages/index.js | 6 +++--- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/components/Editor.js b/components/Editor.js index 7fda32e..c0e55e5 100644 --- a/components/Editor.js +++ b/components/Editor.js @@ -11,37 +11,35 @@ const Editor = ({ sourceCode, setSourceCode, editorRef }) => { useEffect(() => { if (editorRef) { - editorRef.current = { jumpToRange(rangeData) { - if (aceEditorRef.current) { const editor = aceEditorRef.current.editor; const ace = window.ace; if (ace) { + // Required for the selection to not be a solid block + editor.setSelectionStyle("text"); + const Range = ace.require("ace/range").Range; + // Convert 1-based metadata to 0-based Ace indexing const startRow = rangeData.startLine - 1; const startCol = rangeData.startCol - 1; const endRow = rangeData.endLine - 1; const endCol = rangeData.endCol - 1; - // 1. Force scroll to the line first - editor.scrollToLine(rangeData.startLine, true, true); - - // 2. Clear any existing cursor or selection to prevent conflicts + // Clear existing selection to avoid visual ghosting editor.selection.clearSelection(); - // 3. Create and apply the precise range + // Apply the precise syntactic range const newRange = new Range(startRow, startCol, endRow, endCol); editor.selection.setRange(newRange); - // 4. Force focus and center the view on the highlight + // Navigation and Focus + editor.scrollToLine(rangeData.startLine, true, true); editor.focus(); - // Optional: helps if the code block is very long - editor.renderer.scrollSelectionIntoView(); - } else { + editor.renderer.scrollSelectionIntoView(newRange); } } } diff --git a/pages/index.js b/pages/index.js index 6a10a65..38eabaf 100644 --- a/pages/index.js +++ b/pages/index.js @@ -149,7 +149,7 @@ export default function Home() { finalOutput = finalOutput.replace("Subroutine", `Subroutine` ); @@ -171,10 +171,10 @@ export default function Home() { ); // Demo 2: Exact span for Subroutine - finalOutput = finalOutput.replace("Subroutine", + finalOutput = finalOutput.replace("Subroutine", `Subroutine` ); From faf802345ea8ef05a565efa37796f70ca9faf580 Mon Sep 17 00:00:00 2001 From: Amrita mishra Date: Wed, 25 Feb 2026 15:14:42 +0530 Subject: [PATCH 4/4] Refactor span replacement logic and restore console logs in catch blocks --- components/LoadLFortran.js | 58 ++++++++++++++++++++++--------- pages/index.js | 70 ++++++++++++++++++-------------------- 2 files changed, 75 insertions(+), 53 deletions(-) diff --git a/components/LoadLFortran.js b/components/LoadLFortran.js index f79736b..999d8e6 100644 --- a/components/LoadLFortran.js +++ b/components/LoadLFortran.js @@ -85,43 +85,67 @@ function define_imports(outputBuffer, exit_code, stdout_print) { return imports; } -async function setup_lfortran_funcs(lfortran_funcs, myPrint) { +async function setup_lfortran_funcs(lfortran_funcs, CustomPrint) { const compiler_funcs = await getLfortranExportedFuncs(); lfortran_funcs.emit_ast_from_source = function (source_code) { - try { return compiler_funcs.emit_ast_from_source(source_code); } - catch (e) { myPrint(e + "\nERROR: AST could not be generated from the code"); return 0; } + try { + return compiler_funcs.emit_ast_from_source(source_code); + } catch (e) { + console.log(e); + CustomPrint(e + "\nERROR: AST could not be generated from the code"); + return 0; + } } lfortran_funcs.emit_asr_from_source = function (source_code) { - try { return compiler_funcs.emit_asr_from_source(source_code); } - catch (e) { myPrint(e + "\nERROR: ASR could not be generated from the code"); return 0; } + try { + return compiler_funcs.emit_asr_from_source(source_code); + } catch (e) { + console.log(e); + CustomPrint(e + "\nERROR: ASR could not be generated from the code"); + return 0; + } } lfortran_funcs.emit_wat_from_source = function (source_code) { - try { return compiler_funcs.emit_wat_from_source(source_code); } - catch (e) {myPrint(e + "\nERROR: WAT could not be generated from the code"); return 0; } + try { + return compiler_funcs.emit_wat_from_source(source_code); + } catch (e) { + console.log(e); + CustomPrint(e + "\nERROR: WAT could not be generated from the code"); + return 0; + } } lfortran_funcs.emit_cpp_from_source = function (source_code) { - try { return compiler_funcs.emit_cpp_from_source(source_code); } - catch (e) { myPrint(e + "\nERROR: CPP could not be generated from the code"); return 0; } + try { + return compiler_funcs.emit_cpp_from_source(source_code); + } catch (e) { + console.log(e); + CustomPrint(e + "\nERROR: CPP could not be generated from the code"); + return 0; + } } lfortran_funcs.emit_py_from_source = function (source_code) { - try { return compiler_funcs.emit_py_from_source(source_code); } - catch (e) { myPrint(e + "\nERROR: LLVM could not be generated from the code"); return 0; } + try { + return compiler_funcs.emit_py_from_source(source_code); + } catch (e) { + console.log(e); + CustomPrint(e + "\nERROR: LLVM could not be generated from the code"); + return 0; + } } lfortran_funcs.compile_code = function (source_code) { try { return compiler_funcs.emit_wasm_from_source(source_code); - } - catch (e) { - myPrint(e + "\nERROR: The code could not be compiled. Either there is a compile-time error or there is an issue at our end.") + } catch (e) { + console.log(e); + CustomPrint(e + "\nERROR: The code could not be compiled. Either there is a compile-time error or there is an issue at our end."); return 0; } - }; lfortran_funcs.execute_code = async function (bytes, stdout_print) { @@ -159,7 +183,7 @@ function LoadLFortran({ setModuleReady, lfortran_funcs, openNotification, - myPrint, + CustomPrint, }) { const { basePath } = useRouter(); @@ -172,7 +196,7 @@ function LoadLFortran({ }, [basePath]); const setupLFortran = useCallback(async () => { - await setup_lfortran_funcs(lfortran_funcs, myPrint); + await setup_lfortran_funcs(lfortran_funcs, CustomPrint); setModuleReady(true); openNotification("LFortran Module Initialized!", "bottomRight"); }, [moduleReady]); // update the callback if the state changes diff --git a/pages/index.js b/pages/index.js index 38eabaf..eceb852 100644 --- a/pages/index.js +++ b/pages/index.js @@ -4,7 +4,7 @@ import LoadLFortran from "../components/LoadLFortran"; import preinstalled_programs from "../utils/preinstalled_programs"; import { useIsMobile } from "../components/useIsMobile"; -import { useState, useEffect, useRef } from "react"; // Added useRef +import { useState, useEffect, useRef } from "react"; import { Col, Row, Spin } from "antd"; import { notification } from "antd"; import { LoadingOutlined } from "@ant-design/icons"; @@ -47,7 +47,7 @@ export default function Home() { const [output, setOutput] = useState(""); const [dataFetch, setDataFetch] = useState(false); - // Step 1: Initialize the Ref for the Editor + // Initialize the Ref for the Editor const editorRef = useRef(null); const isMobile = useIsMobile(); @@ -64,7 +64,7 @@ export default function Home() { handleUserTabChange("STDOUT"); } }, [moduleReady, dataFetch]); - // Step 2: Jump Handler to be passed to ResultBox + // Jump Handler to be passed to ResultBox const jumpToEditorLine = (rangeData) => { // Use jumpToRange (updated from jumpToLine to match your new Editor.js API) @@ -134,50 +134,48 @@ export default function Home() { } else if (key == "AST") { const res = lfortran_funcs.emit_ast_from_source(sourceCode); if (res) { - let htmlOutput = ansi_up.ansi_to_html(res); - - // Demo 1: Tag "Declaration" covering the integer parameter line (Line 2) - let finalOutput = htmlOutput.replace("Declaration", + const htmlOutput = ansi_up.ansi_to_html(res); + + // Regex /g ensures all instances become interactive + const finalOutput = htmlOutput + .replace(/Declaration/g, `Declaration` - ); - - // Demo 2: Tag "Subroutine" covering the subroutine definition (Line 9) - finalOutput = finalOutput.replace("Subroutine", + ) + .replace(/Subroutine/g, `Subroutine` ); - - setOutput(finalOutput); - } + + setOutput(finalOutput); + } } else if (key == "ASR") { const res = lfortran_funcs.emit_asr_from_source(sourceCode); if (res) { - let htmlOutput = ansi_up.ansi_to_html(res); - - // Demo 1: Exact span for Declaration - let finalOutput = htmlOutput.replace("Declaration", - `Declaration` - ); - - // Demo 2: Exact span for Subroutine - finalOutput = finalOutput.replace("Subroutine", - `Subroutine` - ); + const htmlOutput = ansi_up.ansi_to_html(res); + + // Using global regex (/g) ensures all instances are captured + const finalOutput = htmlOutput + .replace(/Declaration/g, + `Declaration` + ) + .replace(/Subroutine/g, + `Subroutine` + ); setOutput(finalOutput); } @@ -206,7 +204,7 @@ export default function Home() { setModuleReady={setModuleReady} lfortran_funcs={lfortran_funcs} openNotification={openNotification} - myPrint={setOutput} + CustomPrint={setOutput} > @@ -220,7 +218,7 @@ export default function Home() { activeTab={activeTab} handleUserTabChange={handleUserTabChange} myHeight={myHeight} - // Step 4: Pass the editorRef to TextBox + // Pass the editorRef to TextBox editorRef={editorRef} > @@ -232,7 +230,7 @@ export default function Home() { handleUserTabChange={handleUserTabChange} myHeight={myHeight} openNotification={openNotification} - // Step 5: Pass the jump handler to ResultBox + //Pass the jump handler to ResultBox onNodeClick={jumpToEditorLine} > ) : (