Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 57 additions & 11 deletions components/Editor.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,78 @@
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);

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;

// Clear existing selection to avoid visual ghosting
editor.selection.clearSelection();

// Apply the precise syntactic range
const newRange = new Range(startRow, startCol, endRow, endCol);
editor.selection.setRange(newRange);

// Navigation and Focus
editor.scrollToLine(rangeData.startLine, true, true);
editor.focus();
editor.renderer.scrollSelectionIntoView(newRange);
}
}
}
};
}
}, [editorRef]);

return (
<AceEditor
ref={aceEditorRef}
mode="fortran"
theme="monokai"
onChange={(code)=>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;
13 changes: 5 additions & 8 deletions components/LoadLFortran.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,35 +90,34 @@ 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; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
catch (e) { myPrint(e + "\nERROR: AST could not be generated from the code"); return 0; }
catch (e) { CustomPrint(e + "\nERROR: AST could not be generated from the code"); return 0; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here and elsewhere ^^

}

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) {
try {
return compiler_funcs.emit_wasm_from_source(source_code);
}
catch (e) {
console.log(e);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's not remove these.

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;
}
Expand Down Expand Up @@ -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.`);
}
};
Expand All @@ -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 (
Expand Down
37 changes: 30 additions & 7 deletions components/ResultBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,42 @@ import { Button } from "antd";
import { CopyOutlined } from "@ant-design/icons";
import { Segmented } from "antd";

function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotification }) {
// onNodeClick to props
function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotification, onNodeClick }) {
const isMobile = useIsMobile();
const [showCopy, setShowCopy] = useState(false);

const handleOutputClick = (e) => {
// .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 (onNodeClick) {
onNodeClick(rangeData);
}
}
};

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 (
<div
className="card-container"
onClick={() => isMobile && setShowCopy(!showCopy)} // Only toggle if on mobile
onClick={() => isMobile && setShowCopy(!showCopy)}
>
<Segmented
block
Expand All @@ -33,17 +54,19 @@ function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotif
position: "absolute",
right: "40px",
top: "80px",
// If not mobile, always show (opacity 1). If mobile, follow state.
opacity: !isMobile || showCopy ? 1 : 0,
pointerEvents: !isMobile || showCopy ? "auto" : "none",
transition: "opacity 0.2s ease-in-out"
transition: "opacity 0.2s ease-in-out",
zIndex: 10 // Ensure copy button stays above clickable spans
}}
>
<CopyOutlined />
</Button>
<pre style={{ margin: "0px", height: myHeight, overflow: "scroll", border: "1px solid black" }}>
<div
id="outputBox"
// Attach the click listener to the container
onClick={handleOutputClick}
style={{ minHeight: "100%", fontSize: "0.9em", padding: "10px" }}
dangerouslySetInnerHTML={{ __html: output }}
>
Expand All @@ -52,4 +75,4 @@ function ResultBox({ activeTab, output, handleUserTabChange, myHeight, openNotif
</div>
);
}
export default ResultBox;
export default ResultBox;
17 changes: 8 additions & 9 deletions components/TextBox.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
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 }) {
const Editor = dynamic(() => import('./Editor'), {
ssr: false,
});

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);
Expand Down Expand Up @@ -49,15 +49,13 @@ function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleN
});
}

const examples_menu = (<Menu items={menu_items}></Menu>);
const extraOperations = {
right: (
<Space>
<Button
onClick={handleFullScreen}
icon={isFullScreen ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
>
{/* Now both text options only show on desktop, keeping mobile clean */}
{!isMobile && (isFullScreen ? " Exit Fullscreen" : " Fullscreen")}
</Button>
<Button
Expand Down Expand Up @@ -89,6 +87,7 @@ function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleN
<Editor
sourceCode={sourceCode}
setSourceCode={setSourceCode}
editorRef={editorRef}// Pass the ref to the Editor
/>
</div>
),
Expand All @@ -106,4 +105,4 @@ function TextBox({ disabled, sourceCode, setSourceCode, exampleName, setExampleN
);
}

export default TextBox;
export default TextBox;
Loading