Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"start": "vite --host",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint './src/**/*.{tsx,ts}'",
"lint": "eslint \"./src/**/*.{tsx,ts}\"",
"lint-fix": "eslint './src/**/*.{tsx,ts,jsx,js}' --fix",
"tsc": "tsc"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const CodeEditor: React.FC = () => {
useEffect(() => {
// Update currFile based on currFocusFilePath
const file = fileSystem.getFileFromPath(currFocusFilePath) as IFileFileNode;
if (file === undefined) {
return;
}
setOnboardingCurrFile(file.name);
setCurrFile(file);
setCode(file?.data || '');
Expand Down
54 changes: 45 additions & 9 deletions client/src/visualiser-debugger/Component/Console/Console.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// TODO: Proper rework on this file => we want to re-design this anyway. I can't fix lint now because it will potentially change functioanlity of the file
import React, { useEffect, useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import styles from 'styles/Console.module.css';
import classNames from 'classnames';
import { useGlobalStore } from 'visualiser-debugger/Store/globalStateStore';
import { useFrontendStateStore } from 'visualiser-debugger/Store/frontendStateStore';
import { useUserFsStateStore } from 'visualiser-debugger/Store/userFsStateStore';
import useConsolePathStore from 'visualiser-debugger/Store/consolePathStore';
import CustomCaret from './CustomCaret';
import { IFileFileNode } from '../FileTree/FS/IFileSystem';

Expand All @@ -13,8 +14,17 @@ type ConsoleProp = {
};

const Console = ({ scrollToBottom }: ConsoleProp) => {
const PREFIX = 'structs.sh % ';
const [input, setInput] = useState(PREFIX);
const {
prefix,
clearConsole,
printWorkingDir,
createNewDir,
changeDir,
listFiles,
createNewFile,
removeFile,
} = useConsolePathStore();
const [input, setInput] = useState(prefix);
const inputElement = useRef<HTMLInputElement>(null);

const consoleChunks = useGlobalStore((state) => state.consoleChunks);
Expand All @@ -25,21 +35,47 @@ const Console = ({ scrollToBottom }: ConsoleProp) => {
useEffect(() => {
if (isCompiled) {
const file = fileSystem.getFileFromPath(currFocusFilePath) as IFileFileNode;
appendConsoleChunks(`${PREFIX}gcc -g ${file.name} -o a\n`);
appendConsoleChunks(`${prefix}gcc -g ${file.name} -o a\n`);
setInput('');
} else {
setInput(PREFIX);
setInput(prefix);
}
}, [isCompiled]);
}, [isCompiled, prefix]);

const handleInput = (currInput: string) => {
// Every time when user add input to console, check the corresponding command
useEffect(() => {
if (consoleChunks.length <= 0) return;
const consoleIndex = consoleChunks.length - 1;
if (!consoleChunks[consoleIndex].startsWith(prefix)) return;

// Running user command
const command = consoleChunks[consoleIndex].trim().replace(`${prefix}`, '');
if (command === 'clear') clearConsole();
else if (command === 'pwd') printWorkingDir();
else if (command.startsWith('ls')) listFiles();
else if (command.startsWith('mkdir')) {
const dirName = command.replace('mkdir ', '');
createNewDir(dirName);
} else if (command.startsWith('cd')) {
const dirPath = command.replace('cd ', '');
changeDir(dirPath);
} else if (command.startsWith('touch')) {
const fileName = command.replace('touch ', '');
createNewFile(fileName);
} else if (command.startsWith('rm')) {
const fileName = command.replace('rm ', '');
removeFile(fileName);
}
}, [consoleChunks]);

const handleInput = async (currInput: string) => {
if (isCompiled) {
setInput(currInput);
return;
}

// Ensure structs.sh prefix can't be deleted
if (currInput.startsWith(PREFIX)) {
if (currInput.startsWith(prefix)) {
setInput(currInput);
}
};
Expand All @@ -50,7 +86,7 @@ const Console = ({ scrollToBottom }: ConsoleProp) => {
return;
}

setInput(PREFIX);
setInput(prefix);
};

const focus = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface IFileSystem {
getDirFromPath(path: string): IFileDirNode | undefined;

deleteFile(file: IFileFileNode | IFileDirNode): void;
doesDirExists(dirPath: string): boolean;

// Pass back root directory
saveChanges(): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,38 @@ export class LocalStorageFS implements IFileSystem {

return current;
}

doesDirExists(dirPath: string): boolean {
if (!dirPath) {
return false;
}

const segments = dirPath.split('/').filter((seg) => seg !== '');
let currDir: IFileDirNode = this.root;

if (dirPath === this.root.name) {
return true;
}

if (segments[0] === this.root.name) {
segments.shift();
}

const isDirValid = segments.every((seg) => {
if (currDir.type !== 'dir' || !currDir.children || !currDir.children[seg]) {
return false;
}

const child = currDir.children[seg];
// Check if the child is a directory
if (child.type !== 'dir') {
return false;
}

currDir = child;
return true;
});

return isDirValid;
}
}
173 changes: 173 additions & 0 deletions client/src/visualiser-debugger/Store/consolePathStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Store console curr working directory
import { create } from 'zustand';
import { useUserFsStateStore } from './userFsStateStore';
import { useGlobalStore } from './globalStateStore';
import { IFileFileNode, IFileDirNode } from '../Component/FileTree/FS/IFileSystem';

interface ConsolePathState {
prefix: string;
currWorkingDir: string;
setPrefix: (updatedPrefix: string) => void;
setCurrWorkingDir: (newWorkingDir: string) => void;
updatePrefixPath: (newPath: string) => void;
clearConsole: () => void;
printWorkingDir: () => void;
createNewDir: (newDirName: string) => void;
changeDir: (dirPath: string) => void;
listFiles: () => void;
createNewFile: (newFileName: string) => void;
removeFile: (fileName: string) => void;
}

const useConsolePathStore = create<ConsolePathState>((set, get) => ({
prefix: `structs.sh/root % `, // prefix path is set to root
currWorkingDir: 'root',
setPrefix: (updatedPrefix: string) => set({ prefix: updatedPrefix }),
setCurrWorkingDir: (newWorkingDir) => set({ currWorkingDir: newWorkingDir }),
updatePrefixPath: (newPath) => {
const { setCurrWorkingDir, setPrefix } = get();
setCurrWorkingDir(newPath);
setPrefix(`structs.sh/${newPath} % `);
},
clearConsole: () => {
const { resetConsoleChunks } = useGlobalStore.getState();
resetConsoleChunks();
},
printWorkingDir: () => {
const { currWorkingDir } = get();
const { appendConsoleChunks } = useGlobalStore.getState();
appendConsoleChunks(`/${currWorkingDir}\n`);
},
createNewDir: (newDirName: string) => {
const { currWorkingDir } = get();
const { fileSystem } = useUserFsStateStore.getState();
const { appendConsoleChunks } = useGlobalStore.getState();

// Error checking
if (newDirName.length === 0) {
appendConsoleChunks('mkdir: missing operand\n');
return;
}

if (newDirName.includes('/')) {
appendConsoleChunks(`mkdir: cannot create directory ${newDirName}`);
}

// First thing -> findNodeByPath
const newFolder: IFileDirNode = {
name: newDirName,
path: `${currWorkingDir}/${newDirName}`,
type: 'dir',
children: {},
parentPath: currWorkingDir,
};

if (!fileSystem.addDir(newFolder)) {
appendConsoleChunks('failed to create a new directory, directory exists');
return;
}

fileSystem.saveChanges();
},
changeDir: (dirPath) => {
const { currWorkingDir, updatePrefixPath } = get();
const { fileSystem } = useUserFsStateStore.getState();
const { appendConsoleChunks } = useGlobalStore.getState();

// Cannot go further than the root directory
if (dirPath === '..') {
if (currWorkingDir === 'root') return;

// Destructure the current working directory
const dirPathArray = currWorkingDir.split('/');
dirPathArray.pop();
const newPath = dirPathArray.join('/');
updatePrefixPath(newPath);
return;
}

// Update directory path to the root right away
if (dirPath === 'root') {
updatePrefixPath('root');
return;
}

const newPath = `${currWorkingDir}/${dirPath}`;
const getDir = fileSystem.getDirFromPath(newPath);
if (getDir && (getDir.type !== 'dir' || getDir.name === '/root')) {
appendConsoleChunks(`cd: ${dirPath}: Not a directory\n`);
return;
}

const doesDirExists = fileSystem.doesDirExists(newPath);
if (!doesDirExists) {
appendConsoleChunks(`cd: ${dirPath}: No such file or directory\n`);
return;
}

updatePrefixPath(newPath);
},
listFiles: () => {
const { currWorkingDir } = get();
const { fileSystem } = useUserFsStateStore.getState();
const { appendConsoleChunks } = useGlobalStore.getState();

const filesInCurrDir = fileSystem.getDirFromPath(currWorkingDir)?.children;
let filelist = '';
if (filesInCurrDir !== undefined) {
Object.values(filesInCurrDir).forEach((currFile) => {
const fileName = currFile.name.trim();
if (fileName.length > 0) {
filelist = `${filelist} ${fileName.trim()}`;
}
});
}
filelist = filelist.trim();
appendConsoleChunks(`${filelist}\n`);
},
createNewFile: (newFileName: string) => {
const { currWorkingDir } = get();
const { fileSystem } = useUserFsStateStore.getState();
const { appendConsoleChunks } = useGlobalStore.getState();

// Error handling
if (newFileName.includes('/')) {
appendConsoleChunks(`Unable to create file: '/' cannot exists in a file name.\n`);
return;
}
// Handle creating multiple files at the same time
const files = newFileName.split(/\s+/);
files.forEach((file) => {
const newFile: IFileFileNode = {
name: file,
path: `${currWorkingDir}/${file}`,
type: 'file',
data: '',
parentPath: currWorkingDir,
};
fileSystem.addFile(newFile);
});
},
removeFile: (fileName: string) => {
const { currWorkingDir } = get();
const { fileSystem } = useUserFsStateStore.getState();
const { appendConsoleChunks } = useGlobalStore.getState();

const filePath = `${currWorkingDir}/${fileName}`;
// Check if file exists
const fileToBeDeleted = fileSystem.getFileFromPath(filePath);
if (!fileToBeDeleted) {
appendConsoleChunks(`rm: ${fileToBeDeleted}: No such file exists\n`);
return;
}

if (fileToBeDeleted && fileToBeDeleted.type !== 'file') {
appendConsoleChunks(`rm: ${fileName}: is not a file\n`);
return;
}

fileSystem.deleteFile(fileToBeDeleted);
},
}));

export default useConsolePathStore;
Loading