Show errors and warnings

pull/4144/head
ioedeveloper 1 year ago
parent f0d76b9159
commit dd7966aba9
  1. 39
      apps/circuit-compiler/src/app/app.tsx
  2. 10
      apps/circuit-compiler/src/app/components/container.tsx
  3. 54
      apps/circuit-compiler/src/app/components/feedback.tsx
  4. 31
      apps/circuit-compiler/src/app/components/feedbackAlert.tsx
  5. 23
      apps/circuit-compiler/src/app/components/versions.tsx
  6. 16
      apps/circuit-compiler/src/app/reducers/state.ts
  7. 55
      apps/circuit-compiler/src/app/services/circomPluginClient.ts
  8. 35
      apps/circuit-compiler/src/app/types/index.ts
  9. 52
      apps/circuit-compiler/src/css/app.css
  10. 2
      libs/remix-ui/helper/src/lib/remix-ui-helper.ts
  11. 0
      pkg/index.js

@ -28,35 +28,42 @@ function App() {
plugin.on('fileManager', 'currentFileChanged', (filePath) => {
if (filePath.endsWith('.circom')) {
dispatch({ type: 'SET_FILE_PATH', payload: filePath })
plugin.parse(filePath)
} else {
dispatch({ type: 'SET_FILE_PATH', payload: '' })
}
})
// @ts-ignore
plugin.on('editor', 'contentChanged', async () => {
plugin.on('editor', 'contentChanged', async (path: string, content: string) => {
setIsContentChanged(true)
if (path.endsWith('.circom')) {
plugin.parse(path, content)
}
})
setPlugin(plugin)
})
// compiling events
plugin.internalEvents.on('circuit_compiling_start', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'compiling' }))
plugin.internalEvents.on('circuit_compiling_done', (signalInputs) => {
plugin.internalEvents.on('circuit_compiling_done', (signalInputs: string[]) => {
signalInputs = (signalInputs || []).filter(input => input)
dispatch({ type: 'SET_SIGNAL_INPUTS', payload: signalInputs })
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' })
compilerSuccess()
})
plugin.internalEvents.on('circuit_compiling_errored', (err) => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }))
plugin.internalEvents.on('circuit_compiling_errored', compilerErrored)
// r1cs events
plugin.internalEvents.on('circuit_generating_r1cs_start', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'generating' }))
plugin.internalEvents.on('circuit_generating_r1cs_done', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }))
plugin.internalEvents.on('circuit_generating_r1cs_errored', (err) => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }))
plugin.internalEvents.on('circuit_generating_r1cs_done', compilerSuccess)
plugin.internalEvents.on('circuit_generating_r1cs_errored', compilerErrored)
// witness events
plugin.internalEvents.on('circuit_computing_witness_start', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'computing' }))
plugin.internalEvents.on('circuit_computing_witness_done', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }))
plugin.internalEvents.on('circuit_computing_witness_errored', (err) => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }))
plugin.internalEvents.on('circuit_computing_witness_done', compilerSuccess)
plugin.internalEvents.on('circuit_computing_witness_errored', compilerErrored)
// parsing events
plugin.internalEvents.on('circuit_parsing_done', (_, filePathToId) => dispatch({ type: 'SET_FILE_PATH_TO_ID', payload: filePathToId }))
}, [])
useEffect(() => {
@ -89,6 +96,22 @@ function App() {
setLocale(currentLocale)
}
const compilerErrored = (err: ErrorEvent) => {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' })
try {
const report = JSON.parse(err.message)
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: report })
} catch (e) {
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: err.message })
}
}
const compilerSuccess = () => {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' })
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: null })
}
const value = {
appState,
dispatch,

@ -9,6 +9,7 @@ import { Configurations } from './configurations'
import { CircuitActions } from './actions'
import { WitnessToggler } from './witnessToggler'
import { WitnessSection } from './witness'
import { CompilerFeedback } from './feedback'
export function Container () {
const circuitApp = useContext(CircuitAppContext)
@ -18,6 +19,10 @@ export function Container () {
circuitApp.plugin.call('notification', 'modal', { id: 'modal_circuit_compiler_license', title: 'Compiler License', message })
}
const handleVersionSelect = (version: string) => {
circuitApp.dispatch({ type: 'SET_COMPILER_VERSION', payload: version })
}
return (
<section>
<article>
@ -29,7 +34,7 @@ export function Container () {
<CustomTooltip placement="top" tooltipId="showCompilerTooltip" tooltipClasses="text-nowrap" tooltipText={'See compiler license'}>
<span className="fa fa-file-text-o border-0 p-0 ml-2" onClick={() => showCompilerLicense()}></span>
</CustomTooltip>
<VersionList />
<VersionList setVersion={handleVersionSelect} versionList={circuitApp.appState.versionList} currentVersion={circuitApp.appState.version} />
<CompileOptions />
<ConfigToggler>
<Configurations />
@ -40,6 +45,9 @@ export function Container () {
<WitnessSection plugin={circuitApp.plugin} signalInputs={circuitApp.appState.signalInputs} status={circuitApp.appState.status} />
</WitnessToggler>
</RenderIf>
<RenderIf condition={circuitApp.appState.status !== 'compiling' && circuitApp.appState.status !== 'computing' && circuitApp.appState.status !== 'generating'}>
<CompilerFeedback feedback={circuitApp.appState.feedback} filePathToId={circuitApp.appState.filePathToId} />
</RenderIf>
</div>
</div>
</article>

@ -0,0 +1,54 @@
import { useState } from 'react'
import { CompilerFeedbackProps } from '../types'
import { RenderIf } from '@remix-ui/helper'
import {CopyToClipboard} from '@remix-ui/clipboard'
import { FeedbackAlert } from './feedbackAlert'
export function CompilerFeedback ({ feedback, filePathToId }: CompilerFeedbackProps) {
const [ showException, setShowException ] = useState<boolean>(true)
const handleCloseException = () => {
setShowException(false)
}
return (
<div>
<div className="circuit_errors_box py-4">
<RenderIf condition={ (typeof feedback === "string") && showException }>
<div className="circuit_feedback error alert alert-danger">
<span> { feedback } </span>
<div className="close" data-id="renderer" onClick={handleCloseException}>
<i className="fas fa-times"></i>
</div>
<div className="d-flex pt-1 flex-row-reverse">
<span className="ml-3 pt-1 py-1" >
<CopyToClipboard content={feedback} className="p-0 m-0 far fa-copy error" direction={'top'} />
</span>
</div>
</div>
</RenderIf>
<RenderIf condition={ Array.isArray(feedback) }>
<>
{
Array.isArray(feedback) && feedback.map((response, index) => (
<div key={index}>
<RenderIf condition={response.type === 'Error'}>
<div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-danger`}>
<FeedbackAlert message={response.message} location={ response.labels[0] ? response.labels[0].message + ` ${filePathToId[response.labels[0].file_id]}:${response.labels[0].range.start}:${response.labels[0].range.end}` : null} />
</div>
</RenderIf>
<RenderIf condition={response.type === 'Warning'}>
<div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-warning`}>
<FeedbackAlert message={response.message} location={null} />
</div>
</RenderIf>
</div>
)
)
}
</>
</RenderIf>
</div>
</div>
)
}

@ -0,0 +1,31 @@
import { useState } from 'react'
import { FeedbackAlertProps } from '../types'
import { RenderIf } from '@remix-ui/helper'
import {CopyToClipboard} from '@remix-ui/clipboard'
export function FeedbackAlert ({ message, location }: FeedbackAlertProps) {
const [ showAlert, setShowAlert] = useState<boolean>(true)
const handleCloseAlert = () => {
setShowAlert(false)
}
return (
<RenderIf condition={showAlert}>
<>
<span> { message } </span>
<RenderIf condition={location !== null}>
<span> { location }</span>
</RenderIf>
<div className="close" data-id="renderer" onClick={handleCloseAlert}>
<i className="fas fa-times"></i>
</div>
<div className="d-flex pt-1 flex-row-reverse">
<span className="ml-3 pt-1 py-1" >
<CopyToClipboard content={message} className="p-0 m-0 far fa-copy error" direction={'top'} />
</span>
</div>
</>
</RenderIf>
)
}

@ -1,28 +1,21 @@
import { RenderIf } from "@remix-ui/helper";
import { useContext } from "react";
import { CircuitAppContext } from "../contexts";
import { AppState } from "../types";
export function VersionList () {
const { appState, dispatch } = useContext(CircuitAppContext)
const handleVersionSelect = (version: string) => {
dispatch({ type: 'SET_COMPILER_VERSION', payload: version })
}
const versionList = Object.keys(appState.versionList)
export function VersionList ({ currentVersion, versionList, setVersion }: { versionList: AppState['versionList'], currentVersion: string, setVersion: (version: string) => void }) {
const versionListKeys = Object.keys(versionList)
return (
<select
value={appState.version}
onChange={(e) => handleVersionSelect(e.target.value)}
value={currentVersion}
onChange={(e) => setVersion(e.target.value)}
className="custom-select"
>
<RenderIf condition={versionList.length > 0}>
<RenderIf condition={versionListKeys.length > 0}>
<>
{
versionList.map((version, index) => (
versionListKeys.map((version, index) => (
<option value={version} key={index}>
{ appState.versionList[version].name }
{ versionList[version].name }
</option>
))
}

@ -5,10 +5,12 @@ export const appInitialState: AppState = {
version: compiler_list.latest,
versionList: compiler_list.wasm_builds,
filePath: "",
filePathToId: {},
status: "idle",
primeValue: "bn128",
autoCompile: false,
signalInputs: []
signalInputs: [],
feedback: null
}
export const appReducer = (state = appInitialState, action: Actions): AppState => {
@ -50,6 +52,18 @@ export const appReducer = (state = appInitialState, action: Actions): AppState =
signalInputs: action.payload
}
case 'SET_COMPILER_FEEDBACK':
return {
...state,
feedback: action.payload
}
case 'SET_FILE_PATH_TO_ID':
return {
...state,
filePathToId: action.payload
}
default:
throw new Error()
}

@ -13,6 +13,7 @@ export class CircomPluginClient extends PluginClient {
prime: "bn128"
}
public lastCompiledCircuitPath: string = ''
public lastParsedFiles: Record<string, string> = {}
constructor() {
super()
@ -27,29 +28,23 @@ export class CircomPluginClient extends PluginClient {
}
onActivation(): void {
// @ts-ignore
this.on('editor', 'contentChanged', (path: string, fileContent) => {
if (path.endsWith('.circom')) {
this.parse(path, fileContent)
}
})
this.internalEvents.emit('circom_activated')
}
async parse(path: string, fileContent: string): Promise<void> {
let buildFiles = {
async parse(path: string, fileContent?: string): Promise<string> {
if (!fileContent) {
// @ts-ignore
fileContent = await this.call('fileManager', 'readFile', path)
}
this.lastParsedFiles = {
[path]: fileContent,
}
buildFiles = await this.resolveDependencies(path, fileContent, buildFiles)
const parsedOutput = parse(path, buildFiles)
this.lastParsedFiles = await this.resolveDependencies(path, fileContent, this.lastParsedFiles)
const parsedOutput = parse(path, this.lastParsedFiles)
try {
const result = JSON.parse(parsedOutput)
console.log('result: ', result)
if (result.length === 0) {
// @ts-ignore
await this.call('editor', 'clearErrorMarkers', [path])
@ -104,10 +99,22 @@ export class CircomPluginClient extends PluginClient {
} catch (e) {
console.log(e)
}
const mapFilePathToId = {}
const filePaths = Object.keys(this.lastParsedFiles)
for (let index = 0; index < filePaths.length; index++) {
mapFilePathToId[index.toString()] = filePaths[index]
}
this.internalEvents.emit('circuit_parsing_done', parsedOutput, mapFilePathToId)
return parsedOutput
}
async compile(path: string, compilationConfig?: CompilationConfig): Promise<void> {
this.internalEvents.emit('circuit_compiling_start')
const parseErrors = await this.parse(path)
if (parseErrors) throw new Error(parseErrors)
if (compilationConfig) {
const { prime, version } = compilationConfig
@ -116,13 +123,7 @@ export class CircomPluginClient extends PluginClient {
this._compilationConfig.prime = prime
this._compilationConfig.version = version
}
const fileContent = await this.call('fileManager', 'readFile', path)
let buildFiles = {
[path]: fileContent
}
buildFiles = await this.resolveDependencies(path, fileContent, buildFiles)
const circuitApi = compile(path, buildFiles, { prime: this._compilationConfig.prime })
const circuitApi = compile(path, this.lastParsedFiles, { prime: this._compilationConfig.prime })
const circuitProgram = circuitApi.program()
if (circuitProgram.length < 1) {
@ -135,6 +136,7 @@ export class CircomPluginClient extends PluginClient {
this.lastCompiledCircuitPath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'wasm')
// @ts-ignore
await this.call('fileManager', 'writeFile', this.lastCompiledCircuitPath, circuitProgram, true)
const fileContent = this.lastParsedFiles[path]
const searchComponentName = fileContent.match(/component\s+main\s*(?:{[^{}]*})?\s*=\s*([A-Za-z_]\w*)\s*\(.*\)/)
if (searchComponentName) {
@ -207,6 +209,9 @@ export class CircomPluginClient extends PluginClient {
async generateR1cs (path: string, compilationConfig?: CompilationConfig): Promise<void> {
this.internalEvents.emit('circuit_generating_r1cs_start')
const parseErrors = await this.parse(path)
if (parseErrors) throw new Error(parseErrors)
if (compilationConfig) {
const { prime, version } = compilationConfig
@ -215,13 +220,7 @@ export class CircomPluginClient extends PluginClient {
this._compilationConfig.prime = prime
this._compilationConfig.version = version
}
const fileContent = await this.call('fileManager', 'readFile', path)
let buildFiles = {
[path]: fileContent
}
buildFiles = await this.resolveDependencies(path, fileContent, buildFiles)
const r1csApi = generate_r1cs(path, buildFiles, { prime: this._compilationConfig.prime })
const r1csApi = generate_r1cs(path, this.lastParsedFiles, { prime: this._compilationConfig.prime })
const r1csProgram = r1csApi.program()
if (r1csProgram.length < 1) {

@ -15,7 +15,9 @@ export interface ActionPayloadTypes {
SET_COMPILER_STATUS: CompilerStatus,
SET_PRIME_VALUE: PrimeValue,
SET_AUTO_COMPILE: boolean,
SET_SIGNAL_INPUTS: string[]
SET_SIGNAL_INPUTS: string[],
SET_COMPILER_FEEDBACK: string | CompilerReport[]
SET_FILE_PATH_TO_ID: Record<number, string>
}
export interface Action<T extends keyof ActionPayloadTypes> {
type: T
@ -28,10 +30,12 @@ export interface AppState {
version: string,
versionList: typeof compiler_list.wasm_builds,
filePath: string,
filePathToId: Record<string, string>,
status: CompilerStatus,
primeValue: PrimeValue,
autoCompile: boolean,
signalInputs: string[]
signalInputs: string[],
feedback: string | CompilerReport[]
}
export type CompilationConfig = {
@ -39,4 +43,29 @@ export type CompilationConfig = {
version: string
}
export type PrimeValue = "bn128" | "bls12381" | "goldilocks"
export type PrimeValue = "bn128" | "bls12381" | "goldilocks"
export type CompilerFeedbackProps = {
feedback: string | CompilerReport[],
filePathToId: Record<string, string>
}
export type CompilerReport = {
type: "Error" | "Bug" | "Help" | "Note" | "Warning" | "Unknown",
message: string,
labels: {
style: "Primary" | "Secondary" | "Unknown",
file_id: string,
range: {
start: string,
end: string
},
message: string
}[],
notes: string[]
}
export type FeedbackAlertProps = {
message: string,
location: string
}

@ -35,4 +35,54 @@ body {
font-size: 11px;
line-height: 12px;
text-transform: uppercase;
}
}
.circuit_errors_box {
padding-left: 5px;
padding-right: 5px;
word-break: break-word;
}
.circuit_feedback.success,
.circuit_feedback.error,
.circuit_feedback.warning {
white-space: pre-line;
word-wrap: break-word;
cursor: pointer;
position: relative;
margin: 0.5em 0 1em 0;
border-radius: 5px;
line-height: 20px;
padding: 8px 15px;
}
.circuit_feedback.success pre,
.circuit_feedback.error pre,
.circuit_feedback.warning pre {
white-space: pre-line;
overflow-y: hidden;
background-color: transparent;
margin: 0;
font-size: 12px;
border: 0 none;
padding: 0;
border-radius: 0;
}
.circuit_feedback.success .close,
.circuit_feedback.error .close,
.circuit_feedback.warning .close {
visibility: hidden;
white-space: pre-line;
font-weight: bold;
position: absolute;
color: hsl(0, 0%, 0%); /* black in style-guide.js */
top: 0;
right: 0;
padding: 0.5em;
}
.circuit_feedback.success a,
.circuit_feedback.error a,
.circuit_feedback.warning a {
bottom: 0;
right: 0;
}

@ -81,7 +81,7 @@ export const getPathIcon = (path: string) => {
? 'small fak fa-ts-logo' : path.endsWith('.tsc')
? 'fad fa-brackets-curly' : path.endsWith('.cairo')
? 'small fak fa-cairo' : path.endsWith('.circom')
? 'fak fa-circom-plug1' : 'far fa-file'
? 'fak fa-circom' : 'far fa-file'
}
export const isNumeric = (value) => {

Loading…
Cancel
Save