crap
This commit is contained in:
@@ -1,20 +1,30 @@
|
|||||||
p {
|
/* Remove the p styling since we're using div now */
|
||||||
line-height: 0.3;
|
div {
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button, .button {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
background-color: #48156e;
|
background-color: #48156e;
|
||||||
color: #e6e8ff;
|
color: #e6e8ff;
|
||||||
border: 0px;
|
border: 0px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover, .button:hover {
|
||||||
|
background-color: #5a1a82;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
@@ -37,3 +47,15 @@ button {
|
|||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove focus outline on the editor */
|
||||||
|
#editor {
|
||||||
|
outline: none;
|
||||||
|
font-family: monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
spellcheck: false; /* Disable spellcheck squiggly lines */
|
||||||
|
}
|
||||||
|
|
||||||
|
#console {
|
||||||
|
font-family: monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="right">
|
<div class="right">
|
||||||
<button onclick="runCode()">Run</button>
|
<button onclick="runCode()">Run</button>
|
||||||
<input type="file" id="fileInput" hidden accept=".grnd">
|
<input type="file" id="fileInput" hidden accept=".grnd">
|
||||||
<button for="fileInput" id="buttonTrigger">Choose File</button>
|
<label for="fileInput" id="buttonTrigger" class="button">Choose File</label>
|
||||||
<br>
|
<br>
|
||||||
<code id="console">Output will be shown here, click the run button to run your code!</code>
|
<code id="console">Output will be shown here, click the run button to run your code!</code>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
373
client/index.js
373
client/index.js
@@ -1,43 +1,149 @@
|
|||||||
let textbox;
|
let textbox;
|
||||||
let textconsole;
|
let textconsole;
|
||||||
let text = '# welcome to gride! start typing to code a program in Ground!\n';
|
let text = "# welcome to gride!\n# type your code here and click run!\n";
|
||||||
|
|
||||||
let filebutton;
|
let filebutton;
|
||||||
let fileinput;
|
let fileinput;
|
||||||
|
let firstRun = true;
|
||||||
|
|
||||||
|
// Function to save cursor position
|
||||||
|
function saveCursorPosition() {
|
||||||
|
const selection = window.getSelection();
|
||||||
|
if (selection.rangeCount === 0) return null;
|
||||||
|
|
||||||
|
const range = selection.getRangeAt(0);
|
||||||
|
const preCaretRange = range.cloneRange();
|
||||||
|
preCaretRange.selectNodeContents(textbox);
|
||||||
|
preCaretRange.setEnd(range.startContainer, range.startOffset);
|
||||||
|
|
||||||
|
return preCaretRange.toString().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to restore cursor position
|
||||||
|
function restoreCursorPosition(savedPos) {
|
||||||
|
if (savedPos === null || !textbox) return;
|
||||||
|
|
||||||
|
const textNodes = [];
|
||||||
|
const walker = document.createTreeWalker(
|
||||||
|
textbox,
|
||||||
|
NodeFilter.SHOW_TEXT,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let node;
|
||||||
|
while ((node = walker.nextNode())) {
|
||||||
|
textNodes.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentPos = 0;
|
||||||
|
for (const textNode of textNodes) {
|
||||||
|
const nodeLength = textNode.textContent.length;
|
||||||
|
if (currentPos + nodeLength >= savedPos) {
|
||||||
|
const range = document.createRange();
|
||||||
|
const selection = window.getSelection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
range.setStart(textNode, Math.min(savedPos - currentPos, nodeLength));
|
||||||
|
range.collapse(true);
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
} catch (e) {
|
||||||
|
// If positioning fails, just focus the element
|
||||||
|
textbox.focus();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentPos += nodeLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we couldn't find the exact position, focus at the end
|
||||||
|
const range = document.createRange();
|
||||||
|
const selection = window.getSelection();
|
||||||
|
try {
|
||||||
|
range.selectNodeContents(textbox);
|
||||||
|
range.collapse(false);
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
} catch (e) {
|
||||||
|
textbox.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderHighlightedText() {
|
function renderHighlightedText() {
|
||||||
|
if (!textbox) return;
|
||||||
|
|
||||||
|
// Save cursor position before updating
|
||||||
|
const cursorPos = saveCursorPosition();
|
||||||
|
|
||||||
let renderedText = "";
|
let renderedText = "";
|
||||||
const lines = text.split("\n");
|
const lines = text.split("\n");
|
||||||
for (const line of lines) {
|
|
||||||
iscomment = false;
|
for (let i = 0; i < lines.length; i++) {
|
||||||
renderedText += "<p>";
|
const line = lines[i];
|
||||||
const tokens = line.split(" ");
|
if (line.trim() === "") {
|
||||||
for (const token of tokens) {
|
renderedText += "<div> </div>"; // Preserve empty lines
|
||||||
renderedText += highlightToken(token) + " ";
|
} else {
|
||||||
|
const commentIndex = line.indexOf("#");
|
||||||
|
if (commentIndex !== -1) {
|
||||||
|
const beforeComment = line.substring(0, commentIndex);
|
||||||
|
const comment = line.substring(commentIndex);
|
||||||
|
renderedText += `<div>${highlightTokens(beforeComment)}<span style="color: ${colours.comment}">${comment}</span></div>`;
|
||||||
|
} else {
|
||||||
|
renderedText += `<div>${highlightTokens(line)}</div>`;
|
||||||
}
|
}
|
||||||
renderedText += "</p>";
|
|
||||||
instring = false;
|
|
||||||
}
|
}
|
||||||
textbox.innerHTML = renderedText;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textbox.innerHTML = renderedText;
|
||||||
|
|
||||||
|
// Restore cursor position after a brief delay to let the DOM update
|
||||||
|
setTimeout(() => {
|
||||||
|
restoreCursorPosition(cursorPos);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTextFromEditor() {
|
||||||
|
let newText = "";
|
||||||
|
for (let i = 0; i < textbox.childNodes.length; i++) {
|
||||||
|
const div = textbox.childNodes[i];
|
||||||
|
if (div.nodeType === Node.ELEMENT_NODE && div.tagName === 'DIV') {
|
||||||
|
let line = "";
|
||||||
|
for (let j = 0; j < div.childNodes.length; j++) {
|
||||||
|
line += div.childNodes[j].textContent;
|
||||||
|
}
|
||||||
|
newText += line + (i < textbox.childNodes.length - 1 ? "\n" : "");
|
||||||
|
} else if (div.nodeType === Node.TEXT_NODE) {
|
||||||
|
newText += div.textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newText;
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the window to load before doing anything
|
// Wait for the window to load before doing anything
|
||||||
window.addEventListener("load", function () {
|
window.addEventListener("load", function () {
|
||||||
|
|
||||||
// Get all the elements
|
// Get all the elements
|
||||||
textbox = document.getElementById("editor");
|
textbox = document.getElementById("editor");
|
||||||
textconsole = document.getElementById("console");
|
textconsole = document.getElementById("console");
|
||||||
|
filebutton = document.getElementById("buttonTrigger");
|
||||||
|
fileinput = document.getElementById("fileInput");
|
||||||
|
|
||||||
|
// Check if elements exist
|
||||||
|
if (!textbox || !textconsole || !filebutton || !fileinput) {
|
||||||
|
console.error("Some required elements not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
textbox.innerText = text;
|
textbox.innerText = text;
|
||||||
filebutton = document.getElementById('buttonTrigger');
|
|
||||||
fileinput = document.getElementById('fileInput');
|
// Initial render
|
||||||
|
renderHighlightedText();
|
||||||
|
|
||||||
// Set up watchers for everything
|
// Set up watchers for everything
|
||||||
filebutton.addEventListener('click', () => {
|
filebutton.addEventListener("click", () => {
|
||||||
fileinput.click();
|
fileinput.click();
|
||||||
})
|
});
|
||||||
|
|
||||||
fileinput.addEventListener('change', () => {
|
fileinput.addEventListener("change", () => {
|
||||||
const file = fileinput.files[0];
|
const file = fileinput.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
@@ -48,119 +154,186 @@ window.addEventListener("load", function() {
|
|||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
} else {
|
} else {
|
||||||
console.log("what");
|
console.log("No file selected");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// When we press a key, start doing stuff
|
||||||
|
textbox.addEventListener("input", () => {
|
||||||
|
text = getTextFromEditor();
|
||||||
|
renderHighlightedText();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle tab key to insert 4 spaces
|
||||||
|
textbox.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Tab") {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Insert 4 spaces at cursor position
|
||||||
|
const selection = window.getSelection();
|
||||||
|
const range = selection.getRangeAt(0);
|
||||||
|
|
||||||
|
// Create a text node with 4 spaces
|
||||||
|
const textNode = document.createTextNode(" ");
|
||||||
|
range.insertNode(textNode);
|
||||||
|
|
||||||
|
// Move cursor to after the inserted spaces
|
||||||
|
range.setStartAfter(textNode);
|
||||||
|
range.collapse(true);
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
|
||||||
|
// Update our text variable and re-render
|
||||||
|
text = getTextFromEditor();
|
||||||
|
renderHighlightedText();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle paste events
|
||||||
|
textbox.addEventListener("paste", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const paste = (e.clipboardData || window.clipboardData).getData("text");
|
||||||
|
document.execCommand("insertText", false, paste);
|
||||||
|
text = getTextFromEditor();
|
||||||
|
renderHighlightedText();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const keywords = ["if", "jump", "end", "input", "stdin", "print", "stdout", "println", "stdlnout", "set", "gettype", "exists", "setlist", "setlistat", "getlistat", "getlistsize", "listappend", "getstrsize", "getstrcharat", "add", "subtract", "multiply", "divide", "equal", "inequal", "not", "greater", "lesser", "stoi", "stod", "tostring", "fun", "return", "endfun", "pusharg", "call", "struct", "endstruct", "init", "use", "extern", "catch"];
|
const keywords = [
|
||||||
|
"if",
|
||||||
|
"jump",
|
||||||
|
"end",
|
||||||
|
"input",
|
||||||
|
"stdin",
|
||||||
|
"print",
|
||||||
|
"stdout",
|
||||||
|
"println",
|
||||||
|
"stdlnout",
|
||||||
|
"set",
|
||||||
|
"gettype",
|
||||||
|
"exists",
|
||||||
|
"setlist",
|
||||||
|
"setlistat",
|
||||||
|
"getlistat",
|
||||||
|
"getlistsize",
|
||||||
|
"listappend",
|
||||||
|
"getstrsize",
|
||||||
|
"getstrcharat",
|
||||||
|
"add",
|
||||||
|
"subtract",
|
||||||
|
"multiply",
|
||||||
|
"divide",
|
||||||
|
"equal",
|
||||||
|
"inequal",
|
||||||
|
"not",
|
||||||
|
"greater",
|
||||||
|
"lesser",
|
||||||
|
"stoi",
|
||||||
|
"stod",
|
||||||
|
"tostring",
|
||||||
|
"fun",
|
||||||
|
"return",
|
||||||
|
"endfun",
|
||||||
|
"pusharg",
|
||||||
|
"call",
|
||||||
|
"struct",
|
||||||
|
"endstruct",
|
||||||
|
"init",
|
||||||
|
"use",
|
||||||
|
"extern",
|
||||||
|
"catch",
|
||||||
|
];
|
||||||
|
|
||||||
const colours = {
|
const colours = {
|
||||||
keyword: "#42fff2",
|
keyword: "#42fff2",
|
||||||
comment: "#383838",
|
comment: "#383838",
|
||||||
number: "#42ffb7",
|
number: "#42ff62",
|
||||||
string: "#42ff62",
|
string: "#42ff62",
|
||||||
valref: "#9544c7",
|
valref: "#9544c7",
|
||||||
dirref: "#f587ff",
|
dirref: "#f587ff",
|
||||||
|
lineref: "#9bf542",
|
||||||
label: "#fff67d",
|
label: "#fff67d",
|
||||||
function: "#ffa640",
|
function: "#ffa640",
|
||||||
typeref: "#ff40ac"
|
typeref: "#ff40ac",
|
||||||
}
|
};
|
||||||
|
|
||||||
|
function highlightTokens(line) {
|
||||||
|
const tokens = line.split(/(\s+|"[^"]*"|'[^']*')/).filter(Boolean);
|
||||||
let instring = false;
|
let instring = false;
|
||||||
let iscomment = false;
|
return tokens.map(token => {
|
||||||
|
if (token.startsWith('"') || token.startsWith("'")) {
|
||||||
// Function to handle highlighting tokens that have been typed
|
instring = !instring;
|
||||||
function highlightToken(token) {
|
return `<span style="color: ${colours.string}">${token}</span>`;
|
||||||
|
}
|
||||||
if (instring) {
|
if (instring) {
|
||||||
if (token[token.length - 1] == '"' || token[token.length - 1] == '"') {
|
return `<span style="color: ${colours.string}">${token}</span>`;
|
||||||
instring = false;
|
|
||||||
}
|
}
|
||||||
return `<span style="color: ${colours.string}">${token}</span>`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comments
|
|
||||||
if (iscomment) {
|
|
||||||
return `<span style="color: ${colours.comment}"> ${token}</span>"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token[0] == '#') {
|
|
||||||
iscomment = true;
|
|
||||||
return `<span style="color: ${colours.comment}> ${token}</span>"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keywords
|
|
||||||
if (keywords.includes(token)) {
|
if (keywords.includes(token)) {
|
||||||
return `<span style="color: ${colours.keyword}">${token}</span>`;
|
return `<span style="color: ${colours.keyword}">${token}</span>`;
|
||||||
}
|
}
|
||||||
|
if (!isNaN(token) && token.trim() !== "") {
|
||||||
// Direct references
|
return `<span style="color: ${colours.number}">${token}</span>`;
|
||||||
if (token[0] == '&') {
|
}
|
||||||
|
if (token.startsWith('&')) {
|
||||||
return `<span style="color: ${colours.dirref}">${token}</span>`;
|
return `<span style="color: ${colours.dirref}">${token}</span>`;
|
||||||
}
|
}
|
||||||
|
if (token.startsWith('$')) {
|
||||||
// Value references
|
|
||||||
if (token[0] == '$') {
|
|
||||||
return `<span style="color: ${colours.valref}">${token}</span>`;
|
return `<span style="color: ${colours.valref}">${token}</span>`;
|
||||||
}
|
}
|
||||||
|
if (token.startsWith('-')) {
|
||||||
// Strings and characters
|
return `<span style="color: ${colours.typeref}">${token}</span>`;
|
||||||
if (token[0] == '"' || token[0] == "'") {
|
|
||||||
if (!(token[token.length - 1] == '"' || token[token.length - 1] == '"')) {
|
|
||||||
instring = true;
|
|
||||||
}
|
}
|
||||||
return `<span style="color: ${colours.string}">${token}</span>`;
|
if (token.startsWith('!')) {
|
||||||
|
return `<span style="color: ${colours.function}">${token}</span>`;
|
||||||
}
|
}
|
||||||
|
if (token.startsWith('%')) {
|
||||||
return `<span>${token}</span>`;
|
return `<span style="color: ${colours.lineref}">${token}</span>`;
|
||||||
}
|
}
|
||||||
|
if (token.startsWith('@')) {
|
||||||
// When we press a key, start doing stuff
|
return `<span style="color: ${colours.label}">${token}</span>`;
|
||||||
textbox.addEventListener('input', () => {
|
|
||||||
text = textbox.innerText;
|
|
||||||
renderHighlightedText();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Old code
|
|
||||||
/*
|
|
||||||
onkeydown = (event) => {
|
|
||||||
if (event.key == "Enter") {
|
|
||||||
//text += "</p>\n<p>";
|
|
||||||
text += "\n"
|
|
||||||
} else if (event.key == "Tab") {
|
|
||||||
text += " ";
|
|
||||||
} else if (event.key == "Backspace") {
|
|
||||||
text = text.slice(0, -1)
|
|
||||||
} else if (!(event.key == "Control" || event.key == "Alt" || event.key == "Meta" || event.key == "Shift" || event.key == "Escape")) {
|
|
||||||
text += event.key;
|
|
||||||
}
|
}
|
||||||
let renderedText = "";
|
return token;
|
||||||
const lines = text.split("\n");
|
}).join('');
|
||||||
for (const line of lines) {
|
|
||||||
renderedText += "<p>";
|
|
||||||
const tokens = line.split(" ");
|
|
||||||
for (const token of tokens) {
|
|
||||||
renderedText += highlightToken(token) + " ";
|
|
||||||
}
|
}
|
||||||
renderedText += "</p>";
|
|
||||||
instring = false;
|
|
||||||
iscomment = false;
|
|
||||||
}
|
|
||||||
textbox.innerHTML = renderedText;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Function to run code on the server
|
// Function to run code on the server
|
||||||
async function runCode() {
|
async function runCode() {
|
||||||
console.log(text.split("<p>").join("").split("</p>").join(""));
|
if (!textconsole) {
|
||||||
const result = await fetch("http://localhost:5000/runProgram", {
|
console.error("Console element not found!");
|
||||||
"method": "POST",
|
return;
|
||||||
//"mode": "no-cors",
|
}
|
||||||
"body": text.split("<p>").join("").split("</p>").join("")
|
|
||||||
});
|
// Make sure we have the latest text
|
||||||
const data = await result.json();
|
// The `text` variable is now the single source of truth.
|
||||||
// Ensure everything's seperated
|
|
||||||
textconsole.innerHTML = "<p>" + data.stdout.split("\n").join("</p><p>") + "</p>";
|
if (firstRun) {
|
||||||
|
textconsole.innerHTML = "";
|
||||||
|
firstRun = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Running code:", text);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await fetch("/runProgram", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/plain",
|
||||||
|
},
|
||||||
|
body: text,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${result.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await result.json();
|
||||||
|
const output = data.stdout.split('\n').join('</div><div>');
|
||||||
|
const timestamp = new Date().toLocaleTimeString();
|
||||||
|
textconsole.innerHTML += `<div>[${timestamp}]</div><div>${output}</div>`;
|
||||||
|
textconsole.scrollTop = textconsole.scrollHeight; // Scroll to bottom
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error running code:", error);
|
||||||
|
textconsole.innerHTML =
|
||||||
|
"<div style='color: #ff4444'>Error: " + error.message + "</div>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user