Skip to content

Commit

Permalink
Merge pull request #954 from SamDev-7/main
Browse files Browse the repository at this point in the history
Highlight erroring line
  • Loading branch information
grymmy committed Mar 28, 2023
2 parents 9918994 + 374673d commit e1c9db0
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 12 deletions.
22 changes: 16 additions & 6 deletions src/components/big-interactive-pages/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Help from '../popups-etc/help'
import { collapseRanges } from '../../lib/codemirror/util'
import { defaultExampleCode } from '../../lib/examples'
import MigrateToast from '../popups-etc/migrate-toast'
import { highlightError, clearErrorHighlight } from '../../lib/engine/3-editor/error'
import { nanoid } from 'nanoid'

interface EditorProps {
Expand Down Expand Up @@ -86,7 +87,7 @@ export default function Editor({ persistenceState, cookies }: EditorProps) {
return () => window.removeEventListener('resize', updateMaxSize)
}, [])
const realOutputAreaSize = useComputed(() => Math.min(maxOutputAreaSize.value, Math.max(minOutputAreaSize, outputAreaSize.value)))

// Resize bar logic
const resizeState = useSignal<ResizeState | null>(null)
useEffect(() => {
Expand All @@ -106,15 +107,18 @@ export default function Editor({ persistenceState, cookies }: EditorProps) {
const onRun = async () => {
foldAllTemplateLiterals()
if (!screen.current) return

if (cleanup.current) cleanup.current()
errorLog.value = []

const code = codeMirror.value?.state.doc.toString() ?? ''
const res = runGame(code, screen.current, (error) => {
errorLog.value = [ ...errorLog.value, error ]
if (error.line) {
highlightError(error.line);
}
})

screen.current.focus()
screenShake.value++
setTimeout(() => screenShake.value--, 200)
Expand All @@ -123,6 +127,12 @@ export default function Editor({ persistenceState, cookies }: EditorProps) {
if (res.error) {
console.error(res.error.raw)
errorLog.value = [ ...errorLog.value, res.error ]

if (res.error.line) {
highlightError(res.error.line);
}
} else {
clearErrorHighlight();
}
}
useEffect(() => () => cleanup.current?.(), [])
Expand Down Expand Up @@ -189,7 +199,7 @@ export default function Editor({ persistenceState, cookies }: EditorProps) {
return (
<div class={styles.page}>
<Navbar persistenceState={persistenceState} />

<div class={styles.pageMain}>
<div className={styles.codeContainer}>
<CodeMirror
Expand Down Expand Up @@ -223,7 +233,7 @@ export default function Editor({ persistenceState, cookies }: EditorProps) {
<button class={styles.errorClose} onClick={() => errorLog.value = []}>
<IoClose />
</button>

{errorLog.value.map((error, i) => (
<div key={`${i}-${error.description}`}>{error.description}</div>
))}
Expand Down Expand Up @@ -273,7 +283,7 @@ export default function Editor({ persistenceState, cookies }: EditorProps) {
{persistenceState.value.kind === 'IN_MEMORY' && persistenceState.value.showInitialWarning && (
<DraftWarningModal persistenceState={persistenceState} />
)}

<Help initialVisible={!cookies.hideHelp} />
<MigrateToast persistenceState={persistenceState} />
</div>
Expand Down
4 changes: 4 additions & 0 deletions src/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,8 @@ a:hover {

.grecaptcha-badge {
visibility: hidden;
}

.cm-lineNumbers > .cm-gutterElement[err-line] {
background: #ecb2b2!important;
}
72 changes: 66 additions & 6 deletions src/lib/engine/3-editor/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type EsprimaError = Error & {
column: number
}

export type GameError =
export type GameError =
| { kind: 'parse', error: EsprimaError }
| { kind: 'runtime', error: unknown }
| { kind: 'page', error: unknown }
Expand Down Expand Up @@ -52,7 +52,7 @@ const normalizeStack = (stack: string): StackItem[] | null => {
let fileUrl = match[2]!
while (chromeStackUrlRegex.test(fileUrl))
fileUrl = fileUrl.match(chromeStackUrlRegex)![1]!

return {
callSite,
fileUrl,
Expand Down Expand Up @@ -82,22 +82,27 @@ export const normalizeGameError = (gameError: GameError): NormalizedError => {
const line = lineNumber - 1
return {
description: `SyntaxError: ${description}\n at <game>:${line}:${column}`,
raw: gameError.error
raw: gameError.error,
line: line,
column: column
}
} else if (gameError.error instanceof Error) {
const descriptionLines: string[] = []

const stack = (gameError.error.stack ? normalizeStack(gameError.error.stack) : null) ?? []

let [line, col] = findErrorLineCol(gameError.error.stack)

stack.reverse()

let foundEval = false
for (const item of stack) {
if (!foundEval && [ 'eval', 'anonymous' ].includes(item.callSite)) {
if (!foundEval && ['eval', 'anonymous'].includes(item.callSite)) {
foundEval = true
if (item.lineNumber) item.lineNumber -= lineOffset
}
if (!foundEval) continue

let fileName
try {
const url = new URL(item.fileUrl)
Expand All @@ -118,9 +123,64 @@ export const normalizeGameError = (gameError: GameError): NormalizedError => {
descriptionLines.unshift(`${gameError.error.name}: ${gameError.error.message}`)
return {
description: descriptionLines.join('\n'),
raw: gameError.error
raw: gameError.error,
line: line,
column: col
}
} else {
return { description: `Runtime Error: ${gameError.error}`, raw: gameError.error }
}
}

/*
* Finds the line and column of innermost error from a stack.
* This is modified code from V1.
*/
function findErrorLineCol(stack: string | undefined): [number | null, number | null] {
if (!stack) return [null, null]

let line = null
let col = null

// Get the most outer (first) error that is part of ther user's code (not part of the engine and anonymous)
let location = stack.match(/<anonymous>:(.+)\)/)

if (location) {
let lineCol = location[1].split(":").map(Number)
line = lineCol[0] - 2 - 1
col = lineCol[1]
}

return [line, col]
}

/*
* Highlights the line gutter of the speciefied line number to indicate an error.
* This uses a custom attribute to style the gutter.
* This is modified code from V1.
*/
export function highlightError(line: number) {
const cmLineGutters = document.querySelectorAll(".cm-lineNumbers > .cm-gutterElement") // Get all the line gutters
// Find the gutter that matches the line number and is not hidden
for (let i = 0; i < cmLineGutters.length; i++) {
const cmLineGutter = cmLineGutters[i] as HTMLElement;
const innerNumber = cmLineGutter.innerText;
const height = cmLineGutter.style.height;
if (Number(innerNumber) !== line || height === "0px") {
cmLineGutter.removeAttribute("err-line");
continue;
};
cmLineGutter.setAttribute("err-line", "");
}
}

/*
* Clears all exsisting error highlighted from the gutter
*/
export function clearErrorHighlight() {
const cmLineGutters = document.querySelectorAll(".cm-lineNumbers > .cm-gutterElement")
for (let i = 0; i < cmLineGutters.length; i++) {
const cmLineGutter = cmLineGutters[i] as HTMLElement;
cmLineGutter.removeAttribute("err-line");
}
}
2 changes: 2 additions & 0 deletions src/lib/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import type { Game, SessionInfo } from './game-saving/account'
export interface NormalizedError {
raw: unknown
description: string
line?: number | null
column?: number | null
}

// Editor types
Expand Down

1 comment on commit e1c9db0

@vercel
Copy link

@vercel vercel bot commented on e1c9db0 Mar 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

sprig – ./

sprig-git-main-gamer.vercel.app
sprig-gamer.vercel.app
sprig.vercel.app

Please sign in to comment.