Skip to content

Commit

Permalink
get code running working
Browse files Browse the repository at this point in the history
  • Loading branch information
thecodingwizard committed Dec 19, 2023
1 parent 49811ad commit 4bcaf97
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 18 deletions.
16 changes: 9 additions & 7 deletions pages/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { submitToJudge } from '../src/scripts/judge';
import { useAtom, useAtomValue } from 'jotai';
import { useUpdateAtom } from 'jotai/utils';
import {
inputEditorValueAtom,
inputMonacoEditorAtom,
layoutEditorsAtom,
loadingAtom,
mainEditorValueAtom,
mainMonacoEditorAtom,
} from '../src/atoms/workspace';
import {
Expand Down Expand Up @@ -46,8 +48,8 @@ function EditorPage() {
const isDesktop = useMediaQuery('(min-width: 1024px)', true);
const layoutEditors = useUpdateAtom(layoutEditorsAtom);
const [mobileActiveTab, setMobileActiveTab] = useAtom(mobileActiveTabAtom);
const inputEditor = useAtomValue(inputMonacoEditorAtom);
const mainMonacoEditor = useAtomValue(mainMonacoEditorAtom);
const getMainEditorValue = useAtomValue(mainEditorValueAtom);
const getInputEditorValue = useAtomValue(inputEditorValueAtom);
const [judgeResults, setJudgeResults] = useJudgeResults();

useUserFileConnection();
Expand Down Expand Up @@ -101,15 +103,15 @@ function EditorPage() {
expectedOutput?: string,
prefix?: string
) => {
if (!mainMonacoEditor) {
if (!getMainEditorValue) {
// editor is still loading
return;
}

setIsRunning(true);
setResultAt(inputTabIndex, null);

const code = mainMonacoEditor.getValue();
const code = getMainEditorValue();
fetchJudge(code, input)
.then(async resp => {
const data: JudgeResult = await resp.json();
Expand Down Expand Up @@ -138,7 +140,7 @@ function EditorPage() {
};

const runAllSamples = async () => {
if (!problem || !mainMonacoEditor) {
if (!problem || !getMainEditorValue) {
// editor is still loading
return;
}
Expand All @@ -147,7 +149,7 @@ function EditorPage() {
setIsRunning(true);
setResultAt(1, null);

const code = mainMonacoEditor.getValue();
const code = getMainEditorValue();
try {
const promises = [];
for (let index = 0; index < samples.length; ++index) {
Expand Down Expand Up @@ -219,7 +221,7 @@ function EditorPage() {
};

if (inputTab === 'input') {
if (inputEditor) runWithInput(inputEditor.getValue());
if (getInputEditorValue) runWithInput(getInputEditorValue());
} else if (inputTab === 'judge') {
runAllSamples();
} else {
Expand Down
41 changes: 41 additions & 0 deletions src/atoms/workspace.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EditorView } from '@uiw/react-codemirror';
import { atom } from 'jotai';
import type * as monaco from 'monaco-editor';

Expand All @@ -10,8 +11,48 @@ export const inputMonacoEditorAtom =
atom<monaco.editor.IStandaloneCodeEditor | null>(null);
export const outputMonacoEditorAtom =
atom<monaco.editor.IStandaloneCodeEditor | null>(null);

export const mainCodemirrorEditorAtom = atom<EditorView | null>(null);
export const inputCodemirrorEditorAtom = atom<EditorView | null>(null);

export const layoutEditorsAtom = atom(null, (get, _set, _arg) => {
get(mainMonacoEditorAtom)?.layout();
get(inputMonacoEditorAtom)?.layout();
get(outputMonacoEditorAtom)?.layout();
});

// returns a function that can be called to get the value of the editor
export const mainEditorValueAtom = atom(get => {
const monacoEditor = get(mainMonacoEditorAtom);
const codemirrorEditor = get(mainCodemirrorEditorAtom);
if (monacoEditor && codemirrorEditor) {
console.error('Both main editors are defined, this should not happen!');
}
if (!monacoEditor && !codemirrorEditor) {
return null;
}
return () => {
if (monacoEditor) {
return monacoEditor.getValue();
}
return codemirrorEditor!.state.doc.toString();
};
});

// returns a function that can be called to get the value of the editor
export const inputEditorValueAtom = atom(get => {
const monacoEditor = get(inputMonacoEditorAtom);
const codemirrorEditor = get(inputCodemirrorEditorAtom);
if (monacoEditor && codemirrorEditor) {
console.error('Both input editors are defined, this should not happen!');
}
if (!monacoEditor && !codemirrorEditor) {
return null;
}
return () => {
if (monacoEditor) {
return monacoEditor.getValue();
}
return codemirrorEditor!.state.doc.toString();
};
});
23 changes: 22 additions & 1 deletion src/components/CodeInterface/CodeInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import defaultCode from '../../scripts/defaultCode';
import React, { useEffect, useState } from 'react';
import classNames from 'classnames';
import { useAtom } from 'jotai';
import { mainMonacoEditorAtom } from '../../atoms/workspace';
import {
mainCodemirrorEditorAtom,
mainMonacoEditorAtom,
} from '../../atoms/workspace';
import { LazyRealtimeEditor } from '../RealtimeEditor/LazyRealtimeEditor';
import type * as monaco from 'monaco-editor';
import { useEditorContext } from '../../context/EditorContext';
import useUserPermission from '../../hooks/useUserPermission';
import { useUserContext } from '../../context/UserContext';
import { EditorView } from '@uiw/react-codemirror';

export const CodeInterface = ({
className,
Expand All @@ -20,8 +24,15 @@ export const CodeInterface = ({
const readOnly = !(permission === 'OWNER' || permission === 'READ_WRITE');
const [editor, setEditor] =
useState<monaco.editor.IStandaloneCodeEditor | null>(null);
const [codemirrorEditor, setCodemirrorEditor] = useState<EditorView | null>(
null
);
const [, setMainMonacoEditor] = useAtom(mainMonacoEditorAtom);
const [, setMainCodemirrorEditor] = useAtom(mainCodemirrorEditorAtom);

// I think we need these useEffect()s here because otherwise when the component
// is unmounted, the mainMonacoEditorAtom/mainCodemirrorEditorAtom will still
// be set
useEffect(() => {
if (editor) {
setMainMonacoEditor(editor);
Expand All @@ -31,6 +42,15 @@ export const CodeInterface = ({
}
}, [editor, setMainMonacoEditor]);

useEffect(() => {
if (codemirrorEditor) {
setMainCodemirrorEditor(codemirrorEditor);
return () => {
setMainCodemirrorEditor(null);
};
}
}, [codemirrorEditor, setCodemirrorEditor]);

const { tabSize, lightMode } = useUserContext().userData;

return (
Expand Down Expand Up @@ -68,6 +88,7 @@ export const CodeInterface = ({
e.focus();
}, 0);
}}
onCodemirrorMount={(view, state) => setCodemirrorEditor(view)}
defaultValue={defaultCode[lang]}
yjsDocumentId={`${fileData.id}.${lang}`}
useEditorWithVim={true}
Expand Down
11 changes: 7 additions & 4 deletions src/components/JudgeInterface/JudgeInterface.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { useAtomValue } from 'jotai/utils';
import React from 'react';
import { mainMonacoEditorAtom } from '../../atoms/workspace';
import {
mainEditorValueAtom,
mainMonacoEditorAtom,
} from '../../atoms/workspace';
import USACOResults from './USACOResults';
import { ProblemData, StatusData } from '../Workspace/Workspace';
import SubmitButton from './SubmitButton';
Expand Down Expand Up @@ -48,11 +51,11 @@ export default function JudgeInterface({
setStatusData: React.Dispatch<React.SetStateAction<StatusData | null>>;
handleRunCode: () => void;
}): JSX.Element {
const mainMonacoEditor = useAtomValue(mainMonacoEditorAtom);
const getMainEditorValue = useAtomValue(mainEditorValueAtom);
const lang = useEditorContext().fileData.settings.language;

const handleSubmit = async () => {
if (!mainMonacoEditor || !lang) {
if (!getMainEditorValue || !lang) {
alert('Error: Page still loading?');
return;
}
Expand All @@ -64,7 +67,7 @@ export default function JudgeInterface({
const data = {
problemID: problem.id,
language: { cpp: 'c++17', java: 'java', py: 'python3' }[lang],
base64Code: encode(mainMonacoEditor.getValue()),
base64Code: encode(getMainEditorValue()),
};

const resp = await fetch(`${judgePrefix}/submit`, {
Expand Down
30 changes: 25 additions & 5 deletions src/components/NavBar/FileMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import classNames from 'classnames';
import ReactDOM from 'react-dom';
import { usePopper } from 'react-popper';

import { mainMonacoEditorAtom } from '../../atoms/workspace';
import {
mainCodemirrorEditorAtom,
mainEditorValueAtom,
mainMonacoEditorAtom,
} from '../../atoms/workspace';
import { useAtomValue } from 'jotai';
import { useEditorContext } from '../../context/EditorContext';
import defaultCode from '../../scripts/defaultCode';
Expand All @@ -22,7 +26,9 @@ import useUserPermission from '../../hooks/useUserPermission';

export const FileMenu = (props: { onOpenSettings: Function }): JSX.Element => {
const { fileData } = useEditorContext();
const getMainEditorValue = useAtomValue(mainEditorValueAtom);
const mainMonacoEditor = useAtomValue(mainMonacoEditorAtom);
const mainCodemirrorEditor = useAtomValue(mainCodemirrorEditorAtom);
const permission = useUserPermission();

const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(
Expand All @@ -41,12 +47,12 @@ export const FileMenu = (props: { onOpenSettings: Function }): JSX.Element => {

/* ======= BEGIN DROPDOWN ACTIONS ======= */
const handleDownloadFile = () => {
if (!mainMonacoEditor) {
if (!getMainEditorValue) {
alert("Editor hasn't loaded yet. Please wait.");
return;
}

const code = mainMonacoEditor.getValue();
const code = getMainEditorValue();

const fileNames = {
cpp: `${fileData.settings.workspaceName}.cpp`,
Expand All @@ -58,12 +64,26 @@ export const FileMenu = (props: { onOpenSettings: Function }): JSX.Element => {
};

const handleInsertFileTemplate = () => {
if (!mainMonacoEditor) {
if (!mainMonacoEditor && !mainCodemirrorEditor) {
alert("Editor hasn't loaded yet, please wait");
return;
}
if (confirm('Reset current file? Any changes you made will be lost.')) {
mainMonacoEditor.setValue(defaultCode[fileData.settings.language]);
if (mainMonacoEditor)
mainMonacoEditor.setValue(defaultCode[fileData.settings.language]);
else if (mainCodemirrorEditor) {
mainCodemirrorEditor.dispatch({
changes: {
from: 0,
to: mainCodemirrorEditor.state.doc.length,
insert: defaultCode[fileData.settings.language],
},
});
} else {
console.error(
"?? shouldn't happen, both monaco and codemirror editors are not defined"
);
}
}
};

Expand Down
5 changes: 5 additions & 0 deletions src/components/Workspace/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
layoutEditorsAtom,
inputMonacoEditorAtom,
outputMonacoEditorAtom,
inputCodemirrorEditorAtom,
} from '../../atoms/workspace';
import {
mobileActiveTabAtom,
Expand Down Expand Up @@ -82,6 +83,7 @@ export default function Workspace({
const [inputTab, setInputTab] = useAtom(inputTabAtom);
const showSidebar = useAtomValue(showSidebarAtom);
const setInputEditor = useUpdateAtom(inputMonacoEditorAtom);
const setCodemirrorInputEditor = useUpdateAtom(inputCodemirrorEditorAtom);
const setOutputEditor = useUpdateAtom(outputMonacoEditorAtom);
const [problem, setProblem] = useAtom(problemAtom);

Expand Down Expand Up @@ -175,6 +177,9 @@ export default function Workspace({
e.layout();
}, 0);
}}
onCodemirrorMount={(view, state) =>
setCodemirrorInputEditor(view)
}
defaultValue=""
yjsDocumentId={`${fileData.id}.input`}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const CodemirrorEditor = (props: EditorProps): JSX.Element => {
style={{ fontSize: '13px' }}
readOnly={props.options?.readOnly ?? false}
extensions={extensions}
onCreateEditor={props.onCodemirrorMount}
/>
);
};
9 changes: 8 additions & 1 deletion src/components/editor/MonacoEditor/monaco-editor-types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api';
import type { ReactCodeMirrorProps } from '@uiw/react-codemirror';
type Theme = 'vs-dark' | 'light';

export type OnMount = (
Expand Down Expand Up @@ -114,14 +115,20 @@ export interface EditorProps {

onBeforeDispose?: Function;

/**
* Only called for Codemirror editor.
* Same signature as onCreateEditor?(view: EditorView, state: EditorState): void;
*/
onCodemirrorMount?: ReactCodeMirrorProps['onCreateEditor'];

vim?: boolean;

lspEnabled?: boolean;

/**
* If provided, the code editor should create a yjs binding with the given information
*/
yjsInfo: {
yjsInfo?: {
yjsText: any;
yjsAwareness: any;
} | null;
Expand Down

0 comments on commit 4bcaf97

Please sign in to comment.