diff --git a/client/eslint.config.mjs b/client/eslint.config.mjs index fc01beb8f..1f631519a 100644 --- a/client/eslint.config.mjs +++ b/client/eslint.config.mjs @@ -120,9 +120,6 @@ const overrideConfig = defineConfig([ "prettier/prettier": ["error", { endOfLine: "auto", }], - - // TODO: remove this and fix errors (the joy of tech debt amirite) - "react-hooks/exhaustive-deps": "warn", }, } ]) diff --git a/client/package-lock.json b/client/package-lock.json index bee23f6ec..bdc597ae0 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1956,9 +1956,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1979,9 +1976,6 @@ "cpu": [ "arm" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2002,9 +1996,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2025,9 +2016,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2048,9 +2036,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -2071,9 +2056,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6243,9 +6225,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6260,9 +6239,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6277,9 +6253,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6294,9 +6267,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6311,9 +6281,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6328,9 +6295,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6345,9 +6309,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6362,9 +6323,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -13409,6 +13367,22 @@ } } }, + "node_modules/vite-tsconfig-paths/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/vscode-jsonrpc": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", diff --git a/client/src/App.tsx b/client/src/App.tsx index d32415966..d88826dcf 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -19,7 +19,7 @@ const App = () => { window.globalStore = useGlobalStore; // @ts-ignore window.frontendStore = useFrontendStateStore; - }, [useGlobalStore]); + }, []); return ( diff --git a/client/src/Services/useSocketCommunication.ts b/client/src/Services/useSocketCommunication.ts index 8d98d0edd..526a575f7 100644 --- a/client/src/Services/useSocketCommunication.ts +++ b/client/src/Services/useSocketCommunication.ts @@ -45,7 +45,15 @@ export const useSocketCommunication = () => { }); socketClient.setupEventHandlers(handlers); - }, [socketClient]); + }, [ + socketClient, + setActive, + updateNextFrame, + updateTypeDeclaration, + appendConsoleChunks, + updateCurrFocusedTab, + setToastMessage, + ]); // reset entire debuggin session const resetDebugSession = useCallback(() => { @@ -64,15 +72,6 @@ export const useSocketCommunication = () => { resetConsoleChunks, ]); - // error handler for sending code - const handleSendCodeError = () => { - setToastMessage({ - content: 'No file being selected', - colorTheme: 'warning', - durationMs: DEFAULT_MESSAGE_DURATION, - }); - }; - // send code to backend to start debugging session const sendCode = useCallback(() => { if (!socketClient) return; @@ -83,40 +82,44 @@ export const useSocketCommunication = () => { const file = fileSystem.getFileFromPath(currFocusFilePath); if (!file || file.path === 'root') { - handleSendCodeError(); + setToastMessage({ + content: 'No file being selected', + colorTheme: 'warning', + durationMs: DEFAULT_MESSAGE_DURATION, + }); return; } socketClient.serverAction.initializeDebugSession(file.data); - }, [socketClient, resetDebugSession]); + }, [socketClient, resetDebugSession, setToastMessage]); // add event listener with timeout - const addEventListenerWithTimeout = ( - listener: (state: BackendState | null) => void, - timeout: number - ) => { - if (!socketClient) return; - - let resolved = false; - - const wrappedListener = (state: BackendState) => { - if (!resolved) { - resolved = true; - listener(state); - socketClient.socket.off('sendBackendStateToUser', wrappedListener); - } - }; - - socketClient.socket.on('sendBackendStateToUser', wrappedListener); - - setTimeout(() => { - if (!resolved) { - resolved = true; - listener(null); - socketClient.socket.off('sendBackendStateToUser', wrappedListener); - } - }, timeout); - }; + const addEventListenerWithTimeout = useCallback( + (listener: (state: BackendState | null) => void, timeout: number) => { + if (!socketClient) return; + + let resolved = false; + + const wrappedListener = (state: BackendState) => { + if (!resolved) { + resolved = true; + listener(state); + socketClient.socket.off('sendBackendStateToUser', wrappedListener); + } + }; + + socketClient.socket.on('sendBackendStateToUser', wrappedListener); + + setTimeout(() => { + if (!resolved) { + resolved = true; + listener(null); + socketClient.socket.off('sendBackendStateToUser', wrappedListener); + } + }, timeout); + }, + [socketClient] + ); // step debugger and wait for backend to respond const executeNextWithRetry = useCallback(() => { @@ -133,7 +136,7 @@ export const useSocketCommunication = () => { socketClient.serverAction.executeNext(); }) ); - }, [socketClient, queue]); + }, [socketClient, queue, addEventListenerWithTimeout]); // to call multiple next states in bulk const bulkSendNextStates = useCallback( diff --git a/client/src/components/Navbars/DevelopmentModeNavbar.tsx b/client/src/components/Navbars/DevelopmentModeNavbar.tsx index 5da3ef271..998b8e1c4 100644 --- a/client/src/components/Navbars/DevelopmentModeNavbar.tsx +++ b/client/src/components/Navbars/DevelopmentModeNavbar.tsx @@ -7,7 +7,6 @@ import AboutText from '@/visualiser-debugger/Component/FileTree/AboutText'; import BookIcon from '@mui/icons-material/Book'; import classNames from 'classnames'; import { Tooltip } from '@mui/material'; -import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import DarkModeIcon from '@mui/icons-material/DarkMode'; import LightModeIcon from '@mui/icons-material/LightMode'; @@ -18,15 +17,8 @@ const DevelopmentModeNavbar = ({ }: { onButtonClick: (event: React.MouseEvent) => void; }) => { - const [navigateHomePage, setNavigateHomePage] = useState(false); const navigate = useNavigate(); - useEffect(() => { - if (navigateHomePage) { - navigate('/'); - } - }, [navigateHomePage]); - const { darkMode, toggleDarkMode } = useTheme(); const handleDarkModeToggle = (e: React.MouseEvent) => { e.stopPropagation(); @@ -35,11 +27,7 @@ const DevelopmentModeNavbar = ({ return (
-
setNavigateHomePage(!navigateHomePage)} - aria-hidden="true" - > +
navigate('/')} aria-hidden="true"> logo

Structs.sh

diff --git a/client/src/components/Visualiser/VisualiserInterface/Controls.tsx b/client/src/components/Visualiser/VisualiserInterface/Controls.tsx index 0915fec2a..c05ceedbe 100644 --- a/client/src/components/Visualiser/VisualiserInterface/Controls.tsx +++ b/client/src/components/Visualiser/VisualiserInterface/Controls.tsx @@ -93,33 +93,33 @@ const VisualiserControls = ({ const handlePlay = useCallback(() => { controller.play(); handleUpdateIsPlaying(true); - }, [controller]); + }, [controller, handleUpdateIsPlaying]); const handlePause = useCallback(() => { controller.pause(); handleUpdateIsPlaying(false); - }, [controller]); + }, [controller, handleUpdateIsPlaying]); const handleReplay = useCallback(() => { controller.seekPercent(0); handlePlay(); - }, [controller]); + }, [controller, handlePlay]); const handleStepForward = useCallback(() => { controller.stepForwards(); // Stepforward pauses when animation is complete, so set state of isPlaying to false handleUpdateIsPlaying(false); - }, [controller, isPlaying]); + }, [controller, handleUpdateIsPlaying]); const handleStepBackward = useCallback(() => { handlePause(); controller.stepBackwards(); - }, [controller]); + }, [controller, handlePause]); const handleFastRewind = useCallback(() => { handlePause(); controller.seekPercent(0); - }, [controller]); + }, [controller, handlePause]); const handleFastForward = useCallback(() => { controller.seekPercent(100); diff --git a/client/src/components/Visualiser/VisualiserInterface/VisualiserInterface.tsx b/client/src/components/Visualiser/VisualiserInterface/VisualiserInterface.tsx index 82b1e5632..bc4472d90 100644 --- a/client/src/components/Visualiser/VisualiserInterface/VisualiserInterface.tsx +++ b/client/src/components/Visualiser/VisualiserInterface/VisualiserInterface.tsx @@ -40,7 +40,7 @@ const VisualiserInterface: FC = ({ topicTitle, data }) if (data) { controller.loadData(data); } - }, [topicTitle]); + }, [topicTitle, data]); const handleTimelineUpdate = useCallback((val: number) => { const timelineSlider = document.querySelector('#timelineSlider') as HTMLInputElement; @@ -65,7 +65,7 @@ const VisualiserInterface: FC = ({ topicTitle, data }) setIsPlaying(val); }, []); - const contextValue = useMemo(() => ({ controller }), [controller]); + const contextValue = useMemo(() => ({ controller }), []); return ( diff --git a/client/src/visualiser-debugger/Component/CodeEditor/CodeEditor.tsx b/client/src/visualiser-debugger/Component/CodeEditor/CodeEditor.tsx index 27a7a4027..22e3778a2 100644 --- a/client/src/visualiser-debugger/Component/CodeEditor/CodeEditor.tsx +++ b/client/src/visualiser-debugger/Component/CodeEditor/CodeEditor.tsx @@ -29,7 +29,7 @@ const CodeEditor: React.FC = () => { setOnboardingCurrFile(file.name); setCurrFile(file); setCode(file?.data || ''); - }, [currFocusFilePath, fileSystem]); + }, [currFocusFilePath, fileSystem, setOnboardingCurrFile]); const markers: IMarker[] = [ { diff --git a/client/src/visualiser-debugger/Component/Configuration/Configuration.tsx b/client/src/visualiser-debugger/Component/Configuration/Configuration.tsx index 5f524b01c..a29eb1f71 100644 --- a/client/src/visualiser-debugger/Component/Configuration/Configuration.tsx +++ b/client/src/visualiser-debugger/Component/Configuration/Configuration.tsx @@ -29,7 +29,7 @@ const Configuration = () => { stackAnnotation: userAnnotation.stackAnnotation, typeAnnotation: userAnnotation.typeAnnotation, }); - }, [currFrame]); + }, [currFrame, userAnnotation.stackAnnotation, userAnnotation.typeAnnotation]); return (
(createPossibleLinkedListTypeDecl(backendType)); const { updateUserAnnotation, visualizer } = useGlobalStore(); const [nodeAnnotation, setNodeAnnotation] = useState(null); - const handleUpdateNodeAnnotation = (newAnnotation: LinkedListAnnotation) => { - updateUserAnnotation({ - stackAnnotation: visualizer.userAnnotation.stackAnnotation, - typeAnnotation: { - ...visualizer.userAnnotation.typeAnnotation, - [newAnnotation.typeName]: newAnnotation, - }, - }); - }; - const handleLinkedNodeAnnotation = ( - possibleTypeAnnotation: PossibleLinkedListAnnotation | null - ) => { - if (possibleTypeAnnotation === null) return; - const linkedNodeAnnotation: LinkedListAnnotation = { - typeName: backendType.typeName as `struct ${string}`, - type: DataStructureType.LinkedList, - value: { - name: possibleTypeAnnotation.possibleValues[0].name, - typeName: possibleTypeAnnotation.possibleValues[0].typeName, - }, - next: { - name: possibleTypeAnnotation.possibleNexts[0].name, - typeName: possibleTypeAnnotation.possibleNexts[0].typeName, - }, - }; - setNodeAnnotation(linkedNodeAnnotation); - handleUpdateNodeAnnotation(linkedNodeAnnotation); - }; + const handleUpdateNodeAnnotation = useCallback( + (newAnnotation: LinkedListAnnotation) => { + updateUserAnnotation({ + stackAnnotation: visualizer.userAnnotation.stackAnnotation, + typeAnnotation: { + ...visualizer.userAnnotation.typeAnnotation, + [newAnnotation.typeName]: newAnnotation, + }, + }); + }, + [ + updateUserAnnotation, + visualizer.userAnnotation.stackAnnotation, + visualizer.userAnnotation.typeAnnotation, + ] + ); + + const handleLinkedNodeAnnotation = useCallback( + (possibleTypeAnnotation: PossibleLinkedListAnnotation | null) => { + if (possibleTypeAnnotation === null) return; + const linkedNodeAnnotation: LinkedListAnnotation = { + typeName: backendType.typeName as `struct ${string}`, + type: DataStructureType.LinkedList, + value: { + name: possibleTypeAnnotation.possibleValues[0].name, + typeName: possibleTypeAnnotation.possibleValues[0].typeName, + }, + next: { + name: possibleTypeAnnotation.possibleNexts[0].name, + typeName: possibleTypeAnnotation.possibleNexts[0].typeName, + }, + }; + setNodeAnnotation(linkedNodeAnnotation); + handleUpdateNodeAnnotation(linkedNodeAnnotation); + }, + [backendType.typeName, handleUpdateNodeAnnotation] + ); useEffect(() => { handleLinkedNodeAnnotation(possibleTypeDeclForLinkedList); - }, [possibleTypeDeclForLinkedList]); + }, [possibleTypeDeclForLinkedList, handleLinkedNodeAnnotation]); useEffect(() => { const possibleTypeDecls = createPossibleLinkedListTypeDecl(backendType); diff --git a/client/src/visualiser-debugger/Component/Configuration/StackVarDeclaration.tsx b/client/src/visualiser-debugger/Component/Configuration/StackVarDeclaration.tsx index 635ef4aaa..10df01c33 100644 --- a/client/src/visualiser-debugger/Component/Configuration/StackVarDeclaration.tsx +++ b/client/src/visualiser-debugger/Component/Configuration/StackVarDeclaration.tsx @@ -46,7 +46,7 @@ export const StackVarAnnotation: React.FC = ({ if (name in stackAnnotation && stackAnnotation[name] === null) { setSelectedRole(StackVariableRole.Empty); } - }, []); + }, [memoryValue.typeName, name, selectedRole, stackAnnotation, updateStackAnnotation]); return (
diff --git a/client/src/visualiser-debugger/Component/Configuration/TypeAnnotation.tsx b/client/src/visualiser-debugger/Component/Configuration/TypeAnnotation.tsx index 0a6717d3a..6ef7614c8 100644 --- a/client/src/visualiser-debugger/Component/Configuration/TypeAnnotation.tsx +++ b/client/src/visualiser-debugger/Component/Configuration/TypeAnnotation.tsx @@ -28,7 +28,7 @@ export const TypeAnnotation: React.FC = ({ if (createPossibleLinkedListTypeDecl(typeDeclaration) !== null) { setSelectedRole(BackendTypeRole.LinkedList); } - }, []); + }, [typeDeclaration]); return (
diff --git a/client/src/visualiser-debugger/Component/Console/Console.tsx b/client/src/visualiser-debugger/Component/Console/Console.tsx index 2d5b9c9f1..abfbe0518 100644 --- a/client/src/visualiser-debugger/Component/Console/Console.tsx +++ b/client/src/visualiser-debugger/Component/Console/Console.tsx @@ -40,7 +40,7 @@ const Console = ({ scrollToBottom }: ConsoleProp) => { } else { setInput(prefix); } - }, [isCompiled, prefix]); + }, [isCompiled, prefix, appendConsoleChunks, currFocusFilePath, fileSystem]); // Every time when user add input to console, check the corresponding command useEffect(() => { @@ -66,7 +66,17 @@ const Console = ({ scrollToBottom }: ConsoleProp) => { const fileName = command.replace('rm ', ''); removeFile(fileName); } - }, [consoleChunks]); + }, [ + consoleChunks, + changeDir, + clearConsole, + createNewDir, + createNewFile, + listFiles, + prefix, + printWorkingDir, + removeFile, + ]); const handleInput = async (currInput: string) => { if (isCompiled) { diff --git a/client/src/visualiser-debugger/Component/Control/Controls.tsx b/client/src/visualiser-debugger/Component/Control/Controls.tsx index 2edb34d54..35f0a96cc 100644 --- a/client/src/visualiser-debugger/Component/Control/Controls.tsx +++ b/client/src/visualiser-debugger/Component/Control/Controls.tsx @@ -3,7 +3,7 @@ import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import UndoIcon from '@mui/icons-material/Undo'; import RedoIcon from '@mui/icons-material/Redo'; import CircularProgress from '@mui/material/CircularProgress'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState, useCallback } from 'react'; import { Fade } from '@mui/material'; import { handleCompileClicked } from '@/visualiser-debugger/Store/onboardingStore'; import { useSocketCommunication } from '@/Services/useSocketCommunication'; @@ -36,29 +36,32 @@ const Controls = () => { setBufferMode((mode) => !mode); }; - const startBuffering = async (bufferSize: number) => { - if (bufferingRef.current) return; - bufferingRef.current = true; - setLoading(true); + const startBuffering = useCallback( + async (bufferSize: number) => { + if (bufferingRef.current) return; + bufferingRef.current = true; + setLoading(true); - setMessage({ - content: 'Buffer started.', - colorTheme: 'warning', - durationMs: DEFAULT_MESSAGE_DURATION, - }); + setMessage({ + content: 'Buffer started.', + colorTheme: 'warning', + durationMs: DEFAULT_MESSAGE_DURATION, + }); - await bulkSendNextStates(bufferSize); + await bulkSendNextStates(bufferSize); - setMessage({ - content: 'Buffer completed.', - colorTheme: 'info', - durationMs: DEFAULT_MESSAGE_DURATION, - }); + setMessage({ + content: 'Buffer completed.', + colorTheme: 'info', + durationMs: DEFAULT_MESSAGE_DURATION, + }); - setLoading(false); - setBufferMode(false); - bufferingRef.current = false; - }; + setLoading(false); + setBufferMode(false); + bufferingRef.current = false; + }, + [bulkSendNextStates, setMessage] + ); useEffect(() => { if (!isActive) { @@ -81,9 +84,10 @@ const Controls = () => { setBufferMode(false); } - }, [bufferMode]); + }, [bufferMode, currentIndex, setMessage, startBuffering, states.length]); const [autoNext, setAutoNext] = useState(false); + // To Process new backend frame and store it into frontend state useEffect(() => { if (isInitialBackendState(currFrame)) { return; @@ -106,7 +110,7 @@ const Controls = () => { } console.error(`Unable to parse backend state: ${issue} is undefined`); } - }, [currFrame, userAnnotation]); + }, [currFrame, userAnnotation, autoNext, parser, stepForward]); return (
@@ -133,6 +137,8 @@ const Controls = () => { )} + + {/* Step Backward BTN */} + + {/* Step Forward BTN */}