Skip to content

Commit a5e2e65

Browse files
hsy822Aniket-Engg
authored andcommitted
fix compile button
1 parent acee4dd commit a5e2e65

File tree

2 files changed

+177
-33
lines changed

2 files changed

+177
-33
lines changed

libs/remix-ui/tabs/src/lib/components/CompileDropdown.tsx

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,47 @@ export const CompileDropdown: React.FC<CompileDropdownProps> = ({ tabPath, plugi
2525
const compileThen = async (nextAction: () => void) => {
2626
setCompileState('compiling')
2727

28-
setTimeout(async () => {
29-
plugin.once('solidity', 'compilationFinished', (data) => {
30-
const hasErrors = data.errors && data.errors.filter(e => e.severity === 'error').length > 0
31-
if (hasErrors) {
32-
setCompileState('idle')
33-
plugin.call('notification', 'toast', 'Compilation failed')
28+
try {
29+
await plugin.call('fileManager', 'saveCurrentFile')
30+
await plugin.call('manager', 'activatePlugin', 'solidity')
31+
32+
const mySeq = Date.now()
33+
let watchdog: NodeJS.Timeout | null = null
34+
35+
const onFinished = async () => {
36+
if (watchdog) clearTimeout(watchdog)
37+
38+
const fresh = await plugin.call('solidity', 'getCompilationResult').catch(() => null)
39+
40+
if (!fresh) {
41+
setCompileState('idle')
42+
plugin.call('notification', 'toast', 'Compilation failed (no result). See compiler output.')
3443
} else {
35-
setCompileState('compiled')
36-
nextAction()
44+
const errs = Array.isArray(fresh.errors) ? fresh.errors.filter((e: any) => (e.severity || e.type) === 'error') : []
45+
if (errs.length > 0) {
46+
setCompileState('idle')
47+
plugin.call('notification', 'toast', 'Compilation failed (errors). See compiler output.')
48+
} else {
49+
setCompileState('compiled');
50+
nextAction();
51+
}
3752
}
38-
})
39-
40-
try {
41-
await plugin.call('solidity', 'compile', tabPath)
42-
} catch (e) {
43-
console.error(e)
44-
setCompileState('idle')
53+
try { plugin.off('solidity', 'compilationFinished', onFinished) } catch {}
4554
}
46-
}, 0)
55+
56+
try { plugin.off('solidity', 'compilationFinished', onFinished) } catch {}
57+
plugin.on('solidity', 'compilationFinished', onFinished);
58+
59+
watchdog = setTimeout(() => {
60+
onFinished()
61+
}, 10000)
62+
63+
await plugin.call('solidity', 'compile', tabPath)
64+
65+
} catch (e) {
66+
console.error(e)
67+
setCompileState('idle')
68+
}
4769
}
4870

4971
const fetchScripts = async () => {

libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx

Lines changed: 139 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const tabsReducer = (state: ITabsState, action: ITabsAction) => {
7070
return state
7171
}
7272
}
73-
const PlayExtList = ['js', 'ts', 'sol', 'circom', 'vy', 'nr']
73+
const PlayExtList = ['js', 'ts', 'sol', 'circom', 'vy', 'nr', 'yul']
7474

7575
export const TabsUI = (props: TabsUIProps) => {
7676

@@ -83,6 +83,10 @@ export const TabsUI = (props: TabsUIProps) => {
8383
tabs.current = props.tabs // we do this to pass the tabs list to the onReady callbacks
8484
const appContext = useContext(AppContext)
8585

86+
const compileSeq = useRef(0)
87+
const compileWatchdog = useRef<number | null>(null)
88+
const settledSeqRef = useRef<number>(0)
89+
8690
const [compileState, setCompileState] = useState<'idle' | 'compiling' | 'compiled'>('idle')
8791

8892
useEffect(() => {
@@ -193,7 +197,7 @@ export const TabsUI = (props: TabsUIProps) => {
193197
setFileDecorations
194198
})
195199
return () => {
196-
tabsElement.current.removeEventListener('wheel', transformScroll)
200+
if (tabsElement.current) tabsElement.current.removeEventListener('wheel', transformScroll)
197201
}
198202
}, [])
199203

@@ -208,6 +212,25 @@ export const TabsUI = (props: TabsUIProps) => {
208212
setCompileState('idle')
209213
}, [tabsState.selectedIndex])
210214

215+
useEffect(() => {
216+
if (!props.plugin || tabsState.selectedIndex < 0) return
217+
218+
const currentPath = props.tabs[tabsState.selectedIndex]?.name
219+
if (!currentPath) return
220+
221+
const listener = (path: string) => {
222+
if (currentPath.endsWith(path)) {
223+
setCompileState('idle')
224+
}
225+
}
226+
227+
props.plugin.on('editor', 'contentChanged', listener)
228+
229+
return () => {
230+
props.plugin.off('editor', 'contentChanged')
231+
}
232+
}, [tabsState.selectedIndex, props.plugin, props.tabs])
233+
211234
const handleCompileAndPublish = async (storageType: 'ipfs' | 'swarm') => {
212235
setCompileState('compiling')
213236
await props.plugin.call('notification', 'toast', `Switching to Solidity Compiler to publish...`)
@@ -307,17 +330,97 @@ export const TabsUI = (props: TabsUIProps) => {
307330
}
308331
}
309332

333+
const waitForFreshCompilationResult = async (
334+
mySeq: number,
335+
targetPath: string,
336+
startMs: number,
337+
maxWaitMs = 1500,
338+
intervalMs = 120
339+
) => {
340+
const norm = (p: string) => p.replace(/^\/+/, '')
341+
const fileName = norm(targetPath).split('/').pop() || norm(targetPath)
342+
343+
const hasFile = (res: any) => {
344+
if (!res) return false
345+
const byContracts =
346+
res.contracts && typeof res.contracts === 'object' &&
347+
Object.keys(res.contracts).some(k => k.endsWith(fileName) || norm(k) === norm(targetPath))
348+
const bySources =
349+
res.sources && typeof res.sources === 'object' &&
350+
Object.keys(res.sources).some(k => k.endsWith(fileName) || norm(k) === norm(targetPath))
351+
return byContracts || bySources
352+
}
353+
354+
let last: any = null
355+
const until = startMs + maxWaitMs
356+
while (Date.now() < until) {
357+
if (mySeq !== compileSeq.current) return null
358+
try {
359+
const res = await props.plugin.call('solidity', 'getCompilationResult')
360+
last = res
361+
const ts = (res && (res.timestamp || res.timeStamp || res.time || res.generatedAt)) || null
362+
const isFreshTime = typeof ts === 'number' ? ts >= startMs : true
363+
if (res && hasFile(res) && isFreshTime) return res
364+
} catch {}
365+
await new Promise(r => setTimeout(r, intervalMs))
366+
}
367+
return last
368+
}
369+
370+
const attachCompilationListener = (compilerName: string, mySeq: number, path: string, startedAt: number) => {
371+
try { props.plugin.off(compilerName, 'compilationFinished') } catch {}
372+
373+
const onFinished = async (_success: boolean) => {
374+
if (mySeq !== compileSeq.current || settledSeqRef.current === mySeq) return
375+
376+
if (compileWatchdog.current) {
377+
clearTimeout(compileWatchdog.current)
378+
compileWatchdog.current = null
379+
}
380+
381+
const fresh = await waitForFreshCompilationResult(mySeq, path, startedAt)
382+
383+
if (!fresh) {
384+
setCompileState('idle')
385+
props.plugin.call('notification', 'toast', 'Compilation failed (no result). See compiler output.')
386+
} else {
387+
const errs = Array.isArray(fresh.errors) ? fresh.errors.filter((e: any) => (e.severity || e.type) === 'error') : []
388+
if (errs.length > 0) {
389+
setCompileState('idle')
390+
props.plugin.call('notification', 'toast', 'Compilation failed (errors). See compiler output.')
391+
} else {
392+
setCompileState('compiled')
393+
}
394+
}
395+
settledSeqRef.current = mySeq
396+
try { props.plugin.off(compilerName, 'compilationFinished') } catch {}
397+
}
398+
props.plugin.on(compilerName, 'compilationFinished', onFinished)
399+
}
400+
310401
const handleCompileClick = async () => {
311402
setCompileState('compiling')
312403
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
313404

314405
try {
315-
const path = active().substr(active().indexOf('/') + 1, active().length)
406+
const activePathRaw = active()
407+
if (!activePathRaw || activePathRaw.indexOf('/') === -1) {
408+
setCompileState('idle')
409+
props.plugin.call('notification', 'toast', 'No file selected.')
410+
return
411+
}
412+
const path = activePathRaw.substr(activePathRaw.indexOf('/') + 1)
316413

317414
if (tabsState.currentExt === 'js' || tabsState.currentExt === 'ts') {
318-
const content = await props.plugin.call('fileManager', 'readFile', path)
319-
await props.plugin.call('scriptRunnerBridge', 'execute', content, path)
320-
setCompileState('compiled')
415+
try {
416+
const content = await props.plugin.call('fileManager', 'readFile', path)
417+
await props.plugin.call('scriptRunnerBridge', 'execute', content, path)
418+
setCompileState('compiled')
419+
} catch (e) {
420+
console.error(e)
421+
props.plugin.call('notification', 'toast', `Script error: ${e.message}`)
422+
setCompileState('idle')
423+
}
321424
return
322425
}
323426

@@ -329,26 +432,45 @@ export const TabsUI = (props: TabsUIProps) => {
329432
nr: 'noir-compiler'
330433
}[tabsState.currentExt]
331434

332-
if (!compilerName) {
333-
setCompileState('idle')
334-
return
435+
if (!compilerName) {
436+
setCompileState('idle')
437+
return
335438
}
336439

337-
props.plugin.once(compilerName, 'compilationFinished', (fileName, source, languageVersion, data) => {
338-
const hasErrors = data.errors && data.errors.filter(e => e.severity === 'error').length > 0
339-
340-
if (hasErrors) {
341-
setCompileState('idle')
342-
} else {
343-
setCompileState('compiled')
440+
await props.plugin.call('fileManager', 'saveCurrentFile')
441+
await props.plugin.call('manager', 'activatePlugin', compilerName)
442+
443+
const mySeq = ++compileSeq.current
444+
const startedAt = Date.now()
445+
446+
attachCompilationListener(compilerName, mySeq, path, startedAt)
447+
448+
if (compileWatchdog.current) clearTimeout(compileWatchdog.current)
449+
compileWatchdog.current = window.setTimeout(async () => {
450+
if (mySeq !== compileSeq.current || settledSeqRef.current === mySeq) return
451+
const maybe = await props.plugin.call('solidity', 'getCompilationResult').catch(() => null)
452+
if (maybe) {
453+
const fresh = await waitForFreshCompilationResult(mySeq, path, startedAt, 400, 120)
454+
if (fresh) {
455+
const errs = Array.isArray(fresh.errors) ? fresh.errors.filter((e: any) => (e.severity || e.type) === 'error') : []
456+
setCompileState(errs.length ? 'idle' : 'compiled')
457+
if (errs.length) props.plugin.call('notification', 'toast', 'Compilation failed (errors). See compiler output.')
458+
settledSeqRef.current = mySeq
459+
return
460+
}
344461
}
345-
})
462+
setCompileState('idle')
463+
props.plugin.call('notification', 'toast', 'Compilation failed (errors). See compiler output.')
464+
settledSeqRef.current = mySeq
465+
try { props.plugin.off(compilerName, 'compilationFinished') } catch {}
466+
}, 3000)
346467

347468
if (tabsState.currentExt === 'vy') {
348469
await props.plugin.call(compilerName, 'vyperCompileCustomAction')
349470
} else {
350471
await props.plugin.call(compilerName, 'compile', path)
351472
}
473+
352474
} catch (e) {
353475
console.error(e)
354476
setCompileState('idle')

0 commit comments

Comments
 (0)