diff --git a/.coverage-fix.tmp b/.coverage-fix.tmp deleted file mode 100644 index 8b13789179..0000000000 --- a/.coverage-fix.tmp +++ /dev/null @@ -1 +0,0 @@ - diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..495e8a028e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Ensure all TS/TSX files are treated as text and normalized with LF line endings +*.ts text eol=lf +*.tsx text eol=lf diff --git a/et --hard 25755a4c b/et --hard 25755a4c deleted file mode 100644 index a863f0b2f2..0000000000 --- a/et --hard 25755a4c +++ /dev/null @@ -1,80 +0,0 @@ -e00e5f28 (HEAD -> fix-cse, origin/master, origin/HEAD, gigopogo/fix-cse, master) HEAD@{0}: reset: moving to HEAD~1 -25755a4c HEAD@{1}: commit: Version 1 -e00e5f28 (HEAD -> fix-cse, origin/master, origin/HEAD, gigopogo/fix-cse, master) HEAD@{2}: reset: moving to master -7a6fcb7d (gigopogo/fix/cse-layout-machine) HEAD@{3}: checkout: moving from master to fix-cse -e00e5f28 (HEAD -> fix-cse, origin/master, origin/HEAD, gigopogo/fix-cse, master) HEAD@{4}: commit: Fixed the error where the frames WERE not moving due to the Layout team's invariant! -99f83010 HEAD@{5}: commit: Fixed the sentry bot suggestion -2ef3c484 HEAD@{6}: commit: Fixed tsc error -50f2b774 HEAD@{7}: pull: Fast-forward -d6c68817 HEAD@{8}: pull --rebase origin master (finish): returning to refs/heads/master -d6c68817 HEAD@{9}: pull --rebase origin master (pick): Fixed the following errors and implemented suggestions -26345cb6 HEAD@{10}: pull --rebase origin master (start): checkout 26345cb6b61bda45de0f47c64edef54e2dc4cfc8 -b12755a3 HEAD@{11}: commit: Fixed the following errors and implemented suggestions -44e1f30c HEAD@{12}: checkout: moving from fix-cse to master -7a6fcb7d (gigopogo/fix/cse-layout-machine) HEAD@{13}: reset: moving to HEAD -7a6fcb7d (gigopogo/fix/cse-layout-machine) HEAD@{14}: checkout: moving from master to fix-cse -44e1f30c HEAD@{15}: commit: Changed from Hide -> Clear and the icon -2c3897cd HEAD@{16}: checkout: moving from Hulk_fix to master -953df696 (Hulk_fix) HEAD@{17}: commit: Fixed the formatting! -d9ce60bf HEAD@{18}: pull: Fast-forward -039f07de HEAD@{19}: reset: moving to HEAD -039f07de HEAD@{20}: commit: Hulk Fix! -5e84b1d7 (upstream/master) HEAD@{21}: Branch: renamed refs/heads/backup-branch to refs/heads/Hulk_fix -5e84b1d7 (upstream/master) HEAD@{23}: reset: moving to upstream/master -3b75e828 HEAD@{24}: merge upstream/master: Merge made by the 'ort' strategy. -22d7859d HEAD@{25}: checkout: moving from master to backup-branch -2c3897cd HEAD@{26}: commit: Fix : Toggle -> One time cleanup -39a1ac0e HEAD@{27}: commit: Fix : CI format errors and made it from checkbox -> onClick button! -321f22df HEAD@{28}: commit: Fix : Removed gaps -f02260af HEAD@{29}: commit: Added the functionality for toggle of dead vs ONLY live for -baebfb5f HEAD@{30}: merge backup-branch: Merge made by the 'ort' strategy. -5e84b1d7 (upstream/master) HEAD@{31}: reset: moving to upstream/master -22d7859d HEAD@{32}: checkout: moving from backup-branch to master -22d7859d HEAD@{33}: checkout: moving from master to backup-branch -22d7859d HEAD@{34}: commit: Added the removeDeadFrames feature -3908dbea HEAD@{35}: reset: moving to upstream/master -5e84b1d7 (upstream/master) HEAD@{36}: reset: moving to HEAD -5e84b1d7 (upstream/master) HEAD@{37}: pull: Fast-forward -96ce8d89 (gigopogo/master) HEAD@{38}: checkout: moving from shortCircuitFix/master to master -176f3107 (origin/shortCircuitFix/master) HEAD@{39}: commit: Fixed the SENTRY bot error -0e03ed5a HEAD@{40}: commit: Fixed the issue described at #3574 -3908dbea HEAD@{41}: reset: moving to upstream/master -96ce8d89 (gigopogo/master) HEAD@{42}: checkout: moving from master to shortCircuitFix/master -96ce8d89 (gigopogo/master) HEAD@{43}: reset: moving to upstream/master -8fb394fe HEAD@{44}: reset: moving to origin/master -35fd4244 HEAD@{45}: checkout: moving from pr-3575 to master -d406e33d (upstream/partial-revert-3570) HEAD@{46}: checkout: moving from master to pr-3575 -35fd4244 HEAD@{47}: checkout: moving from pr-3575 to master -d406e33d (upstream/partial-revert-3570) HEAD@{48}: checkout: moving from master to pr-3575 -35fd4244 HEAD@{49}: checkout: moving from pr-3575 to master -d406e33d (upstream/partial-revert-3570) HEAD@{50}: checkout: moving from fix/liveness-clean to pr-3575 -35fd4244 HEAD@{51}: checkout: moving from master to fix/liveness-clean -35fd4244 HEAD@{52}: reset: moving to upstream/master -8fb394fe HEAD@{53}: rebase (abort): returning to refs/heads/master -35fd4244 HEAD@{54}: pull --rebase upstream master (start): checkout 35fd42446edc8775d4aee2013dccda960875e750 -8fb394fe HEAD@{55}: checkout: moving from master to master -8fb394fe HEAD@{56}: commit: CI errors fixed commit TRIAL -0a39954d HEAD@{57}: commit: Fixed and removed all the needless import statements and other errors in seperate files! -132b52b6 HEAD@{58}: commit: CI errors for formatting fixed -391f6c4a HEAD@{59}: commit: Fixed the yarn.lock file according to the CI dependencies and req -6b32f2b4 HEAD@{60}: commit: yarn test fail errors in CI FIXED -a3c12ce3 HEAD@{61}: checkout: moving from master to master -a3c12ce3 HEAD@{62}: commit: Fixed the CI yarn test AUTO errors on Nav Bar -560b326c HEAD@{63}: commit: PR CI auto yarn TEST conflict RESOLVED -a13e801b HEAD@{64}: reset: moving to HEAD~1 -dfc9e353 HEAD@{65}: commit: Fixed yarn test CI auto errors for PR conflicts -a13e801b HEAD@{66}: checkout: moving from master to master -a13e801b HEAD@{67}: commit (merge): PR Merge Conflic RESOLVE -e0e3308e HEAD@{68}: pull: Fast-forward -f1f853c6 HEAD@{69}: commit: Final_Draft-I PR - I -4d01e792 HEAD@{70}: reset: moving to HEAD -4d01e792 HEAD@{71}: reset: moving to HEAD -4d01e792 HEAD@{72}: commit: Week 5 Pt 1 BUG FIX -6790c5b7 HEAD@{73}: reset: moving to HEAD -6790c5b7 HEAD@{74}: commit: Week 4 Pt III -43447418 HEAD@{75}: commit: WEEK - 4 Pt II -42ff8a64 HEAD@{76}: commit (amend): WEEK 4 -33c788d4 HEAD@{77}: commit: This commit made the following changes -d90d96fa HEAD@{78}: pull: Fast-forward -902b5eb6 HEAD@{79}: commit: WEEK - 3 -013fd0a9 HEAD@{80}: clone: from https://github.com/Akshay-2007-1/CSE_liveness_frontend.git diff --git a/src/features/cseMachine/CseMachine.tsx b/src/features/cseMachine/CseMachine.tsx index 769f06ff0a..bd54cdfa7f 100644 --- a/src/features/cseMachine/CseMachine.tsx +++ b/src/features/cseMachine/CseMachine.tsx @@ -1,257 +1,257 @@ -import { Context } from 'js-slang'; -import { Control, Stash } from 'js-slang/dist/cse-machine/interpreter'; -import React from 'react'; - -import { arrowSelection } from './components/arrows/ArrowSelection'; -import { Layout, LayoutCache } from './CseMachineLayout'; -import { EnvTree } from './CseMachineTypes'; -import { deepCopyTree, getEnvId } from './CseMachineUtils'; - -type SetVis = (vis: React.ReactNode) => void; -type SetEditorHighlightedLines = (segments: [number, number][]) => void; -type SetisStepLimitExceeded = (isControlEmpty: boolean) => void; - -/** CSE Machine is exposed from this class */ -export default class CseMachine { - /** callback function to update the visualization state in the SideContentCseMachine component */ - private static setVis: SetVis; - /** function to highlight editor lines */ - public static setEditorHighlightedLines: SetEditorHighlightedLines; - /** callback function to update the step limit exceeded state in the SideContentCseMachine component */ - private static setIsStepLimitExceeded: SetisStepLimitExceeded; - // Ghost layout snapshots, separated by mode to keep coordinates fixed within each mode. - public static normalLayoutCache: LayoutCache | null = null; - public static printLayoutCache: LayoutCache | null = null; - private static printableMode: boolean = false; - private static controlStash: boolean = false; // TODO: discuss if the default should be true - private static stackTruncated: boolean = false; - private static centerAlignment: boolean = false; // added for center alignment - private static centerAlignmentToggled: boolean = false; - private static environmentTree: EnvTree | undefined; - private static currentEnvId: string; - private static control: Control | undefined; - private static stash: Stash | undefined; - public static togglePrintableMode(): void { - CseMachine.printableMode = !CseMachine.printableMode; - } - public static toggleControlStash(): void { - CseMachine.controlStash = !CseMachine.controlStash; - } - public static toggleStackTruncated(): void { - CseMachine.stackTruncated = !CseMachine.stackTruncated; - } - public static setClearDeadFrames(enabled: boolean): void { - Layout.clearDeadFrames = enabled; - } - public static clearCachedLayouts(): void { - Layout.currentLight = undefined; - Layout.currentDark = undefined; - Layout.currentStackDark = undefined; - Layout.currentStackTruncDark = undefined; - Layout.currentStackLight = undefined; - Layout.currentStackTruncLight = undefined; - Layout.prevLayout = undefined; - Layout.key = 0; - CseMachine.normalLayoutCache = null; - CseMachine.printLayoutCache = null; - } - // added for center alignment - public static toggleCenterAlignment(): void { - CseMachine.centerAlignment = !CseMachine.centerAlignment; - CseMachine.centerAlignmentToggled = true; - } - - public static getCurrentEnvId(): string { - return CseMachine.currentEnvId; - } - public static getPrintableMode(): boolean { - return CseMachine.printableMode; - } - public static getControlStash(): boolean { - return CseMachine.controlStash; - } - public static getStackTruncated(): boolean { - return CseMachine.stackTruncated; - } - // added for center alignment - public static getCenterAlignment(): boolean { - return CseMachine.centerAlignment; - } - public static getMasterLayout(): LayoutCache | null { - return CseMachine.getPrintableMode() - ? CseMachine.printLayoutCache - : CseMachine.normalLayoutCache; - } - public static setMasterLayout(cache: LayoutCache): void { - if (CseMachine.getPrintableMode()) { - CseMachine.printLayoutCache = cache; - } else { - CseMachine.normalLayoutCache = cache; - } - } - - public static isControl(): boolean { - return this.control ? !this.control.isEmpty() : false; - } - - /** SideContentCseMachine initializes this onMount with the callback function */ - static init( - setVis: SetVis, - width: number, - height: number, - setEditorHighlightedLines: (segments: [number, number][]) => void, - setIsStepLimitExceeded: SetisStepLimitExceeded - ) { - Layout.visibleHeight = height; - Layout.visibleWidth = width; - this.setVis = setVis; - this.setEditorHighlightedLines = setEditorHighlightedLines; - this.setIsStepLimitExceeded = setIsStepLimitExceeded; - } - - static clear() { - Layout.values.clear(); - arrowSelection.clearSelection(); - } - - /** updates the visualization state in the SideContentCseMachine component based on - * the JS Slang context passed in */ - static drawCse(context: Context) { - // store environmentTree at last breakpoint. - CseMachine.environmentTree = deepCopyTree(context.runtime.environmentTree as EnvTree); - CseMachine.currentEnvId = getEnvId(context.runtime.environments[0]); - if (!this.setVis || !context.runtime.control || !context.runtime.stash) - throw new Error('CSE machine not initialized'); - CseMachine.control = context.runtime.control; - CseMachine.stash = context.runtime.stash; - CseMachine.setClearDeadFrames(false); - - Layout.setContext( - context.runtime.environmentTree as EnvTree, - context.runtime.control, - context.runtime.stash, - context.chapter - ); - - // Build ghost layout cache lazily per mode, using mode-specific layout. - if (!CseMachine.normalLayoutCache || !CseMachine.printLayoutCache) { - const originalMode = CseMachine.getPrintableMode(); - - const buildCache = (printable: boolean) => { - CseMachine.printableMode = printable; - Layout.setContext( - context.runtime.environmentTree as EnvTree, - context.runtime.control!, - context.runtime.stash!, - context.chapter - ); - return Layout.getLayoutPositions(this.controlStash); - }; - - CseMachine.printLayoutCache = buildCache(true); - CseMachine.normalLayoutCache = buildCache(false); - - // Restore the user's actual mode setting and layout. - CseMachine.printableMode = originalMode; - Layout.setContext( - context.runtime.environmentTree as EnvTree, - context.runtime.control, - context.runtime.stash, - context.chapter - ); - } - - // Apply Fixed Positions - if (CseMachine.getMasterLayout()) { - Layout.applyFixedPositions(); - } - this.setVis(Layout.draw()); - this.setIsStepLimitExceeded(context.runtime.control.isEmpty()); - Layout.updateDimensions(Layout.visibleWidth, Layout.visibleHeight); - } - - static redraw() { - if (CseMachine.environmentTree && CseMachine.control && CseMachine.stash) { - // checks if the required diagram exists, and updates the dom node using setVis - - // if center alignment is toggled, change the alignment and redraw the diagram with new coordinates - if (this.centerAlignmentToggled) { - Layout.setContext(CseMachine.environmentTree, CseMachine.control, CseMachine.stash); - if (!CseMachine.getMasterLayout()) { - CseMachine.setMasterLayout(Layout.getLayoutPositions(this.controlStash)); - } - if (CseMachine.getMasterLayout()) { - Layout.applyFixedPositions(); - } - this.setVis(Layout.draw()); - this.centerAlignmentToggled = false; - } - - if ( - CseMachine.getPrintableMode() && - CseMachine.getControlStash() && - CseMachine.getStackTruncated() && - Layout.currentStackTruncLight !== undefined - ) { - this.setVis(Layout.currentStackTruncLight); - } else if ( - CseMachine.getPrintableMode() && - CseMachine.getControlStash() && - !CseMachine.getStackTruncated() && - Layout.currentStackLight !== undefined - ) { - this.setVis(Layout.currentStackLight); - } else if ( - !CseMachine.getPrintableMode() && - CseMachine.getControlStash() && - CseMachine.getStackTruncated() && - Layout.currentStackTruncDark !== undefined - ) { - this.setVis(Layout.currentStackTruncDark); - } else if ( - !CseMachine.getPrintableMode() && - CseMachine.getControlStash() && - !CseMachine.getStackTruncated() && - Layout.currentStackDark !== undefined - ) { - this.setVis(Layout.currentStackDark); - } else if ( - CseMachine.getPrintableMode() && - !CseMachine.getControlStash() && - Layout.currentLight !== undefined - ) { - this.setVis(Layout.currentLight); - } else if ( - !CseMachine.getPrintableMode() && - !CseMachine.getControlStash() && - Layout.currentDark !== undefined - ) { - this.setVis(Layout.currentDark); - } else { - Layout.setContext(CseMachine.environmentTree, CseMachine.control, CseMachine.stash); - if (CseMachine.getMasterLayout()) { - Layout.applyFixedPositions(); - } - this.setVis(Layout.draw()); - } - Layout.updateDimensions(Layout.visibleWidth, Layout.visibleHeight); - } - } - - static updateDimensions(width: number, height: number) { - if (Layout.stageRef != null && width !== null && height !== null) { - Layout.updateDimensions(width, height); - } - } - - static clearCse() { - if (this.setVis) { - this.setVis(undefined); - CseMachine.environmentTree = undefined; - CseMachine.control = undefined; - CseMachine.stash = undefined; - } - CseMachine.setClearDeadFrames(false); - this.clear(); - } -} +import { Context } from 'js-slang'; +import { Control, Stash } from 'js-slang/dist/cse-machine/interpreter'; +import React from 'react'; + +import { arrowSelection } from './components/arrows/ArrowSelection'; +import { Layout, LayoutCache } from './CseMachineLayout'; +import { EnvTree } from './CseMachineTypes'; +import { deepCopyTree, getEnvId } from './CseMachineUtils'; + +type SetVis = (vis: React.ReactNode) => void; +type SetEditorHighlightedLines = (segments: [number, number][]) => void; +type SetisStepLimitExceeded = (isControlEmpty: boolean) => void; + +/** CSE Machine is exposed from this class */ +export default class CseMachine { + /** callback function to update the visualization state in the SideContentCseMachine component */ + private static setVis: SetVis; + /** function to highlight editor lines */ + public static setEditorHighlightedLines: SetEditorHighlightedLines; + /** callback function to update the step limit exceeded state in the SideContentCseMachine component */ + private static setIsStepLimitExceeded: SetisStepLimitExceeded; + // Ghost layout snapshots, separated by mode to keep coordinates fixed within each mode. + public static normalLayoutCache: LayoutCache | null = null; + public static printLayoutCache: LayoutCache | null = null; + private static printableMode: boolean = false; + private static controlStash: boolean = false; // TODO: discuss if the default should be true + private static stackTruncated: boolean = false; + private static centerAlignment: boolean = false; // added for center alignment + private static centerAlignmentToggled: boolean = false; + private static environmentTree: EnvTree | undefined; + private static currentEnvId: string; + private static control: Control | undefined; + private static stash: Stash | undefined; + public static togglePrintableMode(): void { + CseMachine.printableMode = !CseMachine.printableMode; + } + public static toggleControlStash(): void { + CseMachine.controlStash = !CseMachine.controlStash; + } + public static toggleStackTruncated(): void { + CseMachine.stackTruncated = !CseMachine.stackTruncated; + } + public static setClearDeadFrames(enabled: boolean): void { + Layout.clearDeadFrames = enabled; + } + public static clearCachedLayouts(): void { + Layout.currentLight = undefined; + Layout.currentDark = undefined; + Layout.currentStackDark = undefined; + Layout.currentStackTruncDark = undefined; + Layout.currentStackLight = undefined; + Layout.currentStackTruncLight = undefined; + Layout.prevLayout = undefined; + Layout.key = 0; + CseMachine.normalLayoutCache = null; + CseMachine.printLayoutCache = null; + } + // added for center alignment + public static toggleCenterAlignment(): void { + CseMachine.centerAlignment = !CseMachine.centerAlignment; + CseMachine.centerAlignmentToggled = true; + } + + public static getCurrentEnvId(): string { + return CseMachine.currentEnvId; + } + public static getPrintableMode(): boolean { + return CseMachine.printableMode; + } + public static getControlStash(): boolean { + return CseMachine.controlStash; + } + public static getStackTruncated(): boolean { + return CseMachine.stackTruncated; + } + // added for center alignment + public static getCenterAlignment(): boolean { + return CseMachine.centerAlignment; + } + public static getMasterLayout(): LayoutCache | null { + return CseMachine.getPrintableMode() + ? CseMachine.printLayoutCache + : CseMachine.normalLayoutCache; + } + public static setMasterLayout(cache: LayoutCache): void { + if (CseMachine.getPrintableMode()) { + CseMachine.printLayoutCache = cache; + } else { + CseMachine.normalLayoutCache = cache; + } + } + + public static isControl(): boolean { + return this.control ? !this.control.isEmpty() : false; + } + + /** SideContentCseMachine initializes this onMount with the callback function */ + static init( + setVis: SetVis, + width: number, + height: number, + setEditorHighlightedLines: (segments: [number, number][]) => void, + setIsStepLimitExceeded: SetisStepLimitExceeded + ) { + Layout.visibleHeight = height; + Layout.visibleWidth = width; + this.setVis = setVis; + this.setEditorHighlightedLines = setEditorHighlightedLines; + this.setIsStepLimitExceeded = setIsStepLimitExceeded; + } + + static clear() { + Layout.values.clear(); + arrowSelection.clearSelection(); + } + + /** updates the visualization state in the SideContentCseMachine component based on + * the JS Slang context passed in */ + static drawCse(context: Context) { + // store environmentTree at last breakpoint. + CseMachine.environmentTree = deepCopyTree(context.runtime.environmentTree as EnvTree); + CseMachine.currentEnvId = getEnvId(context.runtime.environments[0]); + if (!this.setVis || !context.runtime.control || !context.runtime.stash) + throw new Error('CSE machine not initialized'); + CseMachine.control = context.runtime.control; + CseMachine.stash = context.runtime.stash; + CseMachine.setClearDeadFrames(false); + + Layout.setContext( + context.runtime.environmentTree as EnvTree, + context.runtime.control, + context.runtime.stash, + context.chapter + ); + + // Build ghost layout cache lazily per mode, using mode-specific layout. + if (!CseMachine.normalLayoutCache || !CseMachine.printLayoutCache) { + const originalMode = CseMachine.getPrintableMode(); + + const buildCache = (printable: boolean) => { + CseMachine.printableMode = printable; + Layout.setContext( + context.runtime.environmentTree as EnvTree, + context.runtime.control!, + context.runtime.stash!, + context.chapter + ); + return Layout.getLayoutPositions(this.controlStash); + }; + + CseMachine.printLayoutCache = buildCache(true); + CseMachine.normalLayoutCache = buildCache(false); + + // Restore the user's actual mode setting and layout. + CseMachine.printableMode = originalMode; + Layout.setContext( + context.runtime.environmentTree as EnvTree, + context.runtime.control, + context.runtime.stash, + context.chapter + ); + } + + // Apply Fixed Positions + if (CseMachine.getMasterLayout()) { + Layout.applyFixedPositions(); + } + this.setVis(Layout.draw()); + this.setIsStepLimitExceeded(context.runtime.control.isEmpty()); + Layout.updateDimensions(Layout.visibleWidth, Layout.visibleHeight); + } + + static redraw() { + if (CseMachine.environmentTree && CseMachine.control && CseMachine.stash) { + // checks if the required diagram exists, and updates the dom node using setVis + + // if center alignment is toggled, change the alignment and redraw the diagram with new coordinates + if (this.centerAlignmentToggled) { + Layout.setContext(CseMachine.environmentTree, CseMachine.control, CseMachine.stash); + if (!CseMachine.getMasterLayout()) { + CseMachine.setMasterLayout(Layout.getLayoutPositions(this.controlStash)); + } + if (CseMachine.getMasterLayout()) { + Layout.applyFixedPositions(); + } + this.setVis(Layout.draw()); + this.centerAlignmentToggled = false; + } + + if ( + CseMachine.getPrintableMode() && + CseMachine.getControlStash() && + CseMachine.getStackTruncated() && + Layout.currentStackTruncLight !== undefined + ) { + this.setVis(Layout.currentStackTruncLight); + } else if ( + CseMachine.getPrintableMode() && + CseMachine.getControlStash() && + !CseMachine.getStackTruncated() && + Layout.currentStackLight !== undefined + ) { + this.setVis(Layout.currentStackLight); + } else if ( + !CseMachine.getPrintableMode() && + CseMachine.getControlStash() && + CseMachine.getStackTruncated() && + Layout.currentStackTruncDark !== undefined + ) { + this.setVis(Layout.currentStackTruncDark); + } else if ( + !CseMachine.getPrintableMode() && + CseMachine.getControlStash() && + !CseMachine.getStackTruncated() && + Layout.currentStackDark !== undefined + ) { + this.setVis(Layout.currentStackDark); + } else if ( + CseMachine.getPrintableMode() && + !CseMachine.getControlStash() && + Layout.currentLight !== undefined + ) { + this.setVis(Layout.currentLight); + } else if ( + !CseMachine.getPrintableMode() && + !CseMachine.getControlStash() && + Layout.currentDark !== undefined + ) { + this.setVis(Layout.currentDark); + } else { + Layout.setContext(CseMachine.environmentTree, CseMachine.control, CseMachine.stash); + if (CseMachine.getMasterLayout()) { + Layout.applyFixedPositions(); + } + this.setVis(Layout.draw()); + } + Layout.updateDimensions(Layout.visibleWidth, Layout.visibleHeight); + } + } + + static updateDimensions(width: number, height: number) { + if (Layout.stageRef != null && width !== null && height !== null) { + Layout.updateDimensions(width, height); + } + } + + static clearCse() { + if (this.setVis) { + this.setVis(undefined); + CseMachine.environmentTree = undefined; + CseMachine.control = undefined; + CseMachine.stash = undefined; + } + CseMachine.setClearDeadFrames(false); + this.clear(); + } +} diff --git a/src/features/cseMachine/components/arrows/GenericArrow.tsx b/src/features/cseMachine/components/arrows/GenericArrow.tsx index 99403836b3..97c885e066 100644 --- a/src/features/cseMachine/components/arrows/GenericArrow.tsx +++ b/src/features/cseMachine/components/arrows/GenericArrow.tsx @@ -1,285 +1,285 @@ -import Konva from 'konva'; -import { KonvaEventObject } from 'konva/lib/Node'; -import React, { RefObject } from 'react'; -import { Arrow as KonvaArrow, Group as KonvaGroup, Path as KonvaPath } from 'react-konva'; - -import CseMachine from '../../CseMachine'; -import { Config, ShapeDefaultProps } from '../../CseMachineConfig'; -import { Layout } from '../../CseMachineLayout'; -import { IHoverable, IVisible, StepsArray } from '../../CseMachineTypes'; -import { defaultStrokeColor, fadedStrokeColor } from '../../CseMachineUtils'; -import { Visible } from '../Visible'; -import { arrowSelection } from './ArrowSelection'; - -/** this class encapsulates an arrow to be drawn between 2 points */ -export class GenericArrow - extends Visible - implements IHoverable -{ - private _path: string = ''; - points: number[] = []; - source: Source; - target: Target | undefined; - faded: boolean = false; - private pathRef: RefObject = React.createRef(); - private arrowHeadRef: RefObject = React.createRef(); - - // Check if this arrow is selected - protected isSelected(): boolean { - return arrowSelection.isSelected(this); - } - - // Select this arrow - protected select(): void { - arrowSelection.setSelected(this); - } - - isLive: boolean = false; // Added to track if the arrow is live (an inherent property of the arrow) - /* - * The above is added since an arrow can in general be drawn between two points - * that may or may not be a Frame. Hence, we cannot determine if the arrow is live - * based on whether the source or target is a live Frame. Thus, we set this property - * when we create the arrow - */ - - constructor(from: Source) { - super(); - this.source = from; - this.target = undefined; - this._x = from.x(); - this._y = from.y(); - this.isLive = false; // default to false - } - - path(): string { - return this._path; - } - - to(to: Target): GenericArrow { - this.target = to; - this.recomputePath(); - return this; - } - - private recomputePath(): void { - if (!this.target) { - this._path = ''; - this.points = []; - return; - } - - const to = this.target; - this._x = this.source.x(); - this._y = this.source.y(); - this._width = Math.abs(to.x() - this.source.x()); - this._height = Math.abs(to.y() - this.source.y()); - - const points = this.calculateSteps().reduce>( - (acc, step) => [...acc, ...step(acc[acc.length - 2], acc[acc.length - 1])], - [this.source.x(), this.source.y()] - ); - points.splice(0, 2); - - this._path = ''; - if (points.length < 4) { - this.points = points; - return; - } - - // starting point - this._path += `M ${points[0]} ${points[1]} `; - if (points.length === 4) { - // end the path if the line only has starting and ending coordinates - this._path += `L ${points[2]} ${points[3]} `; - } else { - let n = 0; - while (n < points.length - 4) { - const [xa, ya, xb, yb, xc, yc] = points.slice(n, n + 6); - const dx1 = xb - xa; - const dx2 = xc - xb; - const dy1 = yb - ya; - const dy2 = yc - yb; - const br = Math.min( - Config.ArrowCornerRadius, - Math.max(Math.abs(dx1), Math.abs(dy1)) / 2, - Math.max(Math.abs(dx2), Math.abs(dy2)) / 2 - ); - const x1 = xb - br * Math.sign(dx1); - const y1 = yb - br * Math.sign(dy1); - const x2 = xb + br * Math.sign(dx2); - const y2 = yb + br * Math.sign(dy2); - - // draw quadratic curves over corners - this._path += `L ${x1} ${y1} Q ${xb} ${yb} ${x2} ${y2} `; - n += 2; - } - } - // end path - this._path += `L ${points[points.length - 2]} ${points[points.length - 1]} `; - this.points = points; - } - - /** - * Calculates the steps that this arrows takes. - * The arrow is decomposed into numerous straight line segments, each of which we - * can consider as a step of dx in the x direction and of dy in the y direction. - * The line segment is thus defined by 2 points (x, y) and (x + dx, y + dy) - * where (x, y) is the ending coordinate of the previous line segment. - * This function returns an array of such steps, represented by an array of functions - * [ (x, y) => [x + dx1, y + dy1], (x, y) => [x + dx2, y + dy2], ... ]. - * From this, we can retrieve the points that make up the arrow as such: - * (from.x from.y), (from.x + dx1, from.y + dy1), (from.x + dx1 + dx2, from.y + dy1 + dy2), .. - * - * Note that the functions need not be of the form (x, y) => [x + dx, y + dy]; - * (x, y) => [to.x, to.y] is valid as well, and is used to specify a step to the ending coordinates - * - * @return an array of steps represented by functions - */ - protected calculateSteps(): StepsArray { - const to = this.target; - if (!to) return []; - return [() => [to.x(), to.y()]]; - } - - /** - * Returns the hover color for this arrow type. - * Subclasses can override this to provide custom hover colors. - */ - protected getHighlightedColor(): string { - return Config.ArrowHighlightedColor; - } - - onMouseEnter = (e: KonvaEventObject) => { - if (CseMachine.getPrintableMode()) return; - e.cancelBubble = true; - this.setHighlightedStyle(); - // Move entire arrow group to top - if (this.ref.current && this.ref.current.moveToTop) { - // Move the arrow's parent container to top first, then move arrow within that - const parent = this.ref.current.getParent(); - if (parent && parent.moveToTop) { - parent.moveToTop(); - } - this.ref.current.moveToTop(); - } - this.ref.current?.getLayer()?.batchDraw(); - }; - - onMouseLeave = (e: KonvaEventObject) => { - if (CseMachine.getPrintableMode()) return; - e.cancelBubble = true; - - // Don't change color if selected - if (this.isSelected()) { - return; - } - - this.setNormalStyle(); - this.ref.current?.getLayer()?.batchDraw(); - }; - - public setHighlightedStyle() { - const highlightColor = this.getHighlightedColor(); - if (this.pathRef.current) { - this.pathRef.current.stroke(highlightColor); - this.pathRef.current.strokeWidth(Config.ArrowHoveredStrokeWidth); - } - if (this.arrowHeadRef.current) { - this.arrowHeadRef.current.fill(highlightColor); - this.arrowHeadRef.current.pointerWidth(Config.ArrowHoveredHeadSize); - this.arrowHeadRef.current.pointerLength(Config.ArrowHoveredHeadSize); - } - } - - public setNormalStyle() { - const color = this.isLive ? defaultStrokeColor() : fadedStrokeColor(); - if (this.pathRef.current) { - this.pathRef.current.stroke(color); - this.pathRef.current.strokeWidth(Config.ArrowStrokeWidth); - } - if (this.arrowHeadRef.current) { - this.arrowHeadRef.current.fill(color); - this.arrowHeadRef.current.pointerWidth(Config.ArrowHeadSize); - this.arrowHeadRef.current.pointerLength(Config.ArrowHeadSize); - } - } - - onClick = (e: KonvaEventObject) => { - e.cancelBubble = true; - - // Toggle selection - clear first, then select if it wasn't already selected - const wasSelected = this.isSelected(); - const oldArrow = arrowSelection.clearSelection(); - - // Update old arrow's visual state - if (oldArrow && oldArrow !== this) { - oldArrow.setNormalStyle(); - } - - if (!wasSelected) { - this.select(); - // Update this arrow's visual state - this.setHighlightedStyle(); - } - - // Force redraw entire layer to update all arrows - this.ref.current?.getLayer()?.batchDraw(); - }; - - onContextMenu = (e: KonvaEventObject) => { - e.evt.preventDefault(); // Prevent browser context menu - this.onClick(e); - }; - - protected getCurrentColor(): string { - if (this.isSelected()) { - return this.getHighlightedColor(); // Selected uses hover color - } - return this.faded ? fadedStrokeColor() : defaultStrokeColor(); - } - - // Subclasses can override to recompute liveness before drawing - protected updateIsLive(): void {} //kind of an abstract method - - draw() { - this.recomputePath(); - this.updateIsLive(); //just before drawijng, update liveness for the arrows (since this was causing erroes earlier ) - if (Layout.clearDeadFrames && !this.isLive) { - return null; - } - - const stroke = this.isLive ? defaultStrokeColor() : fadedStrokeColor(); - - return ( - (e.cancelBubble = true)} - > - - - - ); - } -} +import Konva from 'konva'; +import { KonvaEventObject } from 'konva/lib/Node'; +import React, { RefObject } from 'react'; +import { Arrow as KonvaArrow, Group as KonvaGroup, Path as KonvaPath } from 'react-konva'; + +import CseMachine from '../../CseMachine'; +import { Config, ShapeDefaultProps } from '../../CseMachineConfig'; +import { Layout } from '../../CseMachineLayout'; +import { IHoverable, IVisible, StepsArray } from '../../CseMachineTypes'; +import { defaultStrokeColor, fadedStrokeColor } from '../../CseMachineUtils'; +import { Visible } from '../Visible'; +import { arrowSelection } from './ArrowSelection'; + +/** this class encapsulates an arrow to be drawn between 2 points */ +export class GenericArrow + extends Visible + implements IHoverable +{ + private _path: string = ''; + points: number[] = []; + source: Source; + target: Target | undefined; + faded: boolean = false; + private pathRef: RefObject = React.createRef(); + private arrowHeadRef: RefObject = React.createRef(); + + // Check if this arrow is selected + protected isSelected(): boolean { + return arrowSelection.isSelected(this); + } + + // Select this arrow + protected select(): void { + arrowSelection.setSelected(this); + } + + isLive: boolean = false; // Added to track if the arrow is live (an inherent property of the arrow) + /* + * The above is added since an arrow can in general be drawn between two points + * that may or may not be a Frame. Hence, we cannot determine if the arrow is live + * based on whether the source or target is a live Frame. Thus, we set this property + * when we create the arrow + */ + + constructor(from: Source) { + super(); + this.source = from; + this.target = undefined; + this._x = from.x(); + this._y = from.y(); + this.isLive = false; // default to false + } + + path(): string { + return this._path; + } + + to(to: Target): GenericArrow { + this.target = to; + this.recomputePath(); + return this; + } + + private recomputePath(): void { + if (!this.target) { + this._path = ''; + this.points = []; + return; + } + + const to = this.target; + this._x = this.source.x(); + this._y = this.source.y(); + this._width = Math.abs(to.x() - this.source.x()); + this._height = Math.abs(to.y() - this.source.y()); + + const points = this.calculateSteps().reduce>( + (acc, step) => [...acc, ...step(acc[acc.length - 2], acc[acc.length - 1])], + [this.source.x(), this.source.y()] + ); + points.splice(0, 2); + + this._path = ''; + if (points.length < 4) { + this.points = points; + return; + } + + // starting point + this._path += `M ${points[0]} ${points[1]} `; + if (points.length === 4) { + // end the path if the line only has starting and ending coordinates + this._path += `L ${points[2]} ${points[3]} `; + } else { + let n = 0; + while (n < points.length - 4) { + const [xa, ya, xb, yb, xc, yc] = points.slice(n, n + 6); + const dx1 = xb - xa; + const dx2 = xc - xb; + const dy1 = yb - ya; + const dy2 = yc - yb; + const br = Math.min( + Config.ArrowCornerRadius, + Math.max(Math.abs(dx1), Math.abs(dy1)) / 2, + Math.max(Math.abs(dx2), Math.abs(dy2)) / 2 + ); + const x1 = xb - br * Math.sign(dx1); + const y1 = yb - br * Math.sign(dy1); + const x2 = xb + br * Math.sign(dx2); + const y2 = yb + br * Math.sign(dy2); + + // draw quadratic curves over corners + this._path += `L ${x1} ${y1} Q ${xb} ${yb} ${x2} ${y2} `; + n += 2; + } + } + // end path + this._path += `L ${points[points.length - 2]} ${points[points.length - 1]} `; + this.points = points; + } + + /** + * Calculates the steps that this arrows takes. + * The arrow is decomposed into numerous straight line segments, each of which we + * can consider as a step of dx in the x direction and of dy in the y direction. + * The line segment is thus defined by 2 points (x, y) and (x + dx, y + dy) + * where (x, y) is the ending coordinate of the previous line segment. + * This function returns an array of such steps, represented by an array of functions + * [ (x, y) => [x + dx1, y + dy1], (x, y) => [x + dx2, y + dy2], ... ]. + * From this, we can retrieve the points that make up the arrow as such: + * (from.x from.y), (from.x + dx1, from.y + dy1), (from.x + dx1 + dx2, from.y + dy1 + dy2), .. + * + * Note that the functions need not be of the form (x, y) => [x + dx, y + dy]; + * (x, y) => [to.x, to.y] is valid as well, and is used to specify a step to the ending coordinates + * + * @return an array of steps represented by functions + */ + protected calculateSteps(): StepsArray { + const to = this.target; + if (!to) return []; + return [() => [to.x(), to.y()]]; + } + + /** + * Returns the hover color for this arrow type. + * Subclasses can override this to provide custom hover colors. + */ + protected getHighlightedColor(): string { + return Config.ArrowHighlightedColor; + } + + onMouseEnter = (e: KonvaEventObject) => { + if (CseMachine.getPrintableMode()) return; + e.cancelBubble = true; + this.setHighlightedStyle(); + // Move entire arrow group to top + if (this.ref.current && this.ref.current.moveToTop) { + // Move the arrow's parent container to top first, then move arrow within that + const parent = this.ref.current.getParent(); + if (parent && parent.moveToTop) { + parent.moveToTop(); + } + this.ref.current.moveToTop(); + } + this.ref.current?.getLayer()?.batchDraw(); + }; + + onMouseLeave = (e: KonvaEventObject) => { + if (CseMachine.getPrintableMode()) return; + e.cancelBubble = true; + + // Don't change color if selected + if (this.isSelected()) { + return; + } + + this.setNormalStyle(); + this.ref.current?.getLayer()?.batchDraw(); + }; + + public setHighlightedStyle() { + const highlightColor = this.getHighlightedColor(); + if (this.pathRef.current) { + this.pathRef.current.stroke(highlightColor); + this.pathRef.current.strokeWidth(Config.ArrowHoveredStrokeWidth); + } + if (this.arrowHeadRef.current) { + this.arrowHeadRef.current.fill(highlightColor); + this.arrowHeadRef.current.pointerWidth(Config.ArrowHoveredHeadSize); + this.arrowHeadRef.current.pointerLength(Config.ArrowHoveredHeadSize); + } + } + + public setNormalStyle() { + const color = this.isLive ? defaultStrokeColor() : fadedStrokeColor(); + if (this.pathRef.current) { + this.pathRef.current.stroke(color); + this.pathRef.current.strokeWidth(Config.ArrowStrokeWidth); + } + if (this.arrowHeadRef.current) { + this.arrowHeadRef.current.fill(color); + this.arrowHeadRef.current.pointerWidth(Config.ArrowHeadSize); + this.arrowHeadRef.current.pointerLength(Config.ArrowHeadSize); + } + } + + onClick = (e: KonvaEventObject) => { + e.cancelBubble = true; + + // Toggle selection - clear first, then select if it wasn't already selected + const wasSelected = this.isSelected(); + const oldArrow = arrowSelection.clearSelection(); + + // Update old arrow's visual state + if (oldArrow && oldArrow !== this) { + oldArrow.setNormalStyle(); + } + + if (!wasSelected) { + this.select(); + // Update this arrow's visual state + this.setHighlightedStyle(); + } + + // Force redraw entire layer to update all arrows + this.ref.current?.getLayer()?.batchDraw(); + }; + + onContextMenu = (e: KonvaEventObject) => { + e.evt.preventDefault(); // Prevent browser context menu + this.onClick(e); + }; + + protected getCurrentColor(): string { + if (this.isSelected()) { + return this.getHighlightedColor(); // Selected uses hover color + } + return this.faded ? fadedStrokeColor() : defaultStrokeColor(); + } + + // Subclasses can override to recompute liveness before drawing + protected updateIsLive(): void {} //kind of an abstract method + + draw() { + this.recomputePath(); + this.updateIsLive(); //just before drawijng, update liveness for the arrows (since this was causing erroes earlier ) + if (Layout.clearDeadFrames && !this.isLive) { + return null; + } + + const stroke = this.isLive ? defaultStrokeColor() : fadedStrokeColor(); + + return ( + (e.cancelBubble = true)} + > + + + + ); + } +} diff --git a/src/features/cseMachine/components/values/Value.tsx b/src/features/cseMachine/components/values/Value.tsx index 0f5b7473c5..58262bcd8f 100644 --- a/src/features/cseMachine/components/values/Value.tsx +++ b/src/features/cseMachine/components/values/Value.tsx @@ -1,43 +1,43 @@ -import React from 'react'; - -import { Data, ReferenceType } from '../../CseMachineTypes'; -import { isDummyReference } from '../../CseMachineUtils'; -import { Visible } from '../Visible'; - -/** the value of a `Binding` or an `ArrayUnit` */ -export abstract class Value extends Visible { - /** the underlying data of this value */ - abstract readonly data: Data; - - /** - * if the value has actual references, i.e. the references - * are not from dummy bindings or from unreferenced arrays - */ - private _isReferenced: boolean = false; - - isReferenced() { - return this._isReferenced; - } - - markAsReferenced() { - this._isReferenced = true; - } - - /** references to this value */ - public references: ReferenceType[] = []; - - /** add reference (binding / array unit) to this value */ - addReference(newReference: ReferenceType): void { - this.references.push(newReference); - this.handleNewReference(newReference); - if (!this.isReferenced() && !isDummyReference(newReference)) { - this.markAsReferenced(); - } - } - - /** additional logic to handle new references */ - abstract handleNewReference(newReference: ReferenceType): void; - - /** draw logic */ - abstract draw(): React.ReactNode; -} +import React from 'react'; + +import { Data, ReferenceType } from '../../CseMachineTypes'; +import { isDummyReference } from '../../CseMachineUtils'; +import { Visible } from '../Visible'; + +/** the value of a `Binding` or an `ArrayUnit` */ +export abstract class Value extends Visible { + /** the underlying data of this value */ + abstract readonly data: Data; + + /** + * if the value has actual references, i.e. the references + * are not from dummy bindings or from unreferenced arrays + */ + private _isReferenced: boolean = false; + + isReferenced() { + return this._isReferenced; + } + + markAsReferenced() { + this._isReferenced = true; + } + + /** references to this value */ + public references: ReferenceType[] = []; + + /** add reference (binding / array unit) to this value */ + addReference(newReference: ReferenceType): void { + this.references.push(newReference); + this.handleNewReference(newReference); + if (!this.isReferenced() && !isDummyReference(newReference)) { + this.markAsReferenced(); + } + } + + /** additional logic to handle new references */ + abstract handleNewReference(newReference: ReferenceType): void; + + /** draw logic */ + abstract draw(): React.ReactNode; +} diff --git a/src/global.ts b/src/global.d.ts similarity index 100% rename from src/global.ts rename to src/global.d.ts