From 442950aed37577e63834a42ec391f683bf93126c Mon Sep 17 00:00:00 2001 From: filip mertens Date: Thu, 21 Jul 2022 17:15:12 +0200 Subject: [PATCH 1/3] add markers --- apps/remix-ide/src/app/editor/editor.js | 11 +++- .../editor/src/lib/remix-ui-editor.tsx | 61 +++++++++++++++++++ package.json | 2 +- yarn.lock | 18 +++--- 4 files changed, 81 insertions(+), 11 deletions(-) diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 97dc63f988..5c770a242c 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -13,7 +13,7 @@ const profile = { name: 'editor', description: 'service - editor', version: packageJson.version, - methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition'] + methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition','addErrorMarker', 'clearErrorMarkers'], } class Editor extends Plugin { @@ -504,6 +504,15 @@ class Editor extends Plugin { } } + // error markers + async addErrorMarker (error){ + this.api.addErrorMarker(error) + } + + async clearErrorMarkers(sources){ + this.api.clearErrorMarkers(sources) + } + /** * Clears all the annotations for the given @arg filePath, the plugin name is retrieved from the context, if none is given, the current sesssion is used. * An annotation has the following shape: diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index 46a5e76d9b..1b1863a4da 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -7,6 +7,8 @@ import { cairoLang, cairoConf } from './cairoSyntax' import './remix-ui-editor.css' import { loadTypes } from './web-types' +import monaco from '../types/monaco' +import { MarkerSeverity } from 'monaco-editor' type cursorPosition = { startLineNumber: number, @@ -70,6 +72,8 @@ export interface EditorUIProps { addDecoration: (marker: sourceMarker, filePath: string, typeOfDecoration: string) => DecorationsReturn clearDecorationsByPlugin: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn + addErrorMarker: (errors: []) => void + clearErrorMarkers: (sources: any) => void } } @@ -346,6 +350,63 @@ export const EditorUI = (props: EditorUIProps) => { return addDecoration(marker, filePath, typeOfDecoration) } + props.editorAPI.addErrorMarker = async (errors: []) => { + + const allMarkersPerfile: Record> = {} + console.log(errors) + for (const error of errors) { + const marker = (error as any).error + const lineColumn = (error as any).lineColumn + let filePath = marker.sourceLocation.file + + if (!filePath) return + const fileFromUrl = await props.plugin.call('fileManager', 'getPathFromUrl', filePath) + filePath = fileFromUrl.file + const model = editorModelsState[filePath]?.model + console.log(filePath) + const errorServerityMap = { + 'error': MarkerSeverity.Error, + 'warning': MarkerSeverity.Warning, + 'info': MarkerSeverity.Info + } + if (model) { + const markerData: monaco.editor.IMarkerData = { + severity: errorServerityMap[marker.severity], + startLineNumber: ((lineColumn.start && lineColumn.start.line) || 0) + 1, + startColumn: ((lineColumn.start && lineColumn.start.column) || 0) + 1, + endLineNumber: ((lineColumn.end && lineColumn.end.line) || 0) + 1, + endColumn: ((lineColumn.end && lineColumn.end.column) || 0) + 1, + message: marker.message, + } + console.log(markerData) + if (!allMarkersPerfile[filePath]) { + allMarkersPerfile[filePath] = [] + } + allMarkersPerfile[filePath].push(markerData) + } + } + console.log(allMarkersPerfile) + for (const filePath in allMarkersPerfile) { + const model = editorModelsState[filePath]?.model + if (model) { + console.log(model) + monacoRef.current.editor.setModelMarkers(model, 'remix-solidity', allMarkersPerfile[filePath]) + } + } + } + + props.editorAPI.clearErrorMarkers = async (sources: any) => { + if (sources) { + for (const source of (Array.isArray(sources) ? sources : Object.keys(sources))) { + const filePath = source + const model = editorModelsState[filePath]?.model + if (model) { + monacoRef.current.editor.setModelMarkers(model, 'remix-solidity', []) + } + } + } + } + props.editorAPI.findMatches = (uri: string, value: string) => { if (!editorRef.current) return const model = editorModelsState[uri]?.model diff --git a/package.json b/package.json index 6faf18ebd2..9bef6c44d1 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "@ethereumjs/vm": "^5.5.3", "@ethersphere/bee-js": "^3.2.0", "@isomorphic-git/lightning-fs": "^4.4.1", - "@monaco-editor/react": "^4.3.1", + "@monaco-editor/react": "4.4.5", "@remixproject/engine": "^0.3.31", "@remixproject/engine-web": "^0.3.31", "@remixproject/plugin": "^0.3.31", diff --git a/yarn.lock b/yarn.lock index 6bac163214..655bc648b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3242,19 +3242,19 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" -"@monaco-editor/loader@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.2.0.tgz#373fad69973384624e3d9b60eefd786461a76acd" - integrity sha512-cJVCG/T/KxXgzYnjKqyAgsKDbH9mGLjcXxN6AmwumBwa2rVFkwvGcUj1RJtD0ko4XqLqJxwqsN/Z/KURB5f1OQ== +"@monaco-editor/loader@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.2.tgz#04effbb87052d19cd7d3c9d81c0635490f9bb6d8" + integrity sha512-BTDbpHl3e47r3AAtpfVFTlAi7WXv4UQ/xZmz8atKl4q7epQV5e7+JbigFDViWF71VBi4IIBdcWP57Hj+OWuc9g== dependencies: state-local "^1.0.6" -"@monaco-editor/react@^4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.3.1.tgz#d65bcbf174c39b6d4e7fec43d0cddda82b70a12a" - integrity sha512-f+0BK1PP/W5I50hHHmwf11+Ea92E5H1VZXs+wvKplWUWOfyMa1VVwqkJrXjRvbcqHL+XdIGYWhWNdi4McEvnZg== +"@monaco-editor/react@4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.4.5.tgz#beabe491efeb2457441a00d1c7651c653697f65b" + integrity sha512-IImtzU7sRc66OOaQVCG+5PFHkSWnnhrUWGBuH6zNmH2h0YgmAhcjHZQc/6MY9JWEbUtVF1WPBMJ9u1XuFbRrVA== dependencies: - "@monaco-editor/loader" "^1.2.0" + "@monaco-editor/loader" "^1.3.2" prop-types "^15.7.2" "@mrmlnc/readdir-enhanced@^2.2.1": From 12e40331ebb2871bb44668c92c9e3384dd34b6a0 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Tue, 9 Aug 2022 13:27:25 +0200 Subject: [PATCH 2/3] add e2e --- .../src/tests/editor_error_marker.test.ts | 84 +++++++++++++++++++ .../editor/src/lib/remix-ui-editor.tsx | 79 +++++++++-------- 2 files changed, 129 insertions(+), 34 deletions(-) create mode 100644 apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts diff --git a/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts b/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts new file mode 100644 index 0000000000..8d8cba54e0 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts @@ -0,0 +1,84 @@ +'use strict' + +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +module.exports = { + + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done, 'http://127.0.0.1:8080', true) + }, + 'Should add error marker': function (browser: NightwatchBrowser) { + browser + .openFile('contracts') + .openFile('contracts/1_Storage.sol') + .addFile('scripts/adderror.ts', {content: addErrorMarker}) + .pause(4000) + .executeScriptInTerminal('remix.exeCurrent()') + .pause(4000) + .openFile('contracts/1_Storage.sol') + .useXpath() + .waitForElementVisible("//*[@class='cdr squiggly-error']") + .waitForElementVisible("//*[@class='cdr squiggly-warning']") + }, + 'Should clear error marker': function (browser: NightwatchBrowser) { + browser + .useCss() + .addFile('scripts/clear.ts', {content: clearMarkers}) + .pause(4000) + .executeScriptInTerminal('remix.exeCurrent()') + .pause(4000) + .openFile('contracts/1_Storage.sol') + .useXpath() + .waitForElementNotPresent("//*[@class='cdr squiggly-error']") + .waitForElementNotPresent("//*[@class='cdr squiggly-warning']") + } +} + +const clearMarkers =` +(async () => { + await remix.call('editor', 'clearErrorMarkers' as any, ['contracts/1_Storage.sol']) +})()` + +const addErrorMarker = ` +(async () => { + + + let errors = [ + { + position: { + start: { + line: 10, + column: 1, + }, + end: { + line: 10, + column: 10 + } + }, + message: 'testing', + severity: 'error', + file: 'contracts/1_Storage.sol' + }, + { + position: { + start: { + line: 18, + column: 1, + }, + end: { + line: 18, + column: 10 + } + }, + message: 'testing2', + severity: 'warning', + file: 'contracts/1_Storage.sol' + }, + ] + + + await remix.call('editor', 'addErrorMarker' as any, errors) + + +})()` \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index d76f21c45b..ec85113640 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -42,6 +42,22 @@ type sourceMarker = { hide: boolean } +type errorMarker = { + message: string + severity: MarkerSeverity + position: { + start: { + line: number + column: number + }, + end: { + line: number + column: number + } + }, + file: string +} + loader.config({ paths: { vs: 'assets/js/monaco-editor/dev/vs' } }) export type DecorationsReturn = { @@ -104,7 +120,7 @@ export const EditorUI = (props: EditorUIProps) => { const currentFileRef = useRef('') // const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor // const registeredDecorations = useRef({}) // registered decorations - + const [editorModelsState, dispatch] = useReducer(reducerActions, initialState) const formatColor = (name) => { @@ -262,7 +278,7 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.editor.setModelLanguage(file.model, 'remix-cairo') } else if (file.language === 'zokrates') { monacoRef.current.editor.setModelLanguage(file.model, 'remix-zokrates') - } + } }, [props.currentFile]) const convertToMonacoDecoration = (decoration: sourceAnnotation | sourceMarker, typeOfDecoration: string) => { @@ -318,7 +334,7 @@ export const EditorUI = (props: EditorUIProps) => { registeredDecorations: newRegisteredDecorations } } - + props.editorAPI.keepDecorationsFor = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => { const model = editorModelsState[filePath]?.model if (!model) return { @@ -347,25 +363,22 @@ export const EditorUI = (props: EditorUIProps) => { registeredDecorations: [{ value: decoration, type: typeOfDecoration }] } } - + props.editorAPI.addDecoration = (marker: sourceMarker, filePath: string, typeOfDecoration: string) => { return addDecoration(marker, filePath, typeOfDecoration) } - props.editorAPI.addErrorMarker = async (errors: []) => { + props.editorAPI.addErrorMarker = async (errors: errorMarker[]) => { const allMarkersPerfile: Record> = {} - console.log(errors) + for (const error of errors) { - const marker = (error as any).error - const lineColumn = (error as any).lineColumn - let filePath = marker.sourceLocation.file + let filePath = error.file if (!filePath) return const fileFromUrl = await props.plugin.call('fileManager', 'getPathFromUrl', filePath) filePath = fileFromUrl.file const model = editorModelsState[filePath]?.model - console.log(filePath) const errorServerityMap = { 'error': MarkerSeverity.Error, 'warning': MarkerSeverity.Warning, @@ -373,14 +386,13 @@ export const EditorUI = (props: EditorUIProps) => { } if (model) { const markerData: monaco.editor.IMarkerData = { - severity: errorServerityMap[marker.severity], - startLineNumber: ((lineColumn.start && lineColumn.start.line) || 0) + 1, - startColumn: ((lineColumn.start && lineColumn.start.column) || 0) + 1, - endLineNumber: ((lineColumn.end && lineColumn.end.line) || 0) + 1, - endColumn: ((lineColumn.end && lineColumn.end.column) || 0) + 1, - message: marker.message, + severity: errorServerityMap[error.severity], + startLineNumber: ((error.position.start && error.position.start.line) || 0), + startColumn: ((error.position.start && error.position.start.column) || 0), + endLineNumber: ((error.position.end && error.position.end.line) || 0), + endColumn: ((error.position.end && error.position.end.column) || 0), + message: error.message, } - console.log(markerData) if (!allMarkersPerfile[filePath]) { allMarkersPerfile[filePath] = [] } @@ -391,7 +403,6 @@ export const EditorUI = (props: EditorUIProps) => { for (const filePath in allMarkersPerfile) { const model = editorModelsState[filePath]?.model if (model) { - console.log(model) monacoRef.current.editor.setModelMarkers(model, 'remix-solidity', allMarkersPerfile[filePath]) } } @@ -463,7 +474,7 @@ export const EditorUI = (props: EditorUIProps) => { } } - function handleEditorDidMount (editor) { + function handleEditorDidMount(editor) { editorRef.current = editor defineAndSetTheme(monacoRef.current) reducerListener(props.plugin, dispatch, monacoRef.current, editorRef.current, props.events) @@ -481,7 +492,7 @@ export const EditorUI = (props: EditorUIProps) => { editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_MINUS, () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) }) - + // add context menu items const zoominAction = { id: "zoomIn", @@ -511,25 +522,25 @@ export const EditorUI = (props: EditorUIProps) => { const editorService = editor._codeEditorService; const openEditorBase = editorService.openCodeEditor.bind(editorService); editorService.openCodeEditor = async (input, source) => { - const result = await openEditorBase(input, source) - if (input && input.resource && input.resource.path) { - try { - await props.plugin.call('fileManager', 'open', input.resource.path) - } catch (e) { - console.log(e) - } + const result = await openEditorBase(input, source) + if (input && input.resource && input.resource.path) { + try { + await props.plugin.call('fileManager', 'open', input.resource.path) + } catch (e) { + console.log(e) } - return result + } + return result } } - function handleEditorWillMount (monaco) { + function handleEditorWillMount(monaco) { monacoRef.current = monaco // Register a new language monacoRef.current.languages.register({ id: 'remix-solidity' }) monacoRef.current.languages.register({ id: 'remix-cairo' }) monacoRef.current.languages.register({ id: 'remix-zokrates' }) - + // Register a tokens provider for the language monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider) monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig) @@ -551,7 +562,7 @@ export const EditorUI = (props: EditorUIProps) => { language={editorModelsState[props.currentFile] ? editorModelsState[props.currentFile].language : 'text'} onMount={handleEditorDidMount} beforeMount={handleEditorWillMount} - options={{ glyphMargin: true, readOnly: true}} + options={{ glyphMargin: true, readOnly: true }} defaultValue={defaultEditorValue} />
@@ -559,9 +570,9 @@ export const EditorUI = (props: EditorUIProps) => { hide={false} gotoLine={(line, column) => props.plugin.call('editor', 'gotoLine', line, column)} openFile={(file) => props.plugin.call('fileManager', 'switchFile', file)} - getLastCompilationResult={() => { return props.plugin.call('compilerArtefacts', 'getLastCompilationResult') } } - offsetToLineColumn={(position, file, sources, asts) => { return props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, file, sources, asts) } } - getCurrentFileName={() => { return props.plugin.call('fileManager', 'file') } } + getLastCompilationResult={() => { return props.plugin.call('compilerArtefacts', 'getLastCompilationResult') }} + offsetToLineColumn={(position, file, sources, asts) => { return props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, file, sources, asts) }} + getCurrentFileName={() => { return props.plugin.call('fileManager', 'file') }} onContextListenerChanged={(listener) => { props.plugin.on('contextualListener', 'contextChanged', listener) }} onCurrentFileChanged={(listener) => { props.plugin.on('fileManager', 'currentFileChanged', listener) }} referencesOf={(node: astNode) => { return props.plugin.call('contextualListener', 'referencesOf', node) }} From 7dff0ad4c7a054171a5556b1e7c4192f41d6c819 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 11 Aug 2022 08:51:21 +0200 Subject: [PATCH 3/3] types --- libs/remix-ui/editor/src/lib/remix-ui-editor.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index cefd299950..f52c916aec 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -109,7 +109,7 @@ export interface EditorUIProps { clearDecorationsByPlugin: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn addErrorMarker: (errors: []) => void - clearErrorMarkers: (sources: any) => void + clearErrorMarkers: (sources: string[] | {[fileName: string]: any}) => void } } @@ -440,7 +440,7 @@ export const EditorUI = (props: EditorUIProps) => { } } - props.editorAPI.clearErrorMarkers = async (sources: any) => { + props.editorAPI.clearErrorMarkers = async (sources: string[] | {[fileName: string]: any}) => { if (sources) { for (const source of (Array.isArray(sources) ? sources : Object.keys(sources))) { const filePath = source